Show search results in trails.

This commit is contained in:
Dillon Kearns 2022-05-18 08:43:50 -07:00
parent 31265b4979
commit b030de85ba
4 changed files with 199 additions and 80 deletions

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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