Initial commit for backend
This commit is contained in:
parent
06bbc5b3ed
commit
9c13cb06bb
12
frontend/.gitignore
vendored
Normal file
12
frontend/.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
# Created by https://www.gitignore.io/api/elm
|
||||||
|
# Edit at https://www.gitignore.io/?templates=elm
|
||||||
|
|
||||||
|
### Elm ###
|
||||||
|
# elm-package generated files
|
||||||
|
elm-stuff
|
||||||
|
# elm-repl generated files
|
||||||
|
repl-temp-*
|
||||||
|
|
||||||
|
# End of https://www.gitignore.io/api/elm
|
||||||
|
index.html
|
28
frontend/elm.json
Normal file
28
frontend/elm.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"type": "application",
|
||||||
|
"source-directories": [
|
||||||
|
"src"
|
||||||
|
],
|
||||||
|
"elm-version": "0.19.0",
|
||||||
|
"dependencies": {
|
||||||
|
"direct": {
|
||||||
|
"elm/browser": "1.0.1",
|
||||||
|
"elm/core": "1.0.2",
|
||||||
|
"elm/html": "1.0.0"
|
||||||
|
},
|
||||||
|
"indirect": {
|
||||||
|
"elm/json": "1.1.3",
|
||||||
|
"elm/time": "1.0.0",
|
||||||
|
"elm/url": "1.0.0",
|
||||||
|
"elm/virtual-dom": "1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test-dependencies": {
|
||||||
|
"direct": {
|
||||||
|
"elm-explorations/test": "1.2.2"
|
||||||
|
},
|
||||||
|
"indirect": {
|
||||||
|
"elm/random": "1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
frontend/src/Data.elm
Normal file
45
frontend/src/Data.elm
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
module Data exposing (Provider(..), Url, pathSeparator, toHost, toUrl)
|
||||||
|
|
||||||
|
|
||||||
|
hostname : String
|
||||||
|
hostname =
|
||||||
|
"https://cdn.hitsofcode.com/"
|
||||||
|
|
||||||
|
|
||||||
|
type Provider
|
||||||
|
= GitHub
|
||||||
|
| Bitbucket
|
||||||
|
|
||||||
|
|
||||||
|
type alias Url =
|
||||||
|
{ prov : Provider
|
||||||
|
, user : String
|
||||||
|
, repo : String
|
||||||
|
, gitref : String
|
||||||
|
, file : String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
toHost : Provider -> String
|
||||||
|
toHost prov =
|
||||||
|
case prov of
|
||||||
|
GitHub ->
|
||||||
|
"github/"
|
||||||
|
|
||||||
|
Bitbucket ->
|
||||||
|
"bitbucket/"
|
||||||
|
|
||||||
|
|
||||||
|
pathSeparator : Provider -> String
|
||||||
|
pathSeparator prov =
|
||||||
|
case prov of
|
||||||
|
GitHub ->
|
||||||
|
"blob"
|
||||||
|
|
||||||
|
Bitbucket ->
|
||||||
|
"src"
|
||||||
|
|
||||||
|
|
||||||
|
toUrl : Url -> String
|
||||||
|
toUrl { prov, user, repo, gitref, file } =
|
||||||
|
hostname ++ toHost prov ++ String.join "/" [ user, repo, pathSeparator prov, gitref, file ]
|
98
frontend/src/Main.elm
Normal file
98
frontend/src/Main.elm
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
module Main exposing (Model, Msg(..), init, main, update, view)
|
||||||
|
|
||||||
|
import Browser
|
||||||
|
import Data exposing (Url, toHost, toUrl)
|
||||||
|
import Html exposing (Html, br, div, input, table, td, text, tr)
|
||||||
|
import Html.Attributes exposing (disabled, placeholder, style, value)
|
||||||
|
import Html.Events exposing (onInput)
|
||||||
|
import Parse exposing (parseUrl)
|
||||||
|
|
||||||
|
|
||||||
|
type Msg
|
||||||
|
= UrlChange String
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
{ url : String
|
||||||
|
, parsed : Maybe Url
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
init : Model
|
||||||
|
init =
|
||||||
|
{ url = ""
|
||||||
|
, parsed = Nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
update : Msg -> Model -> Model
|
||||||
|
update msg state =
|
||||||
|
case msg of
|
||||||
|
UrlChange newUrl ->
|
||||||
|
{ state | url = newUrl, parsed = parseUrl newUrl }
|
||||||
|
|
||||||
|
|
||||||
|
renderUrl : Url -> Html msg
|
||||||
|
renderUrl { prov, user, repo, file } =
|
||||||
|
div myStyle
|
||||||
|
[ table myStyle
|
||||||
|
[ tr myStyle
|
||||||
|
[ td myStyle [ text "host" ]
|
||||||
|
, td myStyle [ text (toHost prov) ]
|
||||||
|
]
|
||||||
|
, tr []
|
||||||
|
[ td myStyle [ text "user" ]
|
||||||
|
, td myStyle [ text user ]
|
||||||
|
]
|
||||||
|
, tr myStyle
|
||||||
|
[ td myStyle [ text "repo" ]
|
||||||
|
, td myStyle [ text repo ]
|
||||||
|
]
|
||||||
|
, tr myStyle
|
||||||
|
[ td myStyle [ text "file" ]
|
||||||
|
, td myStyle [ text file ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
renderMUrl : Maybe Url -> Html msg
|
||||||
|
renderMUrl mUrl =
|
||||||
|
mUrl
|
||||||
|
|> Maybe.map renderUrl
|
||||||
|
|> Maybe.withDefault (div myStyle [ text "Parse Error" ])
|
||||||
|
|
||||||
|
|
||||||
|
displayMUrl : Maybe Url -> String
|
||||||
|
displayMUrl mUrl =
|
||||||
|
mUrl
|
||||||
|
|> Maybe.map toUrl
|
||||||
|
|> Maybe.withDefault ""
|
||||||
|
|
||||||
|
|
||||||
|
myStyle : List (Html.Attribute msg)
|
||||||
|
myStyle =
|
||||||
|
[ style "width" "100%" ]
|
||||||
|
|
||||||
|
|
||||||
|
myStyle2 : List (Html.Attribute msg) -> List (Html.Attribute msg)
|
||||||
|
myStyle2 =
|
||||||
|
List.append myStyle
|
||||||
|
|
||||||
|
|
||||||
|
view : Model -> Html Msg
|
||||||
|
view state =
|
||||||
|
div myStyle
|
||||||
|
[ input (myStyle2 [ placeholder "URL to parse", value state.url, onInput UrlChange ]) []
|
||||||
|
, div myStyle
|
||||||
|
[ text "Parsed URL: "
|
||||||
|
, br [] []
|
||||||
|
, renderMUrl state.parsed
|
||||||
|
]
|
||||||
|
, input (myStyle2 [ placeholder "https://host/<service>/<user>/<repo>/<gitref>/<file>", disabled True, value (displayMUrl state.parsed) ]) []
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
main : Program () Model Msg
|
||||||
|
main =
|
||||||
|
Browser.sandbox { init = init, update = update, view = view }
|
87
frontend/src/Parse.elm
Normal file
87
frontend/src/Parse.elm
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
module Parse exposing (parseUrl)
|
||||||
|
|
||||||
|
import Data exposing (Provider(..), Url, pathSeparator)
|
||||||
|
|
||||||
|
|
||||||
|
parseUrl : String -> Maybe Url
|
||||||
|
parseUrl url =
|
||||||
|
stripProtocol url
|
||||||
|
|> splitProvider
|
||||||
|
|> Maybe.andThen splitOfHead
|
||||||
|
|> Maybe.andThen splitOfHead
|
||||||
|
|> Maybe.andThen splitOfHead
|
||||||
|
|> Maybe.andThen splitOfHead
|
||||||
|
|> Maybe.andThen
|
||||||
|
(\( ( ( ( ( prov, user ), repo ), separator ), gitref ), file ) ->
|
||||||
|
if List.isEmpty file || (separator /= pathSeparator prov) then
|
||||||
|
Nothing
|
||||||
|
|
||||||
|
else
|
||||||
|
Just
|
||||||
|
{ prov = prov
|
||||||
|
, user = user
|
||||||
|
, repo = repo
|
||||||
|
, gitref = gitref
|
||||||
|
, file = String.join "/" file
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
splitOfHead : ( a, List b ) -> Maybe ( ( a, b ), List b )
|
||||||
|
splitOfHead ( head, tail ) =
|
||||||
|
splitPart tail
|
||||||
|
|> Maybe.map (\( h, t ) -> ( ( head, h ), t ))
|
||||||
|
|
||||||
|
|
||||||
|
stripProtocol : String -> String
|
||||||
|
stripProtocol url =
|
||||||
|
let
|
||||||
|
index =
|
||||||
|
String.indexes "://" url
|
||||||
|
|> List.head
|
||||||
|
|> Maybe.withDefault -3
|
||||||
|
in
|
||||||
|
String.dropLeft (index + 3) url
|
||||||
|
|
||||||
|
|
||||||
|
parseProvider : String -> Maybe Provider
|
||||||
|
parseProvider prov =
|
||||||
|
case String.toLower prov of
|
||||||
|
"github.com" ->
|
||||||
|
Just GitHub
|
||||||
|
|
||||||
|
"bitbucket.org" ->
|
||||||
|
Just Bitbucket
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Nothing
|
||||||
|
|
||||||
|
|
||||||
|
splitProvider : String -> Maybe ( Provider, List String )
|
||||||
|
splitProvider url =
|
||||||
|
let
|
||||||
|
split =
|
||||||
|
String.split "/" url
|
||||||
|
|
||||||
|
parts =
|
||||||
|
splitPart split
|
||||||
|
in
|
||||||
|
parts
|
||||||
|
|> Maybe.andThen
|
||||||
|
(\( head, tail ) ->
|
||||||
|
parseProvider head
|
||||||
|
|> Maybe.map (\prov -> ( prov, tail ))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
splitPart : List a -> Maybe ( a, List a )
|
||||||
|
splitPart parts =
|
||||||
|
let
|
||||||
|
head =
|
||||||
|
List.head parts
|
||||||
|
|
||||||
|
tail =
|
||||||
|
List.tail parts
|
||||||
|
in
|
||||||
|
head
|
||||||
|
|> Maybe.andThen (\h -> Maybe.map (\t -> ( h, t )) tail)
|
92
frontend/tests/ParseTest.elm
Normal file
92
frontend/tests/ParseTest.elm
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
module ParseTest exposing
|
||||||
|
( invalidHostInUrlBitbucket
|
||||||
|
, invalidHostInUrlGitHub
|
||||||
|
, invalidUrlBitbucket
|
||||||
|
, invalidUrlGitHub
|
||||||
|
, validHttpUrlBitbucket
|
||||||
|
, validHttpUrlGitHub
|
||||||
|
, validHttpsUrlBitbucket
|
||||||
|
, validHttpsUrlGitHub
|
||||||
|
, validUrlWithoutProtocolBitbucket
|
||||||
|
, validUrlWithoutProtocolGitHub
|
||||||
|
)
|
||||||
|
|
||||||
|
import Data exposing (Provider(..), Url)
|
||||||
|
import Expect
|
||||||
|
import Parse exposing (parseUrl)
|
||||||
|
import Test exposing (Test, test)
|
||||||
|
|
||||||
|
|
||||||
|
expectedUrl : Provider -> Url
|
||||||
|
expectedUrl prov =
|
||||||
|
{ prov = prov, user = "user", repo = "repo", gitref = "master", file = "README.md" }
|
||||||
|
|
||||||
|
|
||||||
|
expectedGitHubUrl : Url
|
||||||
|
expectedGitHubUrl =
|
||||||
|
expectedUrl GitHub
|
||||||
|
|
||||||
|
|
||||||
|
expectedBitbucketUrl : Url
|
||||||
|
expectedBitbucketUrl =
|
||||||
|
expectedUrl Bitbucket
|
||||||
|
|
||||||
|
|
||||||
|
validHttpsUrlGitHub : Test
|
||||||
|
validHttpsUrlGitHub =
|
||||||
|
test "Parsing Valid HTTPS URL for GitHub"
|
||||||
|
(\_ -> Expect.equal (Just expectedGitHubUrl) (parseUrl "https://GiThUb.CoM/user/repo/blob/master/README.md"))
|
||||||
|
|
||||||
|
|
||||||
|
validHttpUrlGitHub : Test
|
||||||
|
validHttpUrlGitHub =
|
||||||
|
test "Parsing Valid HTTP URL for GitHub"
|
||||||
|
(\_ -> Expect.equal (Just expectedGitHubUrl) (parseUrl "http://GiThUb.CoM/user/repo/blob/master/README.md"))
|
||||||
|
|
||||||
|
|
||||||
|
validUrlWithoutProtocolGitHub : Test
|
||||||
|
validUrlWithoutProtocolGitHub =
|
||||||
|
test "Parsing Valid URL without Protocol for GitHub"
|
||||||
|
(\_ -> Expect.equal (Just expectedGitHubUrl) (parseUrl "GiThUb.CoM/user/repo/blob/master/README.md"))
|
||||||
|
|
||||||
|
|
||||||
|
invalidUrlGitHub : Test
|
||||||
|
invalidUrlGitHub =
|
||||||
|
test "Parsing Invalid URL for GitHub"
|
||||||
|
(\_ -> Expect.equal Nothing (parseUrl "https://GiThUb.CoM/user"))
|
||||||
|
|
||||||
|
|
||||||
|
invalidHostInUrlGitHub : Test
|
||||||
|
invalidHostInUrlGitHub =
|
||||||
|
test "Parsing Invalid Host in URL for GitHub"
|
||||||
|
(\_ -> Expect.equal Nothing (parseUrl "https://example.com/user/repo/blob/master/README.md"))
|
||||||
|
|
||||||
|
|
||||||
|
validHttpsUrlBitbucket : Test
|
||||||
|
validHttpsUrlBitbucket =
|
||||||
|
test "Parsing Valid HTTPS URL for Bitbucket"
|
||||||
|
(\_ -> Expect.equal (Just expectedBitbucketUrl) (parseUrl "https://bItBuCkEt.OrG/user/repo/src/master/README.md"))
|
||||||
|
|
||||||
|
|
||||||
|
validHttpUrlBitbucket : Test
|
||||||
|
validHttpUrlBitbucket =
|
||||||
|
test "Parsing Valid HTTP URL for Bitbucket"
|
||||||
|
(\_ -> Expect.equal (Just expectedBitbucketUrl) (parseUrl "http://BiTbUcKeT.oRg/user/repo/src/master/README.md"))
|
||||||
|
|
||||||
|
|
||||||
|
validUrlWithoutProtocolBitbucket : Test
|
||||||
|
validUrlWithoutProtocolBitbucket =
|
||||||
|
test "Parsing Valid URL without Protocol for Bitbucket"
|
||||||
|
(\_ -> Expect.equal (Just expectedBitbucketUrl) (parseUrl "bitbucket.org/user/repo/src/master/README.md"))
|
||||||
|
|
||||||
|
|
||||||
|
invalidUrlBitbucket : Test
|
||||||
|
invalidUrlBitbucket =
|
||||||
|
test "Parsing Invalid URL for Bitbucket"
|
||||||
|
(\_ -> Expect.equal Nothing (parseUrl "https://bitBucket.ORG/user"))
|
||||||
|
|
||||||
|
|
||||||
|
invalidHostInUrlBitbucket : Test
|
||||||
|
invalidHostInUrlBitbucket =
|
||||||
|
test "Parsing Invalid Host in URL for Bitbucket"
|
||||||
|
(\_ -> Expect.equal Nothing (parseUrl "https://example.com/user/repo/blob/src/README.md"))
|
Loading…
Reference in New Issue
Block a user