From b030de85bae1cc550814a4f4f99f54054b37dadd Mon Sep 17 00:00:00 2001 From: Dillon Kearns Date: Wed, 18 May 2022 08:43:50 -0700 Subject: [PATCH] Show search results in trails. --- examples/trails/app/Route/Search.elm | 72 +++++++++++++++++++++---- examples/trails/src/Request/Fauna.elm | 70 ------------------------ examples/trails/src/Request/Hasura.elm | 74 ++++++++++++++++++++++++++ src/Server/Request.elm | 63 ++++++++++++++++++++++ 4 files changed, 199 insertions(+), 80 deletions(-) delete mode 100644 examples/trails/src/Request/Fauna.elm create mode 100644 examples/trails/src/Request/Hasura.elm diff --git a/examples/trails/app/Route/Search.elm b/examples/trails/app/Route/Search.elm index 02ac9e7d..556b5dc8 100644 --- a/examples/trails/app/Route/Search.elm +++ b/examples/trails/app/Route/Search.elm @@ -1,8 +1,15 @@ module Route.Search exposing (ActionData, Data, Model, Msg, route) +import Api.InputObject +import Api.Object +import Api.Object.Trails +import Api.Query import DataSource exposing (DataSource) import Effect exposing (Effect) import ErrorPage exposing (ErrorPage) +import Graphql.Operation exposing (RootQuery) +import Graphql.OptionalArgument exposing (OptionalArgument(..)) +import Graphql.SelectionSet exposing (SelectionSet) import Head import Head.Seo as Seo import Html exposing (Html) @@ -11,6 +18,7 @@ import Pages.Msg import Pages.PageUrl exposing (PageUrl) import Pages.Url import Path exposing (Path) +import Request.Hasura import RouteBuilder exposing (StatefulRoute, StatelessRoute, StaticPayload) import Server.Request as Request import Server.Response as Response exposing (Response) @@ -95,15 +103,18 @@ data routeParams = field "q" |> Request.map (\query -> - DataSource.succeed - (Response.render - { results = - Just - { query = query - , results = [ "Hello" ] + Request.Hasura.dataSource "" + (search query) + |> DataSource.map + (\results -> + Response.render + { results = + Just + { query = query + , results = results + } } - } - ) + ) ) ) , Request.succeed (DataSource.succeed (Response.render { results = Nothing })) @@ -121,7 +132,7 @@ head : head static = Seo.summary { canonicalUrlOverride = Nothing - , siteName = "elm-pages" + , siteName = "Trail Blazer" , image = { url = Pages.Url.external "TODO" , alt = "elm-pages logo" @@ -130,7 +141,16 @@ head static = } , description = "TODO" , locale = Nothing - , title = "TODO title" -- metadata.title -- TODO + , title = + case static.data.results of + Nothing -> + "Find your next trail" + + Just { results, query } -> + query + ++ " at TrailBlazer (" + ++ String.fromInt (List.length results) + ++ " results)" } |> Seo.website @@ -163,4 +183,36 @@ resultsView : SearchResults -> Html msg resultsView results = Html.div [] [ Html.h2 [] [ Html.text <| "Results matching " ++ results.query ] + , results.results + |> List.map + (\result -> + Html.li [] + [ Html.text result + ] + ) + |> Html.ul [] ] + + +search : String -> SelectionSet (List String) RootQuery +search query = + Api.Query.trails + (\optionals -> + { optionals + | where_ = + Present + (Api.InputObject.buildTrails_bool_exp + (\whereOptionals -> + { whereOptionals + | name = + Api.InputObject.buildString_comparison_exp + (\stringOptionals -> + { stringOptionals | ilike_ = Present <| "%" ++ query ++ "%" } + ) + |> Present + } + ) + ) + } + ) + Api.Object.Trails.name diff --git a/examples/trails/src/Request/Fauna.elm b/examples/trails/src/Request/Fauna.elm deleted file mode 100644 index 17ad2e88..00000000 --- a/examples/trails/src/Request/Fauna.elm +++ /dev/null @@ -1,70 +0,0 @@ -module Request.Fauna exposing (dataSource, mutationDataSource) - -import DataSource exposing (DataSource) -import DataSource.Http -import Graphql.Document -import Graphql.Operation exposing (RootMutation, RootQuery) -import Graphql.SelectionSet exposing (SelectionSet) -import Json.Encode as Encode - - -dataSource : String -> SelectionSet value RootQuery -> DataSource value -dataSource timeStamp selectionSet = - DataSource.Http.request - { url = - faunaUrl - -- for now, this timestamp invalidates the dev server cache - -- it would be helpful to have a way to mark a DataSource as uncached. Maybe only allow - -- from server-rendered pages? - ++ "?time=" - ++ timeStamp - , method = "POST" - , headers = [ ( "authorization", faunaAuthValue ) ] - , body = - DataSource.Http.jsonBody - (Encode.object - [ ( "query" - , selectionSet - |> Graphql.Document.serializeQuery - |> Encode.string - ) - ] - ) - } - (selectionSet - |> Graphql.Document.decoder - |> DataSource.Http.expectJson - ) - - -mutationDataSource : String -> SelectionSet value RootMutation -> DataSource value -mutationDataSource timeStamp selectionSet = - DataSource.Http.request - { url = faunaUrl ++ "?time=" ++ timeStamp - , method = "POST" - , headers = [ ( "authorization", faunaAuthValue ) ] - , body = - DataSource.Http.jsonBody - (Encode.object - [ ( "query" - , selectionSet - |> Graphql.Document.serializeMutation - |> Encode.string - ) - ] - ) - } - (selectionSet - |> Graphql.Document.decoder - |> DataSource.Http.expectJson - ) - - -faunaUrl : String -faunaUrl = - "https://graphql.us.fauna.com/graphql" - - -faunaAuthValue : String -faunaAuthValue = - "Bearer fnAEdqJ_JdAAST7wRrjZj7NKSw-vCfE9_W8RyshZ" diff --git a/examples/trails/src/Request/Hasura.elm b/examples/trails/src/Request/Hasura.elm new file mode 100644 index 00000000..9d7acfdc --- /dev/null +++ b/examples/trails/src/Request/Hasura.elm @@ -0,0 +1,74 @@ +module Request.Hasura exposing (dataSource, mutationDataSource) + +import DataSource exposing (DataSource) +import DataSource.Env +import DataSource.Http +import Graphql.Document +import Graphql.Operation exposing (RootMutation, RootQuery) +import Graphql.SelectionSet exposing (SelectionSet) +import Json.Encode as Encode + + +dataSource : String -> SelectionSet value RootQuery -> DataSource value +dataSource timeStamp selectionSet = + DataSource.Env.expect "TRAILS_HASURA_SECRET" + |> DataSource.andThen + (\hasuraSecret -> + DataSource.Http.request + { url = + hasuraUrl + -- for now, this timestamp invalidates the dev server cache + -- it would be helpful to have a way to mark a DataSource as uncached. Maybe only allow + -- from server-rendered pages? + ++ "?time=" + ++ timeStamp + , method = "POST" + , headers = [ ( "x-hasura-admin-secret", hasuraSecret ) ] + , body = + DataSource.Http.jsonBody + (Encode.object + [ ( "query" + , selectionSet + |> Graphql.Document.serializeQuery + |> Encode.string + ) + ] + ) + } + (selectionSet + |> Graphql.Document.decoder + |> DataSource.Http.expectJson + ) + ) + + +mutationDataSource : String -> SelectionSet value RootMutation -> DataSource value +mutationDataSource timeStamp selectionSet = + DataSource.Env.expect "TRAILS_HASURA_SECRET" + |> DataSource.andThen + (\hasuraSecret -> + DataSource.Http.request + { url = hasuraUrl ++ "?time=" ++ timeStamp + , method = "POST" + , headers = [ ( "x-hasura-admin-secret", hasuraSecret ) ] + , body = + DataSource.Http.jsonBody + (Encode.object + [ ( "query" + , selectionSet + |> Graphql.Document.serializeMutation + |> Encode.string + ) + ] + ) + } + (selectionSet + |> Graphql.Document.decoder + |> DataSource.Http.expectJson + ) + ) + + +hasuraUrl : String +hasuraUrl = + "https://striking-mutt-82.hasura.app/v1/graphql" diff --git a/src/Server/Request.elm b/src/Server/Request.elm index 68f63fa4..befc0fa9 100644 --- a/src/Server/Request.elm +++ b/src/Server/Request.elm @@ -14,6 +14,7 @@ module Server.Request exposing , map3, map4, map5, map6, map7, map8, map9 , Method(..), methodToString , errorsToString, errorToString, getDecoder, ValidationError + , expectForm ) {-| @@ -966,6 +967,68 @@ expectFormPost toForm = ) +{-| -} +expectForm : + ({ field : String -> Parser String + , optionalField : String -> Parser (Maybe String) + } + -> Parser decodedForm + ) + -> Parser decodedForm +expectForm toForm = + map2 Tuple.pair + queryParams + method + |> andThen + (\( queryParams_, validMethod ) -> + if validMethod == Get then + queryParams_ + |> Dict.map + (\k v -> + v + |> List.NonEmpty.fromList + |> Maybe.withDefault ( "", [] ) + ) + |> succeed + |> andThen + (\parsedForm -> + let + thing : Json.Encode.Value + thing = + parsedForm + |> Dict.toList + |> List.map + (Tuple.mapSecond + (\( first, _ ) -> + Json.Encode.string first + ) + ) + |> Json.Encode.object + + innerDecoder : Json.Decode.Decoder ( Result ValidationError decodedForm, List ValidationError ) + innerDecoder = + toForm { field = formField_, optionalField = optionalFormField_ } + |> (\(Internal.Request.Parser decoder) -> decoder) + in + Json.Decode.decodeValue innerDecoder thing + |> Result.mapError Json.Decode.errorToString + |> jsonFromResult + |> Internal.Request.Parser + ) + + else + Json.Decode.succeed + ( Err + (ValidationError <| + "TODO validation error for not matching GET form submission" + --"expectFormPost did not match - expected method GET, but the method was " ++ methodToString validMethod + ) + , [] + ) + |> Internal.Request.Parser + ) + + {-| -} expectMultiPartFormPost : ({ field : String -> Parser String