graphile-pg-aggregates 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/AddAggregateTypesPlugin.d.ts +38 -0
  2. package/AddAggregateTypesPlugin.js +268 -0
  3. package/AddConnectionAggregatesPlugin.d.ts +2 -0
  4. package/AddConnectionAggregatesPlugin.js +56 -0
  5. package/AddConnectionGroupedAggregatesPlugin.d.ts +10 -0
  6. package/AddConnectionGroupedAggregatesPlugin.js +118 -0
  7. package/AddGroupByAggregateEnumValuesForAttributesPlugin.d.ts +10 -0
  8. package/AddGroupByAggregateEnumValuesForAttributesPlugin.js +116 -0
  9. package/AddGroupByAggregateEnumsPlugin.d.ts +2 -0
  10. package/AddGroupByAggregateEnumsPlugin.js +51 -0
  11. package/AddHavingAggregateTypesPlugin.d.ts +22 -0
  12. package/AddHavingAggregateTypesPlugin.js +449 -0
  13. package/AggregateSpecsPlugin.d.ts +1 -0
  14. package/AggregateSpecsPlugin.js +206 -0
  15. package/AggregatesSmartTagsPlugin.d.ts +8 -0
  16. package/AggregatesSmartTagsPlugin.js +65 -0
  17. package/EXPORTABLE.d.ts +1 -0
  18. package/EXPORTABLE.js +15 -0
  19. package/FilterRelationalAggregatesPlugin.d.ts +74 -0
  20. package/FilterRelationalAggregatesPlugin.js +443 -0
  21. package/InflectionPlugin.d.ts +67 -0
  22. package/InflectionPlugin.js +71 -0
  23. package/LICENSE +23 -0
  24. package/OrderByAggregatesPlugin.d.ts +31 -0
  25. package/OrderByAggregatesPlugin.js +205 -0
  26. package/README.md +128 -0
  27. package/esm/AddAggregateTypesPlugin.d.ts +38 -0
  28. package/esm/AddAggregateTypesPlugin.js +265 -0
  29. package/esm/AddConnectionAggregatesPlugin.d.ts +2 -0
  30. package/esm/AddConnectionAggregatesPlugin.js +53 -0
  31. package/esm/AddConnectionGroupedAggregatesPlugin.d.ts +10 -0
  32. package/esm/AddConnectionGroupedAggregatesPlugin.js +115 -0
  33. package/esm/AddGroupByAggregateEnumValuesForAttributesPlugin.d.ts +10 -0
  34. package/esm/AddGroupByAggregateEnumValuesForAttributesPlugin.js +113 -0
  35. package/esm/AddGroupByAggregateEnumsPlugin.d.ts +2 -0
  36. package/esm/AddGroupByAggregateEnumsPlugin.js +48 -0
  37. package/esm/AddHavingAggregateTypesPlugin.d.ts +22 -0
  38. package/esm/AddHavingAggregateTypesPlugin.js +446 -0
  39. package/esm/AggregateSpecsPlugin.d.ts +1 -0
  40. package/esm/AggregateSpecsPlugin.js +203 -0
  41. package/esm/AggregatesSmartTagsPlugin.d.ts +8 -0
  42. package/esm/AggregatesSmartTagsPlugin.js +62 -0
  43. package/esm/EXPORTABLE.d.ts +1 -0
  44. package/esm/EXPORTABLE.js +12 -0
  45. package/esm/FilterRelationalAggregatesPlugin.d.ts +74 -0
  46. package/esm/FilterRelationalAggregatesPlugin.js +440 -0
  47. package/esm/InflectionPlugin.d.ts +67 -0
  48. package/esm/InflectionPlugin.js +68 -0
  49. package/esm/OrderByAggregatesPlugin.d.ts +31 -0
  50. package/esm/OrderByAggregatesPlugin.js +202 -0
  51. package/esm/index.d.ts +42 -0
  52. package/esm/index.js +37 -0
  53. package/esm/interfaces.d.ts +145 -0
  54. package/esm/interfaces.js +44 -0
  55. package/esm/utils.d.ts +23 -0
  56. package/esm/utils.js +22 -0
  57. package/index.d.ts +42 -0
  58. package/index.js +40 -0
  59. package/interfaces.d.ts +145 -0
  60. package/interfaces.js +47 -0
  61. package/package.json +61 -0
  62. package/utils.d.ts +23 -0
  63. package/utils.js +25 -0
@@ -0,0 +1,443 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PgAggregatesFilterRelationalAggregatesPlugin = exports.Plugin = void 0;
4
+ const EXPORTABLE_1 = require("./EXPORTABLE");
5
+ const version = '1.0.0';
6
+ const pgAggregateApplyAttributeOrder = (0, EXPORTABLE_1.EXPORTABLE)(() => (PgCondition, sql, spec, attributeName, attrCodec, rawAttrCodec, $parent, input) => {
7
+ if (input == null)
8
+ return;
9
+ const $col = new PgCondition($parent);
10
+ $col.extensions.pgFilterAttribute = {
11
+ codec: attrCodec,
12
+ expression: spec.sqlAggregateWrap(sql `${$col.alias}.${sql.identifier(attributeName)}`, rawAttrCodec)
13
+ };
14
+ return $col;
15
+ }, [], 'pgAggregateApplyAttributeOrder');
16
+ const pgAggregateApplyComputedAttributeOrder = (0, EXPORTABLE_1.EXPORTABLE)(() => (PgCondition, sql, spec, proc, attrCodec, $parent, input) => {
17
+ if (input == null)
18
+ return;
19
+ const $col = new PgCondition($parent);
20
+ const sqlComputedAttributeCall = sql.query `${typeof proc.from === 'function'
21
+ ? proc.from({ placeholder: $col.alias })
22
+ : proc.from}`;
23
+ $col.extensions.pgFilterAttribute = {
24
+ codec: attrCodec,
25
+ expression: spec.sqlAggregateWrap(sqlComputedAttributeCall, proc.codec)
26
+ };
27
+ return $col;
28
+ }, [], 'pgAggregateApplyComputedAttributeOrder');
29
+ const pgAggregateApplyForeignCondition = (0, EXPORTABLE_1.EXPORTABLE)(() => function (PgCondition, $subquery, input) {
30
+ if (input == null)
31
+ return;
32
+ // Enable all the helpers
33
+ const $condition = new PgCondition($subquery, false, 'AND');
34
+ return $condition;
35
+ }, [], 'pgAggregateApplyForeignCondition');
36
+ const pgAggregatesApply = (0, EXPORTABLE_1.EXPORTABLE)(() => (PgAggregateCondition, pgWhereConditionSpecListToSQL, sql, $where, input) => {
37
+ if (input == null)
38
+ return;
39
+ // Constructive fork: read from _manyRelation (set by our
40
+ // ConnectionFilterBackwardRelationsPlugin) instead of upstream's
41
+ // $where.extensions.pgFilterRelation.
42
+ const rel = $where._manyRelation;
43
+ if (!rel) {
44
+ throw new Error(`Invalid use of aggregates filter: '_manyRelation' expected on $where. ` +
45
+ `Ensure graphile-connection-filter backward relations plugin has tagged $where.`);
46
+ }
47
+ const { localAttributes, remoteAttributes, foreignTableExpression, foreignTable } = rel;
48
+ const $subQuery = new PgAggregateCondition($where, {
49
+ sql,
50
+ tableExpression: foreignTableExpression,
51
+ alias: foreignTable.name
52
+ }, pgWhereConditionSpecListToSQL);
53
+ localAttributes.forEach((localAttribute, i) => {
54
+ const remoteAttribute = remoteAttributes[i];
55
+ $subQuery.where(sql `${$where.alias}.${sql.identifier(localAttribute)} = ${$subQuery.alias}.${sql.identifier(remoteAttribute)}`);
56
+ });
57
+ return $subQuery;
58
+ }, [], 'pgAggregatesApply');
59
+ exports.Plugin = {
60
+ name: 'PgAggregatesFilterRelationalAggregatesPlugin',
61
+ description: `\
62
+ Adds the ability to filter a collection by aggregates on relationships via \
63
+ graphile-connection-filter, e.g. filtering all players based on \
64
+ the sum of their points scored.`,
65
+ version,
66
+ // This has to run AFTER any plugins that provide `build.pgAggregateSpecs`
67
+ // otherwise we might add codecs to build.allPgCodecs before all the relevant
68
+ // codecs/specs are available.
69
+ after: ['PgBasicsPlugin', 'PgCodecsPlugin', 'aggregates'],
70
+ provides: ['codecs'],
71
+ before: ['ConnectionFilterArgPlugin'],
72
+ inflection: {
73
+ add: {
74
+ // TODO: rename this!
75
+ filterTableAggregateType(_preset, foreignTable, spec) {
76
+ const foreignTableTypeName = this.tableType(foreignTable.codec);
77
+ return this.filterType(foreignTableTypeName + this.upperCamelCase(spec.id) + 'Aggregate');
78
+ }
79
+ }
80
+ },
81
+ schema: {
82
+ behaviorRegistry: {
83
+ add: {
84
+ 'aggregates:filterBy': {
85
+ description: 'Can we filter by the details of this resource whilst aggregating the resource?',
86
+ entities: ['pgResource']
87
+ },
88
+ 'aggregate:filterBy': {
89
+ description: 'Can we filter using the value of this attribute',
90
+ entities: ['pgCodecAttribute']
91
+ }
92
+ }
93
+ },
94
+ entityBehavior: {
95
+ pgResource: ['aggregates:filterBy', 'aggregate:filterBy'],
96
+ pgCodecAttribute: ['aggregate:filterBy']
97
+ },
98
+ hooks: {
99
+ build(build) {
100
+ const { EXPORTABLE, grafast: { Modifier } } = build;
101
+ if (!build.allPgCodecs) {
102
+ throw new Error('PgAggregatesFilterRelationalAggregatesPlugin must run after build.allPgCodecs has been established');
103
+ }
104
+ if (!build.pgAggregateSpecs) {
105
+ throw new Error('PgAggregatesFilterRelationalAggregatesPlugin must run after build.pgAggregateSpecs has been established');
106
+ }
107
+ // Add aggregate derivative codecs to `allPgCodecs`
108
+ for (const spec of build.pgAggregateSpecs) {
109
+ if (!spec.pgTypeCodecModifier) {
110
+ continue;
111
+ }
112
+ for (const existingCodec of build.allPgCodecs) {
113
+ if (spec.isSuitableType(existingCodec)) {
114
+ const codec = spec.pgTypeCodecModifier(existingCodec);
115
+ if (!build.allPgCodecs.has(codec)) {
116
+ build.allPgCodecs.add(codec);
117
+ }
118
+ }
119
+ }
120
+ }
121
+ const PgAggregateConditionExpression = EXPORTABLE((Modifier) => class PgAggregateConditionExpression extends Modifier {
122
+ spec;
123
+ pgWhereConditionSpecListToSQL;
124
+ alias;
125
+ conditions = [];
126
+ constructor(parent, spec, pgWhereConditionSpecListToSQL) {
127
+ super(parent);
128
+ this.spec = spec;
129
+ this.pgWhereConditionSpecListToSQL = pgWhereConditionSpecListToSQL;
130
+ this.alias = parent.alias;
131
+ }
132
+ where(condition) {
133
+ this.conditions.push(condition);
134
+ }
135
+ apply() {
136
+ const sqlCondition = this.pgWhereConditionSpecListToSQL(this.alias, this.conditions);
137
+ if (sqlCondition) {
138
+ this.parent.expression(sqlCondition);
139
+ }
140
+ }
141
+ }, [Modifier]);
142
+ const PgAggregateCondition = EXPORTABLE((Modifier, PgAggregateConditionExpression) => class PgAggregateCondition extends Modifier {
143
+ pgWhereConditionSpecListToSQL;
144
+ sql;
145
+ tableExpression;
146
+ alias;
147
+ conditions = [];
148
+ expressions = [];
149
+ constructor(parent, options, pgWhereConditionSpecListToSQL) {
150
+ super(parent);
151
+ this.pgWhereConditionSpecListToSQL = pgWhereConditionSpecListToSQL;
152
+ const { sql, tableExpression, alias } = options;
153
+ this.sql = sql;
154
+ this.alias = sql.identifier(Symbol(alias ?? 'aggregate'));
155
+ this.tableExpression = tableExpression;
156
+ }
157
+ where(condition) {
158
+ this.conditions.push(condition);
159
+ }
160
+ expression(expression) {
161
+ this.expressions.push(expression);
162
+ }
163
+ forAggregate(spec) {
164
+ return new PgAggregateConditionExpression(this, spec, this.pgWhereConditionSpecListToSQL);
165
+ }
166
+ apply() {
167
+ const { sql } = this;
168
+ const sqlCondition = this.pgWhereConditionSpecListToSQL(this.alias, this.conditions);
169
+ const where = sqlCondition
170
+ ? sql `where ${sqlCondition}`
171
+ : sql.blank;
172
+ const boolExpr = this.expressions.length === 0
173
+ ? sql.true
174
+ : sql.parens(sql.join(this.expressions.map((expr) => sql.parens(expr)), '\nand\n'));
175
+ const subquery = sql `(${sql.indent `\
176
+ select ${boolExpr}
177
+ from ${this.tableExpression} as ${this.alias}
178
+ ${where}`}
179
+ group by ())`;
180
+ return this.parent.where(subquery);
181
+ }
182
+ }, [Modifier, PgAggregateConditionExpression]);
183
+ return build.extend(build, {
184
+ PgAggregateCondition,
185
+ PgAggregateConditionExpression
186
+ }, 'Adding step classes from postgraphile-plugin-connection-filter');
187
+ },
188
+ init(_, build) {
189
+ const { inflection, dataplanPg: { PgCondition }, EXPORTABLE } = build;
190
+ if (!inflection.filterType) {
191
+ // Filter plugin is not enabled
192
+ return _;
193
+ }
194
+ // Register the aggregate filter type for each table
195
+ for (const foreignTable of Object.values(build.input.pgRegistry.pgResources)) {
196
+ if (foreignTable.parameters || !foreignTable.codec.attributes) {
197
+ continue;
198
+ }
199
+ if (!build.behavior.pgResourceMatches(foreignTable, 'resource:aggregates:filterBy')) {
200
+ continue;
201
+ }
202
+ const foreignTableTypeName = inflection.tableType(foreignTable.codec);
203
+ const foreignTableFilterTypeName = inflection.filterType(foreignTableTypeName);
204
+ const foreignTableAggregateFilterTypeName = inflection.filterType(foreignTableTypeName + 'Aggregates');
205
+ build.recoverable(null, () => {
206
+ const filterFieldName = 'where';
207
+ build.registerInputObjectType(foreignTableAggregateFilterTypeName, {
208
+ pgResource: foreignTable,
209
+ isPgConnectionAggregateFilter: true
210
+ }, () => {
211
+ return {
212
+ description: `A filter to be used against aggregates of \`${foreignTableTypeName}\` object types.`,
213
+ fields: () => {
214
+ const type = build.getTypeByName(foreignTableFilterTypeName);
215
+ if (!type) {
216
+ return {};
217
+ }
218
+ return {
219
+ [filterFieldName]: {
220
+ description: `A filter that must pass for the relevant \`${foreignTableTypeName}\` object to be included within the aggregate.`,
221
+ type,
222
+ apply: EXPORTABLE((PgCondition, pgAggregateApplyForeignCondition) => ($subquery, input) => pgAggregateApplyForeignCondition(PgCondition, $subquery, input), [PgCondition, pgAggregateApplyForeignCondition], `${filterFieldName}Apply`)
223
+ // No need to auto-apply since we're applied manually via `fieldArgs.apply($subQuery)` below.
224
+ }
225
+ };
226
+ }
227
+ };
228
+ }, 'Adding aggregate filter input type');
229
+ });
230
+ // Register the aggregate spec filter type for each aggreage spec for each source
231
+ for (const spec of build.pgAggregateSpecs) {
232
+ if (!build.behavior.pgResourceMatches(foreignTable, `${spec.id}:resource:aggregates:filterBy`)) {
233
+ continue;
234
+ }
235
+ const filterTypeName = inflection.filterTableAggregateType(foreignTable, spec);
236
+ build.registerInputObjectType(filterTypeName, {
237
+ isPgConnectionAggregateAggregateFilter: true,
238
+ pgConnectionAggregateFilterAggregateSpec: spec,
239
+ pgTypeResource: foreignTable
240
+ }, () => ({}), `Add '${spec.id}' aggregate filter type for '${foreignTableTypeName}'`);
241
+ }
242
+ }
243
+ return _;
244
+ },
245
+ // This hook adds 'aggregates' under a "backwards" relation, siblings of
246
+ // every, some, none.
247
+ // See https://github.com/graphile-contrib/postgraphile-plugin-connection-filter/blob/6223cdb1d2ac5723aecdf55f735a18f8e2b98683/src/PgConnectionArgFilterBackwardRelationsPlugin.ts#L374
248
+ GraphQLInputObjectType_fields(inFields, build, context) {
249
+ let fields = inFields;
250
+ const { extend, inflection, sql, pgAggregateSpecs, dataplanPg: { PgCondition, pgWhereConditionSpecListToSQL }, PgAggregateCondition, EXPORTABLE } = build;
251
+ if (!inflection.filterType) {
252
+ // Filter plugin is not enabled
253
+ return inFields;
254
+ }
255
+ const { fieldWithHooks, scope: { foreignTable, isPgConnectionFilterMany, isPgConnectionAggregateFilter, pgResource, isPgConnectionAggregateAggregateFilter, pgConnectionAggregateFilterAggregateSpec: spec, pgTypeResource } } = context;
256
+ // Add 'aggregates' field to relation filters, next to `every`/`some`/`none`
257
+ fields = (() => {
258
+ if (!isPgConnectionFilterMany || !foreignTable)
259
+ return fields;
260
+ const foreignTableTypeName = inflection.tableType(foreignTable.codec);
261
+ const foreignTableAggregateFilterTypeName = inflection.filterType(foreignTableTypeName + 'Aggregates');
262
+ const fieldName = 'aggregates';
263
+ const AggregateType = build.getTypeByName(foreignTableAggregateFilterTypeName);
264
+ if (!AggregateType) {
265
+ return fields;
266
+ }
267
+ return build.extend(fields, {
268
+ [fieldName]: fieldWithHooks({
269
+ fieldName,
270
+ isPgConnectionFilterAggregatesField: true
271
+ }, {
272
+ description: `Aggregates across related \`${foreignTableTypeName}\` match the filter criteria.`,
273
+ type: AggregateType,
274
+ apply: EXPORTABLE((PgAggregateCondition, pgAggregatesApply, pgWhereConditionSpecListToSQL, sql) => function ($where, input) {
275
+ return pgAggregatesApply(PgAggregateCondition, pgWhereConditionSpecListToSQL, sql, $where, input);
276
+ }, [
277
+ PgAggregateCondition,
278
+ pgAggregatesApply,
279
+ pgWhereConditionSpecListToSQL,
280
+ sql
281
+ ])
282
+ // No need to auto-apply, postgraphile-plugin-connection-filter explicitly calls fieldArgs.apply()
283
+ })
284
+ }, "Adding 'aggregates' filter field on relation");
285
+ })();
286
+ // This hook adds our various aggregates to the 'aggregates' input defined in `AggregateType` above
287
+ fields = (() => {
288
+ if (!isPgConnectionAggregateFilter ||
289
+ !pgResource ||
290
+ pgResource.parameters ||
291
+ !pgResource.codec.attributes) {
292
+ return fields;
293
+ }
294
+ const foreignTable = pgResource;
295
+ const foreignTableTypeName = inflection.tableType(foreignTable.codec);
296
+ return pgAggregateSpecs.reduce((memo, spec) => {
297
+ const filterTypeName = inflection.filterTableAggregateType(foreignTable, spec);
298
+ const fieldName = inflection.camelCase(spec.id);
299
+ const type = build.getTypeByName(filterTypeName);
300
+ if (!type) {
301
+ return memo;
302
+ }
303
+ return extend(memo, {
304
+ [fieldName]: fieldWithHooks({ fieldName }, () => ({
305
+ type,
306
+ description: `${spec.HumanLabel} aggregate over matching \`${foreignTableTypeName}\` objects.`,
307
+ apply: EXPORTABLE((spec) => function ($subquery, input) {
308
+ if (input == null)
309
+ return;
310
+ return $subquery.forAggregate(spec);
311
+ }, [spec])
312
+ }))
313
+ }, `Adding aggregate '${spec.id}' filter input for '${pgResource.name}'. `);
314
+ }, fields);
315
+ })();
316
+ // This hook adds matching attributes to the relevant aggregate types.
317
+ fields = (() => {
318
+ if (!isPgConnectionAggregateAggregateFilter ||
319
+ !spec ||
320
+ !pgTypeResource ||
321
+ pgTypeResource.parameters ||
322
+ !pgTypeResource.codec.attributes) {
323
+ return fields;
324
+ }
325
+ const table = pgTypeResource;
326
+ const attributes = table.codec.attributes;
327
+ fields = extend(fields, {
328
+ ...Object.entries(attributes).reduce((memo, [attributeName, attribute]) => {
329
+ if (!build.behavior.pgCodecAttributeMatches([table.codec, attributeName], `${spec.id}:attribute:aggregate:filterBy`)) {
330
+ return memo;
331
+ }
332
+ if ((spec.shouldApplyToEntity &&
333
+ !spec.shouldApplyToEntity({
334
+ type: 'attribute',
335
+ codec: table.codec,
336
+ attributeName: attributeName
337
+ })) ||
338
+ !spec.isSuitableType(attribute.codec)) {
339
+ return memo;
340
+ }
341
+ const rawAttrCodec = attribute.codec;
342
+ const attrCodec = spec.pgTypeCodecModifier
343
+ ? spec.pgTypeCodecModifier(attribute.codec)
344
+ : attribute.codec;
345
+ const fieldName = inflection.attribute({
346
+ codec: table.codec,
347
+ attributeName
348
+ });
349
+ const digest = build.connectionFilterOperatorsDigest(attrCodec);
350
+ if (!digest) {
351
+ return memo;
352
+ }
353
+ const OperatorsType = build.getTypeByName(digest.operatorsTypeName);
354
+ if (!OperatorsType) {
355
+ return memo;
356
+ }
357
+ return build.extend(memo, {
358
+ [fieldName]: {
359
+ type: OperatorsType,
360
+ apply: EXPORTABLE((PgCondition, attrCodec, attributeName, pgAggregateApplyAttributeOrder, rawAttrCodec, spec, sql) => function ($parent, input) {
361
+ return pgAggregateApplyAttributeOrder(PgCondition, sql, spec, attributeName, attrCodec, rawAttrCodec, $parent, input);
362
+ }, [
363
+ PgCondition,
364
+ attrCodec,
365
+ attributeName,
366
+ pgAggregateApplyAttributeOrder,
367
+ rawAttrCodec,
368
+ spec,
369
+ sql
370
+ ])
371
+ // No need to auto-apply since we're called via `fieldArgs.apply($subquery.forAggregate(spec))` above
372
+ }
373
+ }, `Add aggregate '${attributeName}' filter for source '${table.name}' for spec '${spec.id}'`);
374
+ }, Object.create(null))
375
+ }, `Adding per-attribute '${spec.id}' aggregate filters for '${pgTypeResource.name}'`);
376
+ const computedAttributeResources = Object.values(build.input.pgRegistry.pgResources).filter((s) => isComputedScalarAttributeResource(s) &&
377
+ s.parameters[0].codec === table.codec &&
378
+ s.parameters.slice(1).every((p) => p.optional));
379
+ fields = extend(fields, {
380
+ ...computedAttributeResources.reduce((memo, proc) => {
381
+ if ((spec.shouldApplyToEntity &&
382
+ !spec.shouldApplyToEntity({
383
+ type: 'computedAttribute',
384
+ resource: proc
385
+ })) ||
386
+ !spec.isSuitableType(proc.codec)) {
387
+ return memo;
388
+ }
389
+ const fieldName = inflection.computedAttributeField({
390
+ resource: proc
391
+ });
392
+ const attrCodec = spec.pgTypeCodecModifier
393
+ ? spec.pgTypeCodecModifier(proc.codec)
394
+ : proc.codec;
395
+ const digest = build.connectionFilterOperatorsDigest(attrCodec);
396
+ if (!digest) {
397
+ return memo;
398
+ }
399
+ const OperatorsType = build.getTypeByName(digest.operatorsTypeName);
400
+ if (!OperatorsType) {
401
+ return memo;
402
+ }
403
+ return build.extend(memo, {
404
+ [fieldName]: {
405
+ type: OperatorsType,
406
+ apply: EXPORTABLE((PgCondition, attrCodec, pgAggregateApplyComputedAttributeOrder, proc, spec, sql) => function apply($parent, input) {
407
+ return pgAggregateApplyComputedAttributeOrder(PgCondition, sql, spec, proc, attrCodec, $parent, input);
408
+ }, [
409
+ PgCondition,
410
+ attrCodec,
411
+ pgAggregateApplyComputedAttributeOrder,
412
+ proc,
413
+ spec,
414
+ sql
415
+ ])
416
+ }
417
+ }, `Add computed aggregate '${fieldName}' filter for source '${table.name}' for spec '${spec.id}'`);
418
+ }, Object.create(null))
419
+ }, `Adding per-computed-column '${spec.id}' aggregate filters for '${pgTypeResource.name}'`);
420
+ return fields;
421
+ })();
422
+ return fields;
423
+ }
424
+ }
425
+ }
426
+ };
427
+ exports.PgAggregatesFilterRelationalAggregatesPlugin = exports.Plugin;
428
+ function isComputedScalarAttributeResource(s) {
429
+ if (!s.parameters || s.parameters.length < 1) {
430
+ return false;
431
+ }
432
+ if (s.codec.attributes) {
433
+ return false;
434
+ }
435
+ if (!s.isUnique) {
436
+ return false;
437
+ }
438
+ const firstParameter = s.parameters[0];
439
+ if (!firstParameter?.codec.attributes) {
440
+ return false;
441
+ }
442
+ return true;
443
+ }
@@ -0,0 +1,67 @@
1
+ import type { PgCodecWithAttributes, PgRegistry, PgResource } from '@dataplan/pg';
2
+ import type { AggregateGroupBySpec, AggregateSpec, CORE_HAVING_FILTER_SPECS } from './interfaces';
3
+ declare global {
4
+ namespace GraphileBuild {
5
+ interface Inflection {
6
+ aggregateContainerType(this: Inflection, details: {
7
+ resource: PgResource<any, any, any, any, any>;
8
+ }): string;
9
+ aggregateType(this: Inflection, details: {
10
+ resource: PgResource<any, any, any, any, any>;
11
+ aggregateSpec: AggregateSpec;
12
+ }): string;
13
+ aggregatesContainerField(this: Inflection, details: {
14
+ resource: PgResource<any, any, any, any, any>;
15
+ }): string;
16
+ groupedAggregatesContainerField(this: Inflection, details: {
17
+ resource: PgResource<any, any, any, any, any>;
18
+ }): string;
19
+ aggregatesField(this: Inflection, details: {
20
+ aggregateSpec: AggregateSpec;
21
+ }): string;
22
+ aggregateGroupByType(this: Inflection, details: {
23
+ resource: PgResource<any, any, any, any, any>;
24
+ }): string;
25
+ aggregateGroupByAttributeEnum(this: Inflection, details: {
26
+ resource: PgResource<any, any, any, any, any>;
27
+ attributeName: string;
28
+ }): string;
29
+ aggregateHavingInputType(this: Inflection, details: {
30
+ resource: PgResource<any, any, any, any, any>;
31
+ }): string;
32
+ aggregateHavingAggregateInputType(this: Inflection, details: {
33
+ resource: PgResource<any, any, any, any, any>;
34
+ aggregateSpec: AggregateSpec;
35
+ }): string;
36
+ aggregateHavingAggregateComputedAttributeInputType(this: Inflection, details: {
37
+ resource: PgResource<any, any, any, any, any>;
38
+ aggregateSpec: AggregateSpec;
39
+ computedAttributeResource: PgResource<any, any, any, any, any>;
40
+ }): string;
41
+ aggregateHavingAggregateComputedAttributeArgsInputType(this: Inflection, details: {
42
+ resource: PgResource<any, any, any, any, any>;
43
+ aggregateSpec: AggregateSpec;
44
+ computedAttributeResource: PgResource<any, any, any, any, any>;
45
+ }): string;
46
+ aggregateGroupByAttributeDerivativeEnum(this: Inflection, details: {
47
+ resource: PgResource<any, any, any, any, any>;
48
+ attributeName: string;
49
+ aggregateGroupBySpec: AggregateGroupBySpec;
50
+ }): string;
51
+ orderByCountOfManyRelationByKeys(this: Inflection, details: {
52
+ registry: PgRegistry;
53
+ codec: PgCodecWithAttributes;
54
+ relationName: string;
55
+ }): string;
56
+ orderByAttributeAggregateOfManyRelationByKeys(this: Inflection, details: {
57
+ registry: PgRegistry;
58
+ codec: PgCodecWithAttributes;
59
+ relationName: string;
60
+ aggregateSpec: AggregateSpec;
61
+ attributeName: string;
62
+ }): string;
63
+ aggregateHavingFilterInputType(this: Inflection, spec: (typeof CORE_HAVING_FILTER_SPECS)[number]): string;
64
+ }
65
+ }
66
+ }
67
+ export declare const PgAggregatesInflectorsPlugin: GraphileConfig.Plugin;
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PgAggregatesInflectorsPlugin = void 0;
4
+ const version = '1.0.0';
5
+ exports.PgAggregatesInflectorsPlugin = {
6
+ name: 'PgAggregatesInflectorsPlugin',
7
+ description: 'Adds the inflectors used by the pg-aggregates preset.',
8
+ version,
9
+ provides: ['aggregates'],
10
+ inflection: {
11
+ add: {
12
+ aggregateContainerType(_preset, details) {
13
+ return this.upperCamelCase(`${this._singularizedCodecName(details.resource.codec)}-aggregates`);
14
+ },
15
+ aggregateType(_preset, details) {
16
+ return this.upperCamelCase(`${this._singularizedCodecName(details.resource.codec)}-${details.aggregateSpec.id}-aggregates`);
17
+ },
18
+ aggregatesContainerField(_preset, _details) {
19
+ return 'aggregates';
20
+ },
21
+ groupedAggregatesContainerField(_preset, _details) {
22
+ return 'groupedAggregates';
23
+ },
24
+ aggregatesField(_preset, details) {
25
+ return details.aggregateSpec.id;
26
+ },
27
+ aggregateGroupByType(_preset, details) {
28
+ return this.upperCamelCase(`${this._singularizedCodecName(details.resource.codec)}-group-by`);
29
+ },
30
+ aggregateGroupByAttributeEnum(_preset, details) {
31
+ return this.constantCase(`${this._attributeName({
32
+ attributeName: details.attributeName,
33
+ codec: details.resource.codec
34
+ })}`);
35
+ },
36
+ aggregateHavingInputType(_preset, details) {
37
+ return this.upperCamelCase(`${this._singularizedCodecName(details.resource.codec)}-having-input`);
38
+ },
39
+ aggregateHavingAggregateInputType(_preset, details) {
40
+ return this.upperCamelCase(`${this._singularizedCodecName(details.resource.codec)}-having-${details.aggregateSpec.id}-input`);
41
+ },
42
+ aggregateHavingAggregateComputedAttributeInputType(_preset, details) {
43
+ return this.upperCamelCase(`${this._singularizedCodecName(details.resource.codec)}-having-${details.aggregateSpec.id}-${this._resourceName(details.computedAttributeResource)}-input`);
44
+ },
45
+ aggregateHavingAggregateComputedAttributeArgsInputType(_preset, details) {
46
+ return this.upperCamelCase(`${this._singularizedCodecName(details.resource.codec)}-having-${details.aggregateSpec.id}-${this._resourceName(details.computedAttributeResource)}-args-input`);
47
+ },
48
+ aggregateGroupByAttributeDerivativeEnum(_preset, details) {
49
+ return this.constantCase(`${this._attributeName({
50
+ attributeName: details.attributeName,
51
+ codec: details.resource.codec
52
+ })}-${details.aggregateGroupBySpec.id}`);
53
+ },
54
+ orderByCountOfManyRelationByKeys(_preset, details) {
55
+ const relationName = this._manyRelation(details);
56
+ return this.constantCase(`${relationName}-count`);
57
+ },
58
+ orderByAttributeAggregateOfManyRelationByKeys(_preset, details) {
59
+ const relationName = this._manyRelation(details);
60
+ const relation = details.registry.pgRelations[details.codec.name][details.relationName];
61
+ return this.constantCase(`${relationName}-${details.aggregateSpec.id}-${this._attributeName({
62
+ codec: relation.remoteResource.codec,
63
+ attributeName: details.attributeName
64
+ })}`);
65
+ },
66
+ aggregateHavingFilterInputType(_preset, spec) {
67
+ return this.upperCamelCase(`having-${spec}-filter`);
68
+ }
69
+ }
70
+ }
71
+ };
package/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Dan Lynch <pyramation@gmail.com>
4
+ Copyright (c) 2025 Constructive <developers@constructive.io>
5
+ Copyright (c) 2020-present, Interweb, Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ SOFTWARE.
@@ -0,0 +1,31 @@
1
+ declare global {
2
+ namespace GraphileBuild {
3
+ interface BehaviorStrings {
4
+ 'relatedAggregates:orderBy': true;
5
+ 'resource:relatedAggregates:orderBy': true;
6
+ 'aggregates:orderBy': true;
7
+ 'manyRelation:aggregates:orderBy': true;
8
+ 'aggregate:orderBy': true;
9
+ 'attribute:aggregate:orderBy': true;
10
+ 'sum:manyRelation:aggregates:orderBy': true;
11
+ 'distinctCount:manyRelation:aggregates:orderBy': true;
12
+ 'min:manyRelation:aggregates:orderBy': true;
13
+ 'max:manyRelation:aggregates:orderBy': true;
14
+ 'average:manyRelation:aggregates:orderBy': true;
15
+ 'stddevSample:manyRelation:aggregates:orderBy': true;
16
+ 'stddevPopulation:manyRelation:aggregates:orderBy': true;
17
+ 'varianceSample:manyRelation:aggregates:orderBy': true;
18
+ 'variancePopulation:manyRelation:aggregates:orderBy': true;
19
+ 'sum:attribute:aggregate:orderBy': true;
20
+ 'distinctCount:attribute:aggregate:orderBy': true;
21
+ 'min:attribute:aggregate:orderBy': true;
22
+ 'max:attribute:aggregate:orderBy': true;
23
+ 'average:attribute:aggregate:orderBy': true;
24
+ 'stddevSample:attribute:aggregate:orderBy': true;
25
+ 'stddevPopulation:attribute:aggregate:orderBy': true;
26
+ 'varianceSample:attribute:aggregate:orderBy': true;
27
+ 'variancePopulation:attribute:aggregate:orderBy': true;
28
+ }
29
+ }
30
+ }
31
+ export declare const PgAggregatesOrderByAggregatesPlugin: GraphileConfig.Plugin;