UNPKG

11.9 kBJavaScriptView Raw
1"use strict";
2var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3 return new (P || (P = Promise))(function (resolve, reject) {
4 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6 function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
7 step((generator = generator.apply(thisArg, _arguments || [])).next());
8 });
9};
10Object.defineProperty(exports, "__esModule", { value: true });
11const graphql_1 = require("graphql");
12const RelationalDBSchemaTransformerUtils_1 = require("./RelationalDBSchemaTransformerUtils");
13const RelationalDBParsingException_1 = require("./RelationalDBParsingException");
14const graphql_transformer_common_1 = require("graphql-transformer-common");
15/**
16 * This class is used to transition all of the columns and key metadata from a table for use
17 * in generating appropriate GraphQL schema structures. It will track type definitions for
18 * the base table, update mutation inputs, create mutation inputs, and primary key metadata.
19 */
20class TableContext {
21 constructor(typeDefinition, createDefinition, updateDefinition, primaryKeyField, primaryKeyType, stringFieldList, intFieldList) {
22 this.tableTypeDefinition = typeDefinition;
23 this.tableKeyField = primaryKeyField;
24 this.createTypeDefinition = createDefinition;
25 this.updateTypeDefinition = updateDefinition;
26 this.tableKeyFieldType = primaryKeyType;
27 this.stringFieldList = stringFieldList;
28 this.intFieldList = intFieldList;
29 }
30}
31exports.TableContext = TableContext;
32/**
33 * This class is used to transition all of the information needed to generate the
34 * CloudFormation template. This is the class that is outputted by the SchemaTransformer and the one that
35 * RelationalDBTemplateGenerator takes in for the constructor. It tracks the graphql schema document,
36 * map of the primary keys for each of the types. It is also being used to track the CLI inputs needed
37 * for DataSource Creation, as data source creation is apart of the cfn template generation.
38 */
39class TemplateContext {
40 constructor(schemaDoc, typePrimaryKeyMap, stringFieldMap, intFieldMap) {
41 this.schemaDoc = schemaDoc;
42 this.typePrimaryKeyMap = typePrimaryKeyMap;
43 this.stringFieldMap = stringFieldMap;
44 this.intFieldMap = intFieldMap;
45 }
46}
47exports.default = TemplateContext;
48class RelationalDBSchemaTransformer {
49 constructor(dbReader, database) {
50 this.introspectDatabaseSchema = () => __awaiter(this, void 0, void 0, function* () {
51 // Get all of the tables within the provided db
52 let tableNames = null;
53 try {
54 tableNames = yield this.dbReader.listTables();
55 }
56 catch (err) {
57 throw new RelationalDBParsingException_1.RelationalDBParsingException(`Failed to list tables in ${this.database}`, err.stack);
58 }
59 let typeContexts = new Array();
60 let types = new Array();
61 let pkeyMap = new Map();
62 let stringFieldMap = new Map();
63 let intFieldMap = new Map();
64 for (const tableName of tableNames) {
65 let type = null;
66 try {
67 type = yield this.dbReader.describeTable(tableName);
68 }
69 catch (err) {
70 throw new RelationalDBParsingException_1.RelationalDBParsingException(`Failed to describe table ${tableName}`, err.stack);
71 }
72 // NOTE from @mikeparisstuff. The GraphQL schema generation breaks
73 // when the table does not have an explicit primary key.
74 if (type.tableKeyField) {
75 typeContexts.push(type);
76 // Generate the 'connection' type for each table type definition
77 // TODO: Determine if Connection is needed as Data API doesn't provide pagination
78 // TODO: As we add different db sources, we should conditionally do this even if we don't for Aurora serverless.
79 //types.push(this.getConnectionType(tableName))
80 // Generate the create operation input for each table type definition
81 types.push(type.createTypeDefinition);
82 // Generate the default shape for the table's structure
83 types.push(type.tableTypeDefinition);
84 // Generate the update operation input for each table type definition
85 types.push(type.updateTypeDefinition);
86 // Update the field map with the new field lists for the current table
87 stringFieldMap.set(tableName, type.stringFieldList);
88 intFieldMap.set(tableName, type.intFieldList);
89 pkeyMap.set(tableName, type.tableKeyField);
90 }
91 else {
92 console.warn(`Skipping table ${type.tableTypeDefinition.name.value} because it does not have a single PRIMARY KEY.`);
93 }
94 }
95 // Generate the mutations and queries based on the table structures
96 types.push(this.getMutations(typeContexts));
97 types.push(this.getQueries(typeContexts));
98 types.push(this.getSubscriptions(typeContexts));
99 types.push(this.getSchemaType());
100 let context = this.dbReader.hydrateTemplateContext(new TemplateContext({ kind: graphql_1.Kind.DOCUMENT,
101 definitions: types }, pkeyMap, stringFieldMap, intFieldMap));
102 return context;
103 });
104 this.dbReader = dbReader;
105 this.database = database;
106 }
107 /**
108 * Creates a schema type definition node, including operations for each of query, mutation, and subscriptions.
109 *
110 * @returns a basic schema definition node.
111 */
112 getSchemaType() {
113 return {
114 kind: graphql_1.Kind.SCHEMA_DEFINITION,
115 operationTypes: [
116 RelationalDBSchemaTransformerUtils_1.getOperationTypeDefinition('query', RelationalDBSchemaTransformerUtils_1.getNamedType('Query')),
117 RelationalDBSchemaTransformerUtils_1.getOperationTypeDefinition('mutation', RelationalDBSchemaTransformerUtils_1.getNamedType('Mutation')),
118 RelationalDBSchemaTransformerUtils_1.getOperationTypeDefinition('subscription', RelationalDBSchemaTransformerUtils_1.getNamedType('Subscription'))
119 ]
120 };
121 }
122 /**
123 * Generates the basic mutation operations, given the provided table contexts. This will
124 * create a create, delete, and update operation for each table.
125 *
126 * @param types the table contexts from which the mutations are to be generated.
127 * @returns the type definition for mutations, including a create, delete, and update for each table.
128 */
129 getMutations(types) {
130 const fields = [];
131 for (const typeContext of types) {
132 const type = typeContext.tableTypeDefinition;
133 fields.push(RelationalDBSchemaTransformerUtils_1.getOperationFieldDefinition(`delete${graphql_transformer_common_1.toUpper(type.name.value)}`, [RelationalDBSchemaTransformerUtils_1.getInputValueDefinition(RelationalDBSchemaTransformerUtils_1.getNonNullType(RelationalDBSchemaTransformerUtils_1.getNamedType(typeContext.tableKeyFieldType)), typeContext.tableKeyField)], RelationalDBSchemaTransformerUtils_1.getNamedType(`${type.name.value}`), null));
134 fields.push(RelationalDBSchemaTransformerUtils_1.getOperationFieldDefinition(`create${graphql_transformer_common_1.toUpper(type.name.value)}`, [RelationalDBSchemaTransformerUtils_1.getInputValueDefinition(RelationalDBSchemaTransformerUtils_1.getNonNullType(RelationalDBSchemaTransformerUtils_1.getNamedType(`Create${type.name.value}Input`)), `create${type.name.value}Input`)], RelationalDBSchemaTransformerUtils_1.getNamedType(`${type.name.value}`), null));
135 fields.push(RelationalDBSchemaTransformerUtils_1.getOperationFieldDefinition(`update${graphql_transformer_common_1.toUpper(type.name.value)}`, [RelationalDBSchemaTransformerUtils_1.getInputValueDefinition(RelationalDBSchemaTransformerUtils_1.getNonNullType(RelationalDBSchemaTransformerUtils_1.getNamedType(`Update${type.name.value}Input`)), `update${type.name.value}Input`)], RelationalDBSchemaTransformerUtils_1.getNamedType(`${type.name.value}`), null));
136 }
137 return RelationalDBSchemaTransformerUtils_1.getTypeDefinition(fields, 'Mutation');
138 }
139 /**
140 * Generates the basic subscription operations, given the provided table contexts. This will
141 * create an onCreate subscription for each table.
142 *
143 * @param types the table contexts from which the subscriptions are to be generated.
144 * @returns the type definition for subscriptions, including an onCreate for each table.
145 */
146 getSubscriptions(types) {
147 const fields = [];
148 for (const typeContext of types) {
149 const type = typeContext.tableTypeDefinition;
150 fields.push(RelationalDBSchemaTransformerUtils_1.getOperationFieldDefinition(`onCreate${graphql_transformer_common_1.toUpper(type.name.value)}`, [], RelationalDBSchemaTransformerUtils_1.getNamedType(`${type.name.value}`), [RelationalDBSchemaTransformerUtils_1.getDirectiveNode(`create${graphql_transformer_common_1.toUpper(type.name.value)}`)]));
151 }
152 return RelationalDBSchemaTransformerUtils_1.getTypeDefinition(fields, 'Subscription');
153 }
154 /**
155 * Generates the basic query operations, given the provided table contexts. This will
156 * create a get and list operation for each table.
157 *
158 * @param types the table contexts from which the queries are to be generated.
159 * @returns the type definition for queries, including a get and list for each table.
160 */
161 getQueries(types) {
162 const fields = [];
163 for (const typeContext of types) {
164 const type = typeContext.tableTypeDefinition;
165 fields.push(RelationalDBSchemaTransformerUtils_1.getOperationFieldDefinition(`get${graphql_transformer_common_1.toUpper(type.name.value)}`, [RelationalDBSchemaTransformerUtils_1.getInputValueDefinition(RelationalDBSchemaTransformerUtils_1.getNonNullType(RelationalDBSchemaTransformerUtils_1.getNamedType(typeContext.tableKeyFieldType)), typeContext.tableKeyField)], RelationalDBSchemaTransformerUtils_1.getNamedType(`${type.name.value}`), null));
166 fields.push(RelationalDBSchemaTransformerUtils_1.getOperationFieldDefinition(`list${graphql_transformer_common_1.toUpper(type.name.value)}s`, [], RelationalDBSchemaTransformerUtils_1.getNamedType(`[${type.name.value}]`), null));
167 }
168 return RelationalDBSchemaTransformerUtils_1.getTypeDefinition(fields, 'Query');
169 }
170 /**
171 * Creates a GraphQL connection type for a given GraphQL type, corresponding to a SQL table name.
172 *
173 * @param tableName the name of the SQL table (and GraphQL type).
174 * @returns a type definition node defining the connection type for the provided type name.
175 */
176 getConnectionType(tableName) {
177 return RelationalDBSchemaTransformerUtils_1.getTypeDefinition([
178 RelationalDBSchemaTransformerUtils_1.getFieldDefinition('items', RelationalDBSchemaTransformerUtils_1.getNamedType(`[${tableName}]`)),
179 RelationalDBSchemaTransformerUtils_1.getFieldDefinition('nextToken', RelationalDBSchemaTransformerUtils_1.getNamedType('String'))
180 ], `${tableName}Connection`);
181 }
182}
183exports.RelationalDBSchemaTransformer = RelationalDBSchemaTransformer;
184//# sourceMappingURL=RelationalDBSchemaTransformer.js.map
\No newline at end of file