feat: data representations allow custom parsing and formatting of API fields.
See PR #2523. Most notable code changes: - Load data representation casts into schema cache. - Data representations for reads, filters, inserts, updates, views, over joins. - `CoercibleField` represents name references in queries where coercion may be needed. - `ResolverContext` help facilitate field resolution during planning. - Planner 'resolves' names in the API query and pairs them with any implicit conversions to be used in the query builder stage. - Tests for all of the above. - More consistent naming (TypedX -> CoercibleX). New: unit tests for more data representation use cases; helpful as examples as well. New: update CHANGELOG with data representations feature description. Fixed failing idempotence test. New: replace date formatter test with one that does something. Fixup: inadvertent CHANGELOG change after rebase. Cleanup: `tfName` -> `cfName` and related. Document what IRType means. Formatting. New: use a subquery to interpret `IN` literals requiring data rep transformation. - With the previous method, very long queries such as `ANY (ARRAY[test.color('000100'), test.color('CAFE12'), test.color('01E240'), ...` could be generated. Consider the case where the parser function name is 45 characters and there's a hundred literals. That's 4.5kB of SQL just for the function name alone! - New version uses `unnest`: `ANY (SELECT test.color(unnest('{000100,CAFE12,01E240,...}'::text[]))` to produce a much shorter query. - This is likely to be more performant and either way much more readable and debuggable in the logs.
This commit is contained in:
committed by
Steve Chavez
parent
078c6ec08c
commit
0a1564ba5a
@@ -104,6 +104,47 @@ spec = describe "computed relationships" $ do
|
||||
[json|[ {"name":"Final Fantasy I","designer":{"name":"Hironobu Sakaguchi"}} ]|]
|
||||
{ matchStatus = 200 }
|
||||
|
||||
it "applies data representations to response" $ do
|
||||
-- A smoke test for data reps in the presence of computed relations.
|
||||
|
||||
-- The data rep here title cases the designer name before presentation. So here the lowercase version will be saved,
|
||||
-- but the title case version returned. Pulling in a computed relation should not confuse this.
|
||||
request methodPatch "/designers?select=name,videogames:computed_videogames(name)&id=eq.1"
|
||||
[("Prefer", "return=representation"), ("Prefer", "tx=commit")]
|
||||
[json| {"name": "sidney k. meier"} |]
|
||||
`shouldRespondWith`
|
||||
[json|[{"name":"Sidney K. Meier","videogames":[{"name":"Civilization I"}, {"name":"Civilization II"}]}]|]
|
||||
{ matchStatus = 200 }
|
||||
|
||||
-- Verify it was saved the way we requested (there's no text data rep for this column, so if we select with the wrong casing, it should fail.)
|
||||
get "/designers?select=id&name=eq.Sidney%20K.%20Meier"
|
||||
`shouldRespondWith`
|
||||
[json|[]|]
|
||||
{ matchStatus = 200, matchHeaders = [matchContentTypeJson] }
|
||||
-- But with the right casing it works.
|
||||
get "/designers?select=id,name&name=eq.sidney%20k.%20meier"
|
||||
`shouldRespondWith`
|
||||
[json|[{"id": 1, "name":"Sidney K. Meier"}]|]
|
||||
{ matchStatus = 200, matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
-- Most importantly, if you read it back even via a computed relation, the data rep should be applied.
|
||||
get "/videogames?select=name,designer:computed_designers(*)&id=eq.1"
|
||||
`shouldRespondWith`
|
||||
[json|[
|
||||
{"name":"Civilization I","designer":{"id": 1, "name":"Sidney K. Meier"}}
|
||||
]|] { matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
-- reset the test fixture
|
||||
request methodPatch "/designers?id=eq.1"
|
||||
[("Prefer", "tx=commit")]
|
||||
[json| {"name": "Sid Meier"} |]
|
||||
`shouldRespondWith` 204
|
||||
-- need to poke the second one too to prevent inherent ordering from changing
|
||||
request methodPatch "/designers?id=eq.2"
|
||||
[("Prefer", "tx=commit")]
|
||||
[json| {"name": "Hironobu Sakaguchi"} |]
|
||||
`shouldRespondWith` 204
|
||||
|
||||
it "works with self joins" $
|
||||
get "/web_content?select=name,child_web_content(name),parent_web_content(name)&id=in.(0,1)"
|
||||
`shouldRespondWith`
|
||||
|
||||
Reference in New Issue
Block a user