api
Version:
Magical SDK generation from an OpenAPI definition 🪄
112 lines • 5.14 kB
JavaScript
import { findLicense } from 'license';
import { PACKAGE_NAME, PACKAGE_VERSION } from '../packageInfo.js';
export default class CodeGenerator {
/** The associated API definition */
spec;
/**
* The path to the API definion (might be a local path, a URL, or an API registry identifier)
* @example https://raw.githubusercontent.com/readmeio/oas-examples/main/3.0/json/petstore-simple.json
* @example @petstore/v1.0#n6kvf10vakpemvplx
* @example ./petstore.json
*/
specPath;
/**
* The user-specified identifier for the SDK,
* used as the directory name in the `.api/apis` directory
* where the SDK source code is located.
*/
identifier;
/** The user agent which is set for all outgoing fetch requests */
userAgent;
/**
* The license associated with the SDK.
* This is extrapolated from the API definition file.
*/
spdxLicense;
/**
* Contact info for the API and/or SDK author in case users need support.
* This is extrapolated from the API definition file.
*/
apiContact = {};
/**
* An object containing any downstream packages that are required
* for building/executing the SDK.
*/
requiredPackages;
/**
* An example code snippet that a user can run to get started with the SDK.
*/
exampleCodeSnippet;
constructor(spec, specPath, identifier) {
this.spec = spec;
this.specPath = specPath;
this.identifier = identifier;
// User agents should be contextual to the spec in question and the version of `api` that was
// used to generate the SDK. For example, this'll look like `petstore/1.0.0 (api/4.2.0)` for
// a `petstore` spec installed on api@4.2.0.
const info = spec.getDefinition().info;
this.userAgent = `${identifier}/${info.version} (${PACKAGE_NAME}/${PACKAGE_VERSION})`;
/**
* This check is barbaric but there are a number of issues with how the `transformer` work we
* have in `oas` and in `.getParametersAsJSONSchema()` and `.getResponseAsJSONSchema()` that
* are fully crashing when attempting to codegen an SDK for an API definition that has a
* circular reference.
*
* In order to get v5 out the door we're not going to support this case initialy.
*
* @see {@link https://github.com/readmeio/api/issues/549}
*/
if (JSON.stringify(spec.api).includes('"$ref":"#/')) {
throw new Error('Sorry, this library does not yet support generating an SDK for an OpenAPI definition that contains circular references.');
}
const infoObject = this.spec.api.info;
if (infoObject?.license?.name || infoObject?.license?.identifier) {
let spdxLicense;
if (infoObject?.license?.identifier) {
spdxLicense = infoObject?.license?.identifier;
}
else {
// Though `name` is required by the OpenAPI specification but most people seem to use `name`
// instead `identifier` for their licensing identifier so if they've done this we should
// try to make the license name recognizable by the `license` package. For example this'll
// change "Apache 2.0" to the SPDX-recognized "Apache-2.0".
spdxLicense = infoObject?.license?.name.replace(/ /g, '-');
}
// If the license they've got has too many matches we shouldn't try to pick a license because
// we'll run the risk of licensing it under a license they didn't intend.
if (findLicense(spdxLicense).length === 1) {
this.spdxLicense = spdxLicense;
}
}
if (infoObject?.contact) {
if (infoObject.contact?.name || infoObject.contact?.email) {
if (infoObject.contact?.name && infoObject.contact?.email) {
this.apiContact.name = `${infoObject.contact.name} <${infoObject.contact.email}>`;
}
else {
this.apiContact.name = infoObject.contact.email
? `<${infoObject.contact.email}>`
: (infoObject.contact.name ?? undefined);
}
}
if (infoObject.contact?.url) {
this.apiContact.url = infoObject.contact.url;
}
}
}
/**
* It would be better if this were an abstract function but TS/JS doesn't have support for that so
* we instead have to rely on throwing a `TypeError` if it's not been implemented instead of a
* build-time error.
*
* @see {@link https://github.com/microsoft/TypeScript/issues/34516}
*/
// oxlint-disable-next-line no-unused-vars -- This is an abstract class.
static async uninstall(storage, opts) {
throw new TypeError('The uninstallation step for this language has not been implemented');
}
hasRequiredPackages() {
return Boolean(Object.keys(this.requiredPackages));
}
}
//# sourceMappingURL=codegenerator.js.map