@smartive/graphql-magic 23.11.0 → 23.12.0-next.1

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.
@@ -1,8 +1,39 @@
1
+ import { GraphQLScalarType, Kind } from 'graphql';
1
2
  import { Models } from '../models/models';
2
3
  import { and, isCreatable, isRootModel, isUpdatable, merge, not, typeToField } from '../models/utils';
3
4
  import { mutationResolver } from './mutations';
4
5
  import { queryResolver } from './resolver';
5
6
 
7
+ const normalizeTimeToHHmmForSerialize = (value: unknown): string => {
8
+ if (typeof value !== 'string') {
9
+ throw new Error(`Time must be a string in HH:mm format. Received: ${typeof value}`);
10
+ }
11
+
12
+ // Postgres `time` columns may serialize to `HH:mm:ss`.
13
+ // Be permissive here and just extract the leading `HH:mm`.
14
+ const match = value.match(/^(\d{2}:\d{2})/);
15
+ if (!match) {
16
+ throw new Error(`Invalid Time value "${value}". Expected HH:mm.`);
17
+ }
18
+
19
+ return match[1];
20
+ };
21
+
22
+ const normalizeTimeToHHmmForParse = (value: unknown): string => {
23
+ if (typeof value !== 'string') {
24
+ throw new Error(`Time must be a string in HH:mm format. Received: ${typeof value}`);
25
+ }
26
+
27
+ // For input, we only accept time-without-timezone values.
28
+ // Accept `HH:mm` and `HH:mm:ss[.fraction]`, but explicitly reject `Z` and `+/-HH:mm`.
29
+ const match = value.match(/^(\d{2}):(\d{2})(?::\d{2}(?:\.\d+)?)?$/);
30
+ if (!match) {
31
+ throw new Error(`Invalid Time value "${value}". Expected HH:mm.`);
32
+ }
33
+
34
+ return `${match[1]}:${match[2]}`;
35
+ };
36
+
6
37
  export const getResolvers = (models: Models) => {
7
38
  const resolvers: Record<string, any> = {
8
39
  Query: merge([
@@ -25,6 +56,19 @@ export const getResolvers = (models: Models) => {
25
56
  [`${model.pluralField}_AGGREGATE`]: queryResolver,
26
57
  })),
27
58
  ]),
59
+ Time: new GraphQLScalarType({
60
+ name: 'Time',
61
+ description: 'Time without date and timezone (HH:mm)',
62
+ serialize: (value) => (value == null ? value : normalizeTimeToHHmmForSerialize(value)),
63
+ parseValue: (value) => normalizeTimeToHHmmForParse(value),
64
+ parseLiteral: (ast) => {
65
+ if (ast.kind !== Kind.STRING) {
66
+ throw new Error(`Invalid literal for Time scalar. Expected STRING, got ${ast.kind}.`);
67
+ }
68
+
69
+ return normalizeTimeToHHmmForParse(ast.value);
70
+ },
71
+ }),
28
72
  };
29
73
  const mutations = [
30
74
  ...models.entities.filter(and(not(isRootModel), isCreatable)).map((model) => ({
@@ -54,6 +54,7 @@ exports[`query can be executed 1`] = `
54
54
  },
55
55
  "field": null,
56
56
  "id": "fc4e013e-4cb0-4ef8-9f2e-3d475bdf2b90",
57
+ "time": "09:15",
57
58
  "xyz": 2,
58
59
  },
59
60
  {
@@ -68,6 +69,7 @@ exports[`query can be executed 1`] = `
68
69
  },
69
70
  "field": "Some value",
70
71
  "id": "604ab55d-ec3e-4857-9f27-219158f80e64",
72
+ "time": "14:30",
71
73
  "xyz": 1,
72
74
  },
73
75
  ],
@@ -333,3 +335,20 @@ exports[`query processes reverseFilters correctly 1`] = `
333
335
  ],
334
336
  }
335
337
  `;
338
+
339
+ exports[`query returns Time values for SomeObject 1`] = `
340
+ {
341
+ "manyObjects": [
342
+ {
343
+ "id": "fc4e013e-4cb0-4ef8-9f2e-3d475bdf2b90",
344
+ "time": "09:15",
345
+ "xyz": 2,
346
+ },
347
+ {
348
+ "id": "604ab55d-ec3e-4857-9f27-219158f80e64",
349
+ "time": "14:30",
350
+ "xyz": 1,
351
+ },
352
+ ],
353
+ }
354
+ `;
@@ -12,6 +12,7 @@ describe('query', () => {
12
12
  id
13
13
  field
14
14
  xyz
15
+ time
15
16
  another {
16
17
  id
17
18
  manyObjects(where: { id: "${SOME_ID}" }) {
@@ -26,6 +27,22 @@ describe('query', () => {
26
27
  });
27
28
  });
28
29
 
30
+ it('returns Time values for SomeObject', async () => {
31
+ await withServer(async (request) => {
32
+ expect(
33
+ await request(gql`
34
+ query GetTimes {
35
+ manyObjects(where: { another: { id: "${ANOTHER_ID}" } }, orderBy: [{ xyz: DESC }]) {
36
+ id
37
+ xyz
38
+ time
39
+ }
40
+ }
41
+ `),
42
+ ).toMatchSnapshot();
43
+ });
44
+ });
45
+
29
46
  it('processes reverseFilters correctly', async () => {
30
47
  await withServer(async (request) => {
31
48
  expect(
@@ -39,5 +39,6 @@ exports[`resolvers are generated correctly 1`] = `
39
39
  "Reaction": {
40
40
  "__resolveType": [Function],
41
41
  },
42
+ "Time": "Time",
42
43
  }
43
44
  `;
@@ -1,7 +1,6 @@
1
1
  import { Knex } from 'knex';
2
2
  import { pick } from 'lodash';
3
3
  import { getColumnName, isInTable, modelNeedsTable } from '../../../src';
4
- import { SeedData } from '../../generated/db';
5
4
  import { models } from '../models';
6
5
 
7
6
  export const ADMIN_ID = '04e45b48-04cf-4b38-bb25-b9af5ae0b2c4';
@@ -16,7 +15,7 @@ export const QUESTION_ID = '3d0f3254-282f-4f1f-95e3-c1f699f3c1e5';
16
15
  export const ANSWER_ID = 'f2d7b3f1-8ea1-4c2c-91ec-024432da1b0d';
17
16
  export const REVIEW_ID = '817c55de-2f77-4159-bd44-9837d868f889';
18
17
 
19
- export const seed: SeedData = {
18
+ export const seed = {
20
19
  User: [
21
20
  {
22
21
  id: ADMIN_ID,
@@ -42,6 +41,7 @@ export const seed: SeedData = {
42
41
  float: 0,
43
42
  list: ['A'],
44
43
  xyz: 1,
44
+ time: '14:30',
45
45
  },
46
46
  {
47
47
  id: SOME_ID_2,
@@ -50,6 +50,7 @@ export const seed: SeedData = {
50
50
  float: 0.5,
51
51
  list: ['B'],
52
52
  xyz: 2,
53
+ time: '09:15',
53
54
  },
54
55
  {
55
56
  id: SOME_ID_3,
@@ -57,6 +58,7 @@ export const seed: SeedData = {
57
58
  float: 0.5,
58
59
  list: ['B'],
59
60
  xyz: 2,
61
+ time: '23:45',
60
62
  },
61
63
  {
62
64
  id: SOME_ID_4,
@@ -64,6 +66,7 @@ export const seed: SeedData = {
64
66
  float: 0.5,
65
67
  list: ['B'],
66
68
  xyz: 2,
69
+ time: '00:05',
67
70
  },
68
71
  ],
69
72
  Question: [
@@ -107,6 +107,12 @@ const modelDefinitions: ModelDefinitions = [
107
107
  orderable: true,
108
108
  filterable: true,
109
109
  },
110
+ {
111
+ name: 'time',
112
+ type: 'Time',
113
+ creatable: true,
114
+ updatable: true,
115
+ },
110
116
  ],
111
117
  },
112
118
  {