"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ModelTransformer = exports.directiveDefinition = void 0;
const graphql_transformer_core_1 = require("@aws-amplify/graphql-transformer-core");
const graphql_transformer_interfaces_1 = require("@aws-amplify/graphql-transformer-interfaces");
const graphql_transformer_common_1 = require("graphql-transformer-common");
const graphql_types_1 = require("./graphql-types");
const definitions_1 = require("./definitions");
const directive_1 = require("./directive");
const dynamo_model_resource_generator_1 = require("./resources/dynamo-model-resource-generator");
const rds_model_resource_generator_1 = require("./resources/rds-model-resource-generator");
const amplify_dynamo_model_resource_generator_1 = require("./resources/amplify-dynamodb-table/amplify-dynamo-model-resource-generator");
exports.directiveDefinition = `
  directive @model(
    queries: ModelQueryMap
    mutations: ModelMutationMap
    subscriptions: ModelSubscriptionMap
    timestamps: TimestampConfiguration
  ) on OBJECT
  input ModelMutationMap {
    create: String
    update: String
    delete: String
  }
  input ModelQueryMap {
    get: String
    list: String
  }
  input ModelSubscriptionMap {
    onCreate: [String]
    onUpdate: [String]
    onDelete: [String]
    level: ModelSubscriptionLevel
  }
  enum ModelSubscriptionLevel {
    off
    public
    on
  }
  input TimestampConfiguration {
    createdAt: String
    updatedAt: String
  }
`;
const ITERATIVE_TABLE_GENERATOR = 'AmplifyDDB';
const SQL_LAMBDA_GENERATOR = 'SQL';
class ModelTransformer extends graphql_transformer_core_1.TransformerModelBase {
    constructor(options = {}) {
        super('amplify-model-transformer', exports.directiveDefinition);
        this.ddbTableMap = {};
        this.typesWithModelDirective = new Set();
        this.resourceGeneratorMap = new Map();
        this.dataSourceStrategiesProvider = { dataSourceStrategies: {} };
        this.modelDirectiveConfig = new Map();
        this.before = (ctx) => {
            var _a, _b, _c, _d, _e, _f, _g;
            const { dataSourceStrategies, sqlDirectiveDataSourceStrategies } = ctx;
            this.dataSourceStrategiesProvider = { dataSourceStrategies, sqlDirectiveDataSourceStrategies };
            const strategies = Object.values(dataSourceStrategies);
            const customSqlDataSources = (_a = sqlDirectiveDataSourceStrategies === null || sqlDirectiveDataSourceStrategies === void 0 ? void 0 : sqlDirectiveDataSourceStrategies.map((dss) => dss.strategy)) !== null && _a !== void 0 ? _a : [];
            if (strategies.some(graphql_transformer_core_1.isDefaultDynamoDbModelDataSourceStrategy)) {
                (_b = this.resourceGeneratorMap.get(graphql_transformer_core_1.DDB_DB_TYPE)) === null || _b === void 0 ? void 0 : _b.enableGenerator();
                (_c = this.resourceGeneratorMap.get(graphql_transformer_core_1.DDB_DB_TYPE)) === null || _c === void 0 ? void 0 : _c.enableProvisioned();
            }
            if (strategies.some(graphql_transformer_core_1.isSqlStrategy) || customSqlDataSources.length > 0) {
                (_d = this.resourceGeneratorMap.get(SQL_LAMBDA_GENERATOR)) === null || _d === void 0 ? void 0 : _d.enableGenerator();
                (_e = this.resourceGeneratorMap.get(SQL_LAMBDA_GENERATOR)) === null || _e === void 0 ? void 0 : _e.enableUnprovisioned();
            }
            if (strategies.some(graphql_transformer_core_1.isAmplifyDynamoDbModelDataSourceStrategy)) {
                (_f = this.resourceGeneratorMap.get(ITERATIVE_TABLE_GENERATOR)) === null || _f === void 0 ? void 0 : _f.enableGenerator();
                (_g = this.resourceGeneratorMap.get(ITERATIVE_TABLE_GENERATOR)) === null || _g === void 0 ? void 0 : _g.enableProvisioned();
            }
        };
        this.object = (definition, directive, ctx) => {
            var _a, _b, _c, _d, _e, _f, _g, _h;
            const typeName = definition.name.value;
            const isTypeNameReserved = typeName === ctx.output.getQueryTypeName() ||
                typeName === ctx.output.getMutationTypeName() ||
                typeName === ctx.output.getSubscriptionTypeName();
            const isDynamoDB = (0, graphql_transformer_core_1.isDynamoDbModel)(ctx, typeName);
            if (isTypeNameReserved) {
                throw new graphql_transformer_core_1.InvalidDirectiveError(`'${typeName}' is a reserved type name and currently in use within the default schema element.`);
            }
            if (!isDynamoDB) {
                const containsPrimaryKey = (_a = definition.fields) === null || _a === void 0 ? void 0 : _a.some((field) => {
                    var _a;
                    return (_a = field === null || field === void 0 ? void 0 : field.directives) === null || _a === void 0 ? void 0 : _a.some((dir) => dir.name.value === 'primaryKey');
                });
                if (!containsPrimaryKey) {
                    throw new graphql_transformer_core_1.InvalidDirectiveError(`SQL model "${typeName}" must contain a primary key field`);
                }
            }
            if (ctx.isProjectUsingDataStore()) {
                graphql_transformer_core_1.SyncUtils.validateResolverConfigForType(ctx, typeName);
            }
            const directiveWrapped = new graphql_transformer_core_1.DirectiveWrapper(directive);
            const options = directiveWrapped.getArguments({
                queries: {
                    get: (0, graphql_transformer_core_1.getFieldNameFor)('get', typeName),
                    list: (0, graphql_transformer_core_1.getFieldNameFor)('list', typeName),
                    ...(ctx.isProjectUsingDataStore() ? { sync: (0, graphql_transformer_core_1.getFieldNameFor)('sync', typeName) } : undefined),
                },
                mutations: {
                    create: (0, graphql_transformer_core_1.getFieldNameFor)('create', typeName),
                    update: (0, graphql_transformer_core_1.getFieldNameFor)('update', typeName),
                    delete: (0, graphql_transformer_core_1.getFieldNameFor)('delete', typeName),
                },
                subscriptions: {
                    level: directive_1.SubscriptionLevel.on,
                    onCreate: [(0, graphql_transformer_core_1.getFieldNameFor)('onCreate', typeName)],
                    onDelete: [(0, graphql_transformer_core_1.getFieldNameFor)('onDelete', typeName)],
                    onUpdate: [(0, graphql_transformer_core_1.getFieldNameFor)('onUpdate', typeName)],
                },
                timestamps: isDynamoDB
                    ? {
                        createdAt: 'createdAt',
                        updatedAt: 'updatedAt',
                    }
                    : {
                        createdAt: undefined,
                        updatedAt: undefined,
                    },
            }, (0, graphql_transformer_core_1.generateGetArgumentsInput)(ctx.transformParameters));
            if (!ctx.transformParameters.shouldDeepMergeDirectiveConfigDefaults) {
                const publicSubscriptionDefaults = {
                    onCreate: [(0, graphql_transformer_core_1.getFieldNameFor)('onCreate', typeName)],
                    onDelete: [(0, graphql_transformer_core_1.getFieldNameFor)('onDelete', typeName)],
                    onUpdate: [(0, graphql_transformer_core_1.getFieldNameFor)('onUpdate', typeName)],
                };
                const baseArgs = directiveWrapped.getArguments({
                    subscriptions: {
                        level: directive_1.SubscriptionLevel.on,
                        ...publicSubscriptionDefaults,
                    },
                }, (0, graphql_transformer_core_1.generateGetArgumentsInput)(ctx.transformParameters));
                if (((_b = baseArgs === null || baseArgs === void 0 ? void 0 : baseArgs.subscriptions) === null || _b === void 0 ? void 0 : _b.level) === directive_1.SubscriptionLevel.public &&
                    !(((_c = baseArgs === null || baseArgs === void 0 ? void 0 : baseArgs.subscriptions) === null || _c === void 0 ? void 0 : _c.onCreate) || ((_d = baseArgs === null || baseArgs === void 0 ? void 0 : baseArgs.subscriptions) === null || _d === void 0 ? void 0 : _d.onDelete) || ((_e = baseArgs === null || baseArgs === void 0 ? void 0 : baseArgs.subscriptions) === null || _e === void 0 ? void 0 : _e.onUpdate))) {
                    options.subscriptions = { level: directive_1.SubscriptionLevel.public, ...publicSubscriptionDefaults };
                }
            }
            if (((_f = options.subscriptions) === null || _f === void 0 ? void 0 : _f.onCreate) && !Array.isArray(options.subscriptions.onCreate)) {
                options.subscriptions.onCreate = [options.subscriptions.onCreate];
            }
            if (((_g = options.subscriptions) === null || _g === void 0 ? void 0 : _g.onDelete) && !Array.isArray(options.subscriptions.onDelete)) {
                options.subscriptions.onDelete = [options.subscriptions.onDelete];
            }
            if (((_h = options.subscriptions) === null || _h === void 0 ? void 0 : _h.onUpdate) && !Array.isArray(options.subscriptions.onUpdate)) {
                options.subscriptions.onUpdate = [options.subscriptions.onUpdate];
            }
            this.modelDirectiveConfig.set(typeName, options);
            this.typesWithModelDirective.add(typeName);
            const resourceGenerator = this.getResourceGenerator(ctx, typeName);
            if (resourceGenerator) {
                resourceGenerator.addModelDefinition(definition, options);
            }
            else {
                throw Error(`DB Type or Resource Generator not defined for ${typeName}`);
            }
        };
        this.prepare = (context) => {
            this.typesWithModelDirective.forEach((modelTypeName) => {
                const type = context.output.getObject(modelTypeName);
                context.providerRegistry.registerDataSourceProvider(type, this);
            });
        };
        this.transformSchema = (ctx) => {
            (0, graphql_types_1.addModelConditionInputs)(ctx);
            this.ensureModelSortDirectionEnum(ctx);
            this.typesWithModelDirective.forEach((type) => {
                const defBeforeImplicitFields = ctx.output.getObject(type);
                const typeName = defBeforeImplicitFields.name.value;
                if ((0, graphql_transformer_core_1.isDynamoDbModel)(ctx, typeName)) {
                    this.addAutoGeneratableFields(ctx, type);
                }
                const def = ctx.output.getObject(type);
                const hasAuth = def.directives.some((dir) => dir.name.value === 'auth');
                this.createNonModelInputs(ctx, def);
                const queryFields = this.createQueryFields(ctx, def);
                ctx.output.addQueryFields(queryFields);
                const mutationFields = this.createMutationFields(ctx, defBeforeImplicitFields);
                ctx.output.addMutationFields(mutationFields);
                const subscriptionsFields = this.createSubscriptionFields(ctx, def);
                ctx.output.addSubscriptionFields(subscriptionsFields);
                if ((0, graphql_transformer_core_1.isDynamoDbModel)(ctx, typeName)) {
                    if (ctx.isProjectUsingDataStore()) {
                        this.addModelSyncFields(ctx, type);
                    }
                }
                if (!hasAuth) {
                    const serviceDirectiveNames = new Set();
                    if (ctx.transformParameters.sandboxModeEnabled && ctx.synthParameters.enableIamAccess) {
                        serviceDirectiveNames.add(definitions_1.API_KEY_DIRECTIVE);
                        serviceDirectiveNames.add(definitions_1.AWS_IAM_DIRECTIVE);
                    }
                    else if (ctx.transformParameters.sandboxModeEnabled && ctx.authConfig.defaultAuthentication.authenticationType !== 'API_KEY') {
                        serviceDirectiveNames.add(definitions_1.API_KEY_DIRECTIVE);
                    }
                    else if (ctx.synthParameters.enableIamAccess && ctx.authConfig.defaultAuthentication.authenticationType !== 'AWS_IAM') {
                        serviceDirectiveNames.add(definitions_1.AWS_IAM_DIRECTIVE);
                    }
                    const serviceDirectives = [...serviceDirectiveNames].map((directiveName) => (0, graphql_transformer_common_1.makeDirective)(directiveName, []));
                    if (serviceDirectives.length > 0) {
                        (0, graphql_types_1.extendTypeWithDirectives)(ctx, def.name.value, serviceDirectives);
                        (0, graphql_types_1.propagateDirectivesToNestedTypes)(ctx, def, new Set(), serviceDirectives);
                        queryFields.forEach((operationField) => {
                            const operationName = operationField.name.value;
                            (0, graphql_types_1.addDirectivesToOperation)(ctx, ctx.output.getQueryTypeName(), operationName, serviceDirectives);
                        });
                        mutationFields.forEach((operationField) => {
                            const operationName = operationField.name.value;
                            (0, graphql_types_1.addDirectivesToOperation)(ctx, ctx.output.getMutationTypeName(), operationName, serviceDirectives);
                        });
                        subscriptionsFields.forEach((operationField) => {
                            const operationName = operationField.name.value;
                            (0, graphql_types_1.addDirectivesToOperation)(ctx, ctx.output.getSubscriptionTypeName(), operationName, serviceDirectives);
                        });
                    }
                }
            });
        };
        this.generateResolvers = (context) => {
            this.resourceGeneratorMap.forEach((generator) => {
                generator.generateResources(context);
            });
        };
        this.generateGetResolver = (ctx, type, typeName, fieldName, resolverLogicalId) => {
            const resourceGenerator = this.getResourceGenerator(ctx, type.name.value);
            if (resourceGenerator) {
                return resourceGenerator.generateGetResolver(ctx, type, typeName, fieldName, resolverLogicalId);
            }
            throw new Error(`DB Type undefined or resource generator not provided for ${type.name.value}`);
        };
        this.generateListResolver = (ctx, type, typeName, fieldName, resolverLogicalId) => {
            const resourceGenerator = this.getResourceGenerator(ctx, type.name.value);
            if (resourceGenerator) {
                return resourceGenerator.generateListResolver(ctx, type, typeName, fieldName, resolverLogicalId);
            }
            throw new Error(`DB Type undefined or resource generator not provided for ${type.name.value}`);
        };
        this.generateCreateResolver = (ctx, type, typeName, fieldName, resolverLogicalId) => {
            const resourceGenerator = this.getResourceGenerator(ctx, type.name.value);
            if (resourceGenerator) {
                return resourceGenerator.generateCreateResolver(ctx, type, typeName, fieldName, resolverLogicalId);
            }
            throw new Error(`DB Type undefined or resource generator not provided for ${type.name.value}`);
        };
        this.generateUpdateResolver = (ctx, type, typeName, fieldName, resolverLogicalId) => {
            const modelDirectiveConfig = this.modelDirectiveConfig.get(type.name.value);
            const resourceGenerator = this.getResourceGenerator(ctx, type.name.value);
            if (resourceGenerator) {
                return resourceGenerator.generateUpdateResolver(ctx, type, modelDirectiveConfig, typeName, fieldName, resolverLogicalId);
            }
            throw new Error(`DB Type undefined or resource generator not provided for ${type.name.value}`);
        };
        this.generateDeleteResolver = (ctx, type, typeName, fieldName, resolverLogicalId) => {
            const resourceGenerator = this.getResourceGenerator(ctx, type.name.value);
            if (resourceGenerator) {
                return resourceGenerator.generateDeleteResolver(ctx, type, typeName, fieldName, resolverLogicalId);
            }
            throw new Error(`DB Type undefined or resource generator not provided for ${type.name.value}`);
        };
        this.generateOnCreateResolver = (ctx, typeName, fieldName, resolverLogicalId) => {
            if (this.resourceGeneratorMap.has(graphql_transformer_core_1.DDB_DB_TYPE)) {
                return this.resourceGeneratorMap.get(graphql_transformer_core_1.DDB_DB_TYPE).generateOnCreateResolver(ctx, typeName, fieldName, resolverLogicalId);
            }
            throw new Error('Resource generator not provided for DDB');
        };
        this.generateOnUpdateResolver = (ctx, typeName, fieldName, resolverLogicalId) => {
            if (this.resourceGeneratorMap.has(graphql_transformer_core_1.DDB_DB_TYPE)) {
                return this.resourceGeneratorMap.get(graphql_transformer_core_1.DDB_DB_TYPE).generateOnUpdateResolver(ctx, typeName, fieldName, resolverLogicalId);
            }
            throw new Error('Resource generator not provided for DDB');
        };
        this.generateOnDeleteResolver = (ctx, typeName, fieldName, resolverLogicalId) => {
            if (this.resourceGeneratorMap.has(graphql_transformer_core_1.DDB_DB_TYPE)) {
                return this.resourceGeneratorMap.get(graphql_transformer_core_1.DDB_DB_TYPE).generateOnDeleteResolver(ctx, typeName, fieldName, resolverLogicalId);
            }
            throw new Error('Resource generator not provided for DDB');
        };
        this.generateSyncResolver = (ctx, type, typeName, fieldName, resolverLogicalId) => {
            const resourceGenerator = this.getResourceGenerator(ctx, type.name.value);
            if (resourceGenerator) {
                return resourceGenerator.generateSyncResolver(ctx, type, typeName, fieldName, resolverLogicalId);
            }
            throw new Error(`DB Type undefined or resource generator not provided for ${type.name.value}`);
        };
        this.getMutationName = (subscriptionType, mutationMap) => {
            const mutationToSubscriptionTypeMap = {
                [graphql_transformer_interfaces_1.SubscriptionFieldType.ON_CREATE]: graphql_transformer_interfaces_1.MutationFieldType.CREATE,
                [graphql_transformer_interfaces_1.SubscriptionFieldType.ON_UPDATE]: graphql_transformer_interfaces_1.MutationFieldType.UPDATE,
                [graphql_transformer_interfaces_1.SubscriptionFieldType.ON_DELETE]: graphql_transformer_interfaces_1.MutationFieldType.DELETE,
            };
            const mutation = Array.from(mutationMap).find((m) => m.type === mutationToSubscriptionTypeMap[subscriptionType]);
            if (mutation) {
                return mutation.fieldName;
            }
            throw new Error('Unknown Subscription type');
        };
        this.createQueryFields = (ctx, def) => {
            const queryFields = [];
            const queryFieldNames = this.getQueryFieldNames(def);
            queryFieldNames.forEach((queryField) => {
                const outputType = this.getOutputType(ctx, def, queryField);
                const args = this.getInputs(ctx, def, {
                    fieldName: queryField.fieldName,
                    typeName: queryField.typeName,
                    type: queryField.type,
                });
                queryFields.push((0, graphql_transformer_common_1.makeField)(queryField.fieldName, args, (0, graphql_transformer_common_1.makeNamedType)(outputType.name.value)));
            });
            return queryFields;
        };
        this.createMutationFields = (ctx, def) => {
            const mutationFields = [];
            const mutationFieldNames = this.getMutationFieldNames(def);
            mutationFieldNames.forEach((mutationField) => {
                const args = this.getInputs(ctx, def, {
                    fieldName: mutationField.fieldName,
                    typeName: mutationField.typeName,
                    type: mutationField.type,
                });
                mutationFields.push((0, graphql_transformer_common_1.makeField)(mutationField.fieldName, args, (0, graphql_transformer_common_1.makeNamedType)(def.name.value)));
            });
            return mutationFields;
        };
        this.createSubscriptionFields = (ctx, def) => {
            const subscriptionToMutationsMap = this.getSubscriptionToMutationsReverseMap(def);
            const mutationFields = this.getMutationFieldNames(def);
            const subscriptionFields = [];
            Object.keys(subscriptionToMutationsMap).forEach((subscriptionFieldName) => {
                const maps = subscriptionToMutationsMap[subscriptionFieldName];
                const args = [];
                maps.map((it) => args.push(...this.getInputs(ctx, def, {
                    fieldName: it.fieldName,
                    typeName: it.typeName,
                    type: it.type,
                })));
                const mutationNames = maps.map((it) => this.getMutationName(it.type, mutationFields));
                const directive = (0, graphql_transformer_common_1.makeDirective)('aws_subscribe', [(0, graphql_transformer_common_1.makeArgument)('mutations', (0, graphql_transformer_common_1.makeValueNode)(mutationNames))]);
                const field = (0, graphql_transformer_common_1.makeField)(subscriptionFieldName, args, (0, graphql_transformer_common_1.makeNamedType)(def.name.value), [directive]);
                subscriptionFields.push(field);
            });
            return subscriptionFields;
        };
        this.getQueryFieldNames = (type) => {
            const resourceGenerator = this.getResourceGenerator(this.dataSourceStrategiesProvider, type.name.value);
            if (resourceGenerator) {
                return resourceGenerator.getQueryFieldNames(type);
            }
            throw new Error(`Resource Generator or DB Type not defined for ${type.name.value}`);
        };
        this.getMutationFieldNames = (type) => {
            const resourceGenerator = this.getResourceGenerator(this.dataSourceStrategiesProvider, type.name.value);
            if (resourceGenerator) {
                return resourceGenerator.getMutationFieldNames(type);
            }
            throw new Error(`Resource Generator or DB Type not defined for ${type.name.value}`);
        };
        this.getSubscriptionFieldNames = (type) => {
            const resourceGenerator = this.getResourceGenerator(this.dataSourceStrategiesProvider, type.name.value);
            if (resourceGenerator) {
                return resourceGenerator.getSubscriptionFieldNames(type);
            }
            throw new Error(`Resource Generator or DB Type not defined for ${type.name.value}`);
        };
        this.getDataSourceResource = (type) => this.ddbTableMap[type.name.value];
        this.getDataSourceType = () => graphql_transformer_interfaces_1.AppSyncDataSourceType.AMAZON_DYNAMODB;
        this.getInputs = (ctx, type, operation) => {
            const isSyncEnabled = ctx.isProjectUsingDataStore();
            const knownModels = this.typesWithModelDirective;
            let conditionInput;
            if ([graphql_transformer_interfaces_1.MutationFieldType.CREATE, graphql_transformer_interfaces_1.MutationFieldType.DELETE, graphql_transformer_interfaces_1.MutationFieldType.UPDATE].includes(operation.type)) {
                const conditionTypeName = (0, graphql_transformer_core_1.getConditionInputName)(type.name.value);
                const filterInputs = (0, graphql_types_1.createEnumModelFilters)(ctx, type);
                conditionInput = (0, graphql_types_1.makeMutationConditionInput)(ctx, conditionTypeName, type, this.modelDirectiveConfig.get(type.name.value));
                filterInputs.push(conditionInput);
                filterInputs.forEach((input) => {
                    const conditionInputName = input.name.value;
                    if (!ctx.output.getType(conditionInputName)) {
                        ctx.output.addInput(input);
                    }
                });
            }
            switch (operation.type) {
                case graphql_transformer_interfaces_1.QueryFieldType.GET:
                    return [(0, graphql_transformer_common_1.makeInputValueDefinition)('id', (0, graphql_transformer_common_1.makeNonNullType)((0, graphql_transformer_common_1.makeNamedType)('ID')))];
                case graphql_transformer_interfaces_1.QueryFieldType.LIST: {
                    const filterInputName = (0, graphql_transformer_core_1.getFilterInputName)(type.name.value);
                    const filterInputs = (0, graphql_types_1.createEnumModelFilters)(ctx, type);
                    filterInputs.push((0, graphql_types_1.makeListQueryFilterInput)(ctx, filterInputName, type));
                    filterInputs.forEach((input) => {
                        const conditionInputName = input.name.value;
                        if (!ctx.output.getType(conditionInputName)) {
                            ctx.output.addInput(input);
                        }
                    });
                    return [
                        (0, graphql_transformer_common_1.makeInputValueDefinition)('filter', (0, graphql_transformer_common_1.makeNamedType)(filterInputName)),
                        (0, graphql_transformer_common_1.makeInputValueDefinition)('limit', (0, graphql_transformer_common_1.makeNamedType)('Int')),
                        (0, graphql_transformer_common_1.makeInputValueDefinition)('nextToken', (0, graphql_transformer_common_1.makeNamedType)('String')),
                    ];
                }
                case graphql_transformer_interfaces_1.QueryFieldType.SYNC: {
                    const syncFilterInputName = (0, graphql_transformer_core_1.getFilterInputName)(type.name.value);
                    const syncFilterInputs = (0, graphql_types_1.makeListQueryFilterInput)(ctx, syncFilterInputName, type);
                    const conditionInputName = syncFilterInputs.name.value;
                    if (!ctx.output.getType(conditionInputName)) {
                        ctx.output.addInput(syncFilterInputs);
                    }
                    return [
                        (0, graphql_transformer_common_1.makeInputValueDefinition)('filter', (0, graphql_transformer_common_1.makeNamedType)(syncFilterInputName)),
                        (0, graphql_transformer_common_1.makeInputValueDefinition)('limit', (0, graphql_transformer_common_1.makeNamedType)('Int')),
                        (0, graphql_transformer_common_1.makeInputValueDefinition)('nextToken', (0, graphql_transformer_common_1.makeNamedType)('String')),
                        (0, graphql_transformer_common_1.makeInputValueDefinition)('lastSync', (0, graphql_transformer_common_1.makeNamedType)('AWSTimestamp')),
                    ];
                }
                case graphql_transformer_interfaces_1.MutationFieldType.CREATE: {
                    const createInputField = (0, graphql_types_1.makeCreateInputField)(type, this.modelDirectiveConfig.get(type.name.value), knownModels, ctx.inputDocument, isSyncEnabled);
                    const createInputTypeName = createInputField.name.value;
                    if (!ctx.output.getType(createInputField.name.value)) {
                        ctx.output.addInput(createInputField);
                    }
                    return [
                        (0, graphql_transformer_common_1.makeInputValueDefinition)('input', (0, graphql_transformer_common_1.makeNonNullType)((0, graphql_transformer_common_1.makeNamedType)(createInputTypeName))),
                        (0, graphql_transformer_common_1.makeInputValueDefinition)('condition', (0, graphql_transformer_common_1.makeNamedType)(conditionInput.name.value)),
                    ];
                }
                case graphql_transformer_interfaces_1.MutationFieldType.DELETE: {
                    const deleteInputField = (0, graphql_types_1.makeDeleteInputField)(type, isSyncEnabled);
                    const deleteInputTypeName = deleteInputField.name.value;
                    if (!ctx.output.getType(deleteInputField.name.value)) {
                        ctx.output.addInput(deleteInputField);
                    }
                    return [
                        (0, graphql_transformer_common_1.makeInputValueDefinition)('input', (0, graphql_transformer_common_1.makeNonNullType)((0, graphql_transformer_common_1.makeNamedType)(deleteInputTypeName))),
                        (0, graphql_transformer_common_1.makeInputValueDefinition)('condition', (0, graphql_transformer_common_1.makeNamedType)(conditionInput.name.value)),
                    ];
                }
                case graphql_transformer_interfaces_1.MutationFieldType.UPDATE: {
                    const updateInputField = (0, graphql_types_1.makeUpdateInputField)(type, this.modelDirectiveConfig.get(type.name.value), knownModels, ctx.inputDocument, isSyncEnabled);
                    const updateInputTypeName = updateInputField.name.value;
                    if (!ctx.output.getType(updateInputField.name.value)) {
                        ctx.output.addInput(updateInputField);
                    }
                    return [
                        (0, graphql_transformer_common_1.makeInputValueDefinition)('input', (0, graphql_transformer_common_1.makeNonNullType)((0, graphql_transformer_common_1.makeNamedType)(updateInputTypeName))),
                        (0, graphql_transformer_common_1.makeInputValueDefinition)('condition', (0, graphql_transformer_common_1.makeNamedType)(conditionInput.name.value)),
                    ];
                }
                case graphql_transformer_interfaces_1.SubscriptionFieldType.ON_CREATE:
                case graphql_transformer_interfaces_1.SubscriptionFieldType.ON_DELETE:
                case graphql_transformer_interfaces_1.SubscriptionFieldType.ON_UPDATE: {
                    const filterInputName = (0, graphql_transformer_core_1.getSubscriptionFilterInputName)(type.name.value);
                    const filterInputs = (0, graphql_types_1.createEnumModelFilters)(ctx, type);
                    filterInputs.push((0, graphql_types_1.makeSubscriptionQueryFilterInput)(ctx, filterInputName, type));
                    filterInputs.forEach((input) => {
                        const conditionInputName = input.name.value;
                        if (!ctx.output.getType(conditionInputName)) {
                            ctx.output.addInput(input);
                        }
                    });
                    return [(0, graphql_transformer_common_1.makeInputValueDefinition)('filter', (0, graphql_transformer_common_1.makeNamedType)(filterInputName))];
                }
                default:
                    throw new Error('Unknown operation type');
            }
        };
        this.getOutputType = (ctx, type, operation) => {
            let outputType;
            switch (operation.type) {
                case graphql_transformer_interfaces_1.MutationFieldType.CREATE:
                case graphql_transformer_interfaces_1.MutationFieldType.UPDATE:
                case graphql_transformer_interfaces_1.MutationFieldType.DELETE:
                case graphql_transformer_interfaces_1.QueryFieldType.GET:
                case graphql_transformer_interfaces_1.SubscriptionFieldType.ON_CREATE:
                case graphql_transformer_interfaces_1.SubscriptionFieldType.ON_DELETE:
                case graphql_transformer_interfaces_1.SubscriptionFieldType.ON_UPDATE:
                    outputType = type;
                    break;
                case graphql_transformer_interfaces_1.QueryFieldType.SYNC:
                case graphql_transformer_interfaces_1.QueryFieldType.LIST: {
                    const isSyncEnabled = ctx.isProjectUsingDataStore();
                    const connectionFieldName = (0, graphql_transformer_core_1.getConnectionName)(type.name.value);
                    outputType = (0, graphql_types_1.makeListQueryModel)(type, connectionFieldName, isSyncEnabled);
                    break;
                }
                default:
                    throw new Error(`${operation.type} not supported for ${type.name.value}`);
            }
            if (!ctx.output.getObject(outputType.name.value)) {
                ctx.output.addObject(outputType);
            }
            return outputType;
        };
        this.createIAMRole = (context, def, stack, tableName) => {
            const ddbGenerator = this.resourceGeneratorMap.get(graphql_transformer_core_1.DDB_DB_TYPE);
            return ddbGenerator.createIAMRole(context, def, stack, tableName);
        };
        this.createNonModelInputs = (ctx, obj) => {
            var _a;
            ((_a = obj.fields) !== null && _a !== void 0 ? _a : []).forEach((field) => {
                if (!(0, graphql_transformer_common_1.isScalar)(field.type)) {
                    const def = ctx.output.getType((0, graphql_transformer_common_1.getBaseType)(field.type));
                    if (def && def.kind === 'ObjectTypeDefinition' && !this.isModelField(def.name.value)) {
                        const name = this.getNonModelInputObjectName(def.name.value);
                        if (!ctx.output.getType(name)) {
                            const inputObj = graphql_transformer_core_1.InputObjectDefinitionWrapper.fromObject(name, def, ctx.inputDocument);
                            ctx.output.addInput(inputObj.serialize());
                            this.createNonModelInputs(ctx, def);
                        }
                    }
                }
            });
        };
        this.isModelField = (name) => !!this.typesWithModelDirective.has(name);
        this.getNonModelInputObjectName = (name) => `${(0, graphql_transformer_common_1.toUpper)(name)}Input`;
        this.addAutoGeneratableFields = (ctx, name) => {
            var _a, _b;
            const modelDirectiveConfig = this.modelDirectiveConfig.get(name);
            const typeObj = ctx.output.getObject(name);
            if (!typeObj) {
                throw new Error(`Type ${name} is missing in outputs`);
            }
            const typeWrapper = new graphql_transformer_core_1.ObjectDefinitionWrapper(typeObj);
            if (!typeWrapper.hasField('id')) {
                const idField = graphql_transformer_core_1.FieldWrapper.create('id', 'ID');
                typeWrapper.addField(idField);
            }
            const timestamps = [];
            if (modelDirectiveConfig === null || modelDirectiveConfig === void 0 ? void 0 : modelDirectiveConfig.timestamps) {
                if (modelDirectiveConfig.timestamps.createdAt !== null) {
                    timestamps.push((_a = modelDirectiveConfig.timestamps.createdAt) !== null && _a !== void 0 ? _a : 'createdAt');
                }
                if (modelDirectiveConfig.timestamps.updatedAt !== null) {
                    timestamps.push((_b = modelDirectiveConfig.timestamps.updatedAt) !== null && _b !== void 0 ? _b : 'updatedAt');
                }
            }
            timestamps.forEach((fieldName) => {
                if (typeWrapper.hasField(fieldName)) {
                    const field = typeWrapper.getField(fieldName);
                    if (!['String', 'AWSDateTime'].includes(field.getTypeName())) {
                        console.warn(`type ${name}.${fieldName} is not of String or AWSDateTime. Auto population is not supported`);
                    }
                }
                else {
                    const field = graphql_transformer_core_1.FieldWrapper.create(fieldName, 'AWSDateTime');
                    typeWrapper.addField(field);
                }
            });
            ctx.output.updateObject(typeWrapper.serialize());
        };
        this.addModelSyncFields = (ctx, name) => {
            const typeObj = ctx.output.getObject(name);
            if (!typeObj) {
                throw new Error(`Type ${name} is missing in outputs`);
            }
            const typeWrapper = new graphql_transformer_core_1.ObjectDefinitionWrapper(typeObj);
            typeWrapper.addField(graphql_transformer_core_1.FieldWrapper.create('_version', 'Int'));
            typeWrapper.addField(graphql_transformer_core_1.FieldWrapper.create('_deleted', 'Boolean', true));
            typeWrapper.addField(graphql_transformer_core_1.FieldWrapper.create('_lastChangedAt', 'AWSTimestamp'));
            ctx.output.updateObject(typeWrapper.serialize());
        };
        this.getSubscriptionToMutationsReverseMap = (def) => {
            const subscriptionToMutationsMap = {};
            const subscriptionFieldNames = this.getSubscriptionFieldNames(def);
            subscriptionFieldNames.forEach((subscriptionFieldName) => {
                if (!subscriptionToMutationsMap[subscriptionFieldName.fieldName]) {
                    subscriptionToMutationsMap[subscriptionFieldName.fieldName] = [];
                }
                subscriptionToMutationsMap[subscriptionFieldName.fieldName].push(subscriptionFieldName);
            });
            return subscriptionToMutationsMap;
        };
        this.ensureModelSortDirectionEnum = (ctx) => {
            if (!ctx.output.hasType('ModelSortDirection')) {
                const modelSortDirection = (0, graphql_types_1.makeModelSortDirectionEnumObject)();
                ctx.output.addEnum(modelSortDirection);
            }
        };
        this.getOptions = (options) => ({
            EnableDeletionProtection: false,
            ...options,
        });
        this.getResourceGenerator = (ctx, typeName) => {
            const strategy = (0, graphql_transformer_core_1.getModelDataSourceStrategy)(ctx, typeName);
            let generator;
            if ((0, graphql_transformer_core_1.isDefaultDynamoDbModelDataSourceStrategy)(strategy)) {
                generator = this.resourceGeneratorMap.get(graphql_transformer_core_1.DDB_DB_TYPE);
            }
            else if ((0, graphql_transformer_core_1.isAmplifyDynamoDbModelDataSourceStrategy)(strategy)) {
                generator = this.resourceGeneratorMap.get(ITERATIVE_TABLE_GENERATOR);
            }
            else if ((0, graphql_transformer_core_1.isSqlStrategy)(strategy)) {
                generator = this.resourceGeneratorMap.get(SQL_LAMBDA_GENERATOR);
            }
            if (!generator) {
                throw new Error(`No resource generator assigned for ${typeName} with dbType ${strategy.dbType}`);
            }
            return generator;
        };
        this.options = this.getOptions(options);
        this.resourceGeneratorMap.set(graphql_transformer_core_1.DDB_DB_TYPE, new dynamo_model_resource_generator_1.DynamoModelResourceGenerator());
        this.resourceGeneratorMap.set(SQL_LAMBDA_GENERATOR, new rds_model_resource_generator_1.RdsModelResourceGenerator());
        this.resourceGeneratorMap.set(ITERATIVE_TABLE_GENERATOR, new amplify_dynamo_model_resource_generator_1.AmplifyDynamoModelResourceGenerator());
    }
}
exports.ModelTransformer = ModelTransformer;
//# sourceMappingURL=graphql-model-transformer.js.map