UNPKG

16.1 kBTypeScriptView Raw
1import * as React from 'react';
2
3
4import {
5 graphql,
6 GraphQLSchema,
7 GraphQLObjectType,
8 GraphQLString,
9 GraphQLNonNull,
10 GraphQLList,
11 GraphQLInputObjectType
12} from 'graphql';
13
14
15import Types from '../types';
16import { IComponent } from "../types/component";
17import { IInfrastructure } from "../types";
18import { getChildrenArray, findComponentRecursively } from '../libs';
19import { isWebApp } from '../webapp/webapp-component';
20import { isAuthentication } from '../authentication/authentication-component';
21import { isIdentity } from '../identity/identity-component'
22import { isEntry } from './entry-component';
23import { setEntry, ddbGetEntry, ddbListEntries, getEntryListQuery } from './datalayer-libs';
24
25export const DATALAYER_INSTANCE_TYPE = "DataLayerComponent";
26
27
28/**
29 * Arguments provided by the user
30 */
31export interface IDataLayerArgs {
32 /**
33 * a unique id or name of the datalayer
34 */
35 id: string
36}
37
38/**
39 * properties added programmatically
40 */
41export interface IDataLayerProps {
42
43 /**
44 * supported queries, i.e. entries that the user can query
45 */
46 queries: any,
47
48 /**
49 * supported mutations
50 */
51 mutations: any,
52
53 /**
54 * get the entry-data-fields of the specified entry
55 * @param entryId id of the entry to get the fields from
56 *
57 getEntryDataFields: (entryId: string) => any,*/
58
59 /**
60 * wrapper function for getEntryListQuery, this allows us to complement some data
61 * @param entryId
62 * @param dictKey
63 */
64 getEntryListQuery: (entryId: string, dictKey: any ) => any,
65
66 getEntryQuery: (entryId: string, dictKey: any ) => any,
67
68 getEntryScanQuery: (entryId: string, dictKey: any ) => any,
69
70 setEntryMutation: (entryId: string, values: any ) => any,
71
72 deleteEntryMutation: (entryId: string, values: any ) => any,
73
74 updateEntryQuery: (entryId, fDictKey: (oldData) => any) => any,
75
76 getSchema?: any // optional only because it is implemented in a separate object below. but it is required!
77
78 entries: any,
79
80 /**
81 * The Apollo-Client: used at server-side only! Used to provide the Apollo-Client to middlewares
82 */
83 client?: any
84 setClient: (client: any) => void,
85
86 /**
87 * set to true when running in offline mode
88 */
89 isOffline: boolean,
90 setOffline: (offline: boolean) => void
91};
92
93
94/**
95 * identifies a component as a DataLayer
96 *
97 * @param component to be tested
98 */
99export function isDataLayer(component) {
100 return component !== undefined && component.instanceType === DATALAYER_INSTANCE_TYPE
101};
102
103
104
105export default (props: IDataLayerArgs | any) => {
106
107 const componentProps: IInfrastructure & IComponent = {
108 infrastructureType: Types.INFRASTRUCTURE_TYPE_COMPONENT,
109 instanceType: DATALAYER_INSTANCE_TYPE,
110 instanceId: props.id
111 };
112
113 //const listEntities = getChildrenArray(props).filter(child => isEntity(child));
114 //const entries = getChildrenArray(props).filter(child => isEntry(child));
115 const entries = findComponentRecursively(props.children, isEntry);
116
117 const complementedProps = {
118
119 };
120
121 /**
122 * create the
123 * @type {{queries: {}, mutations: {}}}
124 */
125 const datalayerProps = {
126
127 entries: entries,
128
129 mutations: (resolveWithData: boolean) => entries.reduce((result, entry) => {
130
131 result[entry.getSetMutationName()] = {
132 args: entry.createEntryArgs(),
133 type: entry.createEntryType("set_"),
134 resolve: (source, args, context, info) => {
135
136
137 if (!resolveWithData) {
138 return entry.id;
139 }
140
141 //console.log("resolve: ", resolveWithData, source, context, info, args);
142
143 // This context gets the data from the context put into the <Query/> or Mutation...
144 //console.log("context: ", context);
145
146 const result = entry.setEntry(args, context, process.env.TABLE_NAME, complementedProps["isOffline"]);
147
148
149 //console.log("result: ", result);
150 return result;
151 }
152 };
153
154 result[entry.getDeleteMutationName()] = {
155 args: entry.createEntryArgs(),
156 type: entry.createEntryType("delete_"),
157 resolve: (source, args, context, info) => {
158
159
160 if (!resolveWithData) {
161 return entry.id;
162 }
163
164 //console.log("resolve: ", resolveWithData, source, context, info, args);
165
166 // This context gets the data from the context put into the <Query/> or Mutation...
167 //console.log("context: ", context);
168
169 const result = entry.deleteEntry(args, context, process.env.TABLE_NAME, complementedProps["isOffline"]);
170
171
172 //console.log("result: ", result);
173 return result;
174 }
175 };
176
177 //console.log("mutation definition: ", result["set_"+entry.id]);
178
179 return result;
180 }, {}),
181
182 queries: (resolveWithData: boolean) => entries.reduce((result, entry) => {
183
184 const listType = entry.createEntryType("list_");
185 const getType = entry.createEntryType("get_");
186 //console.log("listType: ", listType);
187
188
189 //console.log("dl-comp-props: ", complementedProps["isOffline"], datalayerProps["isOffline"])
190 // list all the items, specifying the primaryKey
191 const inputArgs = {};
192
193 inputArgs[entry.primaryKey] = {name: entry.primaryKey, type: new GraphQLNonNull(GraphQLString)};
194
195 result[entry.getPrimaryListQueryName()] = {
196 args: inputArgs,
197 type: resolveWithData ? new GraphQLList(listType) : listType,
198 resolve: (source, args, context, info) => {
199
200
201 //console.log("resolve list: ", resolveWithData, source, args, context, complementedProps["isOffline"]);
202
203 if (!resolveWithData) {
204 return entry.id;
205 }
206
207 return entry.listEntries(args, context, process.env.TABLE_NAME, "pk", complementedProps["isOffline"]);
208 }
209 };
210
211
212 // list all the items, specifying the RANGE
213
214 const inputRangeArgs = {};
215 inputRangeArgs[entry.rangeKey] = {name: entry.rangeKey, type: new GraphQLNonNull(GraphQLString)};
216
217 result[entry.getRangeListQueryName()] = {
218 args: inputRangeArgs,
219 type: resolveWithData ? new GraphQLList(listType): listType,
220 resolve: (source, args, context, info) => {
221
222 //console.log("resolve: ", resolveWithData, source, args, context, complementedProps["isOffline"]);
223
224 if (!resolveWithData) {
225 return entry.id;
226 }
227
228 return entry.listEntries(args, context, process.env.TABLE_NAME, "sk", complementedProps["isOffline"]);
229
230 }
231 };
232
233 const inputArgsGet = {};
234
235 if (entry.primaryKey) {
236 inputArgsGet[entry.primaryKey] = {name: entry.primaryKey, type: new GraphQLNonNull(GraphQLString)};
237 }
238
239 if (entry.rangeKey) {
240 inputArgsGet[entry.rangeKey] = {name: entry.rangeKey, type: new GraphQLNonNull(GraphQLString)};
241 }
242
243 result[entry.getGetQueryName()] = {
244 args: inputArgsGet,
245 type: getType,
246 resolve: (source, args, context, info) => {
247
248
249 //console.log("resolve: ", resolveWithData, source, args, context);
250
251 if (!resolveWithData) {
252 return entry.id;
253 }
254
255 return entry.getEntry(args, context, process.env.TABLE_NAME, complementedProps["isOffline"]);
256
257
258 }
259 };
260
261
262 const scanRangeArgs = {};
263 scanRangeArgs[`start_${entry.rangeKey}`] = {name: `start_${entry.rangeKey}`, type: new GraphQLNonNull(GraphQLString)};
264 scanRangeArgs[`end_${entry.rangeKey}`] = {name: `end_${entry.rangeKey}`, type: new GraphQLNonNull(GraphQLString)};
265
266 // scan the table
267 result[entry.getRangeScanName()] = {
268 args: scanRangeArgs,
269 type: resolveWithData ? new GraphQLList(listType) : listType,
270 resolve: (source, args, context, info) => {
271
272
273 //console.log("resolve scan: ", resolveWithData, source, args, context);
274
275 if (!resolveWithData) {
276 return entry.id;
277 }
278
279 return entry.scan(args, context, process.env.TABLE_NAME, "sk", complementedProps["isOffline"]);
280
281
282 }
283 };
284
285 const scanPrimaryArgs = {};
286 scanPrimaryArgs[`start_${entry.primaryKey}`] = {name: `start_${entry.primaryKey}`, type: new GraphQLNonNull(GraphQLString)};
287 scanPrimaryArgs[`end_${entry.primaryKey}`] = {name: `end_${entry.primaryKey}`, type: new GraphQLNonNull(GraphQLString)};
288
289 // scan the table
290 result[entry.getPrimaryScanName()] = {
291 args: scanPrimaryArgs,
292 type: resolveWithData ? new GraphQLList(listType) : listType,
293 resolve: (source, args, context, info) => {
294
295
296 //console.log("resolve scan: ", resolveWithData, source, args, context);
297
298 if (!resolveWithData) {
299 return entry.id;
300 }
301
302 return entry.scan(args, context, process.env.TABLE_NAME, "pk", complementedProps["isOffline"]);
303
304
305 }
306 };
307
308 const scanAllArgs = {scanall: {name: "scanall", type: new GraphQLNonNull(GraphQLString)}};
309
310 // scan the table
311 result[entry.getScanName()] = {
312 args: scanAllArgs,
313 type: resolveWithData ? new GraphQLList(listType) : listType,
314 resolve: (source, args, context, info) => {
315
316
317 //console.log("resolve scan: ", resolveWithData, source, args, context);
318
319 if (!resolveWithData) {
320 return entry.id;
321 }
322
323 return entry.scan(args, context, process.env.TABLE_NAME, "pk", complementedProps["isOffline"]);
324
325
326 }
327 };
328
329
330 return result;
331 }, {}),
332
333 /*
334 getEntryDataFields: (entryId) => {
335 const entry = entries.find(entry => entry.id === entryId);
336 if (entry !== undefined) {
337 return entry.createEntryFields()
338 };
339
340 console.warn("could not find entry: ",entryId);
341 return {};
342 },*/
343
344
345 // TODO forward this request to the Entry and let the entry handle the whole request!
346 getEntryListQuery: (entryId, dictKey) => {
347 const entry = entries.find(entry => entry.id === entryId);
348 if (entry !== undefined) {
349 return entry.getEntryListQuery(dictKey)
350 };
351
352 console.warn("could not find entry: ",entryId);
353 return {};
354
355 /*
356 const fields = datalayerProps.getEntryDataFields(entryId);
357 //console.log("fields: ", fields);
358
359 return getEntryListQuery(
360 entryId,
361 dictKey,
362 fields
363 );*/
364 },
365
366 getEntryQuery: (entryId, dictKey) => {
367 const entry = entries.find(entry => entry.id === entryId);
368 if (entry !== undefined) {
369 return entry.getEntryQuery(dictKey)
370 };
371
372 console.warn("could not find entry: ",entryId);
373 return {};
374
375 },
376
377 getEntryScanQuery: (entryId, dictKey) => {
378 const entry = entries.find(entry => entry.id === entryId);
379 if (entry !== undefined) {
380 return entry.getEntryScanQuery(dictKey)
381 };
382
383 console.warn("could not find entry: ",entryId);
384 return {};
385
386 },
387
388
389 setEntryMutation: (entryId, values) => {
390 const entry = entries.find(entry => entry.id === entryId);
391 if (entry !== undefined) {
392 return entry.setEntryMutation(values)
393 };
394
395 console.warn("could not find entry: ", entryId);
396 return {};
397 },
398
399 deleteEntryMutation: (entryId, values) => {
400 const entry = entries.find(entry => entry.id === entryId);
401 if (entry !== undefined) {
402 return entry.deleteEntryMutation(values)
403 };
404
405 console.warn("could not find entry: ", entryId);
406 return {};
407 },
408
409 updateEntryQuery: (entryId, fDictKey: (oldData) => any) => {
410
411 return {
412 entryId: entryId,
413 getEntryQuery: () => datalayerProps.getEntryQuery(entryId, fDictKey({})),
414 setEntryMutation: (oldData) => datalayerProps.setEntryMutation(entryId, fDictKey(oldData)),
415
416 };
417
418 },
419
420
421
422 setClient: (client) => {
423 complementedProps["client"] = client;
424 },
425
426 setOffline: (offline: boolean) => {
427 complementedProps["isOffline"] = offline;
428 }
429
430 };
431
432 const schemaProps = {
433 getSchema: (resolveWithData: boolean) => new GraphQLSchema({
434 query: new GraphQLObjectType({
435 name: 'RootQueryType', // an arbitrary name
436 fields: datalayerProps.queries(resolveWithData)
437 }), mutation: new GraphQLObjectType({
438 name: 'RootMutationType', // an arbitrary name
439 fields: datalayerProps.mutations(resolveWithData)
440 })
441 })
442 }
443
444 // we need to provide the DataLayerId to webApps, these may be anywhere in the tree, not
445 // only direct children. So rather than mapping the children, we need to change them
446 findComponentRecursively(props.children, (child) => child.setDataLayerId !== undefined).forEach( child => {
447 child.setDataLayerId(props.id)
448 });
449
450 findComponentRecursively(props.children, (child) => child.setStoreData !== undefined).forEach( child => {
451
452 child.setStoreData(
453 async function (pkEntity, pkVal, skEntity, skVal, jsonData) {
454 return await setEntry(
455 process.env.TABLE_NAME, //"code-architect-dev-data-layer",
456 pkEntity, // schema.Entry.ENTITY, //pkEntity
457 pkVal, // pkId
458 skEntity, //schema.Data.ENTITY, // skEntity
459 skVal, // skId
460 jsonData, // jsonData
461 complementedProps["isOffline"]
462 )
463 }
464 );
465
466 child.setGetData(
467 async function (pkEntity, pkVal, skEntity, skVal) {
468 if (pkVal !== undefined) {
469 return await ddbGetEntry(
470 process.env.TABLE_NAME, //"code-architect-dev-data-layer",
471 pkEntity, // schema.Entry.ENTITY, //pkEntity
472 pkVal, // pkId
473 skEntity, //schema.Data.ENTITY, // skEntity
474 skVal, // skId
475 complementedProps["isOffline"]
476 )
477 } else {
478 return ddbListEntries(
479 process.env.TABLE_NAME, //tableName
480 "sk", //key
481 skEntity, // entity
482 skVal, //value,
483 pkEntity, // rangeEntity
484 complementedProps["isOffline"]
485 )
486 }
487
488
489 }
490 );
491 });
492
493
494 return Object.assign({}, props, componentProps, datalayerProps, schemaProps, complementedProps);
495
496};
\No newline at end of file