UNPKG

@omnigraph/soap

Version:
957 lines 51.9 kB
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SOAPLoader = void 0;
const fast_xml_parser_1 = require("fast-xml-parser");
const graphql_1 = require("graphql");
const graphql_compose_1 = require("graphql-compose");
const graphql_scalars_1 = require("graphql-scalars");
const cross_helpers_1 = require("@graphql-mesh/cross-helpers");
const string_interpolation_1 = require("@graphql-mesh/string-interpolation");
const utils_1 = require("@graphql-mesh/utils");
const fetch_1 = require("@whatwg-node/fetch");
const utils_js_1 = require("./utils.js");
const soapDirective = new graphql_1.GraphQLDirective({
    name: 'soap',
    locations: [graphql_1.DirectiveLocation.FIELD_DEFINITION],
    args: {
        elementName: {
            type: graphql_1.GraphQLString,
        },
        bindingNamespace: {
            type: graphql_1.GraphQLString,
        },
        endpoint: {
            type: graphql_1.GraphQLString,
        },
        subgraph: {
            type: graphql_1.GraphQLString,
        },
    },
});
const QUERY_PREFIXES = [
    'get',
    'find',
    'list',
    'search',
    'count',
    'exists',
    'fetch',
    'load',
    'query',
    'select',
];
function isQueryOperationName(operationName) {
    return QUERY_PREFIXES.some(prefix => operationName.toLowerCase().startsWith(prefix));
}
class SOAPLoader {
    constructor(options) {
        this.schemaComposer = new graphql_compose_1.SchemaComposer();
        this.namespaceDefinitionsMap = new Map();
        this.namespaceComplexTypesMap = new Map();
        this.namespaceSimpleTypesMap = new Map();
        this.namespacePortTypesMap = new Map();
        this.namespaceBindingMap = new Map();
        this.namespaceMessageMap = new Map();
        this.aliasMap = new WeakMap();
        this.messageOutputTCMap = new WeakMap();
        this.complexTypeInputTCMap = new WeakMap();
        this.complexTypeOutputTCMap = new WeakMap();
        this.simpleTypeTCMap = new WeakMap();
        this.namespaceTypePrefixMap = new Map();
        this.loadedLocations = new Map();
        this.xmlParser = new fast_xml_parser_1.XMLParser(utils_js_1.PARSE_XML_OPTIONS);
        this.fetchFn = options.fetch || fetch_1.fetch;
        this.logger = options.logger || new utils_1.DefaultLogger(options.subgraphName);
        this.subgraphName = options.subgraphName;
        this.loadXMLSchemaNamespace();
        this.schemaComposer.addDirective(soapDirective);
        this.schemaHeadersFactory = (0, string_interpolation_1.getInterpolatedHeadersFactory)(options.schemaHeaders || {});
        this.endpoint = options.endpoint;
        this.cwd = options.cwd;
    }
    loadXMLSchemaNamespace() {
        const namespace = 'http://www.w3.org/2001/XMLSchema';
        const simpleTypeGraphQLScalarMap = new Map([
            ['anyType', graphql_compose_1.GraphQLJSON],
            ['anyURI', graphql_scalars_1.GraphQLURL],
            ['base64Binary', graphql_scalars_1.GraphQLByte],
            ['byte', graphql_scalars_1.GraphQLByte],
            ['boolean', graphql_1.GraphQLBoolean],
            ['date', graphql_scalars_1.GraphQLDate],
            ['dateTime', graphql_scalars_1.GraphQLDateTime],
            ['decimal', graphql_1.GraphQLFloat],
            ['double', graphql_1.GraphQLFloat],
            ['duration', graphql_scalars_1.GraphQLDuration],
            ['float', graphql_1.GraphQLFloat],
            ['int', graphql_1.GraphQLInt],
            ['integer', graphql_1.GraphQLInt],
            ['negativeInteger', graphql_scalars_1.GraphQLNegativeInt],
            ['nonNegativeInteger', graphql_scalars_1.GraphQLNonNegativeInt],
            ['nonPositiveInteger', graphql_scalars_1.GraphQLNonPositiveInt],
            ['positiveInteger', graphql_scalars_1.GraphQLPositiveInt],
            ['hexBinary', graphql_scalars_1.GraphQLHexadecimal],
            ['long', graphql_scalars_1.GraphQLBigInt],
            ['gDay', graphql_1.GraphQLString],
            ['gMonth', graphql_1.GraphQLString],
            ['gMonthDay', graphql_1.GraphQLString],
            ['gYear', graphql_1.GraphQLString],
            ['gYearMonth', graphql_1.GraphQLString],
            ['NOTATION', graphql_1.GraphQLString],
            ['QName', graphql_1.GraphQLString],
            ['short', graphql_1.GraphQLInt],
            ['string', graphql_1.GraphQLString],
            ['unsignedByte', graphql_scalars_1.GraphQLByte],
            ['unsignedInt', graphql_scalars_1.GraphQLUnsignedInt],
            ['unsignedLong', graphql_scalars_1.GraphQLBigInt],
            ['unsignedShort', graphql_scalars_1.GraphQLUnsignedInt],
            ['time', graphql_scalars_1.GraphQLTime],
        ]);
        const namespaceSimpleTypesMap = this.getNamespaceSimpleTypeMap(namespace);
        for (const [singleTypeName, scalarType] of simpleTypeGraphQLScalarMap) {
            const singleType = {
                attributes: {
                    name: singleTypeName,
                },
            };
            namespaceSimpleTypesMap.set(singleTypeName, singleType);
            const simpleTypeTC = this.schemaComposer.createScalarTC(scalarType);
            this.simpleTypeTCMap.set(singleType, simpleTypeTC);
        }
    }
    getNamespaceDefinitions(namespace) {
        let namespaceDefinitions = this.namespaceDefinitionsMap.get(namespace);
        if (!namespaceDefinitions) {
            namespaceDefinitions = [];
            this.namespaceDefinitionsMap.set(namespace, namespaceDefinitions);
        }
        return namespaceDefinitions;
    }
    getNamespaceComplexTypeMap(namespace) {
        let namespaceComplexTypes = this.namespaceComplexTypesMap.get(namespace);
        if (!namespaceComplexTypes) {
            namespaceComplexTypes = new Map();
            this.namespaceComplexTypesMap.set(namespace, namespaceComplexTypes);
        }
        return namespaceComplexTypes;
    }
    getNamespaceSimpleTypeMap(namespace) {
        let namespaceSimpleTypes = this.namespaceSimpleTypesMap.get(namespace);
        if (!namespaceSimpleTypes) {
            namespaceSimpleTypes = new Map();
            this.namespaceSimpleTypesMap.set(namespace, namespaceSimpleTypes);
        }
        return namespaceSimpleTypes;
    }
    getNamespacePortTypeMap(namespace) {
        let namespacePortTypes = this.namespacePortTypesMap.get(namespace);
        if (!namespacePortTypes) {
            namespacePortTypes = new Map();
            this.namespacePortTypesMap.set(namespace, namespacePortTypes);
        }
        return namespacePortTypes;
    }
    getNamespaceBindingMap(namespace) {
        let namespaceBindingMap = this.namespaceBindingMap.get(namespace);
        if (!namespaceBindingMap) {
            namespaceBindingMap = new Map();
            this.namespaceBindingMap.set(namespace, namespaceBindingMap);
        }
        return namespaceBindingMap;
    }
    getNamespaceMessageMap(namespace) {
        let namespaceMessageMap = this.namespaceMessageMap.get(namespace);
        if (!namespaceMessageMap) {
            namespaceMessageMap = new Map();
            this.namespaceMessageMap.set(namespace, namespaceMessageMap);
        }
        return namespaceMessageMap;
    }
    async loadSchema(schemaObj, parentAliasMap = new Map()) {
        const schemaNamespace = schemaObj.attributes.targetNamespace;
        const aliasMap = this.getAliasMapFromAttributes(schemaObj.attributes);
        let typePrefix = this.namespaceTypePrefixMap.get(schemaNamespace);
        if (!typePrefix) {
            typePrefix =
                schemaObj.attributes.id ||
                    [...aliasMap.entries()].find(([, namespace]) => namespace === schemaNamespace)?.[0];
            this.namespaceTypePrefixMap.set(schemaNamespace, typePrefix);
        }
        for (const [alias, namespace] of parentAliasMap) {
            if (!aliasMap.has(alias)) {
                aliasMap.set(alias, namespace);
            }
        }
        if (schemaObj.import) {
            for (const importObj of schemaObj.import) {
                const importLocation = importObj.attributes.schemaLocation;
                if (importLocation && !this.loadedLocations.has(importLocation)) {
                    await this.fetchXSD(importLocation);
                }
            }
        }
        // Complex and simple types can be inside element tag or outside of it
        if (schemaObj.complexType) {
            const namespaceComplexTypes = this.getNamespaceComplexTypeMap(schemaNamespace);
            for (const complexType of schemaObj.complexType) {
                namespaceComplexTypes.set(complexType.attributes.name, complexType);
                this.aliasMap.set(complexType, aliasMap);
            }
        }
        if (schemaObj.simpleType) {
            const namespaceSimpleTypes = this.getNamespaceSimpleTypeMap(schemaNamespace);
            for (const simpleType of schemaObj.simpleType) {
                namespaceSimpleTypes.set(simpleType.attributes.name, simpleType);
                this.aliasMap.set(simpleType, aliasMap);
            }
        }
        if (schemaObj.element) {
            for (const elementObj of schemaObj.element) {
                if (elementObj.complexType) {
                    const namespaceComplexTypes = this.getNamespaceComplexTypeMap(schemaNamespace);
                    for (const complexType of elementObj.complexType) {
                        // Sometimes type name is defined on element object
                        complexType.attributes = complexType.attributes || {};
                        complexType.attributes.name = elementObj.attributes.name;
                        namespaceComplexTypes.set(complexType.attributes.name, complexType);
                        this.aliasMap.set(complexType, aliasMap);
                    }
                }
                if (elementObj.simpleType) {
                    const namespaceSimpleTypes = this.getNamespaceSimpleTypeMap(schemaNamespace);
                    for (const simpleType of elementObj.simpleType) {
                        simpleType.attributes = simpleType.attributes || {};
                        simpleType.attributes.name = elementObj.attributes.name;
                        namespaceSimpleTypes.set(simpleType.attributes.name, simpleType);
                        this.aliasMap.set(simpleType, aliasMap);
                    }
                }
                if (elementObj.attributes?.type) {
                    const [refTypeNamespaceAlias, refTypeName] = elementObj.attributes.type.split(':');
                    const refTypeNamespace = aliasMap.get(refTypeNamespaceAlias);
                    if (!refTypeNamespace) {
                        throw new Error(`Invalid namespace alias: ${refTypeNamespaceAlias}`);
                    }
                    const refComplexType = this.getNamespaceComplexTypeMap(refTypeNamespace).get(refTypeName);
                    if (refComplexType) {
                        this.getNamespaceComplexTypeMap(schemaNamespace).set(elementObj.attributes.name, refComplexType);
                    }
                    const refSimpleType = this.getNamespaceSimpleTypeMap(refTypeNamespace).get(refTypeName);
                    if (refSimpleType) {
                        this.getNamespaceSimpleTypeMap(schemaNamespace).set(elementObj.attributes.name, refSimpleType);
                    }
                }
            }
        }
    }
    async loadDefinition(definition) {
        this.getNamespaceDefinitions(definition.attributes.targetNamespace).push(definition);
        const definitionAliasMap = this.getAliasMapFromAttributes(definition.attributes);
        const definitionNamespace = definition.attributes.targetNamespace;
        const typePrefix = definition.attributes.name ||
            [...definitionAliasMap.entries()].find(([, namespace]) => namespace === definitionNamespace)?.[0] ||
            '';
        this.namespaceTypePrefixMap.set(definition.attributes.targetNamespace, typePrefix);
        if (definition.import) {
            for (const importObj of definition.import) {
                const importLocation = importObj.attributes.location;
                if (importLocation && !this.loadedLocations.has(importLocation)) {
                    await this.fetchWSDL(importLocation);
                }
            }
        }
        if (definition.types) {
            for (const typesObj of definition.types) {
                for (const schemaObj of typesObj.schema) {
                    await this.loadSchema(schemaObj, definitionAliasMap);
                }
            }
        }
        if (definition.portType) {
            const namespacePortTypes = this.getNamespacePortTypeMap(definition.attributes.targetNamespace);
            for (const portTypeObj of definition.portType) {
                namespacePortTypes.set(portTypeObj.attributes.name, portTypeObj);
                this.aliasMap.set(portTypeObj, definitionAliasMap);
            }
        }
        if (definition.binding) {
            const namespaceBindingMap = this.getNamespaceBindingMap(definition.attributes.targetNamespace);
            for (const bindingObj of definition.binding) {
                namespaceBindingMap.set(bindingObj.attributes.name, bindingObj);
                this.aliasMap.set(bindingObj, definitionAliasMap);
            }
        }
        if (definition.message) {
            const namespaceMessageMap = this.getNamespaceMessageMap(definition.attributes.targetNamespace);
            for (const messageObj of definition.message) {
                namespaceMessageMap.set(messageObj.attributes.name, messageObj);
                this.aliasMap.set(messageObj, definitionAliasMap);
            }
        }
        const serviceAndPortAliasMap = this.getAliasMapFromAttributes(definition.attributes);
        if (definition.service) {
            for (const serviceObj of definition.service) {
                const serviceName = serviceObj.attributes.name;
                for (const portObj of serviceObj.port) {
                    const portName = portObj.attributes.name;
                    const [bindingNamespaceAlias, bindingName] = portObj.attributes.binding.split(':');
                    const bindingNamespace = serviceAndPortAliasMap.get(bindingNamespaceAlias);
                    if (!bindingNamespace) {
                        throw new Error(`Namespace alias: ${bindingNamespaceAlias} is undefined!`);
                    }
                    const bindingObj = this.getNamespaceBindingMap(bindingNamespace).get(bindingName);
                    if (!bindingObj) {
                        throw new Error(`Binding: ${bindingName} is not defined in ${bindingNamespace} needed for ${serviceName}->${portName}`);
                    }
                    const bindingAliasMap = this.aliasMap.get(bindingObj);
                    if (!bindingAliasMap) {
                        throw new Error(`Namespace alias definitions couldn't be found for ${bindingName}`);
                    }
                    const [portTypeNamespaceAlias, portTypeName] = bindingObj.attributes.type.split(':');
                    const portTypeNamespace = bindingAliasMap.get(portTypeNamespaceAlias);
                    if (!portTypeNamespace) {
                        throw new Error(`Namespace alias: ${portTypeNamespaceAlias} is undefined!`);
                    }
                    const portTypeObj = this.getNamespacePortTypeMap(portTypeNamespace).get(portTypeName);
                    if (!portTypeObj) {
                        throw new Error(`Port Type: ${portTypeName} is not defined in ${portTypeNamespace} needed for ${bindingNamespaceAlias}->${bindingName}`);
                    }
                    const portTypeAliasMap = this.aliasMap.get(portTypeObj);
                    for (const operationObj of portTypeObj.operation) {
                        const operationName = operationObj.attributes.name;
                        const rootTC = isQueryOperationName(operationName)
                            ? this.schemaComposer.Query
                            : this.schemaComposer.Mutation;
                        const operationFieldName = (0, utils_1.sanitizeNameForGraphQL)(`${typePrefix}_${serviceName}_${portName}_${operationName}`);
                        const outputObj = operationObj.output[0];
                        const [messageNamespaceAlias, messageName] = outputObj.attributes.message.split(':');
                        const messageNamespace = portTypeAliasMap.get(messageNamespaceAlias);
                        if (!messageNamespace) {
                            throw new Error(`Namespace alias: ${messageNamespaceAlias} is undefined!`);
                        }
                        const { type, elementName } = this.getOutputTypeForMessage(this.getNamespaceMessageMap(messageNamespace).get(messageName));
                        const soapAnnotations = {
                            elementName,
                            bindingNamespace,
                            endpoint: this.endpoint || portObj.address[0].attributes.location,
                            subgraph: this.subgraphName,
                        };
                        rootTC.addFields({
                            [operationFieldName]: {
                                type,
                                directives: [
                                    {
                                        name: 'soap',
                                        args: soapAnnotations,
                                    },
                                ],
                            },
                        });
                        const inputObj = operationObj.input[0];
                        const [inputMessageNamespaceAlias, inputMessageName] = inputObj.attributes.message.split(':');
                        const inputMessageNamespace = portTypeAliasMap.get(inputMessageNamespaceAlias);
                        if (!inputMessageNamespace) {
                            throw new Error(`Namespace alias: ${inputMessageNamespaceAlias} is undefined!`);
                        }
                        const inputMessageObj = this.getNamespaceMessageMap(inputMessageNamespace).get(inputMessageName);
                        if (!inputMessageObj) {
                            throw new Error(`Message: ${inputMessageName} is not defined in ${inputMessageNamespace} needed for ${portTypeName}->${operationName}`);
                        }
                        const aliasMap = this.aliasMap.get(inputMessageObj);
                        for (const part of inputMessageObj.part) {
                            if (part.attributes.element) {
                                const [elementNamespaceAlias, elementName] = part.attributes.element.split(':');
                                rootTC.addFieldArgs(operationFieldName, {
                                    [elementName]: {
                                        type: () => {
                                            const elementNamespace = aliasMap.get(elementNamespaceAlias) ||
                                                part.attributes[elementNamespaceAlias];
                                            if (!elementNamespace) {
                                                throw new Error(`Namespace alias: ${elementNamespaceAlias} is not defined.`);
                                            }
                                            return this.getInputTypeForTypeNameInNamespace({
                                                typeName: elementName,
                                                typeNamespace: elementNamespace,
                                            });
                                        },
                                        defaultValue: '',
                                    },
                                });
                            }
                            else if (part.attributes.name) {
                                const partName = part.attributes.name;
                                rootTC.addFieldArgs(operationFieldName, {
                                    [partName]: {
                                        type: () => {
                                            const typeRef = part.attributes.type;
                                            const [typeNamespaceAlias, typeName] = typeRef.split(':');
                                            const typeNamespace = aliasMap.get(typeNamespaceAlias);
                                            if (!typeNamespace) {
                                                throw new Error(`Namespace alias: ${typeNamespaceAlias} is undefined!`);
                                            }
                                            const inputTC = this.getInputTypeForTypeNameInNamespace({
                                                typeName,
                                                typeNamespace,
                                            });
                                            if ('getFields' in inputTC && Object.keys(inputTC.getFields()).length === 0) {
                                                return graphql_compose_1.GraphQLJSON;
                                            }
                                            return inputTC;
                                        },
                                        defaultValue: '',
                                    },
                                });
                            }
                        }
                    }
                }
            }
        }
    }
    async fetchXSD(location, parentAliasMap = new Map()) {
        let xsdText = await (0, utils_1.readFileOrUrl)(location, {
            allowUnknownExtensions: true,
            cwd: this.cwd,
            fetch: this.fetchFn,
            importFn: utils_1.defaultImportFn,
            logger: this.logger,
            headers: this.schemaHeadersFactory({ env: cross_helpers_1.process.env }),
        });
        xsdText = xsdText.split('xmlns:').join('namespace:');
        // WSDL Import is different than XS Import
        const xsdObj = this.xmlParser.parse(xsdText, utils_js_1.PARSE_XML_OPTIONS);
        for (const schemaObj of xsdObj.schema) {
            await this.loadSchema(schemaObj, parentAliasMap);
        }
        this.loadedLocations.set(location, xsdObj);
    }
    async loadWSDL(wsdlText) {
        wsdlText = wsdlText.split('xmlns:').join('namespace:');
        let wsdlObject;
        try {
            wsdlObject = this.xmlParser.parse(wsdlText, utils_js_1.PARSE_XML_OPTIONS);
        }
        catch (e) {
            throw new Error(`Failed to parse WSDL: ${e.message}. \nReturned response;\n${wsdlText}`);
        }
        if (!Array.isArray(wsdlObject.definitions)) {
            throw new Error(`WSDL definitions not found! Please make sure if your WSDL source is correct, and it contains <definitions> tag.\nReturned response;\n${wsdlText}`);
        }
        for (const definition of wsdlObject.definitions) {
            await this.loadDefinition(definition);
        }
        return wsdlObject;
    }
    async fetchWSDL(location) {
        const response = await this.fetchFn(location, {
            headers: this.schemaHeadersFactory({ env: cross_helpers_1.process.env }),
        });
        const wsdlText = await response.text();
        const wsdlObject = await this.loadWSDL(wsdlText);
        this.loadedLocations.set(location, wsdlObject);
    }
    getAliasMapFromAttributes(attributes) {
        const aliasMap = new Map();
        for (const attributeName in attributes) {
            const attributeValue = attributes[attributeName];
            if (attributeName !== 'targetNamespace') {
                aliasMap.set(attributeName, attributeValue);
            }
        }
        return aliasMap;
    }
    getTypeForSimpleType(simpleType, simpleTypeNamespace) {
        let simpleTypeTC = this.simpleTypeTCMap.get(simpleType);
        if (!simpleTypeTC) {
            const simpleTypeName = simpleType.attributes.name;
            const restrictionObj = simpleType.restriction[0];
            const prefix = this.namespaceTypePrefixMap.get(simpleTypeNamespace);
            if (restrictionObj.enumeration) {
                const enumTypeName = `${prefix}_${simpleTypeName}`;
                const values = {};
                for (const enumerationObj of restrictionObj.enumeration) {
                    const enumValue = enumerationObj.attributes.value;
                    const enumKey = (0, utils_1.sanitizeNameForGraphQL)(enumValue);
                    values[enumKey] = {
                        value: enumValue,
                    };
                }
                simpleTypeTC = this.schemaComposer.createEnumTC({
                    name: enumTypeName,
                    values,
                });
            }
            else if (restrictionObj.pattern) {
                const patternObj = restrictionObj.pattern[0];
                const pattern = patternObj.attributes.value;
                const scalarTypeName = `${prefix}_${simpleTypeName}`;
                simpleTypeTC = this.schemaComposer.createScalarTC(new graphql_scalars_1.RegularExpression(scalarTypeName, new RegExp(pattern)));
            }
            else {
                // TODO: Other restrictions are not supported yet
                const aliasMap = this.aliasMap.get(simpleType);
                const [baseTypeNamespaceAlias, baseTypeName] = restrictionObj.attributes.base.split(':');
                const baseTypeNamespace = aliasMap.get(baseTypeNamespaceAlias);
                if (!baseTypeNamespace) {
                    throw new Error(`Invalid base type namespace: ${baseTypeNamespaceAlias}`);
                }
                const baseType = this.getNamespaceSimpleTypeMap(baseTypeNamespace)?.get(baseTypeName);
                if (!baseType) {
                    throw new Error(`Simple Type: ${baseTypeName} couldn't be found in ${baseTypeNamespace} needed for ${simpleTypeName}`);
                }
                simpleTypeTC = this.getTypeForSimpleType(baseType, baseTypeNamespace);
            }
            this.simpleTypeTCMap.set(simpleType, simpleTypeTC);
        }
        return simpleTypeTC;
    }
    getInputTypeForTypeNameInNamespace({ typeName, typeNamespace, }) {
        const complexType = this.getNamespaceComplexTypeMap(typeNamespace)?.get(typeName);
        if (complexType) {
            return this.getInputTypeForComplexType(complexType, typeNamespace);
        }
        const simpleType = this.getNamespaceSimpleTypeMap(typeNamespace)?.get(typeName);
        if (simpleType) {
            return this.getTypeForSimpleType(simpleType, typeNamespace);
        }
        throw new Error(`Type: ${typeName} couldn't be found in ${typeNamespace}`);
    }
    getInputTypeForComplexType(complexType, complexTypeNamespace) {
        let complexTypeTC = this.complexTypeInputTCMap.get(complexType);
        if (!complexTypeTC) {
            const complexTypeName = complexType.attributes.name;
            const prefix = this.namespaceTypePrefixMap.get(complexTypeNamespace);
            const aliasMap = this.aliasMap.get(complexType);
            const fieldMap = {};
            const choiceOrSequenceObjects = [
                ...(complexType.sequence || []),
                ...(complexType.choice || []),
            ];
            for (const sequenceOrChoiceObj of choiceOrSequenceObjects) {
                if (sequenceOrChoiceObj.element) {
                    for (const elementObj of sequenceOrChoiceObj.element) {
                        const fieldName = elementObj.attributes.name;
                        if (fieldName) {
                            fieldMap[fieldName] = {
                                type: () => {
                                    const maxOccurs = sequenceOrChoiceObj.attributes?.maxOccurs || elementObj.attributes?.maxOccurs;
                                    const minOccurs = sequenceOrChoiceObj.attributes?.minOccurs || elementObj.attributes?.minOccurs;
                                    const nillable = sequenceOrChoiceObj.attributes?.nillable || elementObj.attributes?.nillable;
                                    const isPlural = maxOccurs != null && maxOccurs !== '1';
                                    let isNullable = false;
                                    if (minOccurs == null || minOccurs === '0') {
                                        isNullable = true;
                                    }
                                    if (nillable === 'true') {
                                        isNullable = true;
                                    }
                                    if (nillable === 'false') {
                                        isNullable = false;
                                    }
                                    if (elementObj.attributes?.type) {
                                        const [typeNamespaceAlias, typeName] = elementObj.attributes.type.split(':');
                                        let typeNamespace;
                                        if (elementObj.attributes[typeNamespaceAlias]) {
                                            typeNamespace = elementObj.attributes[typeNamespaceAlias];
                                        }
                                        else {
                                            typeNamespace = aliasMap.get(typeNamespaceAlias);
                                        }
                                        if (!typeNamespace) {
                                            throw new Error(`Namespace alias: ${typeNamespaceAlias} is undefined!`);
                                        }
                                        let finalTC = this.getInputTypeForTypeNameInNamespace({
                                            typeName,
                                            typeNamespace,
                                        });
                                        if (isPlural) {
                                            finalTC = finalTC.getTypePlural();
                                        }
                                        if (!isNullable) {
                                            finalTC = finalTC.getTypeNonNull();
                                        }
                                        return finalTC;
                                    }
                                    else if (elementObj.simpleType) {
                                        // eslint-disable-next-line no-unreachable-loop
                                        for (const simpleTypeObj of elementObj.simpleType) {
                                            // Dynamically defined simple type
                                            // So we need to define alias map for this type
                                            this.aliasMap.set(simpleTypeObj, aliasMap);
                                            // Inherit the name from elementObj
                                            simpleTypeObj.attributes = simpleTypeObj.attributes || {};
                                            simpleTypeObj.attributes.name =
                                                simpleTypeObj.attributes.name || elementObj.attributes.name;
                                            let finalTC = this.getTypeForSimpleType(simpleTypeObj, complexTypeNamespace);
                                            if (isPlural) {
                                                finalTC = finalTC.getTypePlural();
                                            }
                                            if (!isNullable) {
                                                finalTC = finalTC.getTypeNonNull();
                                            }
                                            return finalTC;
                                        }
                                    }
                                    else if (elementObj.complexType) {
                                        // eslint-disable-next-line no-unreachable-loop
                                        for (const complexTypeObj of elementObj.complexType) {
                                            // Dynamically defined type
                                            // So we need to define alias map for this type
                                            this.aliasMap.set(complexTypeObj, aliasMap);
                                            // Inherit the name from elementObj
                                            complexTypeObj.attributes = complexTypeObj.attributes || {};
                                            complexTypeObj.attributes.name =
                                                complexTypeObj.attributes.name || elementObj.attributes.name;
                                            let finalTC = this.getInputTypeForComplexType(complexTypeObj, complexTypeNamespace);
                                            if (isPlural) {
                                                finalTC = finalTC.getTypePlural();
                                            }
                                            if (!isNullable) {
                                                finalTC = finalTC.getTypeNonNull();
                                            }
                                            return finalTC;
                                        }
                                    }
                                    throw new Error(`Invalid element type definition: ${complexTypeName}->${fieldName}`);
                                },
                            };
                        }
                        else {
                            if (elementObj.attributes?.ref) {
                                this.logger.warn(`element.ref isn't supported yet.`);
                            }
                            else {
                                this.logger.warn(`Element doesn't have a name in ${complexTypeName}. Ignoring...`);
                            }
                        }
                    }
                }
                if (sequenceOrChoiceObj.any) {
                    for (const anyObj of sequenceOrChoiceObj.any) {
                        const anyNamespace = anyObj.attributes?.namespace;
                        if (anyNamespace) {
                            const anyTypeTC = this.getInputTypeForTypeNameInNamespace({
                                typeName: complexTypeName,
                                typeNamespace: anyNamespace,
                            });
                            if ('getFields' in anyTypeTC) {
                                for (const fieldName in anyTypeTC.getFields()) {
                                    fieldMap[fieldName] = anyTypeTC.getField(fieldName);
                                }
                            }
                        }
                    }
                }
            }
            if (complexType.complexContent) {
                for (const complexContentObj of complexType.complexContent) {
                    for (const extensionObj of complexContentObj.extension) {
                        const [baseTypeNamespaceAlias, baseTypeName] = extensionObj.attributes.base.split(':');
                        let baseTypeNamespace;
                        if (extensionObj.attributes[baseTypeNamespaceAlias]) {
                            baseTypeNamespace = extensionObj.attributes[baseTypeNamespaceAlias];
                        }
                        else {
                            baseTypeNamespace = aliasMap.get(baseTypeNamespaceAlias);
                        }
                        if (!baseTypeNamespace) {
                            throw new Error(`Namespace alias: ${baseTypeNamespaceAlias} is undefined!`);
                        }
                        const baseType = this.getNamespaceComplexTypeMap(baseTypeNamespace)?.get(baseTypeName);
                        if (!baseType) {
                            throw new Error(`Complex Type: ${baseTypeName} couldn't be found in ${baseTypeNamespace} needed for ${complexTypeName}`);
                        }
                        const baseTypeTC = this.getInputTypeForComplexType(baseType, baseTypeNamespace);
                        for (const fieldName in baseTypeTC.getFields()) {
                            fieldMap[fieldName] = baseTypeTC.getField(fieldName);
                        }
                        for (const sequenceObj of extensionObj.sequence) {
                            for (const elementObj of sequenceObj.element) {
                                fieldMap[elementObj.attributes.name] = {
                                    type: () => {
                                        const [typeNamespaceAlias, typeName] = elementObj.attributes.type.split(':');
                                        let typeNamespace;
                                        if (elementObj.attributes[typeNamespaceAlias]) {
                                            typeNamespace = elementObj.attributes[typeNamespaceAlias];
                                        }
                                        else {
                                            typeNamespace = aliasMap.get(typeNamespaceAlias);
                                        }
                                        if (!typeNamespace) {
                                            throw new Error(`Namespace alias: ${typeNamespaceAlias} is undefined!`);
                                        }
                                        return this.getInputTypeForTypeNameInNamespace({ typeName, typeNamespace });
                                    },
                                };
                            }
                        }
                    }
                }
            }
            if (Object.keys(fieldMap).length === 0) {
                complexTypeTC = graphql_compose_1.GraphQLJSON;
            }
            else {
                complexTypeTC = this.schemaComposer.createInputTC({
                    name: `${prefix}_${complexTypeName}_Input`,
                    fields: fieldMap,
                });
            }
            this.complexTypeInputTCMap.set(complexType, complexTypeTC);
        }
        return complexTypeTC;
    }
    getOutputFieldTypeFromElement(elementObj, aliasMap, namespace) {
        if (elementObj.attributes?.type) {
            const [typeNamespaceAlias, typeName] = elementObj.attributes.type.split(':');
            let typeNamespace;
            if (elementObj.attributes[typeNamespaceAlias]) {
                typeNamespace = elementObj.attributes[typeNamespaceAlias];
            }
            else {
                typeNamespace = aliasMap.get(typeNamespaceAlias);
            }
            if (!typeNamespace) {
                throw new Error(`Namespace alias: ${typeNamespaceAlias} is undefined!`);
            }
            const outputTC = this.getOutputTypeForTypeNameInNamespace({ typeName, typeNamespace });
            return outputTC;
        }
        else if (elementObj.simpleType) {
            // eslint-disable-next-line no-unreachable-loop
            for (const simpleTypeObj of elementObj.simpleType) {
                // Dynamically defined simple type
                // So we need to define alias map for this type
                this.aliasMap.set(simpleTypeObj, aliasMap);
                // Inherit the name from elementObj
                simpleTypeObj.attributes = simpleTypeObj.attributes || {};
                simpleTypeObj.attributes.name = simpleTypeObj.attributes.name || elementObj.attributes.name;
                const outputTC = this.getTypeForSimpleType(simpleTypeObj, namespace);
                return outputTC;
            }
        }
        else if (elementObj.complexType) {
            // eslint-disable-next-line no-unreachable-loop
            for (const complexTypeObj of elementObj.complexType) {
                // Dynamically defined type
                // So we need to define alias map for this type
                this.aliasMap.set(complexTypeObj, aliasMap);
                // Inherit the name from elementObj
                complexTypeObj.attributes = complexTypeObj.attributes || {};
                complexTypeObj.attributes.name =
                    complexTypeObj.attributes.name || elementObj.attributes.name;
                const outputTC = this.getOutputTypeForComplexType(complexTypeObj, namespace);
                return outputTC;
            }
        }
        throw new Error(`Invalid element type definition: ${elementObj.attributes.name}`);
    }
    getOutputTypeForComplexType(complexType, complexTypeNamespace) {
        let complexTypeTC = this.complexTypeOutputTCMap.get(complexType);
        if (!complexTypeTC) {
            const complexTypeName = complexType.attributes.name;
            const prefix = this.namespaceTypePrefixMap.get(complexTypeNamespace);
            const aliasMap = this.aliasMap.get(complexType);
            const fieldMap = {};
            const choiceOrSequenceObjects = [
                ...(complexType.sequence || []),
                ...(complexType.choice || []),
            ];
            for (const choiceOrSequenceObj of choiceOrSequenceObjects) {
                if (choiceOrSequenceObj.element) {
                    for (const elementObj of choiceOrSequenceObj.element) {
                        const fieldName = elementObj.attributes.name;
                        if (fieldName) {
                            const maxOccurs = choiceOrSequenceObj.attributes?.maxOccurs || elementObj.attributes?.maxOccurs;
                            const minOccurs = choiceOrSequenceObj.attributes?.minOccurs || elementObj.attributes?.minOccurs;
                            const nillable = choiceOrSequenceObj.attributes?.nillable || elementObj.attributes?.nillable;
                            const isPlural = maxOccurs != null && maxOccurs !== '1';
                            let isNullable = false;
                            if (minOccurs == null || minOccurs === '0') {
                                isNullable = true;
                            }
                            if (nillable === 'true') {
                                isNullable = true;
                            }
                            if (nillable === 'false') {
                                isNullable = false;
                            }
                            fieldMap[fieldName] = {
                                type: () => {
                                    let outputTC = this.getOutputFieldTypeFromElement(elementObj, aliasMap, complexTypeNamespace);
                                    if (isPlural) {
                                        outputTC = outputTC.getTypePlural();
                                    }
                                    if (!isNullable) {
                                        outputTC = outputTC.getTypeNonNull();
                                    }
                                    return outputTC;
                                },
                            };
                        }
                        else {
                            if (elementObj.attributes?.ref) {
                                this.logger.warn(`element.ref isn't supported yet.`, elementObj.attributes?.ref);
                            }
                            else {
                                this.logger.warn(`Element doesn't have a name in ${complexTypeName}. Ignoring...`);
                            }
                        }
                    }
                }
                if (choiceOrSequenceObj.any) {
                    for (const anyObj of choiceOrSequenceObj.any) {
                        const anyNamespace = anyObj.attributes?.namespace;
                        if (anyNamespace) {
                            const anyTypeTC = this.getOutputTypeForTypeNameInNamespace({
                                typeName: complexTypeName,
                                typeNamespace: anyNamespace,
                            });
                            if ('getFields' in anyTypeTC) {
                                for (const fieldName in anyTypeTC.getFields()) {
                                    fieldMap[fieldName] = anyTypeTC.getField(fieldName);
                                }
                            }
                        }
                    }
                }
            }
            if (complexType.complexContent) {
                for (const complexContentObj of complexType.complexContent) {
                    for (const extensionObj of complexContentObj.extension) {
                        const [baseTypeNamespaceAlias, baseTypeName] = extensionObj.attributes.base.split(':');
                        const baseTypeNamespace = aliasMap.get(baseTypeNamespaceAlias) ||
                            extensionObj.attributes[baseTypeNamespaceAlias];
                        if (!baseTypeNamespace) {
                            throw new Error(`Namespace alias: ${baseTypeNamespaceAlias} is undefined!`);
                        }
                        const baseType = this.getNamespaceComplexTypeMap(baseTypeNamespace)?.get(baseTypeName);
                        if (!baseType) {
                            throw new Error(`Complex Type: ${baseTypeName} couldn't be found in ${baseTypeNamespace} needed for ${complexTypeName}`);
                        }
                        const baseTypeTC = this.getOutputTypeForComplexType(baseType, baseTypeNamespace);
                        if ('getFields' in baseTypeTC) {
                            for (const fieldName in baseTypeTC.getFields()) {
                                fieldMap[fieldName] = baseTypeTC.getField(fieldName);
                            }
                        }
                        const choiceOrSequenceObjects = [
                            ...(extensionObj.sequence || []),
                            ...(extensionObj.choice || []),
                        ];
                        for (const choiceOrSequenceObj of choiceOrSequenceObjects) {
                            for (const elementObj of choiceOrSequenceObj.element) {
                                const fieldName = elementObj.attributes.name;
                                const maxOccurs = choiceOrSequenceObj.attributes?.maxOccurs || elementObj.attributes?.maxOccurs;
                                const minOccurs = choiceOrSequenceObj.attributes?.minOccurs || elementObj.attributes?.minOccurs;
                                const nillable = choiceOrSequenceObj.attributes?.nillable || elementObj.attributes?.nillable;
                                const isPlural = maxOccurs != null && maxOccurs !== '1';
                                let isNullable = false;
                                if (minOccurs == null || minOccurs === '0') {
                                    isNullable = true;
                                }
                                if (nillable === 'true') {
                                    isNullable = true;
                                }
                                if (nillable === 'false') {
                                    isNullable = false;
                                }
                                fieldMap[fieldName] = {
                                    type: () => {
                                        let outputTC = this.getOutputFieldTypeFromElement(elementObj, aliasMap, complexTypeNamespace);
                                        if (isPlural) {
                                            outputTC = outputTC.getTypePlural();
                                        }
                                        if (!isNullable) {
                                            outputTC = outputTC.getTypeNonNull();
                                        }
                                        return outputTC;
                                    },
                                };
                            }
                        }
                    }
                }
            }
            if (Object.keys(fieldMap).length === 0) {
                complexTypeTC = this.schemaComposer.createScalarTC(graphql_compose_1.GraphQLJSON);
            }
            else {
                complexTypeTC = this.schemaComposer.createObjectTC({
                    name: `${prefix}_${complexTypeName}`,
                    fields: fieldMap,
                });
            }
            this.complexTypeOutputTCMap.set(complexType, complexTypeTC);
        }
        return complexTypeTC;
    }
    getOutputTypeForTypeNameInNamespace({ typeName, typeNamespace, }) {
        const complexType = this.getNamespaceComplexTypeMap(typeNamespace)?.get(typeName);
        if (complexType) {
            return this.getOutputTypeForComplexType(complexType, typeNamespace);
        }
        const simpleType = this.getNamespaceSimpleTypeMap(typeNamespace)?.get(typeName);
        if (simpleType) {
            return this.getTypeForSimpleType(simpleType, typeNamespace);
        }
        throw new Error(`Type: ${typeName} couldn't be found in ${typeNamespace}`);
    }
    getOutputTypeForMessage(message) {
        let outputTCAndName = this.messageOutputTCMap.get(message);
        if (!outputTCAndName) {
            const aliasMap = this.aliasMap.get(message);
            const partObj = message.part[0];
            if (partObj.attributes.element) {
                const [elementNamespaceAlias, elementName] = partObj.attributes.element.split(':');
                outputTCAndName = {
                    type: () => {
                        const elementTypeNamespace = aliasMap.get(elementNamespaceAlias) || partObj.attributes[elementNamespaceAlias];
                        if (!elementTypeNamespace) {
                            throw new Error(`Namespace alias: ${elementNamespaceAlias} is undefined!`);
                        }
                        return this.getOutputTypeForTypeNameInNamespace({
                            typeName: elementName,
                            typeNamespace: elementTypeNamespace,
                        });
                    },
                    elementName,
                };
            }
            else if (partObj.attributes.type) {
                const elementName = partObj.attributes.name;
                outputTCAndName = {
                    type: () => {
                        const [typeNamespaceAlias, typeName] = partObj.attributes.type.split(':');
                        const typeNamespace = aliasMap.get(typeNamespaceAlias);
                        if (!typeNamespace) {
                            throw new Error(`Namespace alias: ${typeNamespaceAlias} is undefined!`);
                        }
                        return this.getOutputTypeForTypeNameInNamespace({ typeName, typeNamespace });
                    },
                    elementName,
                };
            }
            this.messageOutputTCMap.set(message, outputTCAndName);
        }
        return outputTCAndName;
    }
    buildSchema() {
        if (this.schemaComposer.Query.getFieldNames().length === 0) {
            this.schemaComposer.Query.addFields({
                placeholder: {
                    type: graphql_scalars_1.GraphQLVoid,
                },
            });
        }
        const schema = this.schemaComposer.buildSchema();
        const schemaExts = (schema.extensions ||= {});
        schemaExts.directives ||= {};
        schemaExts.directives.transport = {
            kind: 'soap',
            subgraph: this.subgraphName,
        };
        return schema;
    }
}
exports.SOAPLoader = SOAPLoader;