openapi-contrib / openapi3-generator

Use your API OpenAPI 3 definition to generate code, documentation, and literally anything you need.
Apache License 2.0
90 stars 27 forks source link

Confused by failure to read spec #57

Open robogeek opened 1 month ago

robogeek commented 1 month ago

I'm very intrigued by this tool, and am interested in using it. However, the demo described in the README throws a weird error.

I have a well defined specification that is successfully being used in a variety of OpenAPI tools. The specification is for OpenADR (https://www.openadr.org/). If you need to see the spec, it's freely available so long as you register.

This is the output. This output format is rather inscrutable since I have no idea what the actual error is. The only clue I get is that this uses oas-validator.

Below, I show a quick zx script I wrote to use oas-validator directly. Below that is the JS object output by oas-validator. The JS object looks like oas-validator thinks it is valid, and the structure it generated looks correct per the spec.

$ npx og -o og-md ../../oadr3.0.1.yaml markdown
Invalid OpenAPI file
Error [AssertionError]: expected 'string' to be 'object'
    at Assertion.fail (/home/david/Projects/openadr/openadr-3-ts-types/node/builder/node_modules/should/as-function.js:275:17)
    at Assertion.value (/home/david/Projects/openadr/openadr-3-ts-types/node/builder/node_modules/should/as-function.js:356:19)
    at checkSubSchema (/home/david/Projects/openadr/openadr-3-ts-types/node/builder/node_modules/openapi3-generator/node_modules/oas-validator/index.js:268:28)
    at walkSchema (/home/david/Projects/openadr/openadr-3-ts-types/node/builder/node_modules/oas-schema-walker/index.js:53:5)
    at walkSchema (/home/david/Projects/openadr/openadr-3-ts-types/node/builder/node_modules/oas-schema-walker/index.js:82:13)
    at Object.walkSchema (/home/david/Projects/openadr/openadr-3-ts-types/node/builder/node_modules/oas-schema-walker/index.js:64:9)
    at checkSchema (/home/david/Projects/openadr/openadr-3-ts-types/node/builder/node_modules/openapi3-generator/node_modules/oas-validator/index.js:333:8)
    at checkContent (/home/david/Projects/openadr/openadr-3-ts-types/node/builder/node_modules/openapi3-generator/node_modules/oas-validator/index.js:381:13)
    at checkResponse (/home/david/Projects/openadr/openadr-3-ts-types/node/builder/node_modules/openapi3-generator/node_modules/oas-validator/index.js:570:9)
    at checkPathItem (/home/david/Projects/openadr/openadr-3-ts-types/node/builder/node_modules/openapi3-generator/node_modules/oas-validator/index.js:783:21) {
  operator: 'to be',
  expected: 'object',
  showDiff: true,
  actual: 'string',
  stackStartFunction: [Function: assert],
  negate: false,
  assertion: <ref *1> Assertion {
    obj: 'string',
    anyOne: false,
    negate: false,
    params: {
      operator: 'to be',
      expected: 'object',
      message: undefined,
      showDiff: true,
      actual: 'string',
      stackStartFunction: [Function: assert],
      negate: false,
      assertion: [Circular *1]
    },
    onlyThis: undefined,
    light: false
  }
}
TypeError: Cannot read properties of undefined (reading 'basePath')
    at module.exports (/home/david/Projects/openadr/openadr-3-ts-types/node/builder/node_modules/openapi3-generator/lib/beautifier.js:132:30)
    at /home/david/Projects/openadr/openadr-3-ts-types/node/builder/node_modules/openapi3-generator/lib/generator.js:327:17

I wrote a quick zx script to use oas-validator


import validator from 'oas-validator';

if (!(typeof argv?.input === 'string')) {
    throw new Error(`no --input`);
}

import { promises as fsp } from 'node:fs';

const txt = await fsp.readFile(argv?.input, 'utf-8');

const openapi = YAML.parse(txt);

const options = {};
validator.validate(openapi, options)
.then(function(options){
  // options.valid contains the result of the validation, true in this branch
  console.log(options);
})
.catch(function(err){
  console.warn(err.message);
  if (options.context) console.warn('Location',options.context.pop());
});

And it gave this output:

{
  valid: true,
  context: [ '#/' ],
  warnings: [],
  lintLimit: 5,
  lintSkip: [],
  operationIds: [
    'searchAllPrograms',
    'createProgram',
    'searchProgramByProgramId',
    'updateProgram',
    'deleteProgram',
    'searchAllReports',
    'createReport',
    'searchReportsByReportID',
    'updateReport',
    'deleteReport',
    'searchAllEvents',
    'createEvent',
    'searchEventsByID',
    'updateEvent',
    'deleteEvent',
    'searchSubscriptions',
    'createSubscription',
    'searchSubscriptionByID',
    'updateSubscription',
    'deleteSubscription',
    'searchVens',
    'createVen',
    'searchVenByID',
    'updateVen',
    'deleteVen',
    'searchVenResources',
    'createResource',
    'searchVenResourceByID',
    'updateVenResource',
    'deleteVenResource',
    'fetchToken'
  ],
  allScopes: {
    oAuth2ClientCredentials: {
      read_all: 'VENs and BL can read all resources',
      write_programs: 'Only BL can write to programs',
      write_events: 'Only BL can write to events',
      write_reports: 'only VENs can write to reports',
      write_subscriptions: 'VENs and BL can write to subscriptions',
      write_vens: 'VENS and BL can write to vens and resources'
    }
  },
  openapi: {
    openapi: '3.0.0',
    servers: [ [Object] ],
    info: {
      title: 'OpenADR 3 API',
      version: '3.0.1',
      description: 'The OpenADR 3 API supports energy retailer to energy customer Demand Response programs.\n' +
        'See OpenADR 3 User Guide and Defintions for detailed descriptions of usage.\n' +
        'The API includes the following capabilities and operations:\n' +
        '\n' +
        '__Manage programs:__\n' +
        '\n' +
        '* Create/Update/Delete a program\n' +
        '* Search programs\n' +
        '\n' +
        '__Manage events:__\n' +
        '\n' +
        '* Create/Update/Delete an event\n' +
        '* Search events\n' +
        '\n' +
        '__Manage reports:__\n' +
        '\n' +
        '* Create/Update/Delete a report\n' +
        '* Search reports\n' +
        '\n' +
        '__Manage subscriptions:__\n' +
        '\n' +
        '* Create/Update/Delete subscriptions to programs, events, and reports\n' +
        '* Search subscriptions\n' +
        '* Subscriptions allows clients to register a callback URL (webhook) to be notified\n' +
        '  on the change of state of a resource\n' +
        '\n' +
        '__Manage vens:__\n' +
        '\n' +
        '* Create/Update/Delete vens and ven resources\n' +
        '* Search ven and ven resources\n' +
        '\n' +
        '__Manage tokens:__\n' +
        '\n' +
        '* Obtain an access token\n' +
        '* This endpoint is provided as a convenience and may be neglected in a commercial implementation\n',
      contact: [Object],
      license: [Object]
    },
    paths: {
      '/programs': [Object],
      '/programs/{programID}': [Object],
      '/reports': [Object],
      '/reports/{reportID}': [Object],
      '/events': [Object],
      '/events/{eventID}': [Object],
      '/subscriptions': [Object],
      '/subscriptions/{subscriptionID}': [Object],
      '/vens': [Object],
      '/vens/{venID}': [Object],
      '/vens/{venID}/resources': [Object],
      '/vens/{venID}/resources/{resourceID}': [Object],
      '/auth/token': [Object]
    },
    components: { schemas: [Object], securitySchemes: [Object] }
  },
  cache: {},
  metadata: { lines: -1, count: {} },
  fetch: <ref *1> [Function: fetch] {
    isRedirect: [Function (anonymous)],
    Promise: [Function: Promise],
    default: [Circular *1],
    Headers: [class Headers],
    Request: [class Request],
    Response: [class Response],
    FetchError: [Function: FetchError]
  },
  externals: [],
  externalRefs: {},
  rewriteRefs: true,
  resolver: { depth: 0, base: undefined, actions: [ [] ] },
  isCallback: false
}
robogeek commented 1 month ago

I cloned the repository.

In bundler.js I commented this out:


  // try {
  //   validator.validateSync(bundledJSON, {});
  // } catch (e) {
  //   console.error('Invalid OpenAPI file');
  //   console.error(e);
  //   return;
  // }

With this commented out, the program ran correctly, and generated a Markdown file that looks good. With it uncommented, the failure shown above occurs.