@smartive/graphql-magic 9.1.2 → 10.0.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 (120) hide show
  1. package/.eslintrc +2 -10
  2. package/.github/workflows/release.yml +1 -1
  3. package/.gqmrc.json +6 -0
  4. package/CHANGELOG.md +2 -2
  5. package/README.md +1 -1
  6. package/dist/bin/gqm.cjs +684 -330
  7. package/dist/cjs/index.cjs +998 -554
  8. package/dist/esm/api/execute.js +1 -1
  9. package/dist/esm/api/execute.js.map +1 -1
  10. package/dist/esm/client/mutations.d.ts +2 -2
  11. package/dist/esm/client/mutations.js +5 -4
  12. package/dist/esm/client/mutations.js.map +1 -1
  13. package/dist/esm/client/queries.d.ts +12 -17
  14. package/dist/esm/client/queries.js +30 -50
  15. package/dist/esm/client/queries.js.map +1 -1
  16. package/dist/esm/context.d.ts +1 -2
  17. package/dist/esm/db/generate.d.ts +3 -3
  18. package/dist/esm/db/generate.js +31 -29
  19. package/dist/esm/db/generate.js.map +1 -1
  20. package/dist/esm/migrations/generate.d.ts +3 -4
  21. package/dist/esm/migrations/generate.js +114 -107
  22. package/dist/esm/migrations/generate.js.map +1 -1
  23. package/dist/esm/models/index.d.ts +1 -0
  24. package/dist/esm/models/index.js +1 -0
  25. package/dist/esm/models/index.js.map +1 -1
  26. package/dist/esm/models/model-definitions.d.ts +189 -0
  27. package/dist/esm/models/model-definitions.js +2 -0
  28. package/dist/esm/models/model-definitions.js.map +1 -0
  29. package/dist/esm/models/models.d.ts +128 -174
  30. package/dist/esm/models/models.js +411 -1
  31. package/dist/esm/models/models.js.map +1 -1
  32. package/dist/esm/models/mutation-hook.d.ts +2 -2
  33. package/dist/esm/models/utils.d.ts +35 -497
  34. package/dist/esm/models/utils.js +21 -144
  35. package/dist/esm/models/utils.js.map +1 -1
  36. package/dist/esm/permissions/check.d.ts +3 -3
  37. package/dist/esm/permissions/check.js +14 -7
  38. package/dist/esm/permissions/check.js.map +1 -1
  39. package/dist/esm/permissions/generate.js +6 -6
  40. package/dist/esm/permissions/generate.js.map +1 -1
  41. package/dist/esm/resolvers/filters.d.ts +8 -0
  42. package/dist/esm/resolvers/filters.js +28 -25
  43. package/dist/esm/resolvers/filters.js.map +1 -1
  44. package/dist/esm/resolvers/index.d.ts +1 -0
  45. package/dist/esm/resolvers/index.js +1 -0
  46. package/dist/esm/resolvers/index.js.map +1 -1
  47. package/dist/esm/resolvers/mutations.js +85 -21
  48. package/dist/esm/resolvers/mutations.js.map +1 -1
  49. package/dist/esm/resolvers/node.d.ts +13 -15
  50. package/dist/esm/resolvers/node.js +41 -36
  51. package/dist/esm/resolvers/node.js.map +1 -1
  52. package/dist/esm/resolvers/resolver.js +19 -49
  53. package/dist/esm/resolvers/resolver.js.map +1 -1
  54. package/dist/esm/resolvers/resolvers.d.ts +1 -8
  55. package/dist/esm/resolvers/resolvers.js +15 -7
  56. package/dist/esm/resolvers/resolvers.js.map +1 -1
  57. package/dist/esm/resolvers/selects.d.ts +3 -0
  58. package/dist/esm/resolvers/selects.js +50 -0
  59. package/dist/esm/resolvers/selects.js.map +1 -0
  60. package/dist/esm/resolvers/utils.d.ts +12 -4
  61. package/dist/esm/resolvers/utils.js +30 -22
  62. package/dist/esm/resolvers/utils.js.map +1 -1
  63. package/dist/esm/schema/generate.d.ts +4 -4
  64. package/dist/esm/schema/generate.js +122 -131
  65. package/dist/esm/schema/generate.js.map +1 -1
  66. package/dist/esm/schema/utils.d.ts +1 -1
  67. package/dist/esm/schema/utils.js +2 -1
  68. package/dist/esm/schema/utils.js.map +1 -1
  69. package/knexfile.ts +31 -0
  70. package/migrations/20230912185644_setup.ts +127 -0
  71. package/package.json +16 -14
  72. package/src/api/execute.ts +1 -1
  73. package/src/bin/gqm/gqm.ts +25 -23
  74. package/src/bin/gqm/parse-models.ts +5 -5
  75. package/src/bin/gqm/settings.ts +13 -4
  76. package/src/bin/gqm/static-eval.ts +5 -0
  77. package/src/bin/gqm/templates.ts +23 -3
  78. package/src/client/mutations.ts +11 -5
  79. package/src/client/queries.ts +43 -80
  80. package/src/context.ts +1 -2
  81. package/src/db/generate.ts +41 -41
  82. package/src/migrations/generate.ts +165 -146
  83. package/src/models/index.ts +1 -0
  84. package/src/models/model-definitions.ts +168 -0
  85. package/src/models/models.ts +510 -166
  86. package/src/models/mutation-hook.ts +2 -2
  87. package/src/models/utils.ts +53 -187
  88. package/src/permissions/check.ts +19 -11
  89. package/src/permissions/generate.ts +6 -6
  90. package/src/resolvers/filters.ts +44 -28
  91. package/src/resolvers/index.ts +1 -0
  92. package/src/resolvers/mutations.ts +98 -36
  93. package/src/resolvers/node.ts +79 -51
  94. package/src/resolvers/resolver.ts +20 -74
  95. package/src/resolvers/resolvers.ts +18 -7
  96. package/src/resolvers/selects.ts +77 -0
  97. package/src/resolvers/utils.ts +41 -25
  98. package/src/schema/generate.ts +106 -127
  99. package/src/schema/utils.ts +2 -1
  100. package/tests/api/__snapshots__/inheritance.spec.ts.snap +83 -0
  101. package/tests/api/inheritance.spec.ts +130 -0
  102. package/tests/generated/api/index.ts +1174 -0
  103. package/tests/generated/client/index.ts +1163 -0
  104. package/tests/generated/client/mutations.ts +109 -0
  105. package/tests/generated/db/index.ts +291 -0
  106. package/tests/generated/db/knex.ts +14 -0
  107. package/tests/generated/models.json +675 -0
  108. package/tests/generated/schema.graphql +325 -0
  109. package/tests/unit/__snapshots__/resolve.spec.ts.snap +23 -0
  110. package/tests/unit/queries.spec.ts +5 -5
  111. package/tests/unit/resolve.spec.ts +8 -8
  112. package/tests/utils/database/knex.ts +5 -13
  113. package/tests/utils/database/seed.ts +57 -18
  114. package/tests/utils/models.ts +62 -7
  115. package/tests/utils/server.ts +5 -5
  116. package/tsconfig.eslint.json +1 -0
  117. package/tests/unit/__snapshots__/generate.spec.ts.snap +0 -128
  118. package/tests/unit/generate.spec.ts +0 -8
  119. package/tests/utils/database/schema.ts +0 -64
  120. package/tests/utils/generate-migration.ts +0 -24
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartive/graphql-magic",
3
- "version": "9.1.2",
3
+ "version": "10.0.0",
4
4
  "description": "",
5
5
  "source": "src/index.ts",
6
6
  "type": "module",
@@ -13,16 +13,17 @@
13
13
  "sideEffecs": false,
14
14
  "scripts": {
15
15
  "bootstrap": "npm ci && npm run generate",
16
- "generate": "npm run generate:index-files",
16
+ "generate": "npm run generate:index-files && npm run build:bin && npm run generate:gqm-stuff && npm run generate:setup-migration",
17
17
  "generate:index-files": "cti create ./src --excludes bin --withoutbackup",
18
+ "generate:gqm-stuff": "npx gqm generate",
19
+ "generate:setup-migration": "npm run build:bin && npx gqm generate-migration setup 20230912185644",
18
20
  "lint": "eslint src",
19
21
  "lint:fix": "eslint src --fix",
20
22
  "deps": "docker-compose up",
21
23
  "test": "npm run lint && npm run test:all && npm run build",
22
- "test:all": "jest tests --no-cache --no-watchman",
23
- "test:unit": "jest tests/unit --no-cache --no-watchman",
24
- "test:api": "jest tests/api --no-cache --no-watchman",
25
- "generate-migration": "esbuild tests/utils/generate-migration.ts --bundle --platform=node --outdir=tmp --out-extension:.js=.cjs --format=cjs --packages=external && node tmp/generate-migration.cjs",
24
+ "test:all": "jest tests --no-cache --no-watchman --setupFiles dotenv/config",
25
+ "test:unit": "jest tests/unit --no-cache --no-watchman --setupFiles dotenv/config",
26
+ "test:api": "jest tests/api --no-cache --no-watchman --setupFiles dotenv/config",
26
27
  "clean": "del-cli dist/**",
27
28
  "prebuild": "npm run clean",
28
29
  "build": "npm run build:esm && npm run build:cjs && npm run build:bin",
@@ -35,7 +36,7 @@
35
36
  },
36
37
  "overrides": {
37
38
  "graphql": "$graphql",
38
- "rollup": "3.29.3"
39
+ "rollup": "3.29.4"
39
40
  },
40
41
  "browserslist": "> 0.25%, not dead",
41
42
  "publishConfig": {
@@ -62,16 +63,17 @@
62
63
  "uuid": "^9.0.0"
63
64
  },
64
65
  "devDependencies": {
65
- "@smartive/eslint-config": "3.2.0",
66
+ "@smartive/eslint-config": "3.3.0",
66
67
  "@smartive/prettier-config": "3.1.2",
67
- "@types/jest": "29.5.5",
68
- "@types/lodash": "4.14.199",
69
- "@types/luxon": "3.3.2",
70
- "@types/uuid": "9.0.4",
68
+ "@types/jest": "29.5.6",
69
+ "@types/lodash": "4.14.200",
70
+ "@types/luxon": "3.3.3",
71
+ "@types/pg": "8.10.2",
72
+ "@types/uuid": "9.0.6",
71
73
  "create-ts-index": "1.14.0",
72
74
  "del-cli": "5.1.0",
73
- "esbuild": "0.19.3",
74
- "eslint": "8.50.0",
75
+ "esbuild": "0.19.5",
76
+ "eslint": "8.52.0",
75
77
  "graphql-request": "6.1.0",
76
78
  "jest": "29.7.0",
77
79
  "mock-knex": "0.4.12",
@@ -12,7 +12,7 @@ export const execute = async ({
12
12
  additionalResolvers?: IResolvers<any, any>;
13
13
  body: any;
14
14
  } & Omit<Context, 'document'>) => {
15
- const document = generate(ctx.rawModels);
15
+ const document = generate(ctx.models);
16
16
 
17
17
  const generatedResolvers = getResolvers(ctx.models);
18
18
 
@@ -40,12 +40,13 @@ program
40
40
  .command('generate')
41
41
  .description('Generate all the things')
42
42
  .action(async () => {
43
- const rawModels = await parseModels();
43
+ const models = await parseModels();
44
44
  const generatedFolderPath = await getSetting('generatedFolderPath');
45
- writeToFile(`${generatedFolderPath}/schema.graphql`, printSchemaFromModels(rawModels));
46
- writeToFile(`${generatedFolderPath}/client/mutations.ts`, generateMutations(rawModels));
47
- writeToFile(`${generatedFolderPath}/db/index.ts`, generateDBModels(rawModels));
48
- writeToFile(`${generatedFolderPath}/db/knex.ts`, generateKnexTables(rawModels));
45
+ const gqlModule = await getSetting('gqlModule');
46
+ writeToFile(`${generatedFolderPath}/schema.graphql`, printSchemaFromModels(models));
47
+ writeToFile(`${generatedFolderPath}/client/mutations.ts`, generateMutations(models, gqlModule));
48
+ writeToFile(`${generatedFolderPath}/db/index.ts`, generateDBModels(models));
49
+ writeToFile(`${generatedFolderPath}/db/knex.ts`, generateKnexTables(models));
49
50
  await generateGraphqlApiTypes();
50
51
  await generateGraphqlClientTypes();
51
52
  });
@@ -54,45 +55,44 @@ program
54
55
  .command('generate-models')
55
56
  .description('Generate models.json')
56
57
  .action(async () => {
57
- const rawModels = await parseModels();
58
- const generatedFolderPath = await getSetting('generatedFolderPath');
59
- writeToFile(`${generatedFolderPath}/models.json`, JSON.stringify(rawModels, null, 2));
58
+ await parseModels();
60
59
  });
61
60
 
62
61
  program
63
62
  .command('generate-schema')
64
63
  .description('Generate schema')
65
64
  .action(async () => {
66
- const rawModels = await parseModels();
65
+ const models = await parseModels();
67
66
  const generatedFolderPath = await getSetting('generatedFolderPath');
68
- writeToFile(`${generatedFolderPath}/schema.graphql`, printSchemaFromModels(rawModels));
67
+ writeToFile(`${generatedFolderPath}/schema.graphql`, printSchemaFromModels(models));
69
68
  });
70
69
 
71
70
  program
72
71
  .command('generate-mutation-queries')
73
72
  .description('Generate mutation-queries')
74
73
  .action(async () => {
75
- const rawModels = await parseModels();
74
+ const models = await parseModels();
76
75
  const generatedFolderPath = await getSetting('generatedFolderPath');
77
- writeToFile(`${generatedFolderPath}/client/mutations.ts`, generateMutations(rawModels));
76
+ const gqlModule = await getSetting('gqlModule');
77
+ writeToFile(`${generatedFolderPath}/client/mutations.ts`, generateMutations(models, gqlModule));
78
78
  });
79
79
 
80
80
  program
81
81
  .command('generate-db-types')
82
82
  .description('Generate DB types')
83
83
  .action(async () => {
84
- const rawModels = await parseModels();
84
+ const models = await parseModels();
85
85
  const generatedFolderPath = await getSetting('generatedFolderPath');
86
- writeToFile(`${generatedFolderPath}/db/index.ts`, generateMutations(rawModels));
86
+ writeToFile(`${generatedFolderPath}/db/index.ts`, generateDBModels(models));
87
87
  });
88
88
 
89
89
  program
90
90
  .command('generate-knex-types')
91
91
  .description('Generate Knex types')
92
92
  .action(async () => {
93
- const rawModels = await parseModels();
93
+ const models = await parseModels();
94
94
  const generatedFolderPath = await getSetting('generatedFolderPath');
95
- writeToFile(`${generatedFolderPath}/db/knex.ts`, generateKnexTables(rawModels));
95
+ writeToFile(`${generatedFolderPath}/db/knex.ts`, generateKnexTables(models));
96
96
  });
97
97
 
98
98
  program
@@ -110,14 +110,16 @@ program
110
110
  });
111
111
 
112
112
  program
113
- .command('generate-migration')
113
+ .command('generate-migration [<name>] [<date>]')
114
114
  .description('Generate Migration')
115
- .action(async () => {
115
+ .action(async (name, date) => {
116
116
  const git = simpleGit();
117
117
 
118
- let name = process.argv[process.argv.indexOf('gqm') + 1] || (await git.branch()).current.split('/').pop();
118
+ if (!name) {
119
+ name = (await git.branch()).current.split('/').pop();
120
+ }
119
121
 
120
- if (name && ['staging', 'production'].includes(name)) {
122
+ if (!name || ['main', 'staging', 'production'].includes(name)) {
121
123
  name = await readLine('Migration name:');
122
124
  }
123
125
 
@@ -125,10 +127,10 @@ program
125
127
  const db = knex(knexfile);
126
128
 
127
129
  try {
128
- const rawModels = await parseModels();
129
- const migrations = await new MigrationGenerator(db, rawModels).generate();
130
+ const models = await parseModels();
131
+ const migrations = await new MigrationGenerator(db, models).generate();
130
132
 
131
- writeToFile(`migrations/${getMigrationDate()}_${name}.ts`, migrations);
133
+ writeToFile(`migrations/${date || getMigrationDate()}_${name}.ts`, migrations);
132
134
  } finally {
133
135
  await db.destroy();
134
136
  }
@@ -1,5 +1,5 @@
1
1
  import { IndentationText, Project } from 'ts-morph';
2
- import { RawModels } from '../..';
2
+ import { Models } from '../..';
3
3
  import { getSetting, writeToFile } from './settings';
4
4
  import { staticEval } from './static-eval';
5
5
  import { findDeclarationInFile } from './utils';
@@ -13,12 +13,12 @@ export const parseModels = async () => {
13
13
  const modelsPath = await getSetting('modelsPath');
14
14
  const sourceFile = project.addSourceFileAtPath(modelsPath);
15
15
 
16
- const modelsDeclaration = findDeclarationInFile(sourceFile, 'rawModels');
16
+ const modelsDeclaration = findDeclarationInFile(sourceFile, 'models');
17
17
 
18
- const rawModels = staticEval(modelsDeclaration, {});
18
+ const models = staticEval(modelsDeclaration, {}) as Models;
19
19
 
20
20
  const generatedFolderPath = await getSetting('generatedFolderPath');
21
- writeToFile(`${generatedFolderPath}/models.json`, JSON.stringify(rawModels, null, 2));
21
+ writeToFile(`${generatedFolderPath}/models.json`, JSON.stringify(models.definitions, null, 2));
22
22
 
23
- return rawModels as RawModels;
23
+ return models;
24
24
  };
@@ -30,6 +30,9 @@ const DEFAULTS = {
30
30
  ensureDirectoryExists(path);
31
31
  },
32
32
  },
33
+ gqlModule: {
34
+ defaultValue: '@smartive/graphql-magic',
35
+ },
33
36
  };
34
37
 
35
38
  type Settings = {
@@ -45,8 +48,10 @@ const initSetting = async (name: string) => {
45
48
 
46
49
  const initSettings = async () => {
47
50
  const settings: Settings = {} as Settings;
48
- for (const name of Object.keys(DEFAULTS)) {
49
- settings[name] = await initSetting(name);
51
+ for (const [name, config] of Object.entries(DEFAULTS)) {
52
+ if ('queston' in config) {
53
+ settings[name] = await initSetting(name);
54
+ }
50
55
  }
51
56
  saveSettings(settings);
52
57
  };
@@ -65,8 +70,12 @@ export const getSettings = async (): Promise<Settings> => {
65
70
  export const getSetting = async (name: keyof Settings): Promise<string> => {
66
71
  const settings = await getSettings();
67
72
  if (!(name in settings)) {
68
- settings[name] = await initSetting(name);
69
- saveSettings(settings);
73
+ if ('question' in DEFAULTS[name]) {
74
+ settings[name] = await initSetting(name);
75
+ saveSettings(settings);
76
+ } else {
77
+ return DEFAULTS[name].defaultValue;
78
+ }
70
79
  }
71
80
  return settings[name];
72
81
  };
@@ -11,6 +11,7 @@ import {
11
11
  TemplateExpression,
12
12
  TemplateTail,
13
13
  } from 'ts-morph';
14
+ import { Models } from '../../models/models';
14
15
  import { Visitor, visit } from './visitor';
15
16
 
16
17
  export const staticEval = (node: Node | undefined, context: Dictionary<unknown>) =>
@@ -54,6 +55,8 @@ const VISITOR: Visitor<unknown, Dictionary<unknown>> = {
54
55
  return process;
55
56
  case 'Symbol':
56
57
  return Symbol;
58
+ case 'Models':
59
+ return Models;
57
60
  }
58
61
  const definitionNodes = node.getDefinitionNodes();
59
62
  if (!definitionNodes.length) {
@@ -204,4 +207,6 @@ const VISITOR: Visitor<unknown, Dictionary<unknown>> = {
204
207
  },
205
208
  [SyntaxKind.NoSubstitutionTemplateLiteral]: (node) => node.getLiteralValue(),
206
209
  [SyntaxKind.NullKeyword]: () => null,
210
+ [SyntaxKind.NewExpression]: (node, context) =>
211
+ new (staticEval(node.getExpression(), context))(...node.getArguments().map((arg) => staticEval(arg, context))),
207
212
  };
@@ -1,7 +1,7 @@
1
1
  export const EMPTY_MODELS = `
2
- import { RawModels, getModels } from '@smartive/graphql-magic';
2
+ import { RawModels, Models } from '@smartive/graphql-magic';
3
3
 
4
- export const rawModels: RawModels = [
4
+ const rawModels: RawModels = [
5
5
  {
6
6
  kind: 'entity',
7
7
  name: 'User',
@@ -9,10 +9,23 @@ export const rawModels: RawModels = [
9
9
  },
10
10
  ]
11
11
 
12
- export const models = getModels(rawModels);
12
+ export const models = new Models(rawModels);
13
13
  `;
14
14
 
15
15
  export const KNEXFILE = `
16
+ import { DateTime } from 'luxon';
17
+ import { types } from 'pg';
18
+
19
+ const dateOids = { date: 1082, timestamptz: 1184, timestamp: 1114 };
20
+ for (const oid of Object.values(dateOids)) {
21
+ types.setTypeParser(oid, (val) => DateTime.fromSQL(val));
22
+ }
23
+
24
+ const numberOids = { int8: 20, float8: 701, numeric: 1700 };
25
+ for (const oid of Object.values(numberOids)) {
26
+ types.setTypeParser(oid, Number);
27
+ }
28
+
16
29
  const config = {
17
30
  client: 'postgresql',
18
31
  connection: {
@@ -21,6 +34,13 @@ const config = {
21
34
  user: process.env.DATABASE_USER,
22
35
  password: process.env.DATABASE_PASSWORD,
23
36
  },
37
+ migrations: {
38
+ tableName: 'knex_migrations',
39
+ },
40
+ pool: {
41
+ min: 0,
42
+ max: 30,
43
+ },
24
44
  } as const;
25
45
 
26
46
  export default config;
@@ -1,12 +1,12 @@
1
1
  import upperCase from 'lodash/upperCase';
2
- import { isEntityModel } from '..';
3
- import { RawModels } from '../models/models';
2
+ import { isRootModel, not } from '..';
3
+ import { Models } from '../models/models';
4
4
 
5
5
  const constantCase = (str: string) => upperCase(str).replace(/ /g, '_');
6
6
 
7
- export const generateMutations = (models: RawModels) => {
7
+ export const generateMutations = (models: Models, gqmModule = '@smartive/graphql-magic') => {
8
8
  const parts: string[] = [];
9
- for (const { name, creatable, updatable, deletable } of models.filter(isEntityModel)) {
9
+ for (const { name, creatable, updatable, deletable } of models.entities.filter(not(isRootModel))) {
10
10
  if (creatable) {
11
11
  parts.push(
12
12
  `export const CREATE_${constantCase(
@@ -29,8 +29,14 @@ export const generateMutations = (models: RawModels) => {
29
29
  name
30
30
  )} = gql\`\n mutation Delete${name}Mutation($id: ID!) {\n delete${name}(where: { id: $id })\n }\n\`;`
31
31
  );
32
+
33
+ parts.push(
34
+ `export const RESTORE_${constantCase(
35
+ name
36
+ )} = gql\`\n mutation Restore${name}Mutation($id: ID!) {\n restore${name}(where: { id: $id })\n }\n\`;`
37
+ );
32
38
  }
33
39
  }
34
40
 
35
- return `import { gql } from "@smartive/graphql-magic";\n\n${parts.join('\n\n')}`;
41
+ return `import { gql } from "${gqmModule}";\n\n${parts.join('\n\n')}`;
36
42
  };
@@ -1,23 +1,20 @@
1
1
  import upperFirst from 'lodash/upperFirst';
2
- import { ModelField } from '..';
3
- import { Model, Models, Relation, ReverseRelation } from '../models/models';
2
+ import { ManyToManyRelation } from '..';
3
+ import { EntityModel, Model, Models, Relation } from '../models/models';
4
4
  import {
5
5
  actionableRelations,
6
6
  and,
7
- getModelPlural,
8
- getModelPluralField,
9
7
  isQueriableBy,
10
8
  isRelation,
11
9
  isSimpleField,
12
10
  isToOneRelation,
13
11
  isUpdatableBy,
14
12
  not,
15
- summonByName,
16
13
  typeToField,
17
14
  } from '../models/utils';
18
15
 
19
16
  export const getUpdateEntityQuery = (
20
- model: Model,
17
+ model: EntityModel,
21
18
  role: any,
22
19
  fields?: string[] | undefined,
23
20
  additionalFields = ''
@@ -38,8 +35,7 @@ export const getUpdateEntityQuery = (
38
35
  }`;
39
36
 
40
37
  export const getEditEntityRelationsQuery = (
41
- models: Models,
42
- model: Model,
38
+ model: EntityModel,
43
39
  action: 'create' | 'update' | 'filter',
44
40
  fields?: string[],
45
41
  ignoreFields?: string[],
@@ -53,21 +49,19 @@ export const getEditEntityRelationsQuery = (
53
49
  !!relations.length &&
54
50
  `query ${upperFirst(action)}${model.name}Relations {
55
51
  ${relations
56
- .map(({ name, type }) => {
57
- const model = summonByName(models, type);
58
-
52
+ .map((relation) => {
59
53
  let filters = '';
60
- if (model.displayField) {
61
- const displayField = model.fieldsByName[model.displayField];
54
+ if (relation.targetModel.displayField) {
55
+ const displayField = relation.targetModel.fieldsByName[relation.targetModel.displayField];
62
56
  if (displayField.orderable) {
63
- filters = `(orderBy: [{ ${model.displayField}: ASC }])`;
57
+ filters = `(orderBy: [{ ${relation.targetModel.displayField}: ASC }])`;
64
58
  }
65
59
  }
66
60
 
67
- return `${name}: ${getModelPluralField(model)}${filters} {
61
+ return `${relation.name}: ${relation.targetModel.pluralField}${filters} {
68
62
  id
69
- display: ${model.displayField || 'id'}
70
- ${additionalFields[name] || ''}
63
+ display: ${relation.targetModel.displayField || 'id'}
64
+ ${additionalFields[relation.name] || ''}
71
65
  }`;
72
66
  })
73
67
  .join(' ')}
@@ -75,48 +69,20 @@ export const getEditEntityRelationsQuery = (
75
69
  );
76
70
  };
77
71
 
78
- export const getManyToManyRelations = (model: Model, fields?: string[], ignoreFields?: string[]) => {
79
- const manyToManyRelations: [ReverseRelation, Relation][] = [];
80
- for (const field of model.reverseRelations) {
81
- if ((fields && !fields.includes(field.name)) || (ignoreFields && ignoreFields.includes(field.name))) {
82
- continue;
83
- }
84
-
85
- const relation = field.model.relations.find(
86
- (relation) => !relation.field.generated && relation.field.name !== field.field.name
87
- );
88
- if (!relation) {
89
- continue;
90
- }
91
-
92
- const inapplicableFields = field.model.fields.filter(
93
- (otherField) => !otherField.generated && ![field.field.name, relation.field.name].includes(otherField.name)
94
- );
95
- if (inapplicableFields.length) {
96
- continue;
97
- }
98
-
99
- manyToManyRelations.push([field, relation]);
100
- }
101
- return manyToManyRelations;
102
- };
103
-
104
- export const getManyToManyRelation = (model: Model, name: string) => getManyToManyRelations(model, [name])[0];
105
-
106
72
  export const getManyToManyRelationsQuery = (
107
73
  model: Model,
108
74
  action: 'create' | 'update',
109
- manyToManyRelations: [ReverseRelation, Relation][]
75
+ manyToManyRelations: ManyToManyRelation[]
110
76
  ) =>
111
77
  !!manyToManyRelations.length &&
112
78
  (action === 'update'
113
79
  ? `query Update${model.name}ManyToManyRelations($id: ID!) {
114
80
  ${typeToField(model.name)}(where: { id: $id }) {
115
81
  ${manyToManyRelations
116
- .map(([reverseRelation, { field }]) => {
117
- return `${reverseRelation.name} {
82
+ .map((relation) => {
83
+ return `${relation.name} {
118
84
  id
119
- ${field.name} {
85
+ ${relation.relationToTarget.name} {
120
86
  id
121
87
  }
122
88
  }`;
@@ -124,20 +90,20 @@ export const getManyToManyRelationsQuery = (
124
90
  .join(' ')}
125
91
  }
126
92
  ${manyToManyRelations
127
- .map(([reverseRelation, { model }]) => {
128
- return `${reverseRelation.name}: ${getModelPluralField(model)} {
93
+ .map((relation) => {
94
+ return `${relation.name}: ${relation.targetModel.pluralField} {
129
95
  id
130
- ${model.displayField || ''}
96
+ ${relation.targetModel.displayField || ''}
131
97
  }`;
132
98
  })
133
99
  .join(' ')}
134
100
  }`
135
101
  : `query Create${model.name}ManyToManyRelations {
136
102
  ${manyToManyRelations
137
- .map(([reverseRelation, { model }]) => {
138
- return `${reverseRelation.name}: ${getModelPluralField(model)} {
103
+ .map((relation) => {
104
+ return `${relation.name}: ${relation.targetModel.pluralField} {
139
105
  id
140
- ${model.displayField || ''}
106
+ ${relation.targetModel.displayField || ''}
141
107
  }`;
142
108
  })
143
109
  .join(' ')}
@@ -174,28 +140,28 @@ export const getMutationQuery = (model: Model, action: 'create' | 'update' | 'de
174
140
  }
175
141
  `;
176
142
 
177
- export const displayField = (model: Model) => `
143
+ export const displayField = (model: EntityModel) => `
178
144
  ${model.displayField ? `display: ${model.displayField}` : ''}
179
145
  `;
180
146
 
181
147
  export const getEntityListQuery = (
182
- model: Model,
148
+ model: EntityModel,
183
149
  role: string,
184
150
  additionalFields = '',
185
151
  root?: {
186
- model: Model;
152
+ model: EntityModel;
187
153
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
188
154
  entity: any;
189
155
  reverseRelationName: string;
190
156
  }
191
- ) => `query ${getModelPlural(model)}List(
157
+ ) => `query ${model.plural}List(
192
158
  ${root ? '$id: ID!,' : ''}
193
159
  $limit: Int!,
194
160
  $where: ${model.name}Where!,
195
161
  ${model.fields.some(({ searchable }) => searchable) ? '$search: String,' : ''}
196
162
  ) {
197
163
  ${root ? `root: ${typeToField(root.model.name)}(where: { id: $id }) {` : ''}
198
- data: ${root ? root.reverseRelationName : getModelPluralField(model)}(limit: $limit, where: $where, ${
164
+ data: ${root ? root.reverseRelationName : model.pluralField}(limit: $limit, where: $where, ${
199
165
  model.fields.some(({ searchable }) => searchable) ? ', search: $search' : ''
200
166
  }) {
201
167
  ${displayField(model)}
@@ -209,12 +175,11 @@ export type VisibleRelationsByRole = Record<string, Record<string, string[]>>;
209
175
 
210
176
  export const isVisibleRelation = (visibleRelationsByRole: VisibleRelationsByRole, modelName: string, role: string) => {
211
177
  const whitelist = visibleRelationsByRole[role]?.[modelName];
212
- return ({ name }: ModelField) => (whitelist ? whitelist.includes(name) : true);
178
+ return (relation: Relation) => (whitelist ? whitelist.includes(relation.name) : true);
213
179
  };
214
180
 
215
181
  export const getEntityQuery = (
216
- models: Models,
217
- model: Model,
182
+ model: EntityModel,
218
183
  role: string,
219
184
  visibleRelationsByRole: VisibleRelationsByRole,
220
185
  typesWithSubRelations: string[]
@@ -223,42 +188,40 @@ export const getEntityQuery = (
223
188
  ${displayField(model)}
224
189
  ${model.fields.filter(and(isSimpleField, isQueriableBy(role))).map(({ name }) => name)}
225
190
  ${queryRelations(
226
- models,
227
- model.fields.filter(isRelation).filter(isVisibleRelation(visibleRelationsByRole, model.name, role)),
191
+ model.models,
192
+ model.relations.filter(isVisibleRelation(visibleRelationsByRole, model.name, role)),
228
193
  role,
229
194
  typesWithSubRelations
230
195
  )}
231
196
  ${queryRelations(
232
- models,
233
- model.reverseRelations.filter(and(isToOneRelation, isVisibleRelation(visibleRelationsByRole, model.name, role))),
197
+ model.models,
198
+ model.reverseRelations.filter(
199
+ (reverseRelation) =>
200
+ isToOneRelation(reverseRelation.field) &&
201
+ isVisibleRelation(visibleRelationsByRole, model.name, role)(reverseRelation)
202
+ ),
234
203
  role,
235
204
  typesWithSubRelations
236
205
  )}
237
206
  }
238
207
  }`;
239
208
 
240
- export const getFindEntityQuery = (model: Model, role: string) => `query Find${model.name}($where: ${
209
+ export const getFindEntityQuery = (model: EntityModel, role: string) => `query Find${model.name}($where: ${
241
210
  model.name
242
211
  }Where!, $orderBy: [${model.name}OrderBy!]) {
243
- data: ${getModelPluralField(model)}(limit: 1, where: $where, orderBy: $orderBy) {
212
+ data: ${model.pluralField}(limit: 1, where: $where, orderBy: $orderBy) {
244
213
  ${model.fields.filter(and(isSimpleField, isQueriableBy(role))).map(({ name }) => name)}
245
214
  }
246
215
  }`;
247
216
 
248
- export const queryRelations = (
249
- models: Models,
250
- relations: { name: string; type: string }[],
251
- role: string,
252
- typesWithSubRelations: string[]
253
- ) =>
217
+ export const queryRelations = (models: Models, relations: Relation[], role: string, typesWithSubRelations: string[]) =>
254
218
  relations
255
- .map(({ name, type }): string => {
256
- const relatedModel = summonByName(models, type);
257
- const subRelations = typesWithSubRelations.includes(type) ? relatedModel.fields.filter(isRelation) : [];
219
+ .map((relation): string => {
220
+ const subRelations = typesWithSubRelations.includes(relation.targetModel.name) ? relation.targetModel.relations : [];
258
221
 
259
- return `${name} {
222
+ return `${relation.name} {
260
223
  id
261
- ${displayField(relatedModel)}
224
+ ${displayField(relation.targetModel)}
262
225
  ${subRelations.length > 0 ? queryRelations(models, subRelations, role, typesWithSubRelations) : ''}
263
226
  }`;
264
227
  })
package/src/context.ts CHANGED
@@ -2,7 +2,7 @@ import { DocumentNode, GraphQLResolveInfo } from 'graphql';
2
2
  import { IncomingMessage } from 'http';
3
3
  import { Knex } from 'knex';
4
4
  import { DateTime } from 'luxon';
5
- import { Models, RawModels } from './models/models';
5
+ import { Models } from './models/models';
6
6
  import { Entity, MutationHook } from './models/mutation-hook';
7
7
  import { Permissions } from './permissions/generate';
8
8
  import { AliasGenerator } from './resolvers/utils';
@@ -18,7 +18,6 @@ export type Context = {
18
18
  locale: string;
19
19
  locales: string[];
20
20
  user: User;
21
- rawModels: RawModels;
22
21
  models: Models;
23
22
  permissions: Permissions;
24
23
  mutationHook?: MutationHook;