Compare commits

...

132 Commits

Author SHA1 Message Date
bucky 5631ae386f include rackup 2023-10-13 21:28:47 -07:00
bucky c31c556df5 removed falcon and it's dependencies, because it wasn't working right.
use puma for now
2023-10-13 21:27:25 -07:00
bucky 5fae0c4fa9 Merge pull request 'rspec-coverage' (#22) from rspec-coverage into master
Reviewed-on: #22
2023-10-12 23:49:48 -07:00
bucky 144731062b added URL validation to webpage part 2023-10-12 23:42:52 -07:00
bucky 72f3e03edd added tests for invalid URL entries
+ URL that returns a 404
+ URL where the domain does not resolve with DNS
+ URL that is not properly formatted
2023-10-12 23:42:04 -07:00
bucky b08693e866 remove the puts, don't print out the test result 2023-10-12 23:41:38 -07:00
bucky ae2360a480 include the proper status code 2023-10-12 23:31:15 -07:00
bucky 213265a5d0 added test for submitting with the wrong content type header 2023-10-12 23:30:47 -07:00
bucky dfb6b23e70 run simplecov for rspec tests 2023-10-12 23:12:14 -07:00
bucky c81a7a6160 added rescue for urls that don't resolve 2023-10-12 19:03:03 -07:00
bucky bbb710a5e1 added test for bad TLD, better named 404 url test 2023-10-12 19:02:38 -07:00
bucky 7eaa8eacf8 added rescue for OpenURI HTTPErrors 2023-10-12 18:26:13 -07:00
bucky d78e361c29 added test for validating a url is accessible 2023-10-12 18:25:38 -07:00
bucky 1ae6c02472 rescue BadURIError for invalid URL format 2023-10-12 18:20:56 -07:00
bucky 11206854ad added test for an invalid url 2023-10-12 18:20:35 -07:00
bucky 33089e6fbd build HTML output with cucumber and rspec files in root dir 2023-10-12 16:25:22 -07:00
bucky 5cec47efcc Merge pull request 'create-json' (#21) from create-json into master
Reviewed-on: #21
2023-10-12 14:36:38 -07:00
bucky cabb4daded split up the nil? and empty? cases, because they are different
situations
2023-10-12 14:27:31 -07:00
bucky 1e61a76ded added 2 more tests for empty and missing url data posting to links 2023-10-12 14:27:02 -07:00
bucky ac3e9f14cd added links route for API interactions 2023-10-12 11:42:43 -07:00
bucky 5ee9351362 added db initialization to make sure the db exists and is up to date
before tests
2023-10-12 11:42:14 -07:00
bucky 2964e06639 added before and after actions
+ before initializes the db
+ after cleans the db
2023-10-12 11:40:15 -07:00
bucky d1a0fc0222 change request to use the /links path, to fit more into a RESTful API
format

+ also renamed the test to better describe the behavior
2023-10-12 11:32:00 -07:00
bucky 4a82174526 implemented API create endpoint
+ added plugins to read json and access the header to parse the request
  for an API request
+ fixed logic where url param is nil
+ refactored the new code to a variable to be reused easier
+ if CONTENT_TYPE header is application/json, reply with json
- there could be more refactoring (maybe separating it to a different
  endpoint) to handle other scenarios, we'll work on that later
2023-10-11 19:21:43 -07:00
bucky 3ae20255f6 DRY it out a little 2023-10-11 19:21:21 -07:00
bucky f69c53bf29 removed unnecessary variable 2023-10-11 19:20:07 -07:00
bucky 7d6c098047 added more to the spec test for creating a new link 2023-10-11 19:18:47 -07:00
bucky 3421e18f21 added more logic for running tests without killing the whole pipeline, and outputting rspec results 2023-10-11 12:05:33 -07:00
bucky 0dec27a01c added rspec step in tests stage, adjusted should clause 2023-10-11 11:04:08 -07:00
bucky c45cf6ab51 started building out rspec specs for API functionality 2023-10-11 11:01:46 -07:00
bucky e50667b392 split homepage out into homepage and submit feature sets 2023-10-10 17:29:23 -07:00
bucky d4db64f404 Merge pull request 'cucumber-jenkins' (#20) from cucumber-jenkins into master
Reviewed-on: #20
2023-10-10 17:22:55 -07:00
bucky 05858078d1 added unstable scenario 2023-10-10 17:20:25 -07:00
bucky f75f0378bf separate out success and failure tasks 2023-10-10 17:16:24 -07:00
bucky c3d2fb32e2 added icon for message 2023-10-10 17:11:05 -07:00
bucky 1fe30fc7e7 removed jenkins cucumber section, use the html formatter instead
also added more info to the success message
2023-10-10 17:05:33 -07:00
bucky 43cc151138 moved stuff around a little more 2023-10-10 14:50:01 -07:00
bucky 1fee4f276d maybe put both files in the same report 2023-10-10 14:47:09 -07:00
bucky 0b17f36fde added more test results, just exploring 2023-10-10 14:43:11 -07:00
bucky aca92fdae1 cleaned up message a bit for success 2023-10-10 14:32:54 -07:00
bucky c7bf62062a wrap it 2023-10-10 14:28:41 -07:00
bucky b853ca6fe4 trying to make the message cleaner, and print the env to take a look at other options 2023-10-10 14:25:59 -07:00
bucky d5919fc82a got it working with vars, now try the URLs 2023-10-10 14:23:44 -07:00
bucky e2395989f4 try normal quotes 2023-10-10 14:22:30 -07:00
bucky e93868a483 try DB_NAME since the others are failing 2023-10-10 14:19:59 -07:00
bucky 7f6fc24872 maybe this one will work 2023-10-10 14:17:48 -07:00
bucky 776ec0d30d trying different env vars 2023-10-10 14:10:50 -07:00
bucky 6861740d00 bots can't be easily used with incoming webhooks, but let's try some URLs 2023-10-10 14:05:18 -07:00
bucky 77e72d18e1 use the bot's endpoint 2023-10-10 13:50:48 -07:00
bucky 1792d94082 extra , syntax error in jenkins file 2023-10-10 13:30:40 -07:00
bucky 16d17bcadb added icon 2023-10-10 13:27:40 -07:00
bucky f5d1361d59 swapped text/message 2023-10-10 13:10:19 -07:00
bucky c4932dd6cc message to mattermost example 2023-10-10 13:02:31 -07:00
bucky 031a30ed5f send slack message to mattermost for cucumber results 2023-10-10 10:52:14 -07:00
bucky cc1fe1c3b7 Merge pull request 'cucumber-jenkins' (#19) from cucumber-jenkins into master
Reviewed-on: #19
2023-10-10 09:44:14 -07:00
bucky 72e1cee9d5 split features into 2 separate feature files 2023-10-09 20:19:34 -07:00
bucky a90ff753b1 use cucumber plugin 2023-10-09 18:52:48 -07:00
bucky b6010cd431 remove the config in there 2023-10-09 18:37:46 -07:00
bucky 33bfb23405 add a setting for it 2023-10-09 18:35:50 -07:00
bucky ad4f8492d4 add jenkins cucumber functionality 2023-10-09 18:30:50 -07:00
bucky e6cc0959a4 Merge pull request 'invalid-urls' (#18) from invalid-urls into master
Reviewed-on: #18
2023-10-07 21:13:53 -07:00
bucky 5bce31baf2 refactored missing link scenario to make more sense
+ 404 code, stay on the current page, and load the form
+ also simplified the logic for the create action if the link doesn't
  already exist
2023-10-07 21:11:12 -07:00
bucky c7fee27623 update for missing link scenario
+ if the link isn't found, don't redirect it, keep it on the same page
+ it should have a status code of 404
+ it should have the form fields as well

+ added page status check
2023-10-07 21:10:16 -07:00
bucky f8499a79c2 check if the link exists, redirects home if it doesn't 2023-10-07 13:38:10 -07:00
bucky b97ee3f3bb use the correct clause format since staying on site 2023-10-07 13:37:51 -07:00
bucky beb9b0c14a added new scenario 2023-10-07 13:27:00 -07:00
bucky c874426f2e Merge pull request 'simplecov-jenkins-output' (#17) from simplecov-jenkins-output into master
Reviewed-on: #17
2023-10-07 09:27:01 -07:00
bucky c77cf2cb78 trying to get assets to load as well, and keep previous reports 2023-10-07 09:13:46 -07:00
bucky 02e95ae916 added report results stage that includes the step to publish the
simplecov results to Jenkins
2023-10-07 09:03:39 -07:00
bucky 0f0838ae5a Merge pull request 'jenkins-file' (#15) from jenkins-file into master
Reviewed-on: #15
2023-10-06 20:49:11 -07:00
bucky 47e5a093b4 added tests stage to jenkinsfile and added a note to the readme about pkgconf 2023-10-06 20:00:04 -07:00
bucky a0ba8ba9f3 wrap steps in the steps block 2023-10-06 18:42:44 -07:00
bucky c0a0548662 added dependencies stage 2023-10-06 18:40:51 -07:00
bucky 2448c93fa7 wrap setting the env var in a script wrapper 2023-10-06 18:35:00 -07:00
bucky e720761106 try it again 2023-10-06 18:33:30 -07:00
bucky 450dc26df7 another attempt 2023-10-06 18:11:00 -07:00
bucky 7a32edb6e3 wrap it in an env var and add it to the env file 2023-10-06 18:09:17 -07:00
bucky 6160174c5d generate secret in rb file 2023-10-06 18:07:07 -07:00
bucky d972cc2c3b another shot 2023-10-06 18:04:54 -07:00
bucky 6795508d3e simplified 2023-10-06 17:42:48 -07:00
bucky b5af621ee2 another shot 2023-10-06 17:41:29 -07:00
bucky a4fbdeabe3 going back to the old style 2023-10-06 17:39:45 -07:00
bucky 3a71e58e22 trying to run the script, nothing else 2023-10-06 17:35:21 -07:00
bucky ef9cef8915 wrap it in a script 2023-10-06 17:25:53 -07:00
bucky ee5a6e673b attempting to use variables 2023-10-06 17:23:56 -07:00
bucky 721ce33adb trying by setting the output of the base64 generation to a jenkins variable to use later 2023-10-06 16:59:16 -07:00
bucky e1d0c6162d trying again 2023-10-06 16:49:03 -07:00
bucky 435eeb9601 another quotes try 2023-10-06 14:02:54 -07:00
bucky 95eb1fea4c trying with different quotes around commands 2023-10-06 14:01:55 -07:00
bucky 03eafcdf4c reset the file on the initial append 2023-10-06 13:59:12 -07:00
bucky 38f92d83c3 add app session secret generation to the mix 2023-10-06 13:57:42 -07:00
bucky 6c40ae6311 syntax error in Jenkinsfile, missing " at the end of the line to add to the env file 2023-10-06 13:47:11 -07:00
bucky 5cc79ed54d added line to add db name to the env file 2023-10-06 13:41:53 -07:00
bucky b70b0846a1 syntax error in Jenkinsfile 2023-10-06 12:04:46 -07:00
bucky de61253db6 added jenkinsfile with initial step for init 2023-10-06 12:02:38 -07:00
bucky c93ac10288 fixed vertical scrolling issue with page and margin top on main 2023-10-05 13:55:09 -07:00
bucky 14f5f51d1e made spacing for link a little nicer for smaller screens 2023-10-05 11:41:40 -07:00
bucky 79b12ac4b7 updates for mobile use 2023-10-05 11:03:22 -07:00
bucky 8430e828e9 adjusted UI more
+ set input and button to be wider
+ set min height on the main to be longer to look better between views
+ bumped shortened url down some to fill the gap more
2023-10-05 08:44:41 -07:00
bucky 9ca0e9ca48 adjusted styles a little more
+ rearranged things some
+ changed button colors
2023-10-05 08:32:26 -07:00
bucky 67e99df83e set transition time 2023-10-04 18:55:12 -07:00
bucky d3ab78b17d Merge pull request 'more-ui-updates' (#13) from more-ui-updates into master
Reviewed-on: #13
2023-10-04 18:53:06 -07:00
bucky ce89b584cb more updates!
+ disable autocomplete on the url field
+ added custom colors to make styling a little easier
+ moved body to style block, and added a nice gradient
+ made header a link, removed the "home" link on created route
+ added some nice ui changes for button and input
2023-10-04 18:47:45 -07:00
bucky cfe2080509 added some more colorized styles for the UI to give it a little more
interactive niceness
2023-10-04 11:28:31 -07:00
bucky 9c5fa5366f ignore the coverage directory from simplecov output 2023-10-04 11:28:07 -07:00
bucky a0be5c699a Merge pull request 'code coverage reporting and test for the redirect function' (#12) from add-flash into master
Reviewed-on: #12
2023-10-03 18:32:12 -07:00
bucky 81b12b5d6f added simplecov and a new test
+ simplecov is a test coverage tool to show what code is covered and
  where holes are
+ added newest test to validate that the redirect url works as expected
2023-10-03 18:29:03 -07:00
bucky b3d5c70c8b added simplecov for collecting test results 2023-10-03 18:27:29 -07:00
bucky be17e2f146 Merge pull request 'add-flash' (#11) from add-flash into master
Reviewed-on: #11
2023-10-03 16:12:15 -07:00
bucky 80fae88228 sqlite3 note 2023-10-03 16:11:01 -07:00
bucky d93a8df199 formatting 2023-10-03 16:08:53 -07:00
bucky f0bad17e89 some formatting and more context in README 2023-10-03 16:08:16 -07:00
bucky 5ca4bf707b removed rspec db setup, i probably didn't have it set right to clean up
after each task, implemented that in the before and after steps
2023-10-03 16:02:33 -07:00
bucky 8b2780820c update for features
+ added flash/message assertions
+ adjusted urls to be http://google.com for tests for now
+ added @db-test tags to tests that use db interactions
+ added a scenario for duplicate URLs being used
+ added before and after steps to init and clean up db tests
+ added initialization of a test db for tests instead of interacting
  with the production database
2023-10-03 15:58:56 -07:00
bucky 8639c785b7 added a spot for messages to show up in the layout 2023-10-03 15:58:33 -07:00
bucky 84a047fb0c require the .env file to load ENV variables set in there 2023-10-03 15:58:01 -07:00
bucky b2b41f1aa1 added flash plugin
+ added flash plugin, and the session plugin required by flash
+ set the DB name as an environment variable
+ set @message variable where it needs to be set to show on the UI
2023-10-03 15:56:42 -07:00
bucky fff6b0f5dd added more notes for the README 2023-10-03 15:55:59 -07:00
bucky c51c0afcf2 ignore the local env file 2023-10-03 15:55:37 -07:00
bucky 88b52df610 ignore db files (sqlite) 2023-10-02 16:22:21 -07:00
bucky 0da7ec74e8 ignore .bundle directory 2023-10-02 16:21:44 -07:00
bucky 2bcf76618a Merge pull request 'testing-enviroment-setup' (#10) from testing-enviroment-setup into master
Reviewed-on: #10
2023-10-02 14:55:29 -07:00
bucky d0c0ad5c16 removed an extra line in README 2023-10-02 14:51:58 -07:00
bucky b90b400864 added more startup instructions and updated gems to have a development group 2023-10-02 14:22:26 -07:00
bucky b66a5bb3e1 Merge pull request 'tests' (#7) from tests into master
Reviewed-on: #7
2023-10-02 13:03:46 -07:00
bucky cb027e19c8 generalized fill in field with text action 2023-10-02 13:02:48 -07:00
bucky 52a6bac85f added 3rd test case for submitting a url 2023-10-02 11:37:19 -07:00
bucky 1e9a066cd1 Merge pull request 'tests' (#6) from tests into master
Reviewed-on: #6
2023-10-01 12:26:20 -07:00
bucky a1ea2a09ec expanded click button to click any button for easier use 2023-10-01 12:14:41 -07:00
bucky 445236d735 2nd scenario
+ added a scenario for submitting the button with no text  in url field
+ simplified the visit when clause to be used with any url
+ edited create route redirect if url param is empty (backend validation
  for required URL field)
+ added when clause for clicking submit button
+ added then clause for current path/page
2023-10-01 12:10:30 -07:00
bucky 94026bdad2 added more parts for the first scenario and updated code to work with the changes 2023-09-30 18:39:30 -07:00
bucky b827f72f25 ignore vim swap files 2023-09-30 18:38:56 -07:00
17 changed files with 577 additions and 100 deletions
+5
View File
@@ -0,0 +1,5 @@
*.sw*
.bundle
*.db
.env.rb
coverage
+12 -3
View File
@@ -2,7 +2,6 @@
source "https://rubygems.org"
gem "falcon", "~> 0.42.3"
gem "roda", "~> 3.72"
@@ -10,14 +9,24 @@ gem "sequel", "~> 5.72"
gem "tilt", "~> 2.2"
gem "cucumber", "~> 9.0"
gem "sqlite3", "~> 1.6"
gem "erubi", "~> 1.12"
group :test do
gem "cucumber", "~> 9.0"
gem "capybara", "~> 3.39"
gem "rspec", "~> 3.12"
gem "selenium-webdriver", "~> 4.13"
gem "simplecov"
end
gem "puma", "~> 6.4"
gem "rackup", "~> 2.1"
+20 -67
View File
@@ -3,30 +3,7 @@ GEM
specs:
addressable (2.8.5)
public_suffix (>= 2.0.2, < 6.0)
async (2.6.4)
console (~> 1.10)
fiber-annotation
io-event (~> 1.1)
timers (~> 4.1)
async-container (0.16.12)
async
async-io
async-http (0.60.2)
async (>= 1.25)
async-io (>= 1.28)
async-pool (>= 0.2)
protocol-http (~> 0.24.0)
protocol-http1 (~> 0.15.0)
protocol-http2 (~> 0.15.0)
traces (>= 0.10.0)
async-http-cache (0.4.3)
async-http (~> 0.56)
async-io (1.36.0)
async
async-pool (0.4.0)
async (>= 1.25)
bigdecimal (3.1.4)
build-environment (1.13.0)
builder (3.2.4)
capybara (3.39.2)
addressable
@@ -37,9 +14,6 @@ GEM
rack-test (>= 0.6.3)
regexp_parser (>= 1.5, < 3.0)
xpath (~> 3.2)
console (1.23.2)
fiber-annotation
fiber-local
cucumber (9.0.2)
builder (~> 3.2, >= 3.2.4)
cucumber-ci-environment (~> 9.2, >= 9.2.0)
@@ -65,52 +39,27 @@ GEM
cucumber-messages (21.0.1)
cucumber-tag-expressions (4.1.0)
diff-lcs (1.5.0)
docile (1.4.0)
erubi (1.12.0)
falcon (0.42.3)
async
async-container (~> 0.16.0)
async-http (~> 0.57)
async-http-cache (~> 0.4.0)
async-io (~> 1.22)
build-environment (~> 1.13)
bundler
localhost (~> 1.1)
openssl (~> 3.0)
process-metrics (~> 0.2.0)
protocol-rack (~> 0.1)
samovar (~> 2.1)
ffi (1.15.5)
fiber-annotation (0.2.0)
fiber-local (1.0.0)
io-event (1.3.2)
localhost (1.1.10)
mapping (1.1.1)
ffi (1.16.2)
matrix (0.4.2)
mini_mime (1.1.5)
mini_portile2 (2.8.4)
multi_test (1.1.0)
nio4r (2.5.9)
nokogiri (1.15.4)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
openssl (3.1.0)
process-metrics (0.2.1)
console (~> 1.8)
samovar (~> 2.1)
protocol-hpack (1.4.2)
protocol-http (0.24.7)
protocol-http1 (0.15.1)
protocol-http (~> 0.22)
protocol-http2 (0.15.1)
protocol-hpack (~> 1.4)
protocol-http (~> 0.18)
protocol-rack (0.2.6)
protocol-http (~> 0.23)
rack (>= 1.0)
public_suffix (5.0.3)
puma (6.4.0)
nio4r (~> 2.0)
racc (1.7.1)
rack (3.0.8)
rack-test (2.1.0)
rack (>= 1.3)
rackup (2.1.0)
rack (>= 3)
webrick (~> 1.8)
regexp_parser (2.8.1)
rexml (3.2.6)
roda (3.72.0)
@@ -129,22 +78,24 @@ GEM
rspec-support (~> 3.12.0)
rspec-support (3.12.1)
rubyzip (2.3.2)
samovar (2.2.0)
console (~> 1.0)
mapping (~> 1.0)
selenium-webdriver (4.13.1)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0)
sequel (5.72.0)
sequel (5.73.0)
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)
mini_portile2 (~> 2.8.0)
sys-uname (1.2.3)
ffi (~> 1.1)
tilt (2.2.0)
timers (4.3.5)
traces (0.11.1)
tilt (2.3.0)
webrick (1.8.1)
websocket (1.2.10)
xpath (3.2.0)
nokogiri (~> 1.8)
@@ -156,11 +107,13 @@ DEPENDENCIES
capybara (~> 3.39)
cucumber (~> 9.0)
erubi (~> 1.12)
falcon (~> 0.42.3)
puma (~> 6.4)
rackup (~> 2.1)
roda (~> 3.72)
rspec (~> 3.12)
selenium-webdriver (~> 4.13)
sequel (~> 5.72)
simplecov
sqlite3 (~> 1.6)
tilt (~> 2.2)
Vendored
+61
View File
@@ -0,0 +1,61 @@
pipeline {
agent any
environment {
APP_SESSION_SECRET = ''
DB_NAME = 'url_shortener.db'
}
stages {
stage('Init') {
steps {
sh 'rbenv local 3.2.2'
script {
env.APP_SESSION_SECRET = sh(script: 'ruby secret.rb', returnStdout: true)
}
sh 'echo "ENV[\\\"APP_SESSION_SECRET\\\"] ||= $(ruby secret.rb)" > .env.rb'
sh 'echo "ENV[\\\"DB_NAME\\\"] ||= \\\"${DB_NAME}\\\"" >> .env.rb'
sh 'cat .env.rb'
}
}
stage('Build dependencies') {
steps {
sh 'bundle install'
sh 'sequel -m db/migrations sqlite://db/${DB_NAME}'
}
}
stage('Run tests') {
steps {
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
sh 'cucumber features --format html --out cucumber.html'
}
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
sh 'rspec spec --format html --out spec.html'
}
}
}
stage('Report results') {
steps {
archive(includes: 'pkg/*.gem')
publishHTML (target: [
allowMissing: false,
alwaysLinkToLastBuild: false,
keepAll: true,
reportDir: '.',
reportFiles: 'cucumber.html, spec.html, coverage/index.html',
reportName: 'Test Results',
reportTitles: 'Cucumber Results, RSpec Results, Test Coverage'])
}
}
}
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"
}
}
}
+30 -2
View File
@@ -5,6 +5,34 @@ Roda, Falcon, Sequel, and SQLite
the point of this project is to quickly build something and work on continuous deployment while making small refinements to the functional pieces.
before running the server, you must build the database by running the following:
the outside pieces of software that this project relies on are sqlite3 and pkgconf.
`sequel -m db/migrations sqlite://db/url_shortener.db`
first you have to install the dependencies:
`bundle install`
if you want the development group included run this first:
`bundle config set --local with 'development'`
then create a .env.rb file in the root directory that contains the following ENV attributes:
```
ENV["APP_SESSION_SECRET"] = {output of a random 64 byte secret}
ENV["DB_NAME"] = {db file name}
```
after the dependencies are installed, you have to create the db
`sequel -m db/migrations sqlite://db/{DB_NAME}`
to start the application with Falcon:
`rackup -o {ip address} -p {port} -s falcon`
+80 -7
View File
@@ -2,29 +2,102 @@ require 'roda'
require 'securerandom'
require 'json'
require 'sequel'
require 'open-uri'
class App < Roda
DB = Sequel.sqlite('db/url_shortener.db')
links = DB[:links]
plugin :sessions, secret: ENV.delete('APP_SESSION_SECRET')
plugin :render, escape: true
plugin :flash
plugin :json_parser
plugin :request_headers
DB = Sequel.sqlite("db/#{ENV['DB_NAME']}")
links = DB[:links]
route do |r|
r.root do
@message = flash['message'] || "Enter a URL"
view :home
end
r.get String do | url_code |
link = links.filter(:code => url_code).first[:url]
r.redirect link
link = links.filter(:code => url_code)
r.redirect link.first[:url] unless link.first.nil?
@message = "Link #{url_code} doesn't exist"
response.status = 404
view :home
end
r.post "create" do
url = r.params['url']
if nil == links.filter(:url => url).first
if url.nil? or url.empty?
flash['message'] = "Please enter a valid URL";
r.redirect '/'
end
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 => e
flash['message'] = "URL does not resolve"
r.redirect '/'
end
if links.filter(:url => url).first.nil?
code = SecureRandom.urlsafe_base64 4
links.insert(url: url, code: code)
@message = "Link created"
end
code = links.filter(:url => url).first[:code]
@message ||= "Link exists"
@new_link = 'http://' + request.env['HTTP_HOST'] + '/' + code
view :create
end
r.on "links" do
r.post do
if 'application/json' != r.headers['CONTENT_TYPE']
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 => e
response.status = 400
return {message: "url does not resolve"}.to_json
end
if links.filter(:url => url).first.nil?
code = SecureRandom.urlsafe_base64 4
links.insert(url: url, code: code)
end
@new_link = 'http://' + request.env['HTTP_HOST'] + '/' + links.filter(:url => url).first[:code]
view :create
code = links.filter(:url => url).first[:code]
@new_link = 'http://' + request.env['HTTP_HOST'] + '/' + code
return {url: url, code: code, link: @new_link}.to_json
end
end
end
end
+1
View File
@@ -1,3 +1,4 @@
require './.env'
require './app'
run App.app
+6 -3
View File
@@ -4,6 +4,9 @@ Feature: Homepage
Loading the homepage is the initial starting point for this tool
Scenario: Homepage Loads
Given I visit the homepage
Then I should see "URL Shortener"
Scenario: Homepage Loads with a form
Given I visit the "/" page
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 "Submit" button
+63 -4
View File
@@ -1,15 +1,74 @@
# BEFORE
Before('@db-test') do
@links = Sequel.sqlite("db/#{ENV['DB_NAME']}")[:links]
end
# GIVEN
Given('I visit the homepage') do
visit '/'
Given('I visit the {string} page') do |string|
visit string
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: url, code: code)
end
# WHEN
When('I click the {string} button') do |string|
click_button {string}
end
When('I type {string} in the {string} field') do |text, field|
fill_in field, with: text
end
When('I visit the {string} location') do |string|
visit string
end
# THEN
Then('I should see {string}') do |string|
page.should have_content "URL Shortener"
Then('I should see text {string}') do |string|
page.should have_content string
end
Then('I should see a form field {string}') do |string|
page.should have_field string
end
Then('I should see a {string} button') do |string|
page.should have_button string
end
Then('I should be on {string} page') do |string|
page.should have_current_path string
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
+52
View File
@@ -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"
+12
View File
@@ -1,11 +1,23 @@
require 'simplecov'
SimpleCov.start
require_relative '../../.env'
ENV["DB_NAME"] = "test_#{ENV["DB_NAME"]}"
require_relative '../../app'
require 'rubygems'
require 'roda'
require 'sequel'
require 'capybara'
require 'capybara/dsl'
require 'rspec'
# DB initialization
Sequel.extension :migration
Sequel.sqlite("db/#{ENV['DB_NAME']}") do |db|
Sequel::Migrator.apply(db, "db/migrations")
end
# attach app to Capybara
Capybara.app = App
include Capybara::DSL
+19
View File
@@ -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
+2
View File
@@ -0,0 +1,2 @@
require 'securerandom'
puts SecureRandom.base64(64).inspect()
+101
View File
@@ -0,0 +1,101 @@
require 'simplecov'
SimpleCov.start
require_relative '../.env'
ENV["DB_NAME"] = "test_#{ENV["DB_NAME"]}"
require_relative '../app'
require 'rubygems'
require 'roda'
require 'sequel'
require 'rspec'
require 'rack/test'
# DB initialization
Sequel.extension :migration
Sequel.sqlite("db/#{ENV['DB_NAME']}") 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['DB_NAME']}")[: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
View File
@@ -1,2 +1 @@
<h2><a href="/" style="color: #555555;">Home</a></h2>
<h3 style="overflow-wrap: break-word; word-wrap: break-word;"><a href="<%= @new_link %>" target="_blank" style="color: #555555;"><%= @new_link %></a></h3>
<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>
+6 -2
View File
@@ -1,9 +1,13 @@
<form method="POST" action="/create">
<div style="margin-bottom: 1rem;">
<input type="url"
name="url"
pattern="https?://.+"
placeholder="http://www.example.com/"
style="padding: 0.5rem;"
autocomplete="off"
required />
<input type="submit" value="Submit" style="padding: 0.5rem;" />
</div>
<div>
<button type="submit">Submit</button>
</div>
</form>
+99 -3
View File
@@ -1,11 +1,107 @@
<html>
<head>
<title><%= @page_title || "URL Shortener" %></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<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>
<body style="background: #FEFEFE; font-family: Helvetica, sans-serif; color: #555555;">
<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;">
<h1>URL Shortener</h1>
<body style="">
<main>
<h1><a href="/">URL Shortener</a></h1>
<h4 id="message"><%== @message %></h4>
<%== yield %>
</main>
</body>