"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ensureModelSortDirectionEnum = exports.ensureQueryField = exports.tryAndCreateSortField = exports.createHashField = exports.updateMutationConditionInput = exports.updateInputObjects = exports.updateGetField = exports.removeAutoCreatedPrimaryKey = exports.addKeyConditionInputs = void 0;
const graphql_model_transformer_1 = require("@aws-amplify/graphql-model-transformer");
const graphql_1 = require("graphql");
const graphql_transformer_common_1 = require("graphql-transformer-common");
const graphql_transformer_core_1 = require("@aws-amplify/graphql-transformer-core");
const utils_1 = require("./utils");
const API_KEY_DIRECTIVE = 'aws_api_key';
const AWS_IAM_DIRECTIVE = 'aws_iam';
function addKeyConditionInputs(config, ctx) {
    const { object, sortKey } = config;
    if (sortKey.length > 1) {
        const indexKeyName = config.name;
        const keyName = (0, graphql_transformer_common_1.toUpper)(indexKeyName !== null && indexKeyName !== void 0 ? indexKeyName : 'Primary');
        const keyConditionInput = (0, graphql_transformer_common_1.makeCompositeKeyConditionInputForKey)(object.name.value, keyName, sortKey);
        if (!ctx.output.getType(keyConditionInput.name.value)) {
            ctx.output.addInput(keyConditionInput);
        }
        const compositeKeyInput = (0, graphql_transformer_common_1.makeCompositeKeyInputForKey)(object.name.value, keyName, sortKey);
        if (!ctx.output.getType(compositeKeyInput.name.value)) {
            ctx.output.addInput(compositeKeyInput);
        }
    }
    else if (sortKey.length === 1) {
        const sortKeyField = sortKey[0];
        const typeResolver = (baseType) => {
            const resolvedEnumType = ctx.output.getType(baseType);
            return resolvedEnumType ? 'String' : undefined;
        };
        const sortKeyConditionInput = (0, graphql_transformer_common_1.makeScalarKeyConditionForType)(sortKeyField.type, typeResolver);
        if (sortKeyConditionInput === undefined) {
            throw new graphql_transformer_core_1.InvalidDirectiveError(`Sort Key Condition could not be constructed for field '${sortKeyField.name.value}'`);
        }
        if (!ctx.output.getType(sortKeyConditionInput.name.value)) {
            ctx.output.addInput(sortKeyConditionInput);
        }
    }
}
exports.addKeyConditionInputs = addKeyConditionInputs;
function removeAutoCreatedPrimaryKey(config, ctx) {
    var _a;
    const { object } = config;
    const schemaHasIdField = (_a = object === null || object === void 0 ? void 0 : object.fields) === null || _a === void 0 ? void 0 : _a.some((f) => f.name.value === 'id');
    if (schemaHasIdField) {
        return;
    }
    const obj = ctx.output.getObject(object.name.value);
    const fields = obj.fields.filter((f) => f.name.value !== 'id');
    const newObj = {
        ...obj,
        fields,
    };
    ctx.output.updateObject(newObj);
}
exports.removeAutoCreatedPrimaryKey = removeAutoCreatedPrimaryKey;
function updateGetField(config, ctx) {
    const resolverName = (0, utils_1.lookupResolverName)(config, ctx, 'get');
    let query = ctx.output.getQuery();
    if (!(resolverName && query)) {
        return;
    }
    const { field, sortKey } = config;
    let resolverField = query.fields.find((field) => field.name.value === resolverName);
    if (resolverField) {
        const args = [
            (0, graphql_transformer_common_1.makeInputValueDefinition)(field.name.value, (0, graphql_transformer_common_1.makeNonNullType)((0, graphql_transformer_common_1.makeNamedType)((0, graphql_transformer_common_1.getBaseType)(field.type)))),
            ...sortKey.map((keyField) => {
                return (0, graphql_transformer_common_1.makeInputValueDefinition)(keyField.name.value, (0, graphql_transformer_common_1.makeNonNullType)((0, graphql_transformer_common_1.makeNamedType)((0, graphql_transformer_common_1.getBaseType)(keyField.type))));
            }),
        ];
        resolverField = { ...resolverField, arguments: args };
        query = {
            ...query,
            fields: query.fields.map((field) => {
                return field.name.value === resolverField.name.value ? resolverField : field;
            }),
        };
        ctx.output.updateObject(query);
    }
}
exports.updateGetField = updateGetField;
function updateInputObjects(config, ctx) {
    const { object, modelDirective } = config;
    let shouldMakeCreate = true;
    let shouldMakeUpdate = true;
    let shouldMakeDelete = true;
    for (const argument of modelDirective.arguments) {
        const arg = argument;
        if (arg.name.value === 'mutations') {
            if (arg.value.kind === graphql_1.Kind.NULL) {
                shouldMakeCreate = false;
                shouldMakeUpdate = false;
                shouldMakeDelete = false;
            }
            else if (Array.isArray(arg.value.fields)) {
                for (const argField of arg.value.fields) {
                    const op = argField.name.value;
                    const val = !!argField.value.value;
                    if (op === 'create') {
                        shouldMakeCreate = val;
                    }
                    else if (op === 'update') {
                        shouldMakeUpdate = val;
                    }
                    else if (op === 'delete') {
                        shouldMakeDelete = val;
                    }
                }
            }
            break;
        }
    }
    const hasIdField = object.fields.some((f) => f.name.value === 'id');
    if (!hasIdField) {
        const createInput = ctx.output.getType(graphql_transformer_common_1.ModelResourceIDs.ModelCreateInputObjectName(object.name.value));
        if (createInput && shouldMakeCreate) {
            ctx.output.putType(replaceCreateInput(createInput));
        }
    }
    const updateInput = ctx.output.getType(graphql_transformer_common_1.ModelResourceIDs.ModelUpdateInputObjectName(object.name.value));
    if (updateInput && shouldMakeUpdate) {
        ctx.output.putType(replaceUpdateInput(config, updateInput));
    }
    const deleteInput = ctx.output.getType(graphql_transformer_common_1.ModelResourceIDs.ModelDeleteInputObjectName(object.name.value));
    if (deleteInput && shouldMakeDelete) {
        ctx.output.putType(replaceDeleteInput(config, deleteInput));
    }
}
exports.updateInputObjects = updateInputObjects;
function updateMutationConditionInput(config, ctx) {
    const { field, sortKeyFields, object } = config;
    const tableXMutationConditionInputName = graphql_transformer_common_1.ModelResourceIDs.ModelConditionInputTypeName(object.name.value);
    const tableXMutationConditionInput = ctx.output.getType(tableXMutationConditionInputName);
    if (!tableXMutationConditionInput) {
        return;
    }
    const indexName = config.name;
    const fieldNames = new Set(indexName ? ['id'] : ['id', field.name.value, ...sortKeyFields]);
    const updatedInput = {
        ...tableXMutationConditionInput,
        fields: tableXMutationConditionInput.fields.filter((field) => {
            return !fieldNames.has(field.name.value);
        }),
    };
    ctx.output.putType(updatedInput);
}
exports.updateMutationConditionInput = updateMutationConditionInput;
function createHashField(config) {
    const { field } = config;
    const type = 'queryField' in config ? (0, graphql_transformer_common_1.makeNonNullType)((0, graphql_transformer_common_1.makeNamedType)((0, graphql_transformer_common_1.getBaseType)(field.type))) : (0, graphql_transformer_common_1.makeNamedType)((0, graphql_transformer_common_1.getBaseType)(field.type));
    return (0, graphql_transformer_common_1.makeInputValueDefinition)(field.name.value, type);
}
exports.createHashField = createHashField;
function createSimpleSortField(config, ctx) {
    const { sortKey } = config;
    if (sortKey.length !== 1) {
        throw new Error(`Expected Sort key length to be 1, received list of length ${sortKey.length}`);
    }
    const key = sortKey[0];
    const baseType = (0, graphql_transformer_common_1.getBaseType)(key.type);
    const resolvedTypeIfEnum = ctx.output.getType(baseType) ? 'String' : undefined;
    const resolvedType = resolvedTypeIfEnum !== null && resolvedTypeIfEnum !== void 0 ? resolvedTypeIfEnum : baseType;
    return (0, graphql_transformer_common_1.makeInputValueDefinition)(key.name.value, (0, graphql_transformer_common_1.makeNamedType)(graphql_transformer_common_1.ModelResourceIDs.ModelKeyConditionInputTypeName(resolvedType)));
}
function createCompositeSortField(config, ctx) {
    const { object, sortKeyFields } = config;
    if (sortKeyFields.length <= 1) {
        throw new Error(`Expected Sort key length to be greater than 1, received ${sortKeyFields.length}`);
    }
    const compositeSortKeyName = (0, graphql_transformer_common_1.toCamelCase)(sortKeyFields);
    const indexKeyName = config.name;
    const keyName = (0, graphql_transformer_common_1.toUpper)(indexKeyName !== null && indexKeyName !== void 0 ? indexKeyName : 'Primary');
    return (0, graphql_transformer_common_1.makeInputValueDefinition)(compositeSortKeyName, (0, graphql_transformer_common_1.makeNamedType)(graphql_transformer_common_1.ModelResourceIDs.ModelCompositeKeyConditionInputTypeName(object.name.value, keyName)));
}
const tryAndCreateSortField = (config, ctx) => {
    switch (config.sortKey.length) {
        case 0:
            return null;
        case 1:
            return createSimpleSortField(config, ctx);
        default:
            return createCompositeSortField(config, ctx);
    }
};
exports.tryAndCreateSortField = tryAndCreateSortField;
function replaceCreateInput(input) {
    return { ...input, fields: input.fields.filter((f) => f.name.value !== 'id') };
}
function replaceUpdateInput(config, input) {
    const { field, object, sortKey } = config;
    const schemaHasIdField = object.fields.some((f) => f.name.value === 'id');
    const keyFields = [field, ...sortKey];
    const inputFields = input.fields.filter((f) => {
        if (!schemaHasIdField && f.name.value === 'id') {
            return false;
        }
        return true;
    });
    return {
        ...input,
        fields: inputFields.map((f) => {
            if (keyFields.find((k) => k.name.value === f.name.value)) {
                return (0, graphql_transformer_common_1.makeInputValueDefinition)(f.name.value, (0, graphql_transformer_common_1.wrapNonNull)((0, graphql_transformer_common_1.withNamedNodeNamed)(f.type, (0, graphql_transformer_common_1.getBaseType)(f.type))));
            }
            if (f.name.value === 'id') {
                return (0, graphql_transformer_common_1.makeInputValueDefinition)(f.name.value, (0, graphql_transformer_common_1.unwrapNonNull)((0, graphql_transformer_common_1.withNamedNodeNamed)(f.type, (0, graphql_transformer_common_1.getBaseType)(f.type))));
            }
            return f;
        }),
    };
}
function replaceDeleteInput(config, input) {
    const { field, sortKey } = config;
    const primaryKeyFields = [field, ...sortKey].map((keyField) => {
        return (0, graphql_transformer_common_1.makeInputValueDefinition)(keyField.name.value, (0, graphql_transformer_common_1.makeNonNullType)((0, graphql_transformer_common_1.makeNamedType)((0, graphql_transformer_common_1.getBaseType)(keyField.type))));
    });
    const existingFields = input.fields.filter((f) => !(primaryKeyFields.some((pf) => pf.name.value === f.name.value) || ((0, graphql_transformer_common_1.getBaseType)(f.type) === 'ID' && f.name.value === 'id')));
    return { ...input, fields: [...primaryKeyFields, ...existingFields] };
}
function ensureQueryField(config, ctx) {
    var _a;
    const { name, object, queryField } = config;
    const hasAuth = (_a = object.directives) === null || _a === void 0 ? void 0 : _a.some((dir) => dir.name.value === 'auth');
    const directives = [];
    if (!queryField) {
        return;
    }
    const keyName = `${object.name.value}:indicies`;
    let indicies;
    if (!ctx.metadata.has(keyName)) {
        indicies = new Set([`${name}:${queryField}`]);
    }
    else {
        indicies = ctx.metadata.get(keyName);
        indicies.add(`${name}:${queryField}`);
    }
    ctx.metadata.set(keyName, indicies);
    const args = [createHashField(config)];
    const sortField = (0, exports.tryAndCreateSortField)(config, ctx);
    if (sortField) {
        args.push(sortField);
    }
    args.push((0, graphql_transformer_common_1.makeInputValueDefinition)('sortDirection', (0, graphql_transformer_common_1.makeNamedType)('ModelSortDirection')));
    if (!hasAuth) {
        if (ctx.transformParameters.sandboxModeEnabled && ctx.synthParameters.enableIamAccess) {
            directives.push((0, graphql_transformer_common_1.makeDirective)(API_KEY_DIRECTIVE, []));
            directives.push((0, graphql_transformer_common_1.makeDirective)(AWS_IAM_DIRECTIVE, []));
        }
        else if (ctx.transformParameters.sandboxModeEnabled && ctx.authConfig.defaultAuthentication.authenticationType !== 'API_KEY') {
            directives.push((0, graphql_transformer_common_1.makeDirective)(API_KEY_DIRECTIVE, []));
        }
        else if (ctx.synthParameters.enableIamAccess && ctx.authConfig.defaultAuthentication.authenticationType !== 'AWS_IAM') {
            directives.push((0, graphql_transformer_common_1.makeDirective)(AWS_IAM_DIRECTIVE, []));
        }
    }
    const queryFieldObj = (0, graphql_transformer_common_1.makeConnectionField)(queryField, object.name.value, args, directives);
    ctx.output.addQueryFields([queryFieldObj]);
    ensureModelSortDirectionEnum(ctx);
    generateFilterInputs(config, ctx);
    generateModelXConnectionType(config, ctx);
}
exports.ensureQueryField = ensureQueryField;
function generateModelXConnectionType(config, ctx) {
    const { object } = config;
    const tableXConnectionName = graphql_transformer_common_1.ModelResourceIDs.ModelConnectionTypeName(object.name.value);
    if (ctx.output.hasType(tableXConnectionName)) {
        return;
    }
    const connectionType = (0, graphql_transformer_common_1.blankObject)(tableXConnectionName);
    let connectionTypeExtension = (0, graphql_transformer_common_1.blankObjectExtension)(tableXConnectionName);
    connectionTypeExtension = (0, graphql_transformer_common_1.extensionWithFields)(connectionTypeExtension, [
        (0, graphql_transformer_common_1.makeField)('items', [], (0, graphql_transformer_common_1.makeNonNullType)((0, graphql_transformer_common_1.makeListType)((0, graphql_transformer_common_1.makeNamedType)(object.name.value)))),
    ]);
    connectionTypeExtension = (0, graphql_transformer_common_1.extensionWithFields)(connectionTypeExtension, [(0, graphql_transformer_common_1.makeField)('nextToken', [], (0, graphql_transformer_common_1.makeNamedType)('String'))]);
    ctx.output.addObject(connectionType);
    ctx.output.addObjectExtension(connectionTypeExtension);
}
function ensureModelSortDirectionEnum(ctx) {
    if (!ctx.output.hasType('ModelSortDirection')) {
        const modelSortDirection = (0, graphql_model_transformer_1.makeModelSortDirectionEnumObject)();
        ctx.output.addEnum(modelSortDirection);
    }
}
exports.ensureModelSortDirectionEnum = ensureModelSortDirectionEnum;
function generateFilterInputs(config, ctx) {
    const tableXQueryFilterInput = makeModelXFilterInputObject(config, ctx);
    if (!ctx.output.hasType(tableXQueryFilterInput.name.value)) {
        ctx.output.addInput(tableXQueryFilterInput);
    }
}
function makeModelXFilterInputObject(config, ctx) {
    const supportsConditions = true;
    const { object } = config;
    const name = graphql_transformer_common_1.ModelResourceIDs.ModelFilterInputTypeName(object.name.value);
    const fields = object
        .fields.filter((field) => {
        const fieldType = ctx.output.getType((0, graphql_transformer_common_1.getBaseType)(field.type));
        return (0, graphql_transformer_common_1.isScalar)(field.type) || (fieldType && fieldType.kind === graphql_1.Kind.ENUM_TYPE_DEFINITION);
    })
        .map((field) => {
        const baseType = (0, graphql_transformer_common_1.getBaseType)(field.type);
        const fieldType = ctx.output.getType(baseType);
        const isList = (0, graphql_transformer_common_1.isListType)(field.type);
        const isEnumType = fieldType && fieldType.kind === graphql_1.Kind.ENUM_TYPE_DEFINITION;
        const filterTypeName = isEnumType && isList
            ? graphql_transformer_common_1.ModelResourceIDs.ModelFilterListInputTypeName(baseType, !supportsConditions)
            : graphql_transformer_common_1.ModelResourceIDs.ModelScalarFilterInputTypeName(baseType, !supportsConditions);
        return {
            kind: graphql_1.Kind.INPUT_VALUE_DEFINITION,
            name: field.name,
            type: (0, graphql_transformer_common_1.makeNamedType)(filterTypeName),
            directives: [],
        };
    });
    fields.push({
        kind: graphql_1.Kind.INPUT_VALUE_DEFINITION,
        name: {
            kind: 'Name',
            value: 'and',
        },
        type: (0, graphql_transformer_common_1.makeListType)((0, graphql_transformer_common_1.makeNamedType)(name)),
        directives: [],
    }, {
        kind: graphql_1.Kind.INPUT_VALUE_DEFINITION,
        name: {
            kind: 'Name',
            value: 'or',
        },
        type: (0, graphql_transformer_common_1.makeListType)((0, graphql_transformer_common_1.makeNamedType)(name)),
        directives: [],
    }, {
        kind: graphql_1.Kind.INPUT_VALUE_DEFINITION,
        name: {
            kind: 'Name',
            value: 'not',
        },
        type: (0, graphql_transformer_common_1.makeNamedType)(name),
        directives: [],
    });
    return {
        kind: 'InputObjectTypeDefinition',
        name: {
            kind: 'Name',
            value: name,
        },
        fields,
        directives: [],
    };
}
//# sourceMappingURL=schema.js.map