@fraym/crud 0.2.0 → 0.3.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.
- package/README.md +4 -3
- package/dist/cmd/crud.js +168 -34
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -12,15 +12,16 @@ npm i @fraym/crud
|
|
|
12
12
|
|
|
13
13
|
Use the `crud` cli command to automatically apply your crud schemas to the crud service.
|
|
14
14
|
|
|
15
|
-
The `--config ./path/crud.config.json` flag can be used to configure the
|
|
15
|
+
The `--config ./path/crud.config.json` flag can be used to configure the path of your config file.
|
|
16
16
|
|
|
17
|
-
Your type schemas have to
|
|
17
|
+
Your type schemas have to match the glob you specify in `schemaGlob` of he config file (default: `./src/**/*.graphql`).
|
|
18
|
+
You can specify the address (and port) of the crud service instance you use in `serverAddress` of the config file (default: `127.0.0.1:9000`).
|
|
18
19
|
|
|
19
20
|
### CLI command config
|
|
20
21
|
|
|
21
22
|
```json
|
|
22
23
|
{
|
|
23
|
-
"
|
|
24
|
+
"schemaGlob": "./src/crud/*.graphql", // path to your crud schema files
|
|
24
25
|
"serverAddress": "127.0.0.1:9000" // address of the crud service
|
|
25
26
|
}
|
|
26
27
|
```
|
package/dist/cmd/crud.js
CHANGED
|
@@ -7,78 +7,212 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const yargs_1 = __importDefault(require("yargs/yargs"));
|
|
9
9
|
const helpers_1 = require("yargs/helpers");
|
|
10
|
-
const utilities_1 = require("graphql/utilities");
|
|
11
|
-
const kinds_1 = require("graphql/language/kinds");
|
|
12
10
|
const client_1 = require("../management/client");
|
|
11
|
+
const load_1 = require("@graphql-tools/load");
|
|
12
|
+
const graphql_file_loader_1 = require("@graphql-tools/graphql-file-loader");
|
|
13
|
+
const graphql_1 = require("graphql");
|
|
13
14
|
const run = async () => {
|
|
14
15
|
const argv = await (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
|
|
15
|
-
.config({
|
|
16
|
+
.config({ schemaGlob: "./src/**/*.graphql", serverAddress: "127.0.0.1:9000" })
|
|
16
17
|
.pkgConf("crud")
|
|
17
18
|
.config("config", "Path of your `crud.config.ts`, default: `./crud.config.ts`", configPath => JSON.parse(fs_1.default.readFileSync(configPath, "utf-8"))).argv;
|
|
18
|
-
const
|
|
19
|
+
const schemaGlob = argv.schemaGlob;
|
|
19
20
|
const serverAddress = argv.serverAddress;
|
|
20
|
-
const
|
|
21
|
-
|
|
21
|
+
const schema = await (0, load_1.loadSchema)(`${schemaGlob}`, {
|
|
22
|
+
loaders: [new graphql_file_loader_1.GraphQLFileLoader()],
|
|
23
|
+
});
|
|
24
|
+
const definitions = getTypeDefinition(schema);
|
|
25
|
+
await migrateSchemas(definitions, serverAddress);
|
|
22
26
|
};
|
|
23
27
|
run();
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
28
|
+
const getTypeDefinition = (schema) => {
|
|
29
|
+
const definitions = {};
|
|
30
|
+
schema.toConfig().types.forEach(t => {
|
|
31
|
+
var _a;
|
|
32
|
+
if (((_a = t.astNode) === null || _a === void 0 ? void 0 : _a.kind) !== graphql_1.Kind.OBJECT_TYPE_DEFINITION || !(t instanceof graphql_1.GraphQLObjectType)) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const name = t.toString();
|
|
36
|
+
if (definitions[name]) {
|
|
37
|
+
throw new Error(`duplicate definition for type "${name}" detected, try renaming one of them as they have to be uniquely named`);
|
|
38
|
+
}
|
|
39
|
+
definitions[name] = getTypeDefinitionFromGraphQLObjectType(t);
|
|
40
|
+
});
|
|
41
|
+
return definitions;
|
|
42
|
+
};
|
|
43
|
+
const getTypeDefinitionFromGraphQLObjectType = (t) => {
|
|
44
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
45
|
+
const isCrudType = (_c = (((_a = t.astNode) === null || _a === void 0 ? void 0 : _a.directives) &&
|
|
46
|
+
((_b = t.astNode) === null || _b === void 0 ? void 0 : _b.directives.length) > 0 &&
|
|
47
|
+
t.astNode.directives[0].name.value === "crudType")) !== null && _c !== void 0 ? _c : false;
|
|
48
|
+
const name = t.toString();
|
|
49
|
+
let objectDirectivesString = "";
|
|
50
|
+
let objectFieldsString = "";
|
|
51
|
+
let nestedTypes = [];
|
|
52
|
+
(_e = (_d = t.astNode) === null || _d === void 0 ? void 0 : _d.directives) === null || _e === void 0 ? void 0 : _e.forEach(d => {
|
|
53
|
+
objectDirectivesString += getDirectiveString(d);
|
|
54
|
+
});
|
|
55
|
+
(_g = (_f = t.astNode) === null || _f === void 0 ? void 0 : _f.fields) === null || _g === void 0 ? void 0 : _g.forEach(f => {
|
|
56
|
+
const { str, nestedTypes: newNestedTypes } = getFieldStringAndNestedTypes(f);
|
|
57
|
+
objectFieldsString += str;
|
|
58
|
+
newNestedTypes.forEach(nested => {
|
|
59
|
+
if (nestedTypes.indexOf(nested) === -1) {
|
|
60
|
+
nestedTypes.push(nested);
|
|
41
61
|
}
|
|
42
|
-
schemas[name] = (0, utilities_1.printSchema)(typeSchema);
|
|
43
62
|
});
|
|
44
63
|
});
|
|
45
|
-
|
|
64
|
+
const schema = `type ${name}${objectDirectivesString} {${objectFieldsString}\n}`;
|
|
65
|
+
return {
|
|
66
|
+
isCrudType,
|
|
67
|
+
nestedTypes,
|
|
68
|
+
schema,
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
const getFieldStringAndNestedTypes = (f) => {
|
|
72
|
+
var _a;
|
|
73
|
+
let directivesString = "";
|
|
74
|
+
(_a = f.directives) === null || _a === void 0 ? void 0 : _a.forEach(d => {
|
|
75
|
+
directivesString += getDirectiveString(d);
|
|
76
|
+
});
|
|
77
|
+
const { nestedType, str: typeString } = getTypeData(f.type);
|
|
78
|
+
const nestedTypes = [];
|
|
79
|
+
if (nestedType) {
|
|
80
|
+
nestedTypes.push(nestedType);
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
str: `\n${f.name.value}: ${typeString}${directivesString}`,
|
|
84
|
+
nestedTypes,
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
const getTypeData = (t) => {
|
|
88
|
+
switch (t.kind) {
|
|
89
|
+
case graphql_1.Kind.NAMED_TYPE:
|
|
90
|
+
const name = t.name.value;
|
|
91
|
+
return name === "String" ||
|
|
92
|
+
name === "Float" ||
|
|
93
|
+
name === "ID" ||
|
|
94
|
+
name === "Boolean" ||
|
|
95
|
+
name === "Int"
|
|
96
|
+
? {
|
|
97
|
+
str: name,
|
|
98
|
+
}
|
|
99
|
+
: {
|
|
100
|
+
str: name,
|
|
101
|
+
nestedType: name,
|
|
102
|
+
};
|
|
103
|
+
case graphql_1.Kind.LIST_TYPE:
|
|
104
|
+
const { nestedType: listNestedType, str: listStr } = getTypeData(t.type);
|
|
105
|
+
return {
|
|
106
|
+
str: `[${listStr}]`,
|
|
107
|
+
nestedType: listNestedType,
|
|
108
|
+
};
|
|
109
|
+
case graphql_1.Kind.NON_NULL_TYPE:
|
|
110
|
+
const { nestedType: nonNullNestedType, str: nonNullStr } = getTypeData(t.type);
|
|
111
|
+
return {
|
|
112
|
+
str: `${nonNullStr}!`,
|
|
113
|
+
nestedType: nonNullNestedType,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
46
116
|
};
|
|
47
|
-
const
|
|
117
|
+
const getDirectiveString = (d) => {
|
|
118
|
+
if (!d.arguments || d.arguments.length == 0) {
|
|
119
|
+
return ` @${d.name.value}`;
|
|
120
|
+
}
|
|
121
|
+
let argsString = "";
|
|
122
|
+
d.arguments.forEach(a => {
|
|
123
|
+
if (argsString !== "") {
|
|
124
|
+
argsString += ", ";
|
|
125
|
+
}
|
|
126
|
+
argsString += `${a.name.value}: ${getValueString(a.value)}`;
|
|
127
|
+
});
|
|
128
|
+
return ` @${d.name.value}(${argsString})`;
|
|
129
|
+
};
|
|
130
|
+
const getValueString = (v) => {
|
|
131
|
+
switch (v.kind) {
|
|
132
|
+
case graphql_1.Kind.LIST:
|
|
133
|
+
let valuesString = "";
|
|
134
|
+
v.values.forEach(el => {
|
|
135
|
+
if (valuesString !== "") {
|
|
136
|
+
valuesString += ", ";
|
|
137
|
+
}
|
|
138
|
+
valuesString += getValueString(el);
|
|
139
|
+
});
|
|
140
|
+
return `[${valuesString}]`;
|
|
141
|
+
case graphql_1.Kind.STRING:
|
|
142
|
+
return `"${v.value}"`;
|
|
143
|
+
case graphql_1.Kind.FLOAT:
|
|
144
|
+
case graphql_1.Kind.INT:
|
|
145
|
+
case graphql_1.Kind.BOOLEAN:
|
|
146
|
+
return `${v.value}`;
|
|
147
|
+
case graphql_1.Kind.NULL:
|
|
148
|
+
return `null`;
|
|
149
|
+
case graphql_1.Kind.OBJECT:
|
|
150
|
+
let objectString = "";
|
|
151
|
+
v.fields.forEach(f => {
|
|
152
|
+
if (objectString !== "") {
|
|
153
|
+
objectString += ", ";
|
|
154
|
+
}
|
|
155
|
+
objectString += `${f.name.value}: ${getValueString(f.value)}`;
|
|
156
|
+
});
|
|
157
|
+
return `{${objectString}}`;
|
|
158
|
+
default:
|
|
159
|
+
throw new Error(`values of kind ${v.kind} are currently not supported`);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
const migrateSchemas = async (definitions, serverAddress) => {
|
|
48
163
|
const managementClient = await (0, client_1.newManagementClient)({ serverAddress });
|
|
49
164
|
const existingTypeNames = await managementClient.getAllTypes();
|
|
50
165
|
let createSchema = "";
|
|
51
166
|
let updateSchema = "";
|
|
52
167
|
const typesToCreate = [];
|
|
168
|
+
const nestedTypesToCreate = [];
|
|
53
169
|
const typesToUpdate = [];
|
|
170
|
+
const nestedTypesToUpdate = [];
|
|
54
171
|
const typesToRemove = [];
|
|
55
172
|
existingTypeNames.forEach(existingName => {
|
|
56
|
-
if (!
|
|
173
|
+
if (!definitions[existingName] || !definitions[existingName].isCrudType) {
|
|
57
174
|
typesToRemove.push(existingName);
|
|
58
175
|
}
|
|
59
176
|
else {
|
|
60
177
|
typesToUpdate.push(existingName);
|
|
61
|
-
updateSchema += `\n${
|
|
62
|
-
|
|
178
|
+
updateSchema += `\n${definitions[existingName].schema}`;
|
|
179
|
+
definitions[existingName].nestedTypes.forEach(nestedTypeName => {
|
|
180
|
+
if (nestedTypesToUpdate.indexOf(nestedTypeName) !== -1) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
updateSchema += `\n${definitions[nestedTypeName].schema}`;
|
|
184
|
+
nestedTypesToUpdate.push(nestedTypeName);
|
|
185
|
+
});
|
|
186
|
+
delete definitions[existingName];
|
|
63
187
|
}
|
|
64
188
|
});
|
|
65
|
-
Object.keys(
|
|
189
|
+
Object.keys(definitions).forEach(newName => {
|
|
190
|
+
if (!definitions[newName].isCrudType) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
66
193
|
typesToCreate.push(newName);
|
|
67
|
-
createSchema += `\n${
|
|
194
|
+
createSchema += `\n${definitions[newName].schema}`;
|
|
195
|
+
definitions[newName].nestedTypes.forEach(nestedTypeName => {
|
|
196
|
+
if (nestedTypesToCreate.indexOf(nestedTypeName) !== -1) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
createSchema += `\n${definitions[nestedTypeName].schema}`;
|
|
200
|
+
nestedTypesToCreate.push(nestedTypeName);
|
|
201
|
+
});
|
|
68
202
|
});
|
|
69
203
|
if (typesToRemove.length > 0) {
|
|
70
204
|
console.log(`Removing ${typesToRemove.length} types: ${typesToRemove}...`);
|
|
71
|
-
await managementClient.removeTypes(typesToRemove);
|
|
205
|
+
await managementClient.removeTypes(typesToRemove).catch(console.log);
|
|
72
206
|
console.log(`Removed ${typesToRemove.length} types`);
|
|
73
207
|
}
|
|
74
208
|
if (typesToUpdate.length > 0) {
|
|
75
209
|
console.log(`Updating ${typesToUpdate.length} types: ${typesToUpdate}...`);
|
|
76
|
-
await managementClient.updateTypes(updateSchema);
|
|
210
|
+
await managementClient.updateTypes(updateSchema).catch(console.log);
|
|
77
211
|
console.log(`Updated ${typesToUpdate.length} types`);
|
|
78
212
|
}
|
|
79
213
|
if (typesToCreate.length > 0) {
|
|
80
214
|
console.log(`Creating ${typesToCreate.length} types: ${typesToCreate}...`);
|
|
81
|
-
await managementClient.createTypes(createSchema);
|
|
215
|
+
await managementClient.createTypes(createSchema).catch(console.log);
|
|
82
216
|
console.log(`Created ${typesToCreate.length} types`);
|
|
83
217
|
}
|
|
84
218
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fraym/crud",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"license": "UNLICENSED",
|
|
5
5
|
"homepage": "https://github.com/fraym/crud-nodejs",
|
|
6
6
|
"repository": {
|
|
@@ -29,6 +29,8 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@fraym/crud-proto": "^1.0.0-alpha.5",
|
|
32
|
+
"@graphql-tools/graphql-file-loader": "^7.5.11",
|
|
33
|
+
"@graphql-tools/load": "^7.8.6",
|
|
32
34
|
"@grpc/grpc-js": "^1.7.2",
|
|
33
35
|
"fs": "^0.0.1-security",
|
|
34
36
|
"graphql": "^16.6.0",
|
|
@@ -39,8 +41,8 @@
|
|
|
39
41
|
"@types/uuid": "^8.3.4",
|
|
40
42
|
"@types/yargs": "^17.0.13",
|
|
41
43
|
"prettier": "^2.7.1",
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
+
"typescript": "^4.8.4",
|
|
45
|
+
"uuid": "^9.0.0"
|
|
44
46
|
},
|
|
45
47
|
"prettier": "@becklyn/prettier"
|
|
46
48
|
}
|