jnwltr / swagger-angular-generator

Generator of API layer in TypeScript for Angular 2+ apps
MIT License
90 stars 46 forks source link

TypeError: Cannot read property 'replace' of undefined #92

Open KnutHaraldR opened 5 years ago

KnutHaraldR commented 5 years ago

Stack trace:

...\node_modules\swagger-angular-generator\dist\utils.js:62
    res = input.replace(/^/gm, ' '.repeat(level * conf.indentation));
                ^

TypeError: Cannot read property 'replace' of undefined
    at Object.indent (...\node_modules\swagger-angular-generator\dist\utils.js:62:17)
    at makeField (...\node_modules\swagger-angular-generator\dist\forms\generate-form-service.js:107:38)
    at Object.entries.forEach (...\node_modules\swagger-angular-generator\dist\forms\generate-form-service.js:79:33)
    at Array.forEach (<anonymous>)
    at walkParamOrProp (...\node_modules\swagger-angular-generator\dist\forms\generate-form-service.js:77:28)
    at makeField (...\node_modules\swagger-angular-generator\dist\forms\generate-form-service.js:121:28)
    at Object.entries.forEach (...\node_modules\swagger-angular-generator\dist\forms\generate-form-service.js:79:33)
    at Array.forEach (<anonymous>)
    at walkParamOrProp (...\node_modules\swagger-angular-generator\dist\forms\generate-form-service.js:77:28)
    at getConstructor (...\node_modules\swagger-angular-generator\dist\forms\generate-form-service.js:50:28)

api definition: {"swagger":"2.0","info":{"version":"1.0","title":"myApi"},"paths":{"/api/datasets":{"post":{"tags":["DataSet"],"operationId":"PostDataSet","consumes":["application/json-patch+json","application/json","text/json","application/*+json"],"produces":["application/json"],"parameters":[{"name":"dataSet","in":"body","required":false,"schema":{"$ref":"#/definitions/MetadataValue"}}],"responses":{"422":{"description":"Client Error","schema":{"$ref":"#/definitions/ErrorDetail"}},"500":{"description":"500","schema":{"$ref":"#/definitions/ErrorDetail"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}},"security":[{"Bearer":[]}]}}},"definitions":{"MetadataValue":{"type":"object","properties":{"metaDataId":{"format":"int32","type":"integer"},"children":{"uniqueItems":false,"type":"array","items":{"$ref":"#/definitions/MetadataValue"},"readOnly":true},"metadataValueLanguages":{"uniqueItems":false,"type":"array","items":{"$ref":"#/definitions/MetadataValueLanguage"},"readOnly":true},"value":{"type":"string"}}},"MetadataValueLanguage":{"type":"object","properties":{"languageCode":{"type":"string"},"value":{"type":"string"}}},"ErrorDetail":{"type":"object","properties":{"errorMessage":{"type":"string"}}}},"securityDefinitions":{"Bearer":{"name":"Authorization","in":"header","type":"apiKey","description":"Standard Authorization header using the Bearer scheme. Example: \"bearer {token}\""}}}

Build command: node_modules/.bin/swagger-angular-generator -s api.json -d src/api/forms

KnutHaraldR commented 5 years ago

It happens when I have this property in my C# code used for generating the API definition with SwashBuckle:

public class MetadataValue
{
    public IList<MetadataValue> Children { get; }   // Remove this/change inner type => no error
}

So I was thinking it's a recursion problem or something.

dbykadorov commented 5 years ago

@KnutHaraldR I have same issue with swagger.json, generated by PHP ApiPlatform.

How did you found a place in c# code that causes this error? I have about 100 resources in the project and test them on by one looks too difficult.

Right now i've patched util.js source code to get generation process done (tot test it)

function indent(input, level = 1) {
    // --- start patch
    if (!input) {
      return '';
    }
    // -- end patch

    if (Array.isArray(input))
        input = input.join('\n');
    let res;
    res = input.replace(/^/gm, ' '.repeat(level * conf.indentation));
    res = res.replace(/^\s+$/gm, '');
    return res;
}
KnutHaraldR commented 5 years ago

I found it because it was one of my latest additions to my API. You could always binary search by disabling half your API endpoints each time.

KnutRyagerInmeta commented 4 years ago

A have this problem again when using the latest SwashBuckle, which generates allOf clauses for enums. The allOf causes Swagger-angular-generator to fail.

digitplays commented 3 years ago

there is another change needed in proccess-controller.js processController function.

`
function processController(methods, name, config, definitions) {
    const filename = path.join(config.dest, conf.apiDir, `${name}.ts`);
    let usesGlobalType = false;
    // make simpleNames unique and process responses
    const simpleNames = _.map(methods, 'simpleName');
    methods.forEach(controller => {
        if (simpleNames.filter(n => n === controller.simpleName).length > 1) {

            //start patch
            if(!(controller.operationId === undefined)){
                const preserveCapitals = controller.operationId.replace(/([A-Z])/g, '-$1');
                controller.simpleName = _.lowerFirst(_.camelCase(preserveCapitals));
            }
            //end patch
        }
        controller.responseDef = process_responses_1.processResponses(controller.responses, controller.simpleName, config);
        usesGlobalType = usesGlobalType || controller.responseDef.usesGlobalType;
    });
    const processedMethods = methods.map(m => process_method_1.processMethod(m, config.unwrapSingleParamMethods));
    usesGlobalType = usesGlobalType || processedMethods.some(c => c.usesGlobalType);
    let content = '';
    const angularCommonHttp = ['HttpClient'];
    if (processedMethods.some(c => 'header' in c.paramGroups)) {
        angularCommonHttp.push('HttpHeaders');
    }
    if (processedMethods.some(c => 'query' in c.paramGroups)) {
        angularCommonHttp.push('HttpParams');
    }
    content += `import {${angularCommonHttp.join(', ')}} from \'@angular/common/http\';\n`;
    content += 'import {Injectable} from \'@angular/core\';\n';
    content += 'import {Observable} from \'rxjs\';\n\n';
    if (usesGlobalType) {
        content += `import * as __${conf.modelFile} from \'../${conf.modelFile}\';\n\n`;
    }
    const interfaceDef = _.map(processedMethods, 'interfaceDef').filter(Boolean).join('\n');
    if (interfaceDef) {
        content += interfaceDef;
        content += '\n';
    }
    content += `@Injectable()\n`;
    content += `export class ${name}Service {\n`;
    content += utils_1.indent('constructor(private http: HttpClient) {}');
    content += '\n';
    content += utils_1.indent(_.map(processedMethods, 'methodDef').join('\n\n'));
    content += '\n}\n';
    if (conf.adHocExceptions.api[name]) {
        content = content.replace(conf.adHocExceptions.api[name][0], conf.adHocExceptions.api[name][1]);
    }
    // controllers
    utils_1.writeFile(filename, content, config.header);
    // forms
    generate_form_modules_1.createForms(config, name, processedMethods, definitions);
}`