Skip to content

Commit aa2bd1e

Browse files
committed
fix(codegen): add GeoJSONFilter for PostGIS geometry/geography fields
Adds GeoJSON/Geometry/GeometryPoint/Point → GeoJSONFilter mapping to SCALAR_FILTER_MAP so PostGIS geometry columns appear in generated filter types instead of being silently omitted. Changes: - Add GeoJSON, Geometry, GeometryPoint, Point to SCALAR_FILTER_MAP - Add GeoJSONFilter config with equality/distinct operators - Add GeoJSONFilter unit test - Add PostGIS schema fixture (example-postgis.schema.graphql) - Add 13 PostGIS integration tests (Place/Route with GeoJSON columns) - Update 14 snapshots to include GeoJSONFilter
1 parent 7aa49cb commit aa2bd1e

8 files changed

Lines changed: 564 additions & 7 deletions
Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
"""A universally unique identifier as defined by [RFC 4122](https://tools.ietf.org/html/rfc4122)."""
2+
scalar UUID
3+
4+
"""
5+
A point in time as described by the [ISO
6+
8601](https://en.wikipedia.org/wiki/ISO_8601) standard. May or may not include a timezone.
7+
"""
8+
scalar Datetime
9+
10+
"""A string representing a cursor for pagination."""
11+
scalar Cursor
12+
13+
"""GeoJSON scalar for PostGIS geometry input/output."""
14+
scalar GeoJSON
15+
16+
"""The root query type."""
17+
type Query {
18+
"""Reads and enables pagination through a set of `Place`."""
19+
places(
20+
first: Int
21+
last: Int
22+
offset: Int
23+
before: Cursor
24+
after: Cursor
25+
orderBy: [PlacesOrderBy!] = [PRIMARY_KEY_ASC]
26+
filter: PlaceFilter
27+
condition: PlaceCondition
28+
): PlacesConnection
29+
30+
"""Reads a single `Place` using its globally unique `ID`."""
31+
place(id: UUID!): Place
32+
33+
"""Reads and enables pagination through a set of `Route`."""
34+
routes(
35+
first: Int
36+
last: Int
37+
offset: Int
38+
before: Cursor
39+
after: Cursor
40+
orderBy: [RoutesOrderBy!] = [PRIMARY_KEY_ASC]
41+
filter: RouteFilter
42+
condition: RouteCondition
43+
): RoutesConnection
44+
45+
"""Reads a single `Route` using its globally unique `ID`."""
46+
route(id: UUID!): Route
47+
}
48+
49+
"""The root mutation type."""
50+
type Mutation {
51+
"""Creates a single `Place`."""
52+
createPlace(input: CreatePlaceInput!): CreatePlacePayload
53+
54+
"""Updates a single `Place` using its globally unique `ID`."""
55+
updatePlace(input: UpdatePlaceInput!): UpdatePlacePayload
56+
57+
"""Deletes a single `Place` using its globally unique `ID`."""
58+
deletePlace(input: DeletePlaceInput!): DeletePlacePayload
59+
60+
"""Creates a single `Route`."""
61+
createRoute(input: CreateRouteInput!): CreateRoutePayload
62+
63+
"""Updates a single `Route` using its globally unique `ID`."""
64+
updateRoute(input: UpdateRouteInput!): UpdateRoutePayload
65+
66+
"""Deletes a single `Route` using its globally unique `ID`."""
67+
deleteRoute(input: DeleteRouteInput!): DeleteRoutePayload
68+
}
69+
70+
# ============================================================================
71+
# Entity Types
72+
# ============================================================================
73+
74+
type Place {
75+
id: UUID!
76+
name: String!
77+
location: GeoJSON
78+
createdAt: Datetime!
79+
updatedAt: Datetime
80+
}
81+
82+
type Route {
83+
id: UUID!
84+
name: String!
85+
path: GeoJSON
86+
createdAt: Datetime!
87+
}
88+
89+
# ============================================================================
90+
# Enums
91+
# ============================================================================
92+
93+
enum PlacesOrderBy {
94+
NATURAL
95+
PRIMARY_KEY_ASC
96+
PRIMARY_KEY_DESC
97+
ID_ASC
98+
ID_DESC
99+
NAME_ASC
100+
NAME_DESC
101+
CREATED_AT_ASC
102+
CREATED_AT_DESC
103+
}
104+
105+
enum RoutesOrderBy {
106+
NATURAL
107+
PRIMARY_KEY_ASC
108+
PRIMARY_KEY_DESC
109+
ID_ASC
110+
ID_DESC
111+
NAME_ASC
112+
NAME_DESC
113+
CREATED_AT_ASC
114+
CREATED_AT_DESC
115+
}
116+
117+
# ============================================================================
118+
# Connection Types
119+
# ============================================================================
120+
121+
type PlacesConnection {
122+
nodes: [Place!]!
123+
edges: [PlacesEdge!]!
124+
pageInfo: PageInfo!
125+
totalCount: Int!
126+
}
127+
128+
type PlacesEdge {
129+
node: Place!
130+
cursor: Cursor!
131+
}
132+
133+
type RoutesConnection {
134+
nodes: [Route!]!
135+
edges: [RoutesEdge!]!
136+
pageInfo: PageInfo!
137+
totalCount: Int!
138+
}
139+
140+
type RoutesEdge {
141+
node: Route!
142+
cursor: Cursor!
143+
}
144+
145+
type PageInfo {
146+
hasNextPage: Boolean!
147+
hasPreviousPage: Boolean!
148+
startCursor: Cursor
149+
endCursor: Cursor
150+
}
151+
152+
# ============================================================================
153+
# Filter Types (geometry columns should use GeoJSONFilter)
154+
# ============================================================================
155+
156+
input PlaceFilter {
157+
id: UUIDFilter
158+
name: StringFilter
159+
location: GeoJSONFilter
160+
and: [PlaceFilter!]
161+
or: [PlaceFilter!]
162+
not: PlaceFilter
163+
}
164+
165+
input RouteFilter {
166+
id: UUIDFilter
167+
name: StringFilter
168+
path: GeoJSONFilter
169+
and: [RouteFilter!]
170+
or: [RouteFilter!]
171+
not: RouteFilter
172+
}
173+
174+
input UUIDFilter {
175+
equalTo: UUID
176+
notEqualTo: UUID
177+
in: [UUID!]
178+
notIn: [UUID!]
179+
isNull: Boolean
180+
}
181+
182+
input StringFilter {
183+
equalTo: String
184+
notEqualTo: String
185+
in: [String!]
186+
notIn: [String!]
187+
includes: String
188+
startsWith: String
189+
endsWith: String
190+
isNull: Boolean
191+
}
192+
193+
input GeoJSONFilter {
194+
isNull: Boolean
195+
equalTo: GeoJSON
196+
notEqualTo: GeoJSON
197+
distinctFrom: GeoJSON
198+
notDistinctFrom: GeoJSON
199+
}
200+
201+
# ============================================================================
202+
# Condition Types
203+
# ============================================================================
204+
205+
input PlaceCondition {
206+
id: UUID
207+
name: String
208+
location: GeoJSON
209+
}
210+
211+
input RouteCondition {
212+
id: UUID
213+
name: String
214+
path: GeoJSON
215+
}
216+
217+
# ============================================================================
218+
# Mutation Input Types
219+
# ============================================================================
220+
221+
input CreatePlaceInput {
222+
clientMutationId: String
223+
place: PlaceInput!
224+
}
225+
226+
input PlaceInput {
227+
name: String!
228+
location: GeoJSON
229+
}
230+
231+
input UpdatePlaceInput {
232+
clientMutationId: String
233+
id: UUID!
234+
patch: PlacePatch!
235+
}
236+
237+
input PlacePatch {
238+
name: String
239+
location: GeoJSON
240+
}
241+
242+
input DeletePlaceInput {
243+
clientMutationId: String
244+
id: UUID!
245+
}
246+
247+
input CreateRouteInput {
248+
clientMutationId: String
249+
route: RouteInput!
250+
}
251+
252+
input RouteInput {
253+
name: String!
254+
path: GeoJSON
255+
}
256+
257+
input UpdateRouteInput {
258+
clientMutationId: String
259+
id: UUID!
260+
patch: RoutePatch!
261+
}
262+
263+
input RoutePatch {
264+
name: String
265+
path: GeoJSON
266+
}
267+
268+
input DeleteRouteInput {
269+
clientMutationId: String
270+
id: UUID!
271+
}
272+
273+
# ============================================================================
274+
# Mutation Payload Types
275+
# ============================================================================
276+
277+
type CreatePlacePayload {
278+
clientMutationId: String
279+
place: Place
280+
}
281+
282+
type UpdatePlacePayload {
283+
clientMutationId: String
284+
place: Place
285+
}
286+
287+
type DeletePlacePayload {
288+
clientMutationId: String
289+
place: Place
290+
}
291+
292+
type CreateRoutePayload {
293+
clientMutationId: String
294+
route: Route
295+
}
296+
297+
type UpdateRoutePayload {
298+
clientMutationId: String
299+
route: Route
300+
}
301+
302+
type DeleteRoutePayload {
303+
clientMutationId: String
304+
route: Route
305+
}

0 commit comments

Comments
 (0)