Add PostGIS GeoJSON support

Works for GET, POST, PATCH, PUT, DELETE, RPC.
This commit is contained in:
steve-chavez
2022-06-22 22:16:44 -05:00
committed by Steve Chavez
parent 86c40e8df9
commit 870f7a39b0
13 changed files with 300 additions and 14 deletions
+3
View File
@@ -271,6 +271,7 @@ handleRead headersOnly identifier context@RequestContext{..} = do
(shouldCount iPreferCount)
(iAcceptContentType == CTTextCSV)
(iAcceptContentType == CTTextXML)
(iAcceptContentType == CTGeoJSON)
bField
configDbPreparedStatements
@@ -464,6 +465,7 @@ handleInvoke invMethod proc context@RequestContext{..} = do
(iAcceptContentType == CTSingularJSON)
(iAcceptContentType == CTTextCSV)
(iAcceptContentType == CTTextXML)
(iAcceptContentType == CTGeoJSON)
(iPreferParameters == Just MultipleObjects)
bField
(configDbPreparedStatements ctxConfig)
@@ -551,6 +553,7 @@ writeQuery mutation identifier@QualifiedIdentifier{..} isInsert pkCols context@R
(iAcceptContentType ctxApiRequest == CTSingularJSON)
isInsert
(iAcceptContentType ctxApiRequest == CTTextCSV)
(iAcceptContentType ctxApiRequest == CTGeoJSON)
(iPreferRepresentation ctxApiRequest)
pkCols
(configDbPreparedStatements ctxConfig)
+3
View File
@@ -18,6 +18,7 @@ import Protolude
data ContentType
= CTApplicationJSON
| CTSingularJSON
| CTGeoJSON
| CTTextCSV
| CTTextPlain
| CTTextXML
@@ -40,6 +41,7 @@ toHeader ct = (hContentType, toMime ct <> charset)
-- | Convert from ContentType to a ByteString representing the mime type
toMime :: ContentType -> ByteString
toMime CTApplicationJSON = "application/json"
toMime CTGeoJSON = "application/geo+json"
toMime CTTextCSV = "text/csv"
toMime CTTextPlain = "text/plain"
toMime CTTextXML = "text/xml"
@@ -55,6 +57,7 @@ decodeContentType :: BS.ByteString -> ContentType
decodeContentType ct =
case BS.takeWhile (/= BS.c2w ';') ct of
"application/json" -> CTApplicationJSON
"application/geo+json" -> CTGeoJSON
"text/csv" -> CTTextCSV
"text/plain" -> CTTextPlain
"text/xml" -> CTTextXML
+4
View File
@@ -11,6 +11,7 @@ module PostgREST.Query.SqlFragment
, SqlFragment
, asBinaryF
, asCsvF
, asGeoJsonF
, asJsonF
, asJsonSingleF
, asXmlF
@@ -182,6 +183,9 @@ asJsonSingleF returnsScalar
asXmlF :: FieldName -> SqlFragment
asXmlF fieldName = "coalesce(xmlagg(_postgrest_t." <> pgFmtIdent fieldName <> "), '')"
asGeoJsonF :: SqlFragment
asGeoJsonF = "json_build_object('type', 'FeatureCollection', 'features', coalesce(json_agg(ST_AsGeoJSON(_postgrest_t)::json), '[]'))"
asBinaryF :: FieldName -> SqlFragment
asBinaryF fieldName = "coalesce(string_agg(_postgrest_t." <> pgFmtIdent fieldName <> ", ''), '')"
+9 -6
View File
@@ -43,10 +43,10 @@ import Protolude
-}
type ResultsWithCount = (Maybe Int64, Int64, [BS.ByteString], BS.ByteString, Either Error [GucHeader], Either Error (Maybe Status))
createWriteStatement :: SQL.Snippet -> SQL.Snippet -> Bool -> Bool -> Bool ->
createWriteStatement :: SQL.Snippet -> SQL.Snippet -> Bool -> Bool -> Bool -> Bool ->
PreferRepresentation -> [Text] -> Bool ->
SQL.Statement () ResultsWithCount
createWriteStatement selectQuery mutateQuery wantSingle isInsert asCsv rep pKeys =
createWriteStatement selectQuery mutateQuery wantSingle isInsert asCsv asGeoJson rep pKeys =
SQL.dynamicallyParameterized snippet decodeStandard
where
snippet =
@@ -74,6 +74,7 @@ createWriteStatement selectQuery mutateQuery wantSingle isInsert asCsv rep pKeys
bodyF
| rep /= Full = "''"
| asCsv = asCsvF
| asGeoJson = asGeoJsonF
| wantSingle = asJsonSingleF False
| otherwise = asJsonF False
@@ -86,9 +87,9 @@ createWriteStatement selectQuery mutateQuery wantSingle isInsert asCsv rep pKeys
decodeStandard =
fromMaybe (Nothing, 0, [], mempty, Right [], Right Nothing) <$> HD.rowMaybe standardRow
createReadStatement :: SQL.Snippet -> SQL.Snippet -> Bool -> Bool -> Bool -> Bool -> Maybe FieldName -> Bool ->
createReadStatement :: SQL.Snippet -> SQL.Snippet -> Bool -> Bool -> Bool -> Bool -> Bool -> Maybe FieldName -> Bool ->
SQL.Statement () ResultsWithCount
createReadStatement selectQuery countQuery isSingle countTotal asCsv asXml binaryField =
createReadStatement selectQuery countQuery isSingle countTotal asCsv asXml asGeoJson binaryField =
SQL.dynamicallyParameterized snippet decodeStandard
where
snippet =
@@ -109,6 +110,7 @@ createReadStatement selectQuery countQuery isSingle countTotal asCsv asXml binar
bodyF
| asCsv = asCsvF
| isSingle = asJsonSingleF False
| asGeoJson = asGeoJsonF
| isJust binaryField && asXml = asXmlF $ fromJust binaryField
| isJust binaryField = asBinaryF $ fromJust binaryField
| otherwise = asJsonF False
@@ -130,9 +132,9 @@ standardRow = (,,,,,) <$> nullableColumn HD.int8 <*> column HD.int8
type ProcResults = (Maybe Int64, Int64, ByteString, Either Error [GucHeader], Either Error (Maybe Status))
callProcStatement :: Bool -> Bool -> SQL.Snippet -> SQL.Snippet -> SQL.Snippet -> Bool ->
Bool -> Bool -> Bool -> Bool -> Maybe FieldName -> Bool ->
Bool -> Bool -> Bool -> Bool -> Bool -> Maybe FieldName -> Bool ->
SQL.Statement () ProcResults
callProcStatement returnsScalar returnsSingle callProcQuery selectQuery countQuery countTotal asSingle asCsv asXml multObjects binaryField =
callProcStatement returnsScalar returnsSingle callProcQuery selectQuery countQuery countTotal asSingle asCsv asXml asGeoJson multObjects binaryField =
SQL.dynamicallyParameterized snippet decodeProc
where
snippet =
@@ -152,6 +154,7 @@ callProcStatement returnsScalar returnsSingle callProcQuery selectQuery countQue
bodyF
| asSingle = asJsonSingleF returnsScalar
| asCsv = asCsvF
| asGeoJson = asGeoJsonF
| isJust binaryField && asXml = asXmlF $ fromJust binaryField
| isJust binaryField = asBinaryF $ fromJust binaryField
| returnsSingle
+1 -1
View File
@@ -431,7 +431,7 @@ requestContentTypes conf action path =
++ rawContentTypes conf
++ [CTOpenAPI | pIsRootSpec path]
defaultContentTypes =
[CTApplicationJSON, CTSingularJSON, CTTextCSV]
[CTApplicationJSON, CTSingularJSON, CTGeoJSON, CTTextCSV]
rawContentTypes :: AppConfig -> [ContentType]
rawContentTypes AppConfig{..} =