Compare commits
221 Commits
2bcf76618a
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 1822004460 | |||
| b9345f9cb5 | |||
| 166c68570d | |||
| 28fc8b3b86 | |||
| 2c7a18c374 | |||
| 0a3f6c5fab | |||
| ca29051dc2 | |||
| 0b51b52b84 | |||
| 4659942800 | |||
| 6d0f594bba | |||
| 4ca73cc3ca | |||
| 1f96facbf2 | |||
| e362cef9ba | |||
| 25d94bbfc2 | |||
| 40e3703650 | |||
| 21eeb9db72 | |||
| 2cbe5f85b0 | |||
| b62d45d0d8 | |||
| 0dc792b0bf | |||
| 5f4209dbe8 | |||
| fbba67a9bd | |||
| 5cd296cb70 | |||
| 7c0f6eb603 | |||
| de3dcc48ab | |||
| 958091e000 | |||
| 35583e404e | |||
| 0e9342fab6 | |||
| ca4182d798 | |||
| 7d04c46728 | |||
| 4490ed3e7c | |||
| 30c1192e27 | |||
| 6e30705810 | |||
| d7c9105995 | |||
| a732eb2a61 | |||
| c29d53a33b | |||
| 3fd8e8c630 | |||
| 950c358ae5 | |||
| f81199e31b | |||
| bf1fefd1bc | |||
| c51a6782d9 | |||
| 795447e029 | |||
| 7caf25c4d0 | |||
| 3fa1de5e4e | |||
| f86da2ed6f | |||
| 282a07c5fb | |||
| bf6ed14b5e | |||
| 7d35133e43 | |||
| c505012b00 | |||
| 4f54124519 | |||
| 18e8eac2ab | |||
| 5abf07d0c7 | |||
| 48ffdf560b | |||
| e0392f2483 | |||
| 15a5017c86 | |||
| dd5caa4056 | |||
| 360ddbe6d2 | |||
| b3b886f0b3 | |||
| 93aa007c14 | |||
| 4e32f4a60d | |||
| 2759880a53 | |||
| 7bc1a852db | |||
| 00119f9723 | |||
| ff2a0e867d | |||
| 1fae85ff9d | |||
| 10fe82ab6a | |||
| 9812a92d59 | |||
| e8b12c307a | |||
| ae00ac8672 | |||
| bf92201721 | |||
| 1f2e3d41dd | |||
| f31b6da49c | |||
| f32d153b69 | |||
| 15d9ade2cf | |||
| 4587c35806 | |||
| 340614dadc | |||
| 8a0fef63b2 | |||
| 8691d19e8d | |||
| 35d196bdc2 | |||
| e2c3cb7035 | |||
| a69b3e4226 | |||
| 36285b060b | |||
| a0a9e84b91 | |||
| 01d9f9a21c | |||
| 621a777a46 | |||
| 6fbecd78a4 | |||
| 873b86a809 | |||
| 47393a5ead | |||
| 69c56ae31a | |||
| b2cdf60a32 | |||
| 91f45965a0 | |||
| 3097cb69aa | |||
| eed17d61a3 | |||
| 9e5a4069a4 | |||
| f8c20541d5 | |||
| ee07312b86 | |||
| e4eabf8aa6 | |||
| 3ec0f0ff7b | |||
| 27b32fc8f4 | |||
| ceab408ec0 | |||
| b56b618a8c | |||
| 5631ae386f | |||
| c31c556df5 | |||
| 5fae0c4fa9 | |||
| 144731062b | |||
| 72f3e03edd | |||
| b08693e866 | |||
| ae2360a480 | |||
| 213265a5d0 | |||
| dfb6b23e70 | |||
| c81a7a6160 | |||
| bbb710a5e1 | |||
| 7eaa8eacf8 | |||
| d78e361c29 | |||
| 1ae6c02472 | |||
| 11206854ad | |||
| 33089e6fbd | |||
| 5cec47efcc | |||
| cabb4daded | |||
| 1e61a76ded | |||
| ac3e9f14cd | |||
| 5ee9351362 | |||
| 2964e06639 | |||
| d1a0fc0222 | |||
| 4a82174526 | |||
| 3ae20255f6 | |||
| f69c53bf29 | |||
| 7d6c098047 | |||
| 3421e18f21 | |||
| 0dec27a01c | |||
| c45cf6ab51 | |||
| e50667b392 | |||
| d4db64f404 | |||
| 05858078d1 | |||
| f75f0378bf | |||
| c3d2fb32e2 | |||
| 1fe30fc7e7 | |||
| 43cc151138 | |||
| 1fee4f276d | |||
| 0b17f36fde | |||
| aca92fdae1 | |||
| c7bf62062a | |||
| b853ca6fe4 | |||
| d5919fc82a | |||
| e2395989f4 | |||
| e93868a483 | |||
| 7f6fc24872 | |||
| 776ec0d30d | |||
| 6861740d00 | |||
| 77e72d18e1 | |||
| 1792d94082 | |||
| 16d17bcadb | |||
| f5d1361d59 | |||
| c4932dd6cc | |||
| 031a30ed5f | |||
| cc1fe1c3b7 | |||
| 72e1cee9d5 | |||
| a90ff753b1 | |||
| b6010cd431 | |||
| 33bfb23405 | |||
| ad4f8492d4 | |||
| e6cc0959a4 | |||
| 5bce31baf2 | |||
| c7fee27623 | |||
| f8499a79c2 | |||
| b97ee3f3bb | |||
| beb9b0c14a | |||
| c874426f2e | |||
| c77cf2cb78 | |||
| 02e95ae916 | |||
| 0f0838ae5a | |||
| 47e5a093b4 | |||
| a0ba8ba9f3 | |||
| c0a0548662 | |||
| 2448c93fa7 | |||
| e720761106 | |||
| 450dc26df7 | |||
| 7a32edb6e3 | |||
| 6160174c5d | |||
| d972cc2c3b | |||
| 6795508d3e | |||
| b5af621ee2 | |||
| a4fbdeabe3 | |||
| 3a71e58e22 | |||
| ef9cef8915 | |||
| ee5a6e673b | |||
| 721ce33adb | |||
| e1d0c6162d | |||
| 435eeb9601 | |||
| 95eb1fea4c | |||
| 03eafcdf4c | |||
| 38f92d83c3 | |||
| 6c40ae6311 | |||
| 5cc79ed54d | |||
| b70b0846a1 | |||
| de61253db6 | |||
| c93ac10288 | |||
| 14f5f51d1e | |||
| 79b12ac4b7 | |||
| 8430e828e9 | |||
| 9ca0e9ca48 | |||
| 67e99df83e | |||
| d3ab78b17d | |||
| ce89b584cb | |||
| cfe2080509 | |||
| 9c5fa5366f | |||
| a0be5c699a | |||
| 81b12b5d6f | |||
| b3d5c70c8b | |||
| be17e2f146 | |||
| 80fae88228 | |||
| d93a8df199 | |||
| f0bad17e89 | |||
| 5ca4bf707b | |||
| 8b2780820c | |||
| 8639c785b7 | |||
| 84a047fb0c | |||
| b2b41f1aa1 | |||
| fff6b0f5dd | |||
| c51c0afcf2 | |||
| 88b52df610 | |||
| 0da7ec74e8 |
@@ -1 +1,7 @@
|
|||||||
*.sw*
|
*.sw*
|
||||||
|
.bundle
|
||||||
|
*.db
|
||||||
|
.env.rb
|
||||||
|
coverage
|
||||||
|
vendor
|
||||||
|
.ruby-version
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
AllCops:
|
||||||
|
NewCops: enable
|
||||||
|
SuggestExtensions: false
|
||||||
|
|
||||||
|
Metrics/BlockLength:
|
||||||
|
AllowedMethods: ['describe', 'context', 'route', 'r.on', 'r.post']
|
||||||
@@ -1,27 +1,35 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
source "https://rubygems.org"
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
gem "falcon", "~> 0.42.3"
|
gem 'rackup', '~> 2.1'
|
||||||
|
|
||||||
gem "roda", "~> 3.72"
|
gem 'falcon', '~> 0.42.3'
|
||||||
|
|
||||||
gem "sequel", "~> 5.72"
|
gem 'roda', '~> 3.72'
|
||||||
|
|
||||||
gem "tilt", "~> 2.2"
|
gem 'sequel', '~> 5.72'
|
||||||
|
|
||||||
gem "sqlite3", "~> 1.6"
|
gem 'tilt', '~> 2.2'
|
||||||
|
|
||||||
gem "erubi", "~> 1.12"
|
gem 'sqlite3', '~> 1.6'
|
||||||
|
|
||||||
group :development do
|
gem 'erubi', '~> 1.12'
|
||||||
|
|
||||||
gem "cucumber", "~> 9.0"
|
group :test do
|
||||||
|
gem 'cucumber', '~> 9.0'
|
||||||
|
|
||||||
gem "capybara", "~> 3.39"
|
gem 'capybara', '~> 3.39'
|
||||||
|
|
||||||
gem "rspec", "~> 3.12"
|
gem 'rspec', '~> 3.12'
|
||||||
|
|
||||||
gem "selenium-webdriver", "~> 4.13"
|
gem 'selenium-webdriver', '~> 4.13'
|
||||||
|
|
||||||
|
gem 'simplecov'
|
||||||
|
|
||||||
|
gem 'rubocop', require: false
|
||||||
|
|
||||||
|
gem 'ruby_audit', '~> 2.2'
|
||||||
|
|
||||||
|
gem 'bundle-audit', '~> 0.1.0'
|
||||||
end
|
end
|
||||||
|
|||||||
+57
-8
@@ -3,7 +3,8 @@ GEM
|
|||||||
specs:
|
specs:
|
||||||
addressable (2.8.5)
|
addressable (2.8.5)
|
||||||
public_suffix (>= 2.0.2, < 6.0)
|
public_suffix (>= 2.0.2, < 6.0)
|
||||||
async (2.6.4)
|
ast (2.4.2)
|
||||||
|
async (2.6.5)
|
||||||
console (~> 1.10)
|
console (~> 1.10)
|
||||||
fiber-annotation
|
fiber-annotation
|
||||||
io-event (~> 1.1)
|
io-event (~> 1.1)
|
||||||
@@ -11,23 +12,29 @@ GEM
|
|||||||
async-container (0.16.12)
|
async-container (0.16.12)
|
||||||
async
|
async
|
||||||
async-io
|
async-io
|
||||||
async-http (0.60.2)
|
async-http (0.61.0)
|
||||||
async (>= 1.25)
|
async (>= 1.25)
|
||||||
async-io (>= 1.28)
|
async-io (>= 1.28)
|
||||||
async-pool (>= 0.2)
|
async-pool (>= 0.2)
|
||||||
protocol-http (~> 0.24.0)
|
protocol-http (~> 0.25.0)
|
||||||
protocol-http1 (~> 0.15.0)
|
protocol-http1 (~> 0.16.0)
|
||||||
protocol-http2 (~> 0.15.0)
|
protocol-http2 (~> 0.15.0)
|
||||||
traces (>= 0.10.0)
|
traces (>= 0.10.0)
|
||||||
async-http-cache (0.4.3)
|
async-http-cache (0.4.3)
|
||||||
async-http (~> 0.56)
|
async-http (~> 0.56)
|
||||||
async-io (1.36.0)
|
async-io (1.37.0)
|
||||||
async
|
async
|
||||||
async-pool (0.4.0)
|
async-pool (0.4.0)
|
||||||
async (>= 1.25)
|
async (>= 1.25)
|
||||||
|
base64 (0.1.1)
|
||||||
bigdecimal (3.1.4)
|
bigdecimal (3.1.4)
|
||||||
build-environment (1.13.0)
|
build-environment (1.13.0)
|
||||||
builder (3.2.4)
|
builder (3.2.4)
|
||||||
|
bundle-audit (0.1.0)
|
||||||
|
bundler-audit
|
||||||
|
bundler-audit (0.9.1)
|
||||||
|
bundler (>= 1.2.0, < 3)
|
||||||
|
thor (~> 1.0)
|
||||||
capybara (3.39.2)
|
capybara (3.39.2)
|
||||||
addressable
|
addressable
|
||||||
matrix
|
matrix
|
||||||
@@ -65,6 +72,7 @@ GEM
|
|||||||
cucumber-messages (21.0.1)
|
cucumber-messages (21.0.1)
|
||||||
cucumber-tag-expressions (4.1.0)
|
cucumber-tag-expressions (4.1.0)
|
||||||
diff-lcs (1.5.0)
|
diff-lcs (1.5.0)
|
||||||
|
docile (1.4.0)
|
||||||
erubi (1.12.0)
|
erubi (1.12.0)
|
||||||
falcon (0.42.3)
|
falcon (0.42.3)
|
||||||
async
|
async
|
||||||
@@ -82,7 +90,9 @@ GEM
|
|||||||
ffi (1.16.2)
|
ffi (1.16.2)
|
||||||
fiber-annotation (0.2.0)
|
fiber-annotation (0.2.0)
|
||||||
fiber-local (1.0.0)
|
fiber-local (1.0.0)
|
||||||
io-event (1.3.2)
|
io-event (1.3.3)
|
||||||
|
json (2.6.3)
|
||||||
|
language_server-protocol (3.17.0.3)
|
||||||
localhost (1.1.10)
|
localhost (1.1.10)
|
||||||
mapping (1.1.1)
|
mapping (1.1.1)
|
||||||
matrix (0.4.2)
|
matrix (0.4.2)
|
||||||
@@ -93,12 +103,16 @@ GEM
|
|||||||
mini_portile2 (~> 2.8.2)
|
mini_portile2 (~> 2.8.2)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
openssl (3.2.0)
|
openssl (3.2.0)
|
||||||
|
parallel (1.23.0)
|
||||||
|
parser (3.2.2.4)
|
||||||
|
ast (~> 2.4.1)
|
||||||
|
racc
|
||||||
process-metrics (0.2.1)
|
process-metrics (0.2.1)
|
||||||
console (~> 1.8)
|
console (~> 1.8)
|
||||||
samovar (~> 2.1)
|
samovar (~> 2.1)
|
||||||
protocol-hpack (1.4.2)
|
protocol-hpack (1.4.2)
|
||||||
protocol-http (0.24.7)
|
protocol-http (0.25.0)
|
||||||
protocol-http1 (0.15.1)
|
protocol-http1 (0.16.0)
|
||||||
protocol-http (~> 0.22)
|
protocol-http (~> 0.22)
|
||||||
protocol-http2 (0.15.1)
|
protocol-http2 (0.15.1)
|
||||||
protocol-hpack (~> 1.4)
|
protocol-hpack (~> 1.4)
|
||||||
@@ -111,6 +125,10 @@ GEM
|
|||||||
rack (3.0.8)
|
rack (3.0.8)
|
||||||
rack-test (2.1.0)
|
rack-test (2.1.0)
|
||||||
rack (>= 1.3)
|
rack (>= 1.3)
|
||||||
|
rackup (2.1.0)
|
||||||
|
rack (>= 3)
|
||||||
|
webrick (~> 1.8)
|
||||||
|
rainbow (3.1.1)
|
||||||
regexp_parser (2.8.1)
|
regexp_parser (2.8.1)
|
||||||
rexml (3.2.6)
|
rexml (3.2.6)
|
||||||
roda (3.72.0)
|
roda (3.72.0)
|
||||||
@@ -128,6 +146,23 @@ GEM
|
|||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
rspec-support (~> 3.12.0)
|
rspec-support (~> 3.12.0)
|
||||||
rspec-support (3.12.1)
|
rspec-support (3.12.1)
|
||||||
|
rubocop (1.57.1)
|
||||||
|
base64 (~> 0.1.1)
|
||||||
|
json (~> 2.3)
|
||||||
|
language_server-protocol (>= 3.17.0)
|
||||||
|
parallel (~> 1.10)
|
||||||
|
parser (>= 3.2.2.4)
|
||||||
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
|
regexp_parser (>= 1.8, < 3.0)
|
||||||
|
rexml (>= 3.2.5, < 4.0)
|
||||||
|
rubocop-ast (>= 1.28.1, < 2.0)
|
||||||
|
ruby-progressbar (~> 1.7)
|
||||||
|
unicode-display_width (>= 2.4.0, < 3.0)
|
||||||
|
rubocop-ast (1.29.0)
|
||||||
|
parser (>= 3.2.1.0)
|
||||||
|
ruby-progressbar (1.13.0)
|
||||||
|
ruby_audit (2.2.0)
|
||||||
|
bundler-audit (~> 0.9.0)
|
||||||
rubyzip (2.3.2)
|
rubyzip (2.3.2)
|
||||||
samovar (2.2.0)
|
samovar (2.2.0)
|
||||||
console (~> 1.0)
|
console (~> 1.0)
|
||||||
@@ -138,13 +173,22 @@ GEM
|
|||||||
websocket (~> 1.0)
|
websocket (~> 1.0)
|
||||||
sequel (5.73.0)
|
sequel (5.73.0)
|
||||||
bigdecimal
|
bigdecimal
|
||||||
|
simplecov (0.22.0)
|
||||||
|
docile (~> 1.1)
|
||||||
|
simplecov-html (~> 0.11)
|
||||||
|
simplecov_json_formatter (~> 0.1)
|
||||||
|
simplecov-html (0.12.3)
|
||||||
|
simplecov_json_formatter (0.1.4)
|
||||||
sqlite3 (1.6.6)
|
sqlite3 (1.6.6)
|
||||||
mini_portile2 (~> 2.8.0)
|
mini_portile2 (~> 2.8.0)
|
||||||
sys-uname (1.2.3)
|
sys-uname (1.2.3)
|
||||||
ffi (~> 1.1)
|
ffi (~> 1.1)
|
||||||
|
thor (1.3.0)
|
||||||
tilt (2.3.0)
|
tilt (2.3.0)
|
||||||
timers (4.3.5)
|
timers (4.3.5)
|
||||||
traces (0.11.1)
|
traces (0.11.1)
|
||||||
|
unicode-display_width (2.5.0)
|
||||||
|
webrick (1.8.1)
|
||||||
websocket (1.2.10)
|
websocket (1.2.10)
|
||||||
xpath (3.2.0)
|
xpath (3.2.0)
|
||||||
nokogiri (~> 1.8)
|
nokogiri (~> 1.8)
|
||||||
@@ -153,14 +197,19 @@ PLATFORMS
|
|||||||
x86_64-freebsd-13
|
x86_64-freebsd-13
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
|
bundle-audit (~> 0.1.0)
|
||||||
capybara (~> 3.39)
|
capybara (~> 3.39)
|
||||||
cucumber (~> 9.0)
|
cucumber (~> 9.0)
|
||||||
erubi (~> 1.12)
|
erubi (~> 1.12)
|
||||||
falcon (~> 0.42.3)
|
falcon (~> 0.42.3)
|
||||||
|
rackup (~> 2.1)
|
||||||
roda (~> 3.72)
|
roda (~> 3.72)
|
||||||
rspec (~> 3.12)
|
rspec (~> 3.12)
|
||||||
|
rubocop
|
||||||
|
ruby_audit (~> 2.2)
|
||||||
selenium-webdriver (~> 4.13)
|
selenium-webdriver (~> 4.13)
|
||||||
sequel (~> 5.72)
|
sequel (~> 5.72)
|
||||||
|
simplecov
|
||||||
sqlite3 (~> 1.6)
|
sqlite3 (~> 1.6)
|
||||||
tilt (~> 2.2)
|
tilt (~> 2.2)
|
||||||
|
|
||||||
|
|||||||
Vendored
+169
@@ -0,0 +1,169 @@
|
|||||||
|
def buildArtifact = true
|
||||||
|
|
||||||
|
pipeline {
|
||||||
|
agent { label 'ruby && freebsd' }
|
||||||
|
|
||||||
|
environment {
|
||||||
|
DB_NAME = 'url_shortener.db'
|
||||||
|
}
|
||||||
|
stages {
|
||||||
|
stage('Init') {
|
||||||
|
steps {
|
||||||
|
sh ''' #!/usr/local/bin/bash
|
||||||
|
rbenv local 3.2.2
|
||||||
|
echo "# frozen_string_literal: true\n" > .env.rb
|
||||||
|
echo "ENV['APP_SESSION_SECRET'] ||= '$(ruby -rsecurerandom -e 'puts SecureRandom.base64(64)')'" >> .env.rb
|
||||||
|
echo "ENV['DB_NAME'] ||= '${DB_NAME}'" >> .env.rb
|
||||||
|
cat .env.rb
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Build dependencies') {
|
||||||
|
steps {
|
||||||
|
sh 'bundle install'
|
||||||
|
sh 'sequel -m db/migrations sqlite://db/${DB_NAME}'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Audit Dependencies') {
|
||||||
|
steps {
|
||||||
|
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
|
||||||
|
script {
|
||||||
|
try {
|
||||||
|
sh 'bundle exec ruby-audit check'
|
||||||
|
sh 'bundle exec bundle-audit check >> audit.html'
|
||||||
|
} catch (e) {
|
||||||
|
script {
|
||||||
|
buildArtifact = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Code Linting') {
|
||||||
|
steps {
|
||||||
|
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
|
||||||
|
script {
|
||||||
|
try {
|
||||||
|
sh 'bundle exec rubocop --format html --out rubocop.html'
|
||||||
|
} catch (e) {
|
||||||
|
script {
|
||||||
|
buildArtifact = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Run tests') {
|
||||||
|
steps {
|
||||||
|
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
|
||||||
|
script {
|
||||||
|
try {
|
||||||
|
sh 'bundle exec cucumber features --format html --out cucumber.html'
|
||||||
|
} catch (e) {
|
||||||
|
script {
|
||||||
|
buildArtifact = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
|
||||||
|
script {
|
||||||
|
try {
|
||||||
|
sh 'bundle exec rspec spec --format html --out spec.html'
|
||||||
|
} catch (e) {
|
||||||
|
script {
|
||||||
|
buildArtifact = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Report results') {
|
||||||
|
steps {
|
||||||
|
publishHTML (target: [
|
||||||
|
allowMissing: false,
|
||||||
|
alwaysLinkToLastBuild: false,
|
||||||
|
keepAll: true,
|
||||||
|
reportDir: '.',
|
||||||
|
reportFiles: 'rubocop.html, audit.html, cucumber.html, spec.html, coverage/index.html',
|
||||||
|
reportName: 'Test Results',
|
||||||
|
reportTitles: 'Rubocop Results, Bundler Audit Results, Cucumber Results, RSpec Results, Test Coverage'])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Build production deliverable') {
|
||||||
|
when {
|
||||||
|
expression {
|
||||||
|
buildArtifact
|
||||||
|
}
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
|
||||||
|
sh ''' #!/usr/local/bin/bash
|
||||||
|
ZIP_FILE="url-shortener_${BRANCH_NAME}_$(cat VERSION | cut -d"'" -f2).tgz"
|
||||||
|
CUR_DIR=$(pwd)
|
||||||
|
bundle config set --local without 'test'
|
||||||
|
bundle config set --local path "vendor"
|
||||||
|
bundle install
|
||||||
|
mkdir -p /tmp/url-shortener
|
||||||
|
cp -R * /tmp/url-shortener
|
||||||
|
cp .env.rb /tmp/url-shortener/
|
||||||
|
cp .ruby-version /tmp/url-shortener/
|
||||||
|
cd /tmp/url-shortener
|
||||||
|
rm -rf features spec coverage db/*.db .git* Jenkinsfile *.html .rubocop.yml
|
||||||
|
cd /tmp
|
||||||
|
tar -czvf $ZIP_FILE url-shortener/
|
||||||
|
mv /tmp/$ZIP_FILE $CUR_DIR/
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
archiveArtifacts artifacts: '*.tgz'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Generate SHA256 Hash and Upload to Artifact Repository') {
|
||||||
|
environment {
|
||||||
|
ARTIFACTS_KEY = credentials('artifactor-key')
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sshagent(['artifactor-key']) {
|
||||||
|
sh ''' #!/usr/local/bin/bash
|
||||||
|
UUID=$(uuidgen -r)
|
||||||
|
VERSION=$(cat VERSION | cut -d"'" -f2)
|
||||||
|
BRANCH=${BRANCH_NAME}
|
||||||
|
ZIP_FILE="url-shortener_${BRANCH_NAME}_$(cat VERSION | cut -d"'" -f2).tgz"
|
||||||
|
sha256 $ZIP_FILE >> SHA256.sig
|
||||||
|
ssh artifactor@artifacts mkdir -p projects/url-shortener/$BRANCH/$VERSION/$UUID
|
||||||
|
rsync SHA256.sig artifactor@artifacts:projects/url-shortener/$BRANCH/$VERSION/$UUID/
|
||||||
|
rsync $ZIP_FILE artifactor@artifacts:projects/url-shortener/$BRANCH/$VERSION/$UUID/
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Clean up deliverable') {
|
||||||
|
when {
|
||||||
|
expression {
|
||||||
|
buildArtifact
|
||||||
|
}
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh 'rm -rf /tmp/url-shortener'
|
||||||
|
sh 'rm SHA256.sig'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
post {
|
||||||
|
success {
|
||||||
|
mattermostSend channel: 'git-messages', color: 'good', message: "[${JOB_NAME}](${JOB_URL}) [#${BUILD_NUMBER}](${BUILD_URL}) ([Gitea](${GIT_URL}))", text: 'Build Finished Successfully'
|
||||||
|
}
|
||||||
|
unstable {
|
||||||
|
mattermostSend channel: 'git-messages', color: 'warning', message: "[${JOB_NAME}](${JOB_URL}) [#${BUILD_NUMBER}](${BUILD_URL}) ([Gitea](${GIT_URL}))", text: "Build Unstable"
|
||||||
|
}
|
||||||
|
failure {
|
||||||
|
mattermostSend channel: 'git-messages', color: 'danger', message: "[${JOB_NAME}](${JOB_URL}) [#${BUILD_NUMBER}](${BUILD_URL}) ([Gitea](${GIT_URL}))", text: "Build Failed"
|
||||||
|
}
|
||||||
|
always {
|
||||||
|
cleanWs deleteDirs: true, patterns: [[pattern: '*', type: 'INCLUDE']]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,23 +1,42 @@
|
|||||||
# simple url shortener
|
# simple url shortener
|
||||||
|
|
||||||
this project is a simple solution to build out a URL shortener using
|
this project is a simple solution to build out a URL shortener using
|
||||||
Roda, Falcon, Sequel, and SQLite
|
Roda, Puma, Sequel, and SQLite on a FreeBSD Jail
|
||||||
|
|
||||||
the point of this project is to quickly build something and work on continuous deployment while making small refinements to the functional pieces.
|
the point of this project is to quickly build something and work on continuous deployment while making small refinements to the functional pieces.
|
||||||
|
|
||||||
|
the outside pieces of software that this project relies on are rbenv, ruby-build, sqlite3, and pkgconf.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
first you have to install the dependencies:
|
first you have to install the dependencies:
|
||||||
|
|
||||||
`bundle install`
|
`bundle install`
|
||||||
|
|
||||||
if you want the development group included run this first:
|
|
||||||
|
|
||||||
`bundle config set --local with 'development'
|
if you want to skip the test dependencies:
|
||||||
|
|
||||||
after the dependencies are installed, you have to create the db
|
`bundle config set --local without 'test'`
|
||||||
|
|
||||||
`sequel -m db/migrations sqlite://db/url_shortener.db`
|
|
||||||
|
|
||||||
to start the application with Falcon:
|
then create a .env.rb file in the root directory that contains the following ENV attributes:
|
||||||
|
|
||||||
`rackup -o {ip address} -p {port} -s falcon`
|
```
|
||||||
|
ENV["APP_SESSION_SECRET"] = {output of a random 64 byte secret}
|
||||||
|
|
||||||
|
ENV["DB_NAME"] = {DB_NAME}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
after the dependencies are installed, you have to create the db and schema
|
||||||
|
|
||||||
|
`sequel -m db/migrations sqlite://db/{DB_NAME}`
|
||||||
|
|
||||||
|
|
||||||
|
to start the application with Puma:
|
||||||
|
|
||||||
|
`rackup -o {ip address} -p {port}`
|
||||||
|
|
||||||
|
to run it as a daemon:
|
||||||
|
|
||||||
|
`rackup -o {ip address} -p {port} -D`
|
||||||
|
|||||||
@@ -1,31 +1,106 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'roda'
|
require 'roda'
|
||||||
require 'securerandom'
|
require 'securerandom'
|
||||||
require 'json'
|
require 'json'
|
||||||
require 'sequel'
|
require 'sequel'
|
||||||
|
require 'open-uri'
|
||||||
|
|
||||||
|
# URL Shortener App class
|
||||||
class App < Roda
|
class App < Roda
|
||||||
DB = Sequel.sqlite('db/url_shortener.db')
|
plugin :sessions, secret: ENV.delete('APP_SESSION_SECRET')
|
||||||
links = DB[:links]
|
|
||||||
plugin :render, escape: true
|
plugin :render, escape: true
|
||||||
|
plugin :flash
|
||||||
|
plugin :json_parser
|
||||||
|
plugin :request_headers
|
||||||
|
|
||||||
|
DB = Sequel.sqlite("db/#{ENV.fetch('DB_NAME', nil)}")
|
||||||
|
links = DB[:links]
|
||||||
|
|
||||||
route do |r|
|
route do |r|
|
||||||
r.root do
|
r.root do
|
||||||
|
@message = flash['message'] || 'Enter a URL'
|
||||||
view :home
|
view :home
|
||||||
end
|
end
|
||||||
|
|
||||||
r.get String do |url_code|
|
r.get String do |url_code|
|
||||||
link = links.filter(:code => url_code).first[:url]
|
link = links.filter(code: url_code)
|
||||||
r.redirect link
|
r.redirect link.first[:url] unless link.first.nil?
|
||||||
|
@message = "Link #{url_code} doesn't exist"
|
||||||
|
response.status = 404
|
||||||
|
view :home
|
||||||
end
|
end
|
||||||
|
|
||||||
r.post "create" do
|
r.post 'create' do
|
||||||
url = r.params['url']
|
url = r.params['url']
|
||||||
if url.empty? then r.redirect '/' end
|
if url.nil? || url.empty?
|
||||||
if nil == links.filter(:url => url).first
|
flash['message'] = 'Please enter a valid URL'
|
||||||
code = SecureRandom.urlsafe_base64 4
|
r.redirect '/'
|
||||||
links.insert(url: url, code: code)
|
|
||||||
end
|
end
|
||||||
@new_link = 'http://' + request.env['HTTP_HOST'] + '/' + links.filter(:url => url).first[:code]
|
|
||||||
|
begin
|
||||||
|
OpenURI.open_uri(url)
|
||||||
|
rescue URI::BadURIError
|
||||||
|
flash['message'] = 'Invalid URL'
|
||||||
|
r.redirect '/'
|
||||||
|
rescue OpenURI::HTTPError
|
||||||
|
flash['message'] = 'URL not found'
|
||||||
|
r.redirect '/'
|
||||||
|
rescue SocketError
|
||||||
|
flash['message'] = 'URL does not resolve'
|
||||||
|
r.redirect '/'
|
||||||
|
end
|
||||||
|
|
||||||
|
if links.filter(url:).first.nil?
|
||||||
|
code = SecureRandom.urlsafe_base64 4
|
||||||
|
links.insert(url:, code:)
|
||||||
|
@message = 'Link created'
|
||||||
|
end
|
||||||
|
code = links.filter(url:).first[:code]
|
||||||
|
@message ||= 'Link exists'
|
||||||
|
@new_link = "http://#{request.env['HTTP_HOST']}/#{code}"
|
||||||
view :create
|
view :create
|
||||||
end
|
end
|
||||||
|
r.on 'links' do
|
||||||
|
r.post do
|
||||||
|
if r.headers['CONTENT_TYPE'] != 'application/json'
|
||||||
|
response.status = 400
|
||||||
|
return { message: 'not a valid json request' }.to_json
|
||||||
|
end
|
||||||
|
|
||||||
|
url = r.params['url']
|
||||||
|
if url.nil?
|
||||||
|
response.status = 400
|
||||||
|
return { message: 'missing url parameter' }.to_json
|
||||||
|
end
|
||||||
|
|
||||||
|
if url.empty?
|
||||||
|
response.status = 400
|
||||||
|
return { message: 'invalid url parameter' }.to_json
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
OpenURI.open_uri(url)
|
||||||
|
rescue URI::BadURIError
|
||||||
|
response.status = 400
|
||||||
|
return { message: 'invalid url parameter' }.to_json
|
||||||
|
rescue OpenURI::HTTPError
|
||||||
|
response.status = 400
|
||||||
|
return { message: 'url not found' }.to_json
|
||||||
|
rescue SocketError
|
||||||
|
response.status = 400
|
||||||
|
return { message: 'url does not resolve' }.to_json
|
||||||
|
end
|
||||||
|
|
||||||
|
if links.filter(url:).first.nil?
|
||||||
|
code = SecureRandom.urlsafe_base64 4
|
||||||
|
links.insert(url:, code:)
|
||||||
|
end
|
||||||
|
|
||||||
|
code = links.filter(url:).first[:code]
|
||||||
|
@new_link = "http://#{request.env['HTTP_HOST']}/#{code}"
|
||||||
|
return { url:, code:, link: @new_link }.to_json
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require './.env'
|
||||||
require './app'
|
require './app'
|
||||||
|
|
||||||
run App.app
|
run App.app
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
Sequel.migration do
|
Sequel.migration do
|
||||||
change do
|
change do
|
||||||
create_table :links do
|
create_table :links do
|
||||||
|
|||||||
@@ -7,16 +7,6 @@ Feature: Homepage
|
|||||||
Scenario: Homepage Loads with a form
|
Scenario: Homepage Loads with a form
|
||||||
Given I visit the "/" page
|
Given I visit the "/" page
|
||||||
Then I should see text "URL Shortener"
|
Then I should see text "URL Shortener"
|
||||||
|
And I should see the message "Enter a URL"
|
||||||
And I should see a form field "url"
|
And I should see a form field "url"
|
||||||
And I should see a "Submit" button
|
And I should see a "Submit" button
|
||||||
|
|
||||||
Scenario: Submitting the form without entering a URL
|
|
||||||
Given I visit the "/" page
|
|
||||||
When I click the "Submit" button
|
|
||||||
Then I should be on "/" page
|
|
||||||
|
|
||||||
Scenario: Submitting the form with a correct URL
|
|
||||||
Given I visit the "/" page
|
|
||||||
When I type "http://google.com/" in the "url" field
|
|
||||||
And I click the "Submit" button
|
|
||||||
Then I should be on "/create" page
|
|
||||||
|
|||||||
@@ -1,9 +1,24 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# BEFORE
|
||||||
|
Before('@db-test') do
|
||||||
|
@links = Sequel.sqlite("db/#{ENV.fetch('DB_NAME', nil)}")[:links]
|
||||||
|
end
|
||||||
|
|
||||||
# GIVEN
|
# GIVEN
|
||||||
|
|
||||||
Given('I visit the {string} page') do |string|
|
Given('I visit the {string} page') do |string|
|
||||||
visit string
|
visit string
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Given('A link already exists with the url {string}') do |string|
|
||||||
|
@links.insert(url: string, code: 'aaaaaa')
|
||||||
|
end
|
||||||
|
|
||||||
|
Given('A link already exists with the url {string} and code {string}') do |url, code|
|
||||||
|
@links.insert(url:, code:)
|
||||||
|
end
|
||||||
|
|
||||||
# WHEN
|
# WHEN
|
||||||
|
|
||||||
When('I click the {string} button') do |string|
|
When('I click the {string} button') do |string|
|
||||||
@@ -14,6 +29,10 @@ When('I type {string} in the {string} field') do |text, field|
|
|||||||
fill_in field, with: text
|
fill_in field, with: text
|
||||||
end
|
end
|
||||||
|
|
||||||
|
When('I visit the {string} location') do |string|
|
||||||
|
visit string
|
||||||
|
end
|
||||||
|
|
||||||
# THEN
|
# THEN
|
||||||
|
|
||||||
Then('I should see text {string}') do |string|
|
Then('I should see text {string}') do |string|
|
||||||
@@ -31,3 +50,23 @@ end
|
|||||||
Then('I should be on {string} page') do |string|
|
Then('I should be on {string} page') do |string|
|
||||||
page.should have_current_path string
|
page.should have_current_path string
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Then('I should see the message {string}') do |message|
|
||||||
|
page.should have_selector '#message', text: message
|
||||||
|
end
|
||||||
|
|
||||||
|
Then('I should be redirected to {string}') do |string|
|
||||||
|
actual = URI.parse(current_url).to_s
|
||||||
|
location = actual.index(string)
|
||||||
|
location.should equal(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
Then('The status code should be {int}') do |code|
|
||||||
|
page.status_code.should eq(code)
|
||||||
|
end
|
||||||
|
|
||||||
|
# AFTER
|
||||||
|
|
||||||
|
After('@db-test') do
|
||||||
|
@links.delete
|
||||||
|
end
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# features/submit.feature
|
||||||
|
|
||||||
|
Feature: Submit
|
||||||
|
|
||||||
|
Submitting URL's on the homepage
|
||||||
|
|
||||||
|
Scenario: Submitting the form without entering a URL
|
||||||
|
Given I visit the "/" page
|
||||||
|
When I click the "Submit" button
|
||||||
|
Then I should be on "/" page
|
||||||
|
And I should see the message "Please enter a valid URL"
|
||||||
|
|
||||||
|
@db-test
|
||||||
|
Scenario: Submitting the form with a correct URL
|
||||||
|
Given I visit the "/" page
|
||||||
|
When I type "http://google.com" in the "url" field
|
||||||
|
And I click the "Submit" button
|
||||||
|
Then I should be on "/create" page
|
||||||
|
And I should see the message "Link created"
|
||||||
|
|
||||||
|
@db-test
|
||||||
|
Scenario: Submitting the form with an existing URL
|
||||||
|
Given I visit the "/" page
|
||||||
|
And A link already exists with the url "http://google.com"
|
||||||
|
When I type "http://google.com" in the "url" field
|
||||||
|
And I click the "Submit" button
|
||||||
|
Then I should be on "/create" page
|
||||||
|
And I should see the message "Link exists"
|
||||||
|
|
||||||
|
@db-test
|
||||||
|
Scenario: Submitting the form with a URL that is 404
|
||||||
|
Given I visit the "/" page
|
||||||
|
When I type "http://google.com/example" in the "url" field
|
||||||
|
And I click the "Submit" button
|
||||||
|
Then I should be on "/" page
|
||||||
|
And I should see the message "URL not found"
|
||||||
|
|
||||||
|
@db-test
|
||||||
|
Scenario: Submitting the form with a URL that does not resolve
|
||||||
|
Given I visit the "/" page
|
||||||
|
When I type "http://bad.tld" in the "url" field
|
||||||
|
And I click the "Submit" button
|
||||||
|
Then I should be on "/" page
|
||||||
|
And I should see the message "URL does not resolve"
|
||||||
|
|
||||||
|
@db-test
|
||||||
|
Scenario: Submitting the form with an invalid URL
|
||||||
|
Given I visit the "/" page
|
||||||
|
When I type "not-an-url" in the "url" field
|
||||||
|
And I click the "Submit" button
|
||||||
|
Then I should be on "/" page
|
||||||
|
And I should see the message "Invalid URL"
|
||||||
+18
-4
@@ -1,13 +1,27 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'simplecov'
|
||||||
|
SimpleCov.start
|
||||||
|
|
||||||
|
require_relative '../../.env'
|
||||||
|
ENV['DB_NAME'] = "test_#{ENV.fetch('DB_NAME', nil)}"
|
||||||
require_relative '../../app'
|
require_relative '../../app'
|
||||||
require 'rubygems'
|
require 'rubygems'
|
||||||
require 'roda'
|
require 'roda'
|
||||||
|
require 'sequel'
|
||||||
require 'capybara'
|
require 'capybara'
|
||||||
require 'capybara/dsl'
|
require 'capybara/dsl'
|
||||||
|
require 'capybara/cucumber'
|
||||||
require 'rspec'
|
require 'rspec'
|
||||||
|
###
|
||||||
|
require 'rspec/expectations'
|
||||||
|
require 'rspec/matchers'
|
||||||
|
|
||||||
|
# DB initialization
|
||||||
|
Sequel.extension :migration
|
||||||
|
Sequel.sqlite("db/#{ENV.fetch('DB_NAME', nil)}") do |db|
|
||||||
|
Sequel::Migrator.apply(db, 'db/migrations')
|
||||||
|
end
|
||||||
|
|
||||||
|
# attach app to Capybara
|
||||||
Capybara.app = App
|
Capybara.app = App
|
||||||
|
|
||||||
include Capybara::DSL
|
|
||||||
include RSpec::Expectations
|
|
||||||
include RSpec::Matchers
|
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# features/visit.feature
|
||||||
|
|
||||||
|
Feature: Visit
|
||||||
|
|
||||||
|
Accessing a URL from it's link code
|
||||||
|
|
||||||
|
@db-test
|
||||||
|
Scenario: Accessing the URL based on the shortcode
|
||||||
|
Given A link already exists with the url "https://google.com" and code "aaaaaa"
|
||||||
|
When I visit the "/aaaaaa" location
|
||||||
|
Then I should be redirected to "https://google.com"
|
||||||
|
|
||||||
|
Scenario: Accessing a nonexistent URL code
|
||||||
|
Given I visit the "/aaaaaa" location
|
||||||
|
Then I should be on "/aaaaaa" page
|
||||||
|
And The status code should be 404
|
||||||
|
And I should see the message "Link aaaaaa doesn't exist"
|
||||||
|
And I should see a form field "url"
|
||||||
|
And I should see a "Submit" button
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'simplecov'
|
||||||
|
SimpleCov.start
|
||||||
|
|
||||||
|
require_relative '../.env'
|
||||||
|
ENV['DB_NAME'] = "test_#{ENV.fetch('DB_NAME', nil)}"
|
||||||
|
require_relative '../app'
|
||||||
|
require 'rubygems'
|
||||||
|
require 'roda'
|
||||||
|
require 'sequel'
|
||||||
|
require 'rspec'
|
||||||
|
require 'rack/test'
|
||||||
|
|
||||||
|
# DB initialization
|
||||||
|
Sequel.extension :migration
|
||||||
|
Sequel.sqlite("db/#{ENV.fetch('DB_NAME', nil)}") do |db|
|
||||||
|
Sequel::Migrator.apply(db, 'db/migrations')
|
||||||
|
end
|
||||||
|
|
||||||
|
def app
|
||||||
|
App
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'Submit API request to create new link' do
|
||||||
|
include Rack::Test::Methods
|
||||||
|
before :each do
|
||||||
|
@links = Sequel.sqlite("db/#{ENV.fetch('DB_NAME', nil)}")[:links]
|
||||||
|
end
|
||||||
|
after :each do
|
||||||
|
@links.delete
|
||||||
|
end
|
||||||
|
it 'should return link data in json format when a valid url is submitted' do
|
||||||
|
data = {
|
||||||
|
url: 'http://google.com'
|
||||||
|
}
|
||||||
|
post('/links', data.to_json, 'CONTENT_TYPE' => 'application/json')
|
||||||
|
expect(last_response).to be_ok
|
||||||
|
response_json = JSON.parse(last_response.body)
|
||||||
|
expect(response_json['url']).to eq(data[:url])
|
||||||
|
expect(response_json['code']).not_to eq(nil)
|
||||||
|
expect(response_json['link']).to include(response_json['code'])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return with a 400 status and 'invalid url parameter' message when an empty url is submitted" do
|
||||||
|
data = {
|
||||||
|
url: ''
|
||||||
|
}
|
||||||
|
post('/links', data.to_json, 'CONTENT_TYPE' => 'application/json')
|
||||||
|
expect(last_response.status).to eq(400)
|
||||||
|
response_json = JSON.parse(last_response.body)
|
||||||
|
expect(response_json['message']).to eq('invalid url parameter')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return with a 400 status and 'missing url parameter' message when an empty url is submitted" do
|
||||||
|
data = {}
|
||||||
|
post('/links', data.to_json, 'CONTENT_TYPE' => 'application/json')
|
||||||
|
expect(last_response.status).to eq(400)
|
||||||
|
response_json = JSON.parse(last_response.body)
|
||||||
|
expect(response_json['message']).to eq('missing url parameter')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return with a 400 status and 'invalid url parameter' message when an invalid url is submitted" do
|
||||||
|
data = {
|
||||||
|
url: 'not-an-url'
|
||||||
|
}
|
||||||
|
post('/links', data.to_json, 'CONTENT_TYPE' => 'application/json')
|
||||||
|
expect(last_response.status).to eq(400)
|
||||||
|
response_json = JSON.parse(last_response.body)
|
||||||
|
expect(response_json['message']).to eq('invalid url parameter')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return with a 400 status and 'url not found' message when a 404 url is submitted" do
|
||||||
|
data = {
|
||||||
|
url: 'http://google.com/example'
|
||||||
|
}
|
||||||
|
post('/links', data.to_json, 'CONTENT_TYPE' => 'application/json')
|
||||||
|
expect(last_response.status).to eq(400)
|
||||||
|
response_json = JSON.parse(last_response.body)
|
||||||
|
expect(response_json['message']).to eq('url not found')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return with a 400 status and 'url not found' message when a URL with no DNS is submitted" do
|
||||||
|
data = {
|
||||||
|
url: 'http://bad.tld'
|
||||||
|
}
|
||||||
|
post('/links', data.to_json, 'CONTENT_TYPE' => 'application/json')
|
||||||
|
expect(last_response.status).to eq(400)
|
||||||
|
response_json = JSON.parse(last_response.body)
|
||||||
|
expect(response_json['message']).to eq('url does not resolve')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return with a 400 status and 'not a valid json request'
|
||||||
|
message when a request is made with the wrong content type header" do
|
||||||
|
data = {
|
||||||
|
url: 'http://google.com'
|
||||||
|
}
|
||||||
|
post('/links', data.to_json)
|
||||||
|
expect(last_response.status).to eq(400)
|
||||||
|
response_json = JSON.parse(last_response.body)
|
||||||
|
expect(response_json['message']).to eq('not a valid json request')
|
||||||
|
end
|
||||||
|
end
|
||||||
+1
-2
@@ -1,2 +1 @@
|
|||||||
<h2><a href="/" style="color: #555555;">Home</a></h2>
|
<h3 style="overflow-wrap: break-word; word-wrap: break-word; margin-top: 3rem; line-height: 2rem;"><a href="<%= @new_link %>" target="_blank"><%= @new_link %></a></h3>
|
||||||
<h3 style="overflow-wrap: break-word; word-wrap: break-word;"><a href="<%= @new_link %>" target="_blank" style="color: #555555;"><%= @new_link %></a></h3>
|
|
||||||
|
|||||||
+6
-2
@@ -1,9 +1,13 @@
|
|||||||
<form method="POST" action="/create">
|
<form method="POST" action="/create">
|
||||||
|
<div style="margin-bottom: 1rem;">
|
||||||
<input type="url"
|
<input type="url"
|
||||||
name="url"
|
name="url"
|
||||||
pattern="https?://.+"
|
pattern="https?://.+"
|
||||||
placeholder="http://www.example.com/"
|
placeholder="http://www.example.com/"
|
||||||
style="padding: 0.5rem;"
|
autocomplete="off"
|
||||||
required />
|
required />
|
||||||
<button type="submit" style="padding: 0.5rem;">Submit</button>
|
</div>
|
||||||
|
<div>
|
||||||
|
<button type="submit">Submit</button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
+99
-3
@@ -1,11 +1,107 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title><%= @page_title || "URL Shortener" %></title>
|
<title><%= @page_title || "URL Shortener" %></title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link rel="icon" href="data:,">
|
<link rel="icon" href="data:,">
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--white: #FEFEFE;
|
||||||
|
--shadow: #333;
|
||||||
|
--black: #555;
|
||||||
|
--darker-blue: #809DFF;
|
||||||
|
--dark-blue: #99b1ff;
|
||||||
|
--medium-blue: #B3C6FF;
|
||||||
|
--light-blue: #CCD8FF;
|
||||||
|
--lightest-blue: #E6EBFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: var(--white);
|
||||||
|
background-image: linear-gradient(to bottom right, var(--lightest-blue), var(--white), var(--white), var(--white), var(--lightest-blue));
|
||||||
|
font-family: Helvetica, sans-serif;
|
||||||
|
color: var(--black);
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 5rem 0 0;
|
||||||
|
border: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
width: 30rem;
|
||||||
|
max-width: calc(100% - 1rem);
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 5px solid var(--medium-blue);
|
||||||
|
border-radius: 20px;
|
||||||
|
margin: auto;
|
||||||
|
padding: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
min-height: 260px;
|
||||||
|
box-shadow: 3px 2px 10px var(--shadow);
|
||||||
|
background: var(--white);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
input, button {
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: 3px solid var(--dark-blue);
|
||||||
|
outline: none;
|
||||||
|
transition: all .25s ease-in-out;
|
||||||
|
-moz-transition: all .25s ease-in-out;
|
||||||
|
-webkit-transition: all .25s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus, input:hover {
|
||||||
|
border: 3px solid var(--darker-blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
color: var(--white);
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: 2px solid var(--darker-blue);
|
||||||
|
background: var(--dark-blue);
|
||||||
|
font-weight: bold;
|
||||||
|
transition: background .25s ease-in-out;
|
||||||
|
-moz-transition: background .25s ease-in-out;
|
||||||
|
-webkit-transition: background .25s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: var(--darker-blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
button:active {
|
||||||
|
background: var(--light-blue);
|
||||||
|
color: var(--black);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--black);
|
||||||
|
text-decoration: none;
|
||||||
|
border-bottom: 4px solid var(--white);
|
||||||
|
transition: all .25s ease-in-out;
|
||||||
|
-moz-transition: all .25s ease-in-out;
|
||||||
|
-webkit-transition: all .25s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover, a:active, a:focus {
|
||||||
|
color: var(--darker-blue);
|
||||||
|
border-bottom: 4px solid var(--darker-blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body style="background: #FEFEFE; font-family: Helvetica, sans-serif; color: #555555;">
|
<body style="">
|
||||||
<main style="max-width: 30rem; min-width: 18rem; border: 5px solid #999999; border-radius: 20px; margin: auto; padding: 1rem; margin-top: 5rem; text-align: center; min-height: 175px;">
|
<main>
|
||||||
<h1>URL Shortener</h1>
|
<h1><a href="/">URL Shortener</a></h1>
|
||||||
|
<h4 id="message"><%== @message %></h4>
|
||||||
<%== yield %>
|
<%== yield %>
|
||||||
</main>
|
</main>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user