@trackunit/react-graphql-tools 1.16.4-alpha-3e7014314a8.0 → 1.17.3
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.
- package/CHANGELOG.md +81 -0
- package/package.json +4 -2
- package/src/executors/createHooks/collectPermissions.d.ts +25 -0
- package/src/executors/createHooks/collectPermissions.js +200 -0
- package/src/executors/createHooks/scrubFile.js +1 -1
- package/src/executors/createHooks/scrubMockFile.d.ts +1 -1
- package/src/executors/createHooks/scrubMockFile.js +37 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,84 @@
|
|
|
1
|
+
## 1.17.3 (2026-08-27)
|
|
2
|
+
|
|
3
|
+
### 🧱 Updated Dependencies
|
|
4
|
+
|
|
5
|
+
- Updated iris-app-build-utilities to 2.4.23
|
|
6
|
+
|
|
7
|
+
## 1.17.2 (2026-08-27)
|
|
8
|
+
|
|
9
|
+
### 🧱 Updated Dependencies
|
|
10
|
+
|
|
11
|
+
- Updated iris-app-build-utilities to 2.4.22
|
|
12
|
+
|
|
13
|
+
## 1.17.1 (2026-08-27)
|
|
14
|
+
|
|
15
|
+
### 🧱 Updated Dependencies
|
|
16
|
+
|
|
17
|
+
- Updated iris-app-build-utilities to 2.4.21
|
|
18
|
+
|
|
19
|
+
## 1.17.0 (2026-08-27)
|
|
20
|
+
|
|
21
|
+
### 🚀 Features
|
|
22
|
+
|
|
23
|
+
- **react-graphql-tools:** derive the authorization scopes a project requires ([7c5c1db1258](https://github.com/Trackunit/manager/commit/7c5c1db1258))
|
|
24
|
+
|
|
25
|
+
### 🧱 Updated Dependencies
|
|
26
|
+
|
|
27
|
+
- Updated iris-app-build-utilities to 2.4.20
|
|
28
|
+
|
|
29
|
+
### ❤️ Thank You
|
|
30
|
+
|
|
31
|
+
- Cursor @cursoragent
|
|
32
|
+
- Mikkel Thorbjørn Andersen
|
|
33
|
+
|
|
34
|
+
## 1.16.3 (2026-08-27)
|
|
35
|
+
|
|
36
|
+
### 🧱 Updated Dependencies
|
|
37
|
+
|
|
38
|
+
- Updated iris-app-build-utilities to 2.4.19
|
|
39
|
+
|
|
40
|
+
## 1.16.2 (2026-08-25)
|
|
41
|
+
|
|
42
|
+
### 🩹 Fixes
|
|
43
|
+
|
|
44
|
+
- **react-graphql-tools:** repair scope collection after applied review suggestions ([3890f3a9706](https://github.com/Trackunit/manager/commit/3890f3a9706))
|
|
45
|
+
|
|
46
|
+
### 🧱 Updated Dependencies
|
|
47
|
+
|
|
48
|
+
- Updated iris-app-build-utilities to 2.4.18
|
|
49
|
+
- Updated react-vite-test-setup to 0.0.114
|
|
50
|
+
|
|
51
|
+
### ❤️ Thank You
|
|
52
|
+
|
|
53
|
+
- Cursor @cursoragent
|
|
54
|
+
- Mikkel Thorbjørn Andersen
|
|
55
|
+
|
|
56
|
+
## 1.16.1 (2026-08-25)
|
|
57
|
+
|
|
58
|
+
### 🧱 Updated Dependencies
|
|
59
|
+
|
|
60
|
+
- Updated iris-app-build-utilities to 2.4.17
|
|
61
|
+
- Updated react-vite-test-setup to 0.0.113
|
|
62
|
+
|
|
63
|
+
## 1.16.0 (2026-08-24)
|
|
64
|
+
|
|
65
|
+
### 🚀 Features
|
|
66
|
+
|
|
67
|
+
- **react-graphql-tools:** document required authorization scopes in generated mocks ([8b500687a87](https://github.com/Trackunit/manager/commit/8b500687a87))
|
|
68
|
+
|
|
69
|
+
### 🩹 Fixes
|
|
70
|
+
|
|
71
|
+
- **react-graphql-tools:** repair scope collection after applied review suggestions ([e0fe4fad522](https://github.com/Trackunit/manager/commit/e0fe4fad522))
|
|
72
|
+
|
|
73
|
+
### 🧱 Updated Dependencies
|
|
74
|
+
|
|
75
|
+
- Updated iris-app-build-utilities to 2.4.16
|
|
76
|
+
|
|
77
|
+
### ❤️ Thank You
|
|
78
|
+
|
|
79
|
+
- Cursor @cursoragent
|
|
80
|
+
- Mikkel Thorbjørn Andersen
|
|
81
|
+
|
|
1
82
|
## 1.15.17 (2026-08-20)
|
|
2
83
|
|
|
3
84
|
### 🧱 Updated Dependencies
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trackunit/react-graphql-tools",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.17.3",
|
|
4
4
|
"main": "src/index.js",
|
|
5
5
|
"executors": "./executors.json",
|
|
6
6
|
"generators": "./generators.json",
|
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
"win-ca": "^3.5.1",
|
|
19
19
|
"tslib": "^2.6.2",
|
|
20
20
|
"@nx/devkit": "23.1.0",
|
|
21
|
-
"@nx/js": "23.1.0"
|
|
21
|
+
"@nx/js": "23.1.0",
|
|
22
|
+
"@trackunit/iris-app-build-utilities": "2.4.23",
|
|
23
|
+
"graphql": "^16.10.0"
|
|
22
24
|
},
|
|
23
25
|
"migrations": "./migrations.json",
|
|
24
26
|
"types": "./src/index.d.ts",
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { GraphQLSchema } from "graphql";
|
|
2
|
+
/**
|
|
3
|
+
* Loads the authorization-annotated schema, reusing codegen's own schema resolution
|
|
4
|
+
* (`resolveSchema`) so scopes are read from the exact schema operations are generated against - the
|
|
5
|
+
* internal superset (which carries every `@authorization` directive) when checked out, otherwise
|
|
6
|
+
* the public schema. Returns null when only the remote endpoint is available (nothing to read from
|
|
7
|
+
* disk) or the schema file is missing (e.g. isolated test workspaces).
|
|
8
|
+
*/
|
|
9
|
+
export declare const loadAuthSchema: (nxRoot: string) => GraphQLSchema | null;
|
|
10
|
+
/**
|
|
11
|
+
* Builds a map of operation name -> sorted, de-duplicated authorization scopes required by that
|
|
12
|
+
* operation, from raw GraphQL operation/fragment source documents.
|
|
13
|
+
*
|
|
14
|
+
* @param schema The authorization-annotated schema to resolve field directives against.
|
|
15
|
+
* @param documents Raw `.graphql` source strings (operations and any fragments they spread).
|
|
16
|
+
*/
|
|
17
|
+
export declare const collectPermissionsFromDocuments: (schema: GraphQLSchema, documents: Array<string>) => Map<string, Array<string>>;
|
|
18
|
+
/**
|
|
19
|
+
* Builds a map of operation name -> sorted, de-duplicated authorization scopes required by that
|
|
20
|
+
* operation, based on the `@authorization` directives of every field the operation touches.
|
|
21
|
+
*
|
|
22
|
+
* @param schema The authorization-annotated schema to resolve field directives against.
|
|
23
|
+
* @param generatedFilePath Path to the generated `graphql.ts`; its lib `src` root holds the operations.
|
|
24
|
+
*/
|
|
25
|
+
export declare const collectOperationPermissions: (schema: GraphQLSchema, generatedFilePath: string) => Map<string, Array<string>>;
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.collectOperationPermissions = exports.collectPermissionsFromDocuments = exports.loadAuthSchema = void 0;
|
|
4
|
+
const iris_app_build_utilities_1 = require("@trackunit/iris-app-build-utilities");
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const graphql_1 = require("graphql");
|
|
7
|
+
const path_1 = require("path");
|
|
8
|
+
const AUTH_DIRECTIVE = "authorization";
|
|
9
|
+
const ALLOW_ARGUMENT = "allow";
|
|
10
|
+
/**
|
|
11
|
+
* Schemas are large (tens of thousands of lines) and every generated lib triggers a scrub, so cache
|
|
12
|
+
* the built schema per absolute path for the lifetime of the process.
|
|
13
|
+
*/
|
|
14
|
+
const schemaCache = new Map();
|
|
15
|
+
/**
|
|
16
|
+
* Loads the authorization-annotated schema, reusing codegen's own schema resolution
|
|
17
|
+
* (`resolveSchema`) so scopes are read from the exact schema operations are generated against - the
|
|
18
|
+
* internal superset (which carries every `@authorization` directive) when checked out, otherwise
|
|
19
|
+
* the public schema. Returns null when only the remote endpoint is available (nothing to read from
|
|
20
|
+
* disk) or the schema file is missing (e.g. isolated test workspaces).
|
|
21
|
+
*/
|
|
22
|
+
const loadAuthSchema = (nxRoot) => {
|
|
23
|
+
const schemaPointer = (0, iris_app_build_utilities_1.resolveSchema)("auto");
|
|
24
|
+
// `resolveSchema` returns the public endpoint URL when no schema file is checked out; we can only
|
|
25
|
+
// inspect directives from a schema we can read off disk, so skip in that case.
|
|
26
|
+
if (/^https?:\/\//.test(schemaPointer))
|
|
27
|
+
return null;
|
|
28
|
+
const schemaPath = (0, path_1.isAbsolute)(schemaPointer) ? schemaPointer : (0, path_1.join)(nxRoot, schemaPointer);
|
|
29
|
+
if (!(0, fs_1.existsSync)(schemaPath))
|
|
30
|
+
return null;
|
|
31
|
+
const cached = schemaCache.get(schemaPath);
|
|
32
|
+
if (cached !== undefined)
|
|
33
|
+
return cached;
|
|
34
|
+
try {
|
|
35
|
+
const schema = (0, graphql_1.buildSchema)((0, fs_1.readFileSync)(schemaPath, { encoding: "utf-8" }), { assumeValidSDL: true });
|
|
36
|
+
schemaCache.set(schemaPath, schema);
|
|
37
|
+
return schema;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
schemaCache.set(schemaPath, null);
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
exports.loadAuthSchema = loadAuthSchema;
|
|
45
|
+
/**
|
|
46
|
+
* Flattens a GraphQL value node into the string scopes it contains.
|
|
47
|
+
* `allow` accepts both a single string (`allow: "asset.view"`) and a list (`allow: ["asset.view"]`).
|
|
48
|
+
*/
|
|
49
|
+
const valueToScopes = (value) => {
|
|
50
|
+
if (value.kind === "StringValue")
|
|
51
|
+
return [value.value];
|
|
52
|
+
if (value.kind === "ListValue")
|
|
53
|
+
return value.values.flatMap(valueToScopes);
|
|
54
|
+
return [];
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Reads the scopes declared by any `@authorization(allow: ...)` directive in the given list.
|
|
58
|
+
*/
|
|
59
|
+
const scopesFromDirectives = (directives) => {
|
|
60
|
+
if (!directives)
|
|
61
|
+
return [];
|
|
62
|
+
return directives
|
|
63
|
+
.filter(directive => directive.name.value === AUTH_DIRECTIVE)
|
|
64
|
+
.flatMap(directive => {
|
|
65
|
+
const allowArgument = directive.arguments?.find(argument => argument.name.value === ALLOW_ARGUMENT);
|
|
66
|
+
return allowArgument ? valueToScopes(allowArgument.value) : [];
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Collects the object-level `@authorization` scopes declared directly on a type definition
|
|
71
|
+
* (the directive is valid on both FIELD_DEFINITION and OBJECT).
|
|
72
|
+
*/
|
|
73
|
+
const scopesFromType = (type) => {
|
|
74
|
+
const definitionDirectives = scopesFromDirectives(type.astNode?.directives);
|
|
75
|
+
const extensionDirectives = type.extensionASTNodes.flatMap(extension => scopesFromDirectives(extension.directives));
|
|
76
|
+
return [...definitionDirectives, ...extensionDirectives];
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Walks a selection set against the schema, following inline fragments and fragment spreads, and
|
|
80
|
+
* accumulates every authorization scope referenced by the accessed fields and their return types.
|
|
81
|
+
*/
|
|
82
|
+
const collectSelectionScopes = (schema, parentType, selectionSet, fragments, scopes, visitedFragments) => {
|
|
83
|
+
if (!parentType)
|
|
84
|
+
return;
|
|
85
|
+
for (const selection of selectionSet.selections) {
|
|
86
|
+
if (selection.kind === "Field") {
|
|
87
|
+
if (selection.name.value === "__typename")
|
|
88
|
+
continue;
|
|
89
|
+
if (!(0, graphql_1.isObjectType)(parentType) && !(0, graphql_1.isInterfaceType)(parentType))
|
|
90
|
+
continue;
|
|
91
|
+
const fieldDefinition = parentType.getFields()[selection.name.value];
|
|
92
|
+
if (!fieldDefinition)
|
|
93
|
+
continue;
|
|
94
|
+
scopesFromDirectives(fieldDefinition.astNode?.directives).forEach(scope => scopes.add(scope));
|
|
95
|
+
const fieldType = (0, graphql_1.getNamedType)(fieldDefinition.type);
|
|
96
|
+
scopesFromType(fieldType).forEach(scope => scopes.add(scope));
|
|
97
|
+
if (selection.selectionSet) {
|
|
98
|
+
collectSelectionScopes(schema, fieldType, selection.selectionSet, fragments, scopes, visitedFragments);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else if (selection.kind === "InlineFragment") {
|
|
102
|
+
const targetType = selection.typeCondition ? schema.getType(selection.typeCondition.name.value) : parentType;
|
|
103
|
+
if (targetType)
|
|
104
|
+
scopesFromType(targetType).forEach(scope => scopes.add(scope));
|
|
105
|
+
collectSelectionScopes(schema, targetType ?? undefined, selection.selectionSet, fragments, scopes, visitedFragments);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
const fragmentName = selection.name.value;
|
|
109
|
+
if (visitedFragments.has(fragmentName))
|
|
110
|
+
continue;
|
|
111
|
+
visitedFragments.add(fragmentName);
|
|
112
|
+
const fragment = fragments.get(fragmentName);
|
|
113
|
+
if (!fragment)
|
|
114
|
+
continue;
|
|
115
|
+
const targetType = schema.getType(fragment.typeCondition.name.value);
|
|
116
|
+
if (targetType)
|
|
117
|
+
scopesFromType(targetType).forEach(scope => scopes.add(scope));
|
|
118
|
+
collectSelectionScopes(schema, targetType ?? undefined, fragment.selectionSet, fragments, scopes, visitedFragments);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Recursively reads every `.graphql` source document under a directory, skipping generated output.
|
|
124
|
+
* `*-manager.graphql` is excluded to match codegen's default document globs, so an operation that was
|
|
125
|
+
* never generated can't shadow a generated one of the same name.
|
|
126
|
+
*/
|
|
127
|
+
const readGraphqlDocuments = (directory) => {
|
|
128
|
+
if (!(0, fs_1.existsSync)(directory))
|
|
129
|
+
return [];
|
|
130
|
+
const documents = [];
|
|
131
|
+
for (const entry of (0, fs_1.readdirSync)(directory, { withFileTypes: true })) {
|
|
132
|
+
const fullPath = (0, path_1.join)(directory, entry.name);
|
|
133
|
+
if (entry.isDirectory()) {
|
|
134
|
+
if (entry.name === "generated" || entry.name === "node_modules")
|
|
135
|
+
continue;
|
|
136
|
+
documents.push(...readGraphqlDocuments(fullPath));
|
|
137
|
+
}
|
|
138
|
+
else if (entry.name.endsWith(".graphql") && !entry.name.endsWith("-manager.graphql")) {
|
|
139
|
+
documents.push((0, fs_1.readFileSync)(fullPath, { encoding: "utf-8" }));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return documents;
|
|
143
|
+
};
|
|
144
|
+
/**
|
|
145
|
+
* Builds a map of operation name -> sorted, de-duplicated authorization scopes required by that
|
|
146
|
+
* operation, from raw GraphQL operation/fragment source documents.
|
|
147
|
+
*
|
|
148
|
+
* @param schema The authorization-annotated schema to resolve field directives against.
|
|
149
|
+
* @param documents Raw `.graphql` source strings (operations and any fragments they spread).
|
|
150
|
+
*/
|
|
151
|
+
const collectPermissionsFromDocuments = (schema, documents) => {
|
|
152
|
+
const result = new Map();
|
|
153
|
+
if (documents.length === 0)
|
|
154
|
+
return result;
|
|
155
|
+
const definitions = [];
|
|
156
|
+
for (const document of documents) {
|
|
157
|
+
try {
|
|
158
|
+
definitions.push(...(0, graphql_1.parse)(document).definitions);
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
// A single unparseable document must not silently strip scope docs from every other operation.
|
|
162
|
+
// eslint-disable-next-line no-console
|
|
163
|
+
console.warn(`[graphql-mock] Skipping unparseable GraphQL document: ${error.message}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const fragments = new Map();
|
|
167
|
+
for (const definition of definitions) {
|
|
168
|
+
if (definition.kind === "FragmentDefinition") {
|
|
169
|
+
fragments.set(definition.name.value, definition);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
for (const definition of definitions) {
|
|
173
|
+
if (definition.kind !== "OperationDefinition" || !definition.name)
|
|
174
|
+
continue;
|
|
175
|
+
const rootType = definition.operation === "query"
|
|
176
|
+
? schema.getQueryType()
|
|
177
|
+
: definition.operation === "mutation"
|
|
178
|
+
? schema.getMutationType()
|
|
179
|
+
: schema.getSubscriptionType();
|
|
180
|
+
const scopes = new Set();
|
|
181
|
+
collectSelectionScopes(schema, rootType ?? undefined, definition.selectionSet, fragments, scopes, new Set());
|
|
182
|
+
result.set(definition.name.value, [...scopes].sort());
|
|
183
|
+
}
|
|
184
|
+
return result;
|
|
185
|
+
};
|
|
186
|
+
exports.collectPermissionsFromDocuments = collectPermissionsFromDocuments;
|
|
187
|
+
/**
|
|
188
|
+
* Builds a map of operation name -> sorted, de-duplicated authorization scopes required by that
|
|
189
|
+
* operation, based on the `@authorization` directives of every field the operation touches.
|
|
190
|
+
*
|
|
191
|
+
* @param schema The authorization-annotated schema to resolve field directives against.
|
|
192
|
+
* @param generatedFilePath Path to the generated `graphql.ts`; its lib `src` root holds the operations.
|
|
193
|
+
*/
|
|
194
|
+
const collectOperationPermissions = (schema, generatedFilePath) => {
|
|
195
|
+
// <lib>/src/generated/graphql-api/graphql.ts -> <lib>/src
|
|
196
|
+
const documentsDirectory = (0, path_1.join)((0, path_1.dirname)(generatedFilePath), "..", "..");
|
|
197
|
+
return (0, exports.collectPermissionsFromDocuments)(schema, readGraphqlDocuments(documentsDirectory));
|
|
198
|
+
};
|
|
199
|
+
exports.collectOperationPermissions = collectOperationPermissions;
|
|
200
|
+
//# sourceMappingURL=collectPermissions.js.map
|
|
@@ -93,7 +93,7 @@ const scrubFile = async (file, nxRoot, isVerbose, generateMocks = true) => {
|
|
|
93
93
|
// mutates the shared source file via insertStatements().
|
|
94
94
|
const scrubbedFileContent = await (0, exports.scrubFileContent)(fileContent, nxRoot, isVerbose, sharedSourceFile);
|
|
95
95
|
const scrubbedMockFileContent = generateMocks
|
|
96
|
-
? await (0, scrubMockFile_1.scrubMockFileContent)(fileContent, nxRoot, isVerbose, sharedSourceFile)
|
|
96
|
+
? await (0, scrubMockFile_1.scrubMockFileContent)(fileContent, nxRoot, isVerbose, sharedSourceFile, file)
|
|
97
97
|
: undefined;
|
|
98
98
|
const prettierOptions = await getPrettierOptions(nxRoot);
|
|
99
99
|
let prettySrc = scrubbedFileContent;
|
|
@@ -2,4 +2,4 @@ import { SourceFile } from "ts-morph";
|
|
|
2
2
|
/**
|
|
3
3
|
* Generates React hooks from your graphql files.
|
|
4
4
|
*/
|
|
5
|
-
export declare const scrubMockFileContent: (fileContent: string,
|
|
5
|
+
export declare const scrubMockFileContent: (fileContent: string, nxRoot: string, isVerbose?: boolean, providedSourceFile?: SourceFile, generatedFilePath?: string) => Promise<string>;
|
|
@@ -4,6 +4,7 @@ exports.scrubMockFileContent = void 0;
|
|
|
4
4
|
/* eslint-disable no-console */
|
|
5
5
|
const faker_1 = require("@faker-js/faker");
|
|
6
6
|
const ts_morph_1 = require("ts-morph");
|
|
7
|
+
const collectPermissions_1 = require("./collectPermissions");
|
|
7
8
|
const UNDEFINED_SENTINEL = "__UNDEFINED__";
|
|
8
9
|
/**
|
|
9
10
|
* Strips surrounding double-quotes from a ts-morph property name.
|
|
@@ -281,7 +282,7 @@ const generateMockFromType = (type, text) => {
|
|
|
281
282
|
/**
|
|
282
283
|
* Generates React hooks from your graphql files.
|
|
283
284
|
*/
|
|
284
|
-
const scrubMockFileContent = async (fileContent,
|
|
285
|
+
const scrubMockFileContent = async (fileContent, nxRoot, isVerbose, providedSourceFile, generatedFilePath) => {
|
|
285
286
|
const start = new Date().getTime();
|
|
286
287
|
const generatedFile = providedSourceFile ??
|
|
287
288
|
new ts_morph_1.Project({
|
|
@@ -307,6 +308,10 @@ const scrubMockFileContent = async (fileContent, _nxRoot, isVerbose, providedSou
|
|
|
307
308
|
const time = end - start;
|
|
308
309
|
console.log(`Mock Execution time: ${time}`);
|
|
309
310
|
}
|
|
311
|
+
// Resolve which authorization scopes each mocked operation requires. `@authorization(allow: [...])`
|
|
312
|
+
// directives only live in the schema, so we walk each operation's selection set against it.
|
|
313
|
+
const schema = generatedFilePath ? (0, collectPermissions_1.loadAuthSchema)(nxRoot) : null;
|
|
314
|
+
const permissionsByOperation = schema && generatedFilePath ? (0, collectPermissions_1.collectOperationPermissions)(schema, generatedFilePath) : new Map();
|
|
310
315
|
const mock = mockThese.map(mocker => {
|
|
311
316
|
const postFix = (mocker.getName().includes("Query") ? "Query" : "Mutation").length;
|
|
312
317
|
const variablesTypeName = `${mocker.getName()}Variables`;
|
|
@@ -346,7 +351,15 @@ const scrubMockFileContent = async (fileContent, _nxRoot, isVerbose, providedSou
|
|
|
346
351
|
: `
|
|
347
352
|
const default${mocker.getName()}Variables: gql.${variablesTypeName} = ${defaultVarsSource};
|
|
348
353
|
`;
|
|
349
|
-
|
|
354
|
+
const permissions = permissionsByOperation.get(docName) ?? [];
|
|
355
|
+
const permissionsComment = permissions.length > 0
|
|
356
|
+
? `
|
|
357
|
+
/**
|
|
358
|
+
* Required authorization scopes (the token needs at least one allowed scope per accessed field):
|
|
359
|
+
${permissions.map(scope => ` * - ${scope}`).join("\n")}
|
|
360
|
+
*/`
|
|
361
|
+
: "";
|
|
362
|
+
return `${constBlock}${permissionsComment}
|
|
350
363
|
export const mockFor${mocker.getName()} = (${variablesParam}, data?: DeepPartialNullable<gql.${mocker.getName()}> ) => {
|
|
351
364
|
return queryFor(gql.${docName}Document, mergeDeepVars(${defaultVarsRef}, variables),
|
|
352
365
|
mergeDeep(
|
|
@@ -355,6 +368,11 @@ const scrubMockFileContent = async (fileContent, _nxRoot, isVerbose, providedSou
|
|
|
355
368
|
);
|
|
356
369
|
}`;
|
|
357
370
|
});
|
|
371
|
+
const mockedOperationNames = mockThese.map(mocker => {
|
|
372
|
+
const postFix = (mocker.getName().includes("Query") ? "Query" : "Mutation").length;
|
|
373
|
+
return mocker.getName().substring(0, mocker.getName().length - postFix);
|
|
374
|
+
});
|
|
375
|
+
logRequiredPermissions(mockedOperationNames, permissionsByOperation, generatedFilePath, isVerbose);
|
|
358
376
|
return `
|
|
359
377
|
import { mergeDeep } from "@apollo/client/utilities";
|
|
360
378
|
import { mergeDeepVars, queryFor, DeepPartialNullable, PickDeepPartialNullable } from "@trackunit/react-core-contexts-test";
|
|
@@ -364,4 +382,21 @@ const scrubMockFileContent = async (fileContent, _nxRoot, isVerbose, providedSou
|
|
|
364
382
|
`;
|
|
365
383
|
};
|
|
366
384
|
exports.scrubMockFileContent = scrubMockFileContent;
|
|
385
|
+
/**
|
|
386
|
+
* Prints a summary of the authorization scopes required by the mocked operations, plus the full
|
|
387
|
+
* de-duplicated list of scopes a token must be granted to satisfy every mock in the file.
|
|
388
|
+
*/
|
|
389
|
+
const logRequiredPermissions = (operationNames, permissionsByOperation, generatedFilePath, isVerbose) => {
|
|
390
|
+
if (!isVerbose || permissionsByOperation.size === 0)
|
|
391
|
+
return;
|
|
392
|
+
const allScopes = new Set();
|
|
393
|
+
console.log(`[graphql-mock] Required authorization scopes${generatedFilePath ? ` for ${generatedFilePath}` : ""}:`);
|
|
394
|
+
operationNames.forEach(operationName => {
|
|
395
|
+
const scopes = permissionsByOperation.get(operationName) ?? [];
|
|
396
|
+
scopes.forEach(scope => allScopes.add(scope));
|
|
397
|
+
console.log(` ${operationName}: ${scopes.length > 0 ? scopes.join(", ") : "(none)"}`);
|
|
398
|
+
});
|
|
399
|
+
const sortedScopes = [...allScopes].sort();
|
|
400
|
+
console.log(`[graphql-mock] All scopes referenced (${sortedScopes.length}): ${sortedScopes.length > 0 ? sortedScopes.join(", ") : "(none)"}`);
|
|
401
|
+
};
|
|
367
402
|
//# sourceMappingURL=scrubMockFile.js.map
|