@fraym/projections 0.12.1 → 0.14.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 CHANGED
@@ -44,6 +44,29 @@ PERMISSIONS_SCHEMA_GLOB=
44
44
  PROJECTIONS_NAMESPACE=
45
45
  ```
46
46
 
47
+ ## Env variable placeholders in migrations
48
+
49
+ You can use placeholders that match environment variables in argument strings in your schema definitions:
50
+
51
+ In the following example the `{{env.BACKEND_HOSTNAME}}` part will be replaced by the value of the `BACKEND_HOSTNAME` environment variable.
52
+ Please add your used env variables to the `.env` file that is used to [configure the migration command](#config).
53
+
54
+ ```graphql
55
+ type TestType {
56
+ value: String
57
+ @webhook(
58
+ url: "http://{{env.BACKEND_HOSTNAME}}/event-organizing/contingent/projections/frontend/contingent-management/webhook"
59
+ method: "GET"
60
+ header: [{ key: "Content-Type", value: "'application/json'" }]
61
+ body: [
62
+ { key: "metadata", value: "metadata" }
63
+ { key: "payload", value: "payload" }
64
+ { key: "projection", value: "projection" }
65
+ ]
66
+ )
67
+ }
68
+ ```
69
+
47
70
  ## Usage
48
71
 
49
72
  ### Create the clients
@@ -103,6 +126,17 @@ Fields:
103
126
  - `scopes`: Slice of scopes to use for the action
104
127
  - `data`: Data that is used in directives like `@filterFromJwtData`
105
128
 
129
+ ### Event Metadata
130
+
131
+ You can specify the correlation and causation IDs for the upsert and delete functions. The `eventMetadata` parameter is optional for all these functions and has the following structure:
132
+
133
+ ```typescript
134
+ const eventMetadata = {
135
+ correlationId: "some-correlation-id",
136
+ causationId: "some-causation-id",
137
+ };
138
+ ```
139
+
106
140
  ### Upsert data in projection
107
141
 
108
142
  In general you upsert data by publishing events on the event stream.
@@ -115,7 +149,8 @@ const response = await client.upsertData<{ fieldName: string }>(
115
149
  "dataId",
116
150
  {
117
151
  fieldName: "value",
118
- }
152
+ },
153
+ eventMetadata
119
154
  );
120
155
  ```
121
156
 
@@ -138,13 +173,13 @@ There are cases where you want to improve performance and get detailed validatio
138
173
  Delete by Id:
139
174
 
140
175
  ```go
141
- const numberOfDeletedEntries = await client.deleteDataById("ProjectionName", authData, "dataId")
176
+ const numberOfDeletedEntries = await client.deleteDataById("ProjectionName", authData, "dataId", eventMetadata)
142
177
  ```
143
178
 
144
179
  Delete by filter:
145
180
 
146
181
  ```go
147
- const numberOfDeletedEntries = client.deleteDataByFilter("ProjectionName", authData, filter)
182
+ const numberOfDeletedEntries = client.deleteDataByFilter("ProjectionName", authData, filter, eventMetadata)
148
183
  ```
149
184
 
150
185
  ### Get a single projection element
@@ -302,12 +302,12 @@ const migrateSchemas = async (definitions, serverAddress, apiToken, namespace) =
302
302
  });
303
303
  if (projectionsToCreate.length > 0) {
304
304
  console.log(`Creating ${projectionsToCreate.length} projections: ${projectionsToCreate}...`);
305
- await managementClient.upsert(createSchema);
305
+ await managementClient.upsert(replaceWithEnvData(createSchema));
306
306
  console.log(`Created ${projectionsToCreate.length} projections`);
307
307
  }
308
308
  if (projectionsToUpdate.length > 0) {
309
309
  console.log(`Updating ${projectionsToUpdate.length} projections: ${projectionsToUpdate}...`);
310
- await managementClient.upsert(updateSchema);
310
+ await managementClient.upsert(replaceWithEnvData(updateSchema));
311
311
  console.log(`Updated ${projectionsToUpdate.length} projections`);
312
312
  }
313
313
  if (projectionsToRemove.length > 0) {
@@ -316,6 +316,23 @@ const migrateSchemas = async (definitions, serverAddress, apiToken, namespace) =
316
316
  console.log(`Removed ${projectionsToRemove.length} projections`);
317
317
  }
318
318
  };
319
+ const replaceWithEnvData = (str) => {
320
+ const regex = /{{env\.([a-zA-Z_]+)}}/g;
321
+ const matches = str.match(regex);
322
+ const envData = {};
323
+ matches === null || matches === void 0 ? void 0 : matches.forEach(match => {
324
+ var _a;
325
+ const variable = match.replace("{{env.", "").replace("}}", "");
326
+ if (!envData[variable]) {
327
+ envData[variable] = (_a = process.env[variable]) !== null && _a !== void 0 ? _a : "";
328
+ }
329
+ });
330
+ let outputStr = str;
331
+ Object.keys(envData).forEach(key => {
332
+ outputStr = outputStr.replaceAll(`{{env.${key}}}`, envData[key]);
333
+ });
334
+ return outputStr;
335
+ };
319
336
  const ensureValidName = (name) => {
320
337
  if (name.startsWith("Fraym")) {
321
338
  throw new Error(`Cannot use Fraym as projection name prefix as it is reserved for fraym apps, got ${name}`);
@@ -4,12 +4,13 @@ import { GetProjectionDataList } from "./getDataList";
4
4
  import { Order } from "./order";
5
5
  import { AuthData } from "./auth";
6
6
  import { UpsertResponse } from "./upsert";
7
+ import { EventMetadata } from "./eventMetadata";
7
8
  export interface DeliveryClient {
8
9
  getData: <T extends {}>(projection: string, authData: AuthData, id: string, filter?: Filter, returnEmptyDataIfNotFound?: boolean) => Promise<T | null>;
9
10
  getDataList: <T extends {}>(projection: string, authData: AuthData, limit?: number, page?: number, filter?: Filter, order?: Order[]) => Promise<GetProjectionDataList<T> | null>;
10
- upsertData: <T extends {}>(projection: string, authData: AuthData, dataId: string, payload: T) => Promise<UpsertResponse<T>>;
11
- deleteDataById: (projection: string, authData: AuthData, dataId: string) => Promise<number>;
12
- deleteDataByFilter: (projection: string, authData: AuthData, filter?: Filter) => Promise<number>;
11
+ upsertData: <T extends {}>(projection: string, authData: AuthData, dataId: string, payload: T, eventMetadata?: EventMetadata) => Promise<UpsertResponse<T>>;
12
+ deleteDataById: (projection: string, authData: AuthData, dataId: string, eventMetadata?: EventMetadata) => Promise<number>;
13
+ deleteDataByFilter: (projection: string, authData: AuthData, filter?: Filter, eventMetadata?: EventMetadata) => Promise<number>;
13
14
  close: () => Promise<void>;
14
15
  }
15
16
  export declare const newDeliveryClient: (config?: DeliveryClientConfig) => Promise<DeliveryClient>;
@@ -21,14 +21,14 @@ const newDeliveryClient = async (config) => {
21
21
  const getDataList = async (projection, auth, limit = 0, page = 1, filter = { fields: {}, and: [], or: [] }, order = []) => {
22
22
  return await (0, getDataList_1.getProjectionDataList)(projection, auth, limit, page, filter, order, serviceClient);
23
23
  };
24
- const upsertData = async (projection, authData, dataId, payload) => {
25
- return (0, upsert_1.upsertProjectionData)(projection, authData, dataId, payload, serviceClient);
24
+ const upsertData = async (projection, authData, dataId, payload, eventMetadata = { causationId: "", correlationId: "" }) => {
25
+ return (0, upsert_1.upsertProjectionData)(projection, authData, dataId, payload, eventMetadata, serviceClient);
26
26
  };
27
- const deleteDataById = async (projection, authData, dataId) => {
28
- return (0, delete_1.deleteProjectionData)(projection, authData, dataId, { fields: {}, and: [], or: [] }, serviceClient);
27
+ const deleteDataById = async (projection, authData, dataId, eventMetadata = { causationId: "", correlationId: "" }) => {
28
+ return (0, delete_1.deleteProjectionData)(projection, authData, dataId, { fields: {}, and: [], or: [] }, eventMetadata, serviceClient);
29
29
  };
30
- const deleteDataByFilter = async (projection, authData, filter = { fields: {}, and: [], or: [] }) => {
31
- return (0, delete_1.deleteProjectionData)(projection, authData, "", filter, serviceClient);
30
+ const deleteDataByFilter = async (projection, authData, filter = { fields: {}, and: [], or: [] }, eventMetadata = { causationId: "", correlationId: "" }) => {
31
+ return (0, delete_1.deleteProjectionData)(projection, authData, "", filter, eventMetadata, serviceClient);
32
32
  };
33
33
  const close = async () => {
34
34
  serviceClient.close();
@@ -1,4 +1,5 @@
1
1
  import { DeliveryServiceClient } from "@fraym/projections-proto";
2
2
  import { AuthData } from "./auth";
3
+ import { EventMetadata } from "./eventMetadata";
3
4
  import { Filter } from "./filter";
4
- export declare const deleteProjectionData: (projection: string, auth: AuthData, dataId: string, filter: Filter, serviceClient: DeliveryServiceClient) => Promise<number>;
5
+ export declare const deleteProjectionData: (projection: string, auth: AuthData, dataId: string, filter: Filter, eventMetadata: EventMetadata, serviceClient: DeliveryServiceClient) => Promise<number>;
@@ -3,13 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.deleteProjectionData = void 0;
4
4
  const auth_1 = require("./auth");
5
5
  const filter_1 = require("./filter");
6
- const deleteProjectionData = async (projection, auth, dataId, filter, serviceClient) => {
6
+ const deleteProjectionData = async (projection, auth, dataId, filter, eventMetadata, serviceClient) => {
7
7
  return new Promise((resolve, reject) => {
8
8
  serviceClient.deleteData({
9
9
  projection,
10
10
  auth: (0, auth_1.getProtobufAuthData)(auth),
11
11
  dataId,
12
12
  filter: (0, filter_1.getProtobufDataFilter)(filter),
13
+ eventMetadata,
13
14
  }, (error, response) => {
14
15
  if (error) {
15
16
  reject(error.message);
@@ -0,0 +1,4 @@
1
+ export interface EventMetadata {
2
+ correlationId: string;
3
+ causationId: string;
4
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,5 +1,6 @@
1
1
  import { DeliveryServiceClient } from "@fraym/projections-proto";
2
2
  import { AuthData } from "./auth";
3
+ import { EventMetadata } from "./eventMetadata";
3
4
  export type UpsertResponse<T extends {}> = UpsertSuccessResponse<T> | UpsertValidationResponse;
4
5
  export interface UpsertSuccessResponse<T extends {}> {
5
6
  data: T;
@@ -10,4 +11,4 @@ export interface UpsertValidationResponse {
10
11
  }
11
12
  export declare const isUpsertSuccessResponse: <T extends {}>(response: UpsertResponse<T>) => response is UpsertSuccessResponse<T>;
12
13
  export declare const isUpsertValidationResponse: <T extends {}>(response: UpsertResponse<T>) => response is UpsertValidationResponse;
13
- export declare const upsertProjectionData: <T extends {}>(projection: string, auth: AuthData, dataId: string, payload: T, serviceClient: DeliveryServiceClient) => Promise<UpsertResponse<T>>;
14
+ export declare const upsertProjectionData: <T extends {}>(projection: string, auth: AuthData, dataId: string, payload: T, eventMetadata: EventMetadata, serviceClient: DeliveryServiceClient) => Promise<UpsertResponse<T>>;
@@ -10,13 +10,14 @@ const isUpsertValidationResponse = (response) => {
10
10
  return !response.hasOwnProperty("data");
11
11
  };
12
12
  exports.isUpsertValidationResponse = isUpsertValidationResponse;
13
- const upsertProjectionData = async (projection, auth, dataId, payload, serviceClient) => {
13
+ const upsertProjectionData = async (projection, auth, dataId, payload, eventMetadata, serviceClient) => {
14
14
  return new Promise((resolve, reject) => {
15
15
  serviceClient.upsertData({
16
16
  projection,
17
17
  auth: (0, auth_1.getProtobufAuthData)(auth),
18
18
  dataId,
19
19
  payload,
20
+ eventMetadata,
20
21
  }, (error, response) => {
21
22
  if (error) {
22
23
  reject(error.message);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fraym/projections",
3
- "version": "0.12.1",
3
+ "version": "0.14.0",
4
4
  "license": "UNLICENSED",
5
5
  "homepage": "https://github.com/fraym/projections-nodejs",
6
6
  "repository": {
@@ -27,7 +27,7 @@
27
27
  "projections": "dist/cmd/projections.js"
28
28
  },
29
29
  "dependencies": {
30
- "@fraym/projections-proto": "^1.0.0-alpha.15",
30
+ "@fraym/projections-proto": "^1.0.0-alpha.16",
31
31
  "@graphql-tools/graphql-file-loader": "^7.5.16",
32
32
  "@graphql-tools/load": "^7.8.13",
33
33
  "@grpc/grpc-js": "^1.8.12",