@fraym/projections 0.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.
- package/README.md +156 -0
- package/dist/cmd/projections.d.ts +2 -0
- package/dist/cmd/projections.js +248 -0
- package/dist/config/config.d.ts +7 -0
- package/dist/config/config.js +38 -0
- package/dist/delivery/client.d.ts +9 -0
- package/dist/delivery/client.js +31 -0
- package/dist/delivery/getData.d.ts +3 -0
- package/dist/delivery/getData.js +31 -0
- package/dist/delivery/getDataList.d.ts +7 -0
- package/dist/delivery/getDataList.js +35 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +18 -0
- package/dist/management/client.d.ts +9 -0
- package/dist/management/client.js +41 -0
- package/dist/management/create.d.ts +2 -0
- package/dist/management/create.js +17 -0
- package/dist/management/getAll.d.ts +2 -0
- package/dist/management/getAll.js +15 -0
- package/dist/management/remove.d.ts +2 -0
- package/dist/management/remove.js +17 -0
- package/dist/management/update.d.ts +2 -0
- package/dist/management/update.js +17 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# projections-nodejs
|
|
2
|
+
|
|
3
|
+
Client implementation in javascript for the projections service [streams](https://github.com/fraym/projections).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```shell
|
|
8
|
+
npm i @fraym/projections
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## GraphQL
|
|
12
|
+
|
|
13
|
+
You can access the graphQL api at `http://projections:3000/delivery/graphql`.
|
|
14
|
+
There is a sandbox available at `http://projections:3000/delivery/graphql/sandbox`.
|
|
15
|
+
|
|
16
|
+
You need to add the `Tenant-Id` header in order to use the graphQL Endpoint and the sandbox.
|
|
17
|
+
|
|
18
|
+
## CLI command
|
|
19
|
+
|
|
20
|
+
Use the `projections` cli command to automatically apply your projection schemas to the projections service.
|
|
21
|
+
|
|
22
|
+
Your type schemas have to match the glob you specify in the `PROJECTIONS_SCHEMA_GLOB` env variable (default: `./src/**/*.graphql`).
|
|
23
|
+
You can specify the address (and port) of the crud service instance you use in the `PROJECTIONS_SERVER_ADDRESS` env variable (default: `127.0.0.1:9000`).
|
|
24
|
+
|
|
25
|
+
You need to add a file that contains all built-in directives to your type schemas. The latest version of this file can be found [here](default.graphql).
|
|
26
|
+
|
|
27
|
+
### Config
|
|
28
|
+
|
|
29
|
+
Use a `.env` file or env variables to configure cte clients and the command:
|
|
30
|
+
|
|
31
|
+
```env
|
|
32
|
+
PROJECTIONS_SERVER_ADDRESS=127.0.0.1:9000
|
|
33
|
+
PROJECTIONS_SCHEMA_GLOB=./src/projections/*.graphql
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
### Create the clients
|
|
39
|
+
|
|
40
|
+
delivery client:
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
const deliveryClient = await newDeliveryClient();
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
management client:
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
const managementClient = await newManagementClient();
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Create one or multipe projections
|
|
53
|
+
|
|
54
|
+
Projectionw are defined by schemas. A schema can contain more than one projection definition. See [SCHEMA.md](https://github.com/fraym/projections/blob/develop/SCHEMA.md) for a reference.
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
await managementClient.create("your schema here");
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Update one or multipe projections
|
|
61
|
+
|
|
62
|
+
Projectionw are defined by schemas. A schema can contain more than one projection definition. See [SCHEMA.md](https://github.com/fraym/projections/blob/develop/SCHEMA.md) for a reference.
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
await managementClient.update("your schema here");
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Remove one or multipe projections
|
|
69
|
+
|
|
70
|
+
The name of `YourProjection` has to equal your projection name in your schema (also in casing).
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
await managementClient.remove(["YourProjection"]);
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Get list of existing projections
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
const list = await managementClient.getAll();
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Get a single projection element
|
|
83
|
+
|
|
84
|
+
The name of `YourProjection` has to equal your projection name in your schema (also in casing).
|
|
85
|
+
The `id` has to match the id of the projection element that you want to get.
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
const data = await deliveryClient.getData("tenantId", "YourProjection", "id");
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
You can specify a fourth parameter if you want to return a empty dataset instead of getting an error when querying a non existing element:
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
const data = await deliveryClient.getData("tenantId", "YourProjection", "id", true);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Get (paginated) data
|
|
98
|
+
|
|
99
|
+
The name of `YourProjection` has to equal your type name in your schema (also in casing).
|
|
100
|
+
|
|
101
|
+
No pagination:
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
const data = await deliveryClient.getDataList("tenantId", "YourProjection");
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
With pagination:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
const limit = 50; // elements to query per page
|
|
111
|
+
const page = 1; // number of the page you want to select, first page starts at: 1
|
|
112
|
+
const data = await deliveryClient.getDataList("tenantId", "YourProjection", limit, page);
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Gracefully close the clients
|
|
116
|
+
|
|
117
|
+
You won't lose any data if you don't. Use it for your peace of mind.
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
client.close();
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Development
|
|
124
|
+
|
|
125
|
+
You'll need the following apps for a smooth development experience:
|
|
126
|
+
|
|
127
|
+
- minikube
|
|
128
|
+
- lens
|
|
129
|
+
- okteto
|
|
130
|
+
- helm
|
|
131
|
+
|
|
132
|
+
### Running the dev environment
|
|
133
|
+
|
|
134
|
+
- Start minikube if not already done:
|
|
135
|
+
|
|
136
|
+
```shell
|
|
137
|
+
minikube start
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
- add mongodb and minio to your lokal kubernetes
|
|
141
|
+
- use Makefiles in `./.dev/*`
|
|
142
|
+
- copy `.env.build` to `.env.build.local`
|
|
143
|
+
- add your personal access token (needs read access for private fraym org repositories)
|
|
144
|
+
- deploy the app to your cluster
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
make init
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
- start okteto
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
make dev
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
- connect your IDE to that okteto instance
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
#! /usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const yargs_1 = __importDefault(require("yargs/yargs"));
|
|
8
|
+
const helpers_1 = require("yargs/helpers");
|
|
9
|
+
const dotenv_1 = require("dotenv");
|
|
10
|
+
const graphql_file_loader_1 = require("@graphql-tools/graphql-file-loader");
|
|
11
|
+
const load_1 = require("@graphql-tools/load");
|
|
12
|
+
const graphql_1 = require("graphql");
|
|
13
|
+
const client_1 = require("../management/client");
|
|
14
|
+
const run = async () => {
|
|
15
|
+
(0, dotenv_1.config)();
|
|
16
|
+
const argv = await (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
|
|
17
|
+
.config({ schemaGlob: "./src/**/*.graphql", serverAddress: "127.0.0.1:9000" })
|
|
18
|
+
.pkgConf("projections").argv;
|
|
19
|
+
let schemaGlob = argv.schemaGlob;
|
|
20
|
+
let serverAddress = argv.serverAddress;
|
|
21
|
+
if (process.env.PROJECTIONS_SCHEMA_GLOB) {
|
|
22
|
+
schemaGlob = process.env.PROJECTIONS_SCHEMA_GLOB;
|
|
23
|
+
}
|
|
24
|
+
if (process.env.PROJECTIONS_SERVER_ADDRESS) {
|
|
25
|
+
serverAddress = process.env.PROJECTIONS_SERVER_ADDRESS;
|
|
26
|
+
}
|
|
27
|
+
const schema = await (0, load_1.loadSchema)(`${schemaGlob}`, {
|
|
28
|
+
loaders: [new graphql_file_loader_1.GraphQLFileLoader()],
|
|
29
|
+
});
|
|
30
|
+
const definitions = getTypeDefinition(schema);
|
|
31
|
+
await migrateSchemas(definitions, serverAddress);
|
|
32
|
+
};
|
|
33
|
+
run();
|
|
34
|
+
const getTypeDefinition = (schema) => {
|
|
35
|
+
const definitions = {};
|
|
36
|
+
schema.toConfig().types.forEach(t => {
|
|
37
|
+
if (!(t instanceof graphql_1.GraphQLObjectType) && !(t instanceof graphql_1.GraphQLEnumType)) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const name = t.toString();
|
|
41
|
+
if (definitions[name]) {
|
|
42
|
+
throw new Error(`duplicate definition for type "${name}" detected, try renaming one of them as they have to be uniquely named`);
|
|
43
|
+
}
|
|
44
|
+
if (t instanceof graphql_1.GraphQLObjectType) {
|
|
45
|
+
definitions[name] = getTypeDefinitionFromGraphQLObjectType(t);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (t instanceof graphql_1.GraphQLEnumType) {
|
|
49
|
+
definitions[name] = getTypeDefinitionFromGraphQLEnumType(t);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
return definitions;
|
|
54
|
+
};
|
|
55
|
+
const getTypeDefinitionFromGraphQLEnumType = (t) => {
|
|
56
|
+
var _a, _b;
|
|
57
|
+
const name = t.toString();
|
|
58
|
+
let enumValuesString = "";
|
|
59
|
+
(_b = (_a = t.astNode) === null || _a === void 0 ? void 0 : _a.values) === null || _b === void 0 ? void 0 : _b.forEach(value => {
|
|
60
|
+
enumValuesString += `\n\t${value.name.value}`;
|
|
61
|
+
});
|
|
62
|
+
const schema = `enum ${name} {${enumValuesString}\n}`;
|
|
63
|
+
return {
|
|
64
|
+
isProjection: false,
|
|
65
|
+
nestedTypes: [],
|
|
66
|
+
schema,
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
const getTypeDefinitionFromGraphQLObjectType = (t) => {
|
|
70
|
+
var _a, _b, _c, _d, _e, _f;
|
|
71
|
+
let isProjection = false;
|
|
72
|
+
if (((_a = t.astNode) === null || _a === void 0 ? void 0 : _a.directives) && ((_b = t.astNode) === null || _b === void 0 ? void 0 : _b.directives.length) > 0) {
|
|
73
|
+
const directiveNames = t.astNode.directives.map(directive => directive.name.value);
|
|
74
|
+
isProjection = directiveNames.includes("identifyBy");
|
|
75
|
+
}
|
|
76
|
+
const name = t.toString();
|
|
77
|
+
let objectDirectivesString = "";
|
|
78
|
+
let objectFieldsString = "";
|
|
79
|
+
let nestedTypes = [];
|
|
80
|
+
(_d = (_c = t.astNode) === null || _c === void 0 ? void 0 : _c.directives) === null || _d === void 0 ? void 0 : _d.forEach(d => {
|
|
81
|
+
objectDirectivesString += getDirectiveString(d);
|
|
82
|
+
});
|
|
83
|
+
(_f = (_e = t.astNode) === null || _e === void 0 ? void 0 : _e.fields) === null || _f === void 0 ? void 0 : _f.forEach(f => {
|
|
84
|
+
const { str, nestedTypes: newNestedTypes } = getFieldStringAndNestedTypes(f);
|
|
85
|
+
objectFieldsString += str;
|
|
86
|
+
newNestedTypes.forEach(nested => {
|
|
87
|
+
if (nestedTypes.indexOf(nested) === -1) {
|
|
88
|
+
nestedTypes.push(nested);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
const schema = `type ${name}${objectDirectivesString} {${objectFieldsString}\n}`;
|
|
93
|
+
return {
|
|
94
|
+
isProjection,
|
|
95
|
+
nestedTypes,
|
|
96
|
+
schema,
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
const getFieldStringAndNestedTypes = (f) => {
|
|
100
|
+
var _a;
|
|
101
|
+
let directivesString = "";
|
|
102
|
+
(_a = f.directives) === null || _a === void 0 ? void 0 : _a.forEach(d => {
|
|
103
|
+
directivesString += getDirectiveString(d);
|
|
104
|
+
});
|
|
105
|
+
const { nestedType, str: typeString } = getTypeData(f.type);
|
|
106
|
+
const nestedTypes = [];
|
|
107
|
+
if (nestedType) {
|
|
108
|
+
nestedTypes.push(nestedType);
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
str: `\n\t${f.name.value}: ${typeString}${directivesString}`,
|
|
112
|
+
nestedTypes,
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
const getTypeData = (t) => {
|
|
116
|
+
switch (t.kind) {
|
|
117
|
+
case graphql_1.Kind.NAMED_TYPE:
|
|
118
|
+
const name = t.name.value;
|
|
119
|
+
return name === "String" ||
|
|
120
|
+
name === "Float" ||
|
|
121
|
+
name === "ID" ||
|
|
122
|
+
name === "Boolean" ||
|
|
123
|
+
name === "Int" ||
|
|
124
|
+
name === "DateTime"
|
|
125
|
+
? {
|
|
126
|
+
str: name,
|
|
127
|
+
}
|
|
128
|
+
: {
|
|
129
|
+
str: name,
|
|
130
|
+
nestedType: name,
|
|
131
|
+
};
|
|
132
|
+
case graphql_1.Kind.LIST_TYPE:
|
|
133
|
+
const { nestedType: listNestedType, str: listStr } = getTypeData(t.type);
|
|
134
|
+
return {
|
|
135
|
+
str: `[${listStr}]`,
|
|
136
|
+
nestedType: listNestedType,
|
|
137
|
+
};
|
|
138
|
+
case graphql_1.Kind.NON_NULL_TYPE:
|
|
139
|
+
const { nestedType: nonNullNestedType, str: nonNullStr } = getTypeData(t.type);
|
|
140
|
+
return {
|
|
141
|
+
str: `${nonNullStr}!`,
|
|
142
|
+
nestedType: nonNullNestedType,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
const getDirectiveString = (d) => {
|
|
147
|
+
if (!d.arguments || d.arguments.length == 0) {
|
|
148
|
+
return ` @${d.name.value}`;
|
|
149
|
+
}
|
|
150
|
+
let argsString = "";
|
|
151
|
+
d.arguments.forEach(a => {
|
|
152
|
+
if (argsString !== "") {
|
|
153
|
+
argsString += ", ";
|
|
154
|
+
}
|
|
155
|
+
argsString += `${a.name.value}: ${getValueString(a.value)}`;
|
|
156
|
+
});
|
|
157
|
+
return ` @${d.name.value}(${argsString})`;
|
|
158
|
+
};
|
|
159
|
+
const getValueString = (v) => {
|
|
160
|
+
switch (v.kind) {
|
|
161
|
+
case graphql_1.Kind.LIST:
|
|
162
|
+
let valuesString = "";
|
|
163
|
+
v.values.forEach(el => {
|
|
164
|
+
if (valuesString !== "") {
|
|
165
|
+
valuesString += ", ";
|
|
166
|
+
}
|
|
167
|
+
valuesString += getValueString(el);
|
|
168
|
+
});
|
|
169
|
+
return `[${valuesString}]`;
|
|
170
|
+
case graphql_1.Kind.STRING:
|
|
171
|
+
return `"${v.value}"`;
|
|
172
|
+
case graphql_1.Kind.FLOAT:
|
|
173
|
+
case graphql_1.Kind.INT:
|
|
174
|
+
case graphql_1.Kind.BOOLEAN:
|
|
175
|
+
return `${v.value}`;
|
|
176
|
+
case graphql_1.Kind.NULL:
|
|
177
|
+
return `null`;
|
|
178
|
+
case graphql_1.Kind.OBJECT:
|
|
179
|
+
let objectString = "";
|
|
180
|
+
v.fields.forEach(f => {
|
|
181
|
+
if (objectString !== "") {
|
|
182
|
+
objectString += ", ";
|
|
183
|
+
}
|
|
184
|
+
objectString += `${f.name.value}: ${getValueString(f.value)}`;
|
|
185
|
+
});
|
|
186
|
+
return `{${objectString}}`;
|
|
187
|
+
default:
|
|
188
|
+
throw new Error(`values of kind ${v.kind} are currently not supported`);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
const migrateSchemas = async (definitions, serverAddress) => {
|
|
192
|
+
const managementClient = await (0, client_1.newManagementClient)({ serverAddress });
|
|
193
|
+
let existingProjections = (await managementClient.getAll()).filter(projectionName => !projectionName.startsWith("Crud"));
|
|
194
|
+
let createSchema = "";
|
|
195
|
+
let updateSchema = "";
|
|
196
|
+
const projectionsToCreate = [];
|
|
197
|
+
const nestedTypesToCreate = [];
|
|
198
|
+
const projectionsToUpdate = [];
|
|
199
|
+
const nestedTypesToUpdate = [];
|
|
200
|
+
const projectionsToRemove = [];
|
|
201
|
+
existingProjections.forEach(projectionName => {
|
|
202
|
+
if (!definitions[projectionName] || !definitions[projectionName].isProjection) {
|
|
203
|
+
projectionsToRemove.push(projectionName);
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
projectionsToUpdate.push(projectionName);
|
|
207
|
+
updateSchema += `\n${definitions[projectionName].schema}`;
|
|
208
|
+
definitions[projectionName].nestedTypes.forEach(nestedTypeName => {
|
|
209
|
+
if (nestedTypesToUpdate.indexOf(nestedTypeName) !== -1 ||
|
|
210
|
+
(definitions[nestedTypeName] && definitions[nestedTypeName].isProjection)) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
updateSchema += `\n${definitions[nestedTypeName].schema}`;
|
|
214
|
+
nestedTypesToUpdate.push(nestedTypeName);
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
Object.keys(definitions).forEach(newName => {
|
|
219
|
+
if (!definitions[newName].isProjection || existingProjections.includes(newName)) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
projectionsToCreate.push(newName);
|
|
223
|
+
createSchema += `\n${definitions[newName].schema}`;
|
|
224
|
+
definitions[newName].nestedTypes.forEach(nestedTypeName => {
|
|
225
|
+
if (nestedTypesToCreate.indexOf(nestedTypeName) !== -1 ||
|
|
226
|
+
(definitions[nestedTypeName] && definitions[nestedTypeName].isProjection)) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
createSchema += `\n${definitions[nestedTypeName].schema}`;
|
|
230
|
+
nestedTypesToCreate.push(nestedTypeName);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
if (projectionsToCreate.length > 0) {
|
|
234
|
+
console.log(`Creating ${projectionsToCreate.length} projections: ${projectionsToCreate}...`);
|
|
235
|
+
await managementClient.create(createSchema).catch(console.log);
|
|
236
|
+
console.log(`Created ${projectionsToCreate.length} projections`);
|
|
237
|
+
}
|
|
238
|
+
if (projectionsToUpdate.length > 0) {
|
|
239
|
+
console.log(`Updating ${projectionsToUpdate.length} projections: ${projectionsToUpdate}...`);
|
|
240
|
+
await managementClient.update(updateSchema).catch(console.log);
|
|
241
|
+
console.log(`Updated ${projectionsToUpdate.length} projections`);
|
|
242
|
+
}
|
|
243
|
+
if (projectionsToRemove.length > 0) {
|
|
244
|
+
console.log(`Removing ${projectionsToRemove.length} projections: ${projectionsToRemove}...`);
|
|
245
|
+
await managementClient.remove(projectionsToRemove).catch(console.log);
|
|
246
|
+
console.log(`Removed ${projectionsToRemove.length} projections`);
|
|
247
|
+
}
|
|
248
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface ClientConfig {
|
|
2
|
+
serverAddress: string;
|
|
3
|
+
keepaliveInterval?: number;
|
|
4
|
+
keepaliveTimeout?: number;
|
|
5
|
+
}
|
|
6
|
+
export declare const getEnvConfig: () => ClientConfig;
|
|
7
|
+
export declare const useConfigDefaults: (config?: ClientConfig) => Required<ClientConfig>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useConfigDefaults = exports.getEnvConfig = void 0;
|
|
4
|
+
const dotenv_1 = require("dotenv");
|
|
5
|
+
const getEnvConfig = () => {
|
|
6
|
+
var _a;
|
|
7
|
+
(0, dotenv_1.config)();
|
|
8
|
+
const serverAddress = (_a = process.env.PROJECTIONS_SERVER_ADDRESS) !== null && _a !== void 0 ? _a : "";
|
|
9
|
+
let keepaliveInterval;
|
|
10
|
+
let keepaliveTimeout;
|
|
11
|
+
const keepaliveIntervalString = process.env.PROJECTIONS_CONNECTION_KEEPALIVE_INTERVAL;
|
|
12
|
+
const keepaliveTimeoutString = process.env.PROJECTIONS_CONNECTION_KEEPALIVE_INTERVAL;
|
|
13
|
+
if (keepaliveIntervalString) {
|
|
14
|
+
keepaliveInterval = parseInt(keepaliveIntervalString, 10);
|
|
15
|
+
}
|
|
16
|
+
if (keepaliveTimeoutString) {
|
|
17
|
+
keepaliveTimeout = parseInt(keepaliveTimeoutString, 10);
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
serverAddress,
|
|
21
|
+
keepaliveInterval,
|
|
22
|
+
keepaliveTimeout,
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
exports.getEnvConfig = getEnvConfig;
|
|
26
|
+
const useConfigDefaults = (config) => {
|
|
27
|
+
var _a, _b;
|
|
28
|
+
if (!config) {
|
|
29
|
+
config = (0, exports.getEnvConfig)();
|
|
30
|
+
}
|
|
31
|
+
console.log(config);
|
|
32
|
+
return {
|
|
33
|
+
serverAddress: config.serverAddress,
|
|
34
|
+
keepaliveTimeout: (_a = config.keepaliveTimeout) !== null && _a !== void 0 ? _a : 3 * 1000,
|
|
35
|
+
keepaliveInterval: (_b = config.keepaliveInterval) !== null && _b !== void 0 ? _b : 40 * 1000,
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
exports.useConfigDefaults = useConfigDefaults;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ClientConfig } from "../config/config";
|
|
2
|
+
import { GetProjectionData } from "./getData";
|
|
3
|
+
import { GetProjectionDataList } from "./getDataList";
|
|
4
|
+
export interface DeliveryClient {
|
|
5
|
+
getData: (tenantId: string, type: string, id: string, returnEmptyDataIfNotFound?: boolean) => Promise<GetProjectionData | null>;
|
|
6
|
+
getDataList: (tenantId: string, type: string, limit?: number, page?: number) => Promise<GetProjectionDataList | null>;
|
|
7
|
+
close: () => Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
export declare const newDeliveryClient: (config?: ClientConfig) => Promise<DeliveryClient>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.newDeliveryClient = void 0;
|
|
4
|
+
const projections_proto_1 = require("@fraym/projections-proto");
|
|
5
|
+
const grpc_js_1 = require("@grpc/grpc-js");
|
|
6
|
+
const config_1 = require("../config/config");
|
|
7
|
+
const getData_1 = require("./getData");
|
|
8
|
+
const getDataList_1 = require("./getDataList");
|
|
9
|
+
const newDeliveryClient = async (config) => {
|
|
10
|
+
config = (0, config_1.useConfigDefaults)(config);
|
|
11
|
+
const serviceClient = new projections_proto_1.DeliveryServiceClient(config.serverAddress, grpc_js_1.credentials.createInsecure(), {
|
|
12
|
+
"grpc.keepalive_time_ms": config.keepaliveInterval,
|
|
13
|
+
"grpc.keepalive_timeout_ms": config.keepaliveTimeout,
|
|
14
|
+
"grpc.keepalive_permit_without_calls": 1,
|
|
15
|
+
});
|
|
16
|
+
const getData = async (tenantId, projection, id, returnEmptyDataIfNotFound = false) => {
|
|
17
|
+
return await (0, getData_1.getProjectionData)(tenantId, projection, id, returnEmptyDataIfNotFound, serviceClient);
|
|
18
|
+
};
|
|
19
|
+
const getDataList = async (tenantId, projection, limit = 0, page = 1) => {
|
|
20
|
+
return await (0, getDataList_1.getProjectionDataList)(tenantId, projection, limit, page, serviceClient);
|
|
21
|
+
};
|
|
22
|
+
const close = async () => {
|
|
23
|
+
serviceClient.close();
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
getData,
|
|
27
|
+
getDataList,
|
|
28
|
+
close,
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
exports.newDeliveryClient = newDeliveryClient;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { DeliveryServiceClient } from "@fraym/projections-proto";
|
|
2
|
+
export declare type GetProjectionData = Record<string, any>;
|
|
3
|
+
export declare const getProjectionData: (tenantId: string, projection: string, dataId: string, returnEmptyDataIfNotFound: boolean, serviceClient: DeliveryServiceClient) => Promise<GetProjectionData | null>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getProjectionData = void 0;
|
|
4
|
+
const getProjectionData = async (tenantId, projection, dataId, returnEmptyDataIfNotFound, serviceClient) => {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
serviceClient.getData({
|
|
7
|
+
tenantId,
|
|
8
|
+
projection,
|
|
9
|
+
dataId,
|
|
10
|
+
limit: 0,
|
|
11
|
+
page: 0,
|
|
12
|
+
returnEmptyDataIfNotFound,
|
|
13
|
+
}, (error, response) => {
|
|
14
|
+
if (error) {
|
|
15
|
+
reject(error.message);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (response.result.length !== 1) {
|
|
19
|
+
resolve(null);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const data = {};
|
|
23
|
+
const resultData = response.result[0].data;
|
|
24
|
+
for (const key in resultData) {
|
|
25
|
+
data[key] = JSON.parse(resultData[key]);
|
|
26
|
+
}
|
|
27
|
+
resolve(data);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
exports.getProjectionData = getProjectionData;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { DeliveryServiceClient } from "@fraym/projections-proto";
|
|
2
|
+
export interface GetProjectionDataList {
|
|
3
|
+
limit: number;
|
|
4
|
+
page: number;
|
|
5
|
+
data: Record<string, any>[];
|
|
6
|
+
}
|
|
7
|
+
export declare const getProjectionDataList: (tenantId: string, projection: string, limit: number, page: number, serviceClient: DeliveryServiceClient) => Promise<GetProjectionDataList | null>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getProjectionDataList = void 0;
|
|
4
|
+
const getProjectionDataList = async (tenantId, projection, limit, page, serviceClient) => {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
serviceClient.getData({
|
|
7
|
+
tenantId,
|
|
8
|
+
projection,
|
|
9
|
+
dataId: "",
|
|
10
|
+
limit,
|
|
11
|
+
page,
|
|
12
|
+
returnEmptyDataIfNotFound: false,
|
|
13
|
+
}, (error, response) => {
|
|
14
|
+
if (error) {
|
|
15
|
+
reject(error.message);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const data = [];
|
|
19
|
+
for (const result of response.result) {
|
|
20
|
+
const dataRecord = {};
|
|
21
|
+
const resultData = result.data;
|
|
22
|
+
for (const key in resultData) {
|
|
23
|
+
dataRecord[key] = JSON.parse(resultData[key]);
|
|
24
|
+
}
|
|
25
|
+
data.push(dataRecord);
|
|
26
|
+
}
|
|
27
|
+
resolve({
|
|
28
|
+
limit: response.limit,
|
|
29
|
+
page: response.page,
|
|
30
|
+
data,
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
exports.getProjectionDataList = getProjectionDataList;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./management/client"), exports);
|
|
18
|
+
__exportStar(require("./delivery/client"), exports);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ClientConfig } from "../config/config";
|
|
2
|
+
export interface ManagementClient {
|
|
3
|
+
create: (schema: string) => Promise<void>;
|
|
4
|
+
update: (schema: string) => Promise<void>;
|
|
5
|
+
remove: (projectionNames: string[]) => Promise<void>;
|
|
6
|
+
getAll: () => Promise<string[]>;
|
|
7
|
+
close: () => Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
export declare const newManagementClient: (config?: ClientConfig) => Promise<ManagementClient>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.newManagementClient = void 0;
|
|
4
|
+
const projections_proto_1 = require("@fraym/projections-proto");
|
|
5
|
+
const grpc_js_1 = require("@grpc/grpc-js");
|
|
6
|
+
const config_1 = require("../config/config");
|
|
7
|
+
const create_1 = require("./create");
|
|
8
|
+
const getAll_1 = require("./getAll");
|
|
9
|
+
const remove_1 = require("./remove");
|
|
10
|
+
const update_1 = require("./update");
|
|
11
|
+
const newManagementClient = async (config) => {
|
|
12
|
+
config = (0, config_1.useConfigDefaults)(config);
|
|
13
|
+
const serviceClient = new projections_proto_1.ManagementServiceClient(config.serverAddress, grpc_js_1.credentials.createInsecure(), {
|
|
14
|
+
"grpc.keepalive_time_ms": config.keepaliveInterval,
|
|
15
|
+
"grpc.keepalive_timeout_ms": config.keepaliveTimeout,
|
|
16
|
+
"grpc.keepalive_permit_without_calls": 1,
|
|
17
|
+
});
|
|
18
|
+
const create = async (schema) => {
|
|
19
|
+
await (0, create_1.createProjections)(schema, serviceClient);
|
|
20
|
+
};
|
|
21
|
+
const update = async (schema) => {
|
|
22
|
+
await (0, update_1.updateProjections)(schema, serviceClient);
|
|
23
|
+
};
|
|
24
|
+
const remove = async (projectionNames) => {
|
|
25
|
+
await (0, remove_1.removeProjections)(projectionNames, serviceClient);
|
|
26
|
+
};
|
|
27
|
+
const getAll = async () => {
|
|
28
|
+
return await (0, getAll_1.getAllProjections)(serviceClient);
|
|
29
|
+
};
|
|
30
|
+
const close = async () => {
|
|
31
|
+
serviceClient.close();
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
create,
|
|
35
|
+
update,
|
|
36
|
+
remove,
|
|
37
|
+
getAll,
|
|
38
|
+
close,
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
exports.newManagementClient = newManagementClient;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createProjections = void 0;
|
|
4
|
+
const createProjections = async (schema, serviceClient) => {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
serviceClient.createProjections({
|
|
7
|
+
schema,
|
|
8
|
+
}, error => {
|
|
9
|
+
if (error) {
|
|
10
|
+
reject(error.message);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
resolve();
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
exports.createProjections = createProjections;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getAllProjections = void 0;
|
|
4
|
+
const getAllProjections = async (serviceClient) => {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
serviceClient.getProjections({}, (error, response) => {
|
|
7
|
+
if (error) {
|
|
8
|
+
reject(error.message);
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
resolve(response.projectionNames);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
exports.getAllProjections = getAllProjections;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.removeProjections = void 0;
|
|
4
|
+
const removeProjections = async (projectionNames, serviceClient) => {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
serviceClient.removeProjections({
|
|
7
|
+
projectionNames,
|
|
8
|
+
}, error => {
|
|
9
|
+
if (error) {
|
|
10
|
+
reject(error.message);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
resolve();
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
exports.removeProjections = removeProjections;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.updateProjections = void 0;
|
|
4
|
+
const updateProjections = async (schema, serviceClient) => {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
serviceClient.updateProjections({
|
|
7
|
+
schema,
|
|
8
|
+
}, error => {
|
|
9
|
+
if (error) {
|
|
10
|
+
reject(error.message);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
resolve();
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
exports.updateProjections = updateProjections;
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fraym/projections",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "UNLICENSED",
|
|
5
|
+
"homepage": "https://github.com/fraym/projections-nodejs",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/fraym/projections-nodejs.git"
|
|
9
|
+
},
|
|
10
|
+
"description": "nodejs client implementation for our projections service",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"test": "echo \"Error: no test specified\" && exit 0",
|
|
13
|
+
"format": "prettier --write \"**/*.{ts,tsx,json}\"",
|
|
14
|
+
"lint": "prettier --check \"**/*.{ts,tsx,json}\"",
|
|
15
|
+
"build": "npm run clean && tsc && chmod +x dist/cmd/projections.js",
|
|
16
|
+
"clean": "rm -rf dist",
|
|
17
|
+
"prepublishOnly": "npm test && npm run lint && npm run build",
|
|
18
|
+
"preversion": "npm run lint",
|
|
19
|
+
"cmd": "dist/cmd/projections.js"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist/**/*"
|
|
23
|
+
],
|
|
24
|
+
"main": "dist/index.js",
|
|
25
|
+
"types": "dist/index.d.ts",
|
|
26
|
+
"bin": {
|
|
27
|
+
"projections": "dist/cmd/projections.js"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@fraym/projections-proto": "^1.0.0-alpha.9",
|
|
31
|
+
"@graphql-tools/graphql-file-loader": "^7.5.11",
|
|
32
|
+
"@graphql-tools/load": "^7.8.6",
|
|
33
|
+
"@grpc/grpc-js": "^1.7.2",
|
|
34
|
+
"dotenv": "^16.0.3",
|
|
35
|
+
"graphql": "^16.6.0",
|
|
36
|
+
"yargs": "^17.6.2"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@becklyn/prettier": "^1.0.2",
|
|
40
|
+
"@types/uuid": "^8.3.4",
|
|
41
|
+
"@types/yargs": "^17.0.13",
|
|
42
|
+
"prettier": "^2.7.1",
|
|
43
|
+
"typescript": "^4.8.4",
|
|
44
|
+
"uuid": "^9.0.0"
|
|
45
|
+
},
|
|
46
|
+
"prettier": "@becklyn/prettier"
|
|
47
|
+
}
|