UNPKG

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