"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.keyFieldsMissingExternal = void 0;
const graphql_1 = require("graphql");
const apollo_graphql_1 = require("apollo-graphql");
const directives_1 = require("../../../directives");
const utils_1 = require("../../utils");
exports.keyFieldsMissingExternal = ({ name: serviceName, typeDefs, }) => {
    const errors = [];
    let keyDirectiveInfoOnTypeExtensions = [];
    graphql_1.visit(typeDefs, {
        ObjectTypeExtension(node) {
            const keyDirectivesOnTypeExtension = utils_1.findDirectivesOnTypeOrField(node, 'key');
            const keyDirectivesInfo = keyDirectivesOnTypeExtension
                .map(keyDirective => keyDirective.arguments &&
                utils_1.isStringValueNode(keyDirective.arguments[0].value)
                ? {
                    typeName: node.name.value,
                    keyArgument: keyDirective.arguments[0].value.value,
                }
                : null)
                .filter(utils_1.isNotNullOrUndefined);
            keyDirectiveInfoOnTypeExtensions.push(...keyDirectivesInfo);
        },
    });
    let schema = new graphql_1.GraphQLSchema({
        query: undefined,
        directives: [...graphql_1.specifiedDirectives, ...directives_1.federationDirectives],
    });
    try {
        schema = apollo_graphql_1.buildSchemaFromSDL(typeDefs, schema);
    }
    catch (e) {
        errors.push(e);
        return errors;
    }
    const typeInfo = new graphql_1.TypeInfo(schema);
    for (const { typeName, keyArgument } of keyDirectiveInfoOnTypeExtensions) {
        const keyDirectiveSelectionSet = graphql_1.parse(`fragment __generated on ${typeName} { ${keyArgument} }`);
        graphql_1.visit(keyDirectiveSelectionSet, graphql_1.visitWithTypeInfo(typeInfo, {
            Field() {
                const fieldDef = typeInfo.getFieldDef();
                const parentType = typeInfo.getParentType();
                if (parentType) {
                    if (!fieldDef) {
                        errors.push(utils_1.errorWithCode('KEY_FIELDS_MISSING_EXTERNAL', utils_1.logServiceAndType(serviceName, parentType.name) +
                            `A @key directive specifies a field which is not found in this service. Add a field to this type with @external.`));
                        return;
                    }
                    const externalDirectivesOnField = utils_1.findDirectivesOnTypeOrField(fieldDef.astNode, 'external');
                    if (externalDirectivesOnField.length === 0) {
                        errors.push(utils_1.errorWithCode('KEY_FIELDS_MISSING_EXTERNAL', utils_1.logServiceAndType(serviceName, parentType.name) +
                            `A @key directive specifies the \`${fieldDef.name}\` field which has no matching @external field.`));
                    }
                }
            },
        }));
    }
    return errors;
};
//# sourceMappingURL=keyFieldsMissingExternal.js.map