initial commit

This commit is contained in:
Daniel Flanagan 2020-02-21 14:40:32 -06:00
commit 343020f450
Signed by: lytedev
GPG key ID: 5B2020A0F9921EF4
8 changed files with 1555 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
/dist
/elm-stuff
/node_modules

28
elm.json Normal file
View file

@ -0,0 +1,28 @@
{
"type": "application",
"source-directories": [
"src"
],
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"elm/browser": "1.0.2",
"elm/core": "1.0.5",
"elm/html": "1.0.0",
"elm/http": "2.0.0",
"elm/json": "1.1.3",
"elm/url": "1.0.0",
"wittjosiah/elm-ordered-dict": "1.0.1"
},
"indirect": {
"elm/bytes": "1.0.8",
"elm/file": "1.0.5",
"elm/time": "1.0.0",
"elm/virtual-dom": "1.0.2"
}
},
"test-dependencies": {
"direct": {},
"indirect": {}
}
}

24
makefile Normal file
View file

@ -0,0 +1,24 @@
HOST ?= localhost
.PHONY: build dev watch serve-and-watch
build: node_modules dist/index.html dist/styles.css dist/main.js
node_modules: package.json yarn.lock ; yarn install
dev: build node_modules
./node_modules/.bin/elm-live src/Main.elm --host="${HOST}" --dir=dist --hot --start-page=index.html -- --debug --output=dist/main.js
reactor: node_modules ; yarn elm reactor
watch: node_modules ; fswi "make build" src
dist/main.js: node_modules dist src/Main.elm
yarn elm make --output=dist/main.js src/Main.elm
dist/index.html: dist src/index.html
cp src/index.html dist/
dist/styles.css: node_modules dist src/styles.styl
yarn stylus src/styles.styl -o dist/
dist: ; mkdir -p dist/
clean: ; rm -rf dist/

7
package.json Normal file
View file

@ -0,0 +1,7 @@
{
"devDependencies": {
"elm": "^0.19.1-3",
"elm-live": "^4.0.2",
"stylus": "^0.54.7"
}
}

341
src/Main.elm Normal file
View file

@ -0,0 +1,341 @@
module Main exposing (main)
import Browser
import Dict
import Html as H exposing (Attribute, Html)
import Html.Attributes as A
import Html.Events as Ev
import Http
import Json.Decode as J exposing (Decoder)
import Json.Encode as Encode
import OrderedDict exposing (OrderedDict)
import Url
main =
Browser.element
{ init = init
, update = update
, subscriptions = \_ -> Sub.none
, view = view
}
type alias ArgType =
{ kind : String
, name : Maybe String
, ofType : OfArgType
}
type alias FieldType =
ArgType
type OfArgType
= OfArgType (Maybe ArgType)
type alias Arg =
{ defaultValue : J.Value
, description : Maybe String
, name : String
, argType : ArgType
}
type alias Directive =
{ args : List Arg
, locations : Maybe (List String)
, description : Maybe String
, name : String
}
type alias Field =
{ name : String
, description : Maybe String
, args : List Arg
, fieldType : FieldType
, isDeprecated : Bool
, deprecationReason : Maybe String
}
type alias Interface =
J.Value
type alias EnumValue =
J.Value
type alias PossibleType =
ArgType
type alias QLType =
{ kind : String
, name : String
, description : Maybe String
, fields : Maybe (List Field)
, inputFields : Maybe (List Field)
, interfaces : Maybe (List Interface)
, enumValues : Maybe (List EnumValue)
, possibleTypes : Maybe (List PossibleType)
}
type alias Introspection =
{ directives : List Directive
, mutationType : Maybe String
, queryType : Maybe String
, subscriptionType : Maybe String
, types : List QLType
}
type IntrospectionResult
= Loading
| Error Http.Error
| Success Introspection
type alias Model =
{ introspectUrl : String
, introspections : OrderedDict String IntrospectionResult
}
init : () -> ( Model, Cmd Msg )
init _ =
( Model "https://www.graphqlhub.com/graphql" OrderedDict.empty, Cmd.none )
type Msg
= UpdateIntrospectUrl String
| RequestIntrospection
| IntrospectionRequest String (Result Http.Error Introspection)
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
UpdateIntrospectUrl url ->
( { model | introspectUrl = url }, Cmd.none )
RequestIntrospection ->
( { model | introspections = OrderedDict.insert model.introspectUrl Loading model.introspections }, requestIntrospection model.introspectUrl )
IntrospectionRequest url result ->
let
val =
case result of
Ok i ->
Success i
Err e ->
Error e
in
( { model | introspections = OrderedDict.insert url val model.introspections }, Cmd.none )
requestIntrospection : String -> Cmd Msg
requestIntrospection url =
Http.post
{ url = url
, body = introspectionQuery
, expect = Http.expectJson (IntrospectionRequest url) decodeIntrospection
}
view : Model -> Html Msg
view model =
H.div [ A.id "app" ]
[ header model
, H.main_
[]
[ H.ul []
(List.filterMap
(\u -> introspectionResultView u (Dict.get u model.introspections.dict))
model.introspections.order
)
]
]
header : Model -> Html Msg
header model =
H.header (styleGroup [ ( "display", "flex" ), ( "align-items", "center" ) ])
[ H.h1 (styleGroup [ ( "font-size", "inherit" ), ( "padding", "0.5em" ), ( "width", "100%" ), ( "white-space", "nowrap" ), ( "flex", "1" ) ]) [ H.text "GraphQL Introspector" ]
, H.input
(styleGroup [ ( "width", "100%" ), ( "padding", "0.5em" ), ( "margin", "0.25em 0 0.25em 0" ) ]
++ [ A.value model.introspectUrl, Ev.onInput UpdateIntrospectUrl, A.placeholder "https://your-graphql-endpoint.example.com/graphql" ]
)
[]
, H.button (Ev.onClick RequestIntrospection :: styleGroup [ ( "padding", "0.5em" ), ( "margin", "0.25em" ) ]) [ H.text "Add" ]
]
introspectionResultView : String -> Maybe IntrospectionResult -> Maybe (Html Msg)
introspectionResultView url mir =
case mir of
Just ir ->
Just
(H.li []
([ H.text url ]
++ (case ir of
Loading ->
[ H.text "Loading..." ]
Error e ->
[ H.text ("Error: " ++ httpErrorToString e) ]
Success i ->
[ introspectionView i ]
)
)
)
Nothing ->
Nothing
introspectionView : Introspection -> Html Msg
introspectionView i =
let
allTypes =
List.filter
(\t ->
not
(List.member t.name <|
List.filterMap identity [ i.mutationType, i.queryType ]
)
)
i.types
in
H.div [] (List.map typeView allTypes)
typeView : QLType -> Html Msg
typeView qt =
H.div []
[ H.text (qt.name ++ ": " ++ qt.kind) ]
httpErrorToString : Http.Error -> String
httpErrorToString e =
case e of
Http.BadUrl s ->
"Bad URL: " ++ s
Http.Timeout ->
"Timeout"
Http.NetworkError ->
"Network Error"
Http.BadStatus n ->
"Bad Status: " ++ String.fromInt n
Http.BadBody s ->
"Bad Body: " ++ s
decodeIntrospection : Decoder Introspection
decodeIntrospection =
J.field "data"
(J.field "__schema"
(J.map5 Introspection
(J.field "directives" (J.list decodeDirective))
(J.maybe (J.field "queryType" (J.field "name" J.string)))
(J.maybe (J.field "mutationType" (J.field "name" J.string)))
(J.maybe (J.field "subscriptionType" (J.field "name" J.string)))
(J.field "types" (J.list decodeType))
)
)
decodeType : Decoder QLType
decodeType =
J.map8 QLType
(J.field "kind" J.string)
(J.field "name" J.string)
(J.maybe (J.field "description" J.string))
(J.maybe (J.field "fields" (J.list decodeField)))
(J.maybe (J.field "inputFields" (J.list decodeField)))
(J.maybe (J.field "interfaces" (J.list decodeInterface)))
(J.maybe (J.field "enumValues" (J.list decodeEnumValue)))
(J.maybe (J.field "possibleTypes" (J.list decodePossibleType)))
decodeField : Decoder Field
decodeField =
J.map6 Field
(J.field "name" J.string)
(J.maybe (J.field "description" J.string))
(J.field "args" (J.list decodeArg))
(J.field "type" decodeFieldType)
(J.field "isDeprecated" J.bool)
(J.maybe (J.field "deprecationReason" J.string))
decodePossibleType =
decodeArgType
decodeEnumValue =
J.value
decodeInterface =
J.value
decodeDirective : Decoder Directive
decodeDirective =
J.map4 Directive
(J.field "args" (J.list decodeArg))
(J.maybe (J.field "locations" (J.list J.string)))
(J.maybe (J.field "description" J.string))
(J.field "name" J.string)
decodeArg : Decoder Arg
decodeArg =
J.map4 Arg
(J.field "defaultValue" J.value)
(J.maybe (J.field "description" J.string))
(J.field "name" J.string)
(J.field "type" decodeArgType)
decodeFieldType =
J.map3 ArgType
(J.field "kind" J.string)
(J.maybe (J.field "name" J.string))
(J.field "ofType" decodeLazyArgType)
decodeArgType : Decoder ArgType
decodeArgType =
J.map3 ArgType
(J.field "kind" J.string)
(J.maybe (J.field "name" J.string))
(J.field "ofType" decodeLazyArgType)
decodeLazyArgType : Decoder OfArgType
decodeLazyArgType =
J.map OfArgType (J.maybe (J.lazy (\_ -> decodeArgType)))
introspectionQuery : Http.Body
introspectionQuery =
-- this query was copied from the one graphiql made
Http.jsonBody (Encode.object [ ( "query", Encode.string "query IntrospectionQuery { __schema { queryType { name } mutationType { name } subscriptionType { name } types { ...FullType } directives { name description args { ...InputValue }}}} fragment FullType on __Type { kind name description fields(includeDeprecated: true) { name description args { ...InputValue } type { ...TypeRef } isDeprecated deprecationReason } inputFields { ...InputValue } interfaces { ...TypeRef } enumValues(includeDeprecated: true) { name description isDeprecated deprecationReason } possibleTypes { ...TypeRef }}fragment InputValue on __InputValue { name description type { ...TypeRef } defaultValue}fragment TypeRef on __Type { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name }}}}}}}}" ) ])
styleGroup : List ( String, String ) -> List (Attribute Msg)
styleGroup l =
List.map (\( k, v ) -> A.style k v) l

16
src/index.html Normal file
View file

@ -0,0 +1,16 @@
<html>
<head>
<meta charset="utf-8" />
<title>GraphQL Introspector</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<div id="elm-main-mount"></div>
<script src="./main.js"></script>
<script>
Elm.Main.init({
node: document.getElementById('elm-main-mount')
})
</script>
</body>
</html>

25
src/styles.styl Normal file
View file

@ -0,0 +1,25 @@
#app
background #111
color #fff
font-family Iosevka, monospace
display flex
flex-direction column
justify-content space-between
min-height 100vh
a
color #0af
h1, h2, h3, h4, h5, h6
font-weight 400
input, select, button
font inherit
border 0
background #333
border-radius 0.25em
*
box-sizing border-box
margin 0
padding 0

1111
yarn.lock Normal file

File diff suppressed because it is too large Load diff