"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SearchableModelTransformer = void 0;
const path = require("path");
const graphql_transformer_core_1 = require("graphql-transformer-core");
const graphql_directives_1 = require("@aws-amplify/graphql-directives");
const graphql_1 = require("graphql");
const graphql_transformer_common_1 = require("graphql-transformer-common");
const graphql_mapping_template_1 = require("graphql-mapping-template");
const definitions_1 = require("./definitions");
const resources_1 = require("./resources");
const STACK_NAME = 'SearchableStack';
const nonKeywordTypes = ['Int', 'Float', 'Boolean', 'AWSTimestamp', 'AWSDate', 'AWSDateTime'];
class SearchableModelTransformer extends graphql_transformer_core_1.Transformer {
    constructor() {
        super(`SearchableModelTransformer`, (0, graphql_1.parse)(graphql_directives_1.SearchableDirectiveV1.definition));
        this.before = (ctx) => {
            const template = this.resources.initTemplate(ctx.isProjectUsingDataStore());
            ctx.mergeResources(template.Resources);
            ctx.mergeParameters(template.Parameters);
            ctx.mergeOutputs(template.Outputs);
            ctx.mergeMappings(template.Mappings);
            ctx.metadata.set(graphql_transformer_common_1.ResourceConstants.RESOURCES.ElasticsearchStreamingLambdaFunctionLogicalID, path.resolve(`${__dirname}/../lib/streaming-lambda.zip`));
            for (const resourceId of Object.keys(template.Resources)) {
                ctx.mapResourceToStack(STACK_NAME, resourceId);
            }
            for (const outputId of Object.keys(template.Outputs)) {
                ctx.mapResourceToStack(STACK_NAME, outputId);
            }
            for (const mappingId of Object.keys(template.Mappings)) {
                ctx.mapResourceToStack(STACK_NAME, mappingId);
            }
        };
        this.object = (def, directive, ctx) => {
            const modelDirective = def.directives.find((dir) => dir.name.value === 'model');
            if (!modelDirective) {
                throw new graphql_transformer_core_1.InvalidDirectiveError('Types annotated with @searchable must also be annotated with @model.');
            }
            const directiveArguments = (0, graphql_transformer_core_1.getDirectiveArguments)(directive);
            let shouldMakeSearch = true;
            let searchFieldNameOverride = undefined;
            if (directiveArguments.queries) {
                if (!directiveArguments.queries.search) {
                    shouldMakeSearch = false;
                }
                else {
                    searchFieldNameOverride = directiveArguments.queries.search;
                }
            }
            const typeName = def.name.value;
            ctx.setResource(graphql_transformer_common_1.SearchableResourceIDs.SearchableEventSourceMappingID(typeName), this.resources.makeDynamoDBStreamEventSourceMapping(typeName));
            ctx.mapResourceToStack(STACK_NAME, graphql_transformer_common_1.SearchableResourceIDs.SearchableEventSourceMappingID(typeName));
            const queryFields = [];
            const nonKeywordFields = [];
            def.fields.forEach((field) => {
                if (nonKeywordTypes.includes((0, graphql_transformer_common_1.getBaseType)(field.type))) {
                    nonKeywordFields.push((0, graphql_mapping_template_1.str)(field.name.value));
                }
            });
            const primaryKey = this.getPrimaryKey(ctx, typeName);
            if (shouldMakeSearch) {
                this.generateSearchableInputs(ctx, def);
                this.generateSearchableXConnectionType(ctx, def);
                const searchResolver = this.resources.makeSearchResolver(def.name.value, nonKeywordFields, primaryKey, ctx.getQueryTypeName(), ctx.featureFlags.getBoolean('improvePluralization'), searchFieldNameOverride, ctx.isProjectUsingDataStore());
                ctx.setResource(graphql_transformer_common_1.ResolverResourceIDs.ElasticsearchSearchResolverResourceID(def.name.value), searchResolver);
                ctx.mapResourceToStack(STACK_NAME, graphql_transformer_common_1.ResolverResourceIDs.ElasticsearchSearchResolverResourceID(def.name.value));
                queryFields.push((0, graphql_transformer_common_1.makeField)(searchResolver.Properties.FieldName.toString(), [
                    (0, graphql_transformer_common_1.makeInputValueDefinition)('filter', (0, graphql_transformer_common_1.makeNamedType)(`Searchable${def.name.value}FilterInput`)),
                    (0, graphql_transformer_common_1.makeInputValueDefinition)('sort', (0, graphql_transformer_common_1.makeNamedType)(`Searchable${def.name.value}SortInput`)),
                    (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)('from', (0, graphql_transformer_common_1.makeNamedType)('Int')),
                ], (0, graphql_transformer_common_1.makeNamedType)(`Searchable${def.name.value}Connection`)));
            }
            ctx.addQueryFields(queryFields);
        };
        this.resources = new resources_1.ResourceFactory();
    }
    generateSearchableXConnectionType(ctx, def) {
        const searchableXConnectionName = `Searchable${def.name.value}Connection`;
        if (this.typeExist(searchableXConnectionName, ctx)) {
            return;
        }
        const connectionType = (0, graphql_transformer_common_1.blankObject)(searchableXConnectionName);
        ctx.addObject(connectionType);
        let connectionTypeExtension = (0, graphql_transformer_common_1.blankObjectExtension)(searchableXConnectionName);
        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)(def.name.value)))),
        ]);
        connectionTypeExtension = (0, graphql_transformer_common_1.extensionWithFields)(connectionTypeExtension, [
            (0, graphql_transformer_common_1.makeField)('nextToken', [], (0, graphql_transformer_common_1.makeNamedType)('String')),
            (0, graphql_transformer_common_1.makeField)('total', [], (0, graphql_transformer_common_1.makeNamedType)('Int')),
        ]);
        ctx.addObjectExtension(connectionTypeExtension);
    }
    typeExist(type, ctx) {
        return Boolean(type in ctx.nodeMap);
    }
    generateSearchableInputs(ctx, def) {
        const inputs = Object.keys(graphql_transformer_common_1.STANDARD_SCALARS);
        inputs
            .filter((input) => !this.typeExist(`Searchable${input}FilterInput`, ctx))
            .map((input) => (0, definitions_1.makeSearchableScalarInputObject)(input))
            .forEach((node) => ctx.addInput(node));
        const searchableXQueryFilterInput = (0, definitions_1.makeSearchableXFilterInputObject)(def);
        if (!this.typeExist(searchableXQueryFilterInput.name.value, ctx)) {
            ctx.addInput(searchableXQueryFilterInput);
        }
        if (!this.typeExist('SearchableSortDirection', ctx)) {
            const searchableSortDirection = (0, definitions_1.makeSearchableSortDirectionEnumObject)();
            ctx.addEnum(searchableSortDirection);
        }
        if (!this.typeExist(`Searchable${def.name.value}SortableFields`, ctx)) {
            const searchableXSortableFieldsDirection = (0, definitions_1.makeSearchableXSortableFieldsEnumObject)(def);
            ctx.addEnum(searchableXSortableFieldsDirection);
        }
        if (!this.typeExist(`Searchable${def.name.value}SortInput`, ctx)) {
            const searchableXSortableInputDirection = (0, definitions_1.makeSearchableXSortInputObject)(def);
            ctx.addInput(searchableXSortableInputDirection);
        }
    }
    getPrimaryKey(ctx, typeName) {
        const tableResourceID = graphql_transformer_common_1.ModelResourceIDs.ModelTableResourceID(typeName);
        const tableResource = ctx.getResource(tableResourceID);
        const primaryKeySchemaElement = tableResource.Properties.KeySchema.find((keyElement) => keyElement.KeyType === 'HASH');
        return primaryKeySchemaElement.AttributeName;
    }
}
exports.SearchableModelTransformer = SearchableModelTransformer;
//# sourceMappingURL=SearchableModelTransformer.js.map