@aws-amplify/cli-internal
Version:
Amplify CLI
205 lines • 10.3 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DataGenerator = exports.classifyVtlFiles = exports.parseVtlFilename = void 0;
const node_path_1 = __importDefault(require("node:path"));
const promises_1 = __importDefault(require("node:fs/promises"));
const node_fs_1 = require("node:fs");
const glob_1 = require("glob");
const amplify_cli_core_1 = require("@aws-amplify/amplify-cli-core");
const ts_1 = require("../../ts");
const data_renderer_1 = require("./data.renderer");
function parseVtlFilename(filename) {
const segments = filename.split('.');
if (segments.length === 4) {
const [typeName, fieldName, templateType] = segments;
return {
kind: 'override',
typeName,
fieldName,
templateType: templateType,
filename,
};
}
if (segments.length === 6) {
const [typeName, fieldName, slot, orderStr, templateType] = segments;
return {
kind: 'extended',
typeName,
fieldName,
slot,
order: Number(orderStr),
templateType: templateType,
filename,
};
}
return undefined;
}
exports.parseVtlFilename = parseVtlFilename;
function classifyVtlFiles(filenames) {
const overrides = [];
const extended = [];
const seen = new Map();
for (const filename of filenames) {
const parsed = parseVtlFilename(filename);
if (!parsed)
continue;
if (parsed.kind === 'override') {
overrides.push(parsed);
}
else {
const segments = filename.split('.');
const orderStr = segments[3];
if (!/^\d+$/.test(orderStr)) {
throw new Error(`Non-numeric order '${orderStr}' in extended resolver file '${filename}'`);
}
const key = `${parsed.typeName}.${parsed.fieldName}.${parsed.slot}.${parsed.order}.${parsed.templateType}`;
const existing = seen.get(key);
if (existing) {
throw new Error(`Duplicate extended resolver: '${existing}' and '${filename}'`);
}
seen.set(key, filename);
extended.push(parsed);
}
}
return { overrides, extended };
}
exports.classifyVtlFiles = classifyVtlFiles;
class DataGenerator {
constructor(gen1App, backendGenerator, outputDir, resource, logger) {
this.gen1App = gen1App;
this.backendGenerator = backendGenerator;
this.outputDir = outputDir;
this.resource = resource;
this.renderer = new data_renderer_1.DataRenderer(gen1App.envName);
this.logger = logger;
}
async plan() {
const schema = this.collectUserSchema();
const apiId = this.gen1App.resourceMetaOutput(this.resource, 'GraphQLAPIIdOutput');
const tableMappings = this.createTableMappings(apiId);
this.logger.debug(`Fetching AppSync API '${apiId}'`);
const graphqlApi = await this.gen1App.aws.fetchGraphqlApi(apiId);
if (!graphqlApi) {
throw new amplify_cli_core_1.AmplifyError('AppSyncApiNotFoundError', {
message: `AppSync API '${apiId}' not found`,
resolution: 'Verify the AppSync API exists and the CLI has the correct AWS credentials and region configured.',
});
}
const dataDir = node_path_1.default.join(this.outputDir, 'amplify', 'data');
const hasAdditionalAuthProviders = graphqlApi.additionalAuthenticationProviders !== undefined && graphqlApi.additionalAuthenticationProviders.length > 0;
const hasAuth = this.gen1App.categoryMeta('auth') !== undefined;
const authorizationModes = supplementOidcConfig(this.gen1App.resourceMetaOutput(this.resource, 'authConfig'), graphqlApi);
const hasIamAuth = this.detectIamAuth(authorizationModes, graphqlApi);
const vtlFiles = this.findResolverVtlFiles(this.resource.resourceName);
const hasResolvers = vtlFiles.length > 0;
const classifiedResolvers = hasResolvers ? classifyVtlFiles([...vtlFiles]) : undefined;
const needsEscapeHatches = hasAdditionalAuthProviders || (hasIamAuth && hasAuth) || hasResolvers;
const operations = [
{
resource: this.resource,
validate: () => undefined,
describe: async () => ['Generate amplify/data/resource.ts'],
execute: async () => {
this.logger.info('Rendering data/resource.ts');
const nodes = this.renderer.render({
schema,
tableMappings,
authorizationModes,
graphqlApi,
hasAuth,
apiId,
classifiedResolvers,
});
const content = ts_1.TS.printNodes(nodes);
await promises_1.default.mkdir(dataDir, { recursive: true });
await promises_1.default.writeFile(node_path_1.default.join(dataDir, 'resource.ts'), content, 'utf-8');
this.backendGenerator.addNamespaceImport('data', './data/resource');
this.backendGenerator.addDefineBackendEntry('data', 'data', 'data');
if (needsEscapeHatches) {
this.backendGenerator.addApplyEscapeHatchesCall({ alias: 'data', extraArgs: [] });
}
},
},
];
if (hasResolvers) {
const gen1ResolversDir = node_path_1.default.join(this.gen1App.ccbDir, 'api', this.resource.resourceName, 'resolvers');
const destResolversDir = node_path_1.default.join(dataDir, 'resolvers');
operations.push({
resource: this.resource,
validate: () => undefined,
describe: async () => ['Copy VTL resolver files to amplify/data/resolvers/'],
execute: async () => {
await promises_1.default.mkdir(destResolversDir, { recursive: true });
for (const file of vtlFiles) {
await promises_1.default.copyFile(node_path_1.default.join(gen1ResolversDir, file), node_path_1.default.join(destResolversDir, file));
}
},
});
}
return operations;
}
collectUserSchema() {
const schemaFilePath = node_path_1.default.join('api', this.resource.resourceName, 'schema.graphql');
if (this.gen1App.fileExists(schemaFilePath)) {
return this.gen1App.file(schemaFilePath);
}
const schemaDirPath = node_path_1.default.join('api', this.resource.resourceName, 'schema');
const fullDirPath = node_path_1.default.join(this.gen1App.ccbDir, schemaDirPath);
const files = (0, glob_1.globSync)('**/*.graphql', { cwd: fullDirPath }).sort();
return files.map((f) => this.gen1App.file(node_path_1.default.join(schemaDirPath, f))).join('\n');
}
findResolverVtlFiles(apiName) {
const resolversDir = node_path_1.default.join(this.gen1App.ccbDir, 'api', apiName, 'resolvers');
if (!(0, node_fs_1.existsSync)(resolversDir)) {
return [];
}
return (0, node_fs_1.readdirSync)(resolversDir).filter((f) => f.endsWith('.vtl'));
}
createTableMappings(apiId) {
const buildSchemaPath = node_path_1.default.join('api', this.resource.resourceName, 'build', 'schema.graphql');
const buildSchema = this.gen1App.file(buildSchemaPath);
const connectionRegex = /type\s+Model(\w+)Connection\b/g;
const mapping = {};
let match;
while ((match = connectionRegex.exec(buildSchema)) !== null) {
mapping[match[1]] = [match[1], apiId, this.gen1App.envName].join('-');
}
return mapping;
}
detectIamAuth(authorizationModes, graphqlApi) {
var _a, _b, _c;
const defaultAuthType = (_a = authorizationModes === null || authorizationModes === void 0 ? void 0 : authorizationModes.defaultAuthentication) === null || _a === void 0 ? void 0 : _a.authenticationType;
if (defaultAuthType === 'AWS_IAM')
return true;
return (_c = (_b = graphqlApi.additionalAuthenticationProviders) === null || _b === void 0 ? void 0 : _b.some((p) => p.authenticationType === 'AWS_IAM')) !== null && _c !== void 0 ? _c : false;
}
}
exports.DataGenerator = DataGenerator;
function supplementOidcConfig(authConfig, graphqlApi) {
var _a, _b, _c, _d;
if (!authConfig)
return authConfig;
const result = JSON.parse(JSON.stringify(authConfig));
if (((_a = result.defaultAuthentication) === null || _a === void 0 ? void 0 : _a.authenticationType) === 'OPENID_CONNECT' &&
result.defaultAuthentication.openIDConnectConfig &&
!result.defaultAuthentication.openIDConnectConfig.clientId &&
((_b = graphqlApi.openIDConnectConfig) === null || _b === void 0 ? void 0 : _b.clientId)) {
result.defaultAuthentication.openIDConnectConfig.clientId = graphqlApi.openIDConnectConfig.clientId;
}
if (result.additionalAuthenticationProviders) {
for (const provider of result.additionalAuthenticationProviders) {
if (provider.authenticationType !== 'OPENID_CONNECT' || !provider.openIDConnectConfig || provider.openIDConnectConfig.clientId) {
continue;
}
const match = (_c = graphqlApi.additionalAuthenticationProviders) === null || _c === void 0 ? void 0 : _c.find((p) => { var _a; return p.authenticationType === 'OPENID_CONNECT' && ((_a = p.openIDConnectConfig) === null || _a === void 0 ? void 0 : _a.issuer) === provider.openIDConnectConfig.issuerUrl; });
if ((_d = match === null || match === void 0 ? void 0 : match.openIDConnectConfig) === null || _d === void 0 ? void 0 : _d.clientId) {
provider.openIDConnectConfig.clientId = match.openIDConnectConfig.clientId;
}
}
}
return result;
}
//# sourceMappingURL=data.generator.js.map