Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ISSUE-685): constants support #831

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions src/transform/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
tsReadonly,
tsTupleOf,
tsUnionOf,
parseSingleSimpleValue,
ParsedSimpleValue,
} from "../utils.js";

interface TransformSchemaObjOptions extends GlobalContext {
Expand Down Expand Up @@ -112,12 +114,15 @@ export function transformSchemaObj(node: any, options: TransformSchemaObjOptions
output += nodeType(node);
break;
}
case "const": {
output += parseSingleSimpleValue(node.const, node.nullable);
break;
}
case "enum": {
const items: Array<string | number | boolean> = [];
const items: Array<ParsedSimpleValue> = [];
(node.enum as unknown[]).forEach((item) => {
if (typeof item === "string") items.push(`'${item.replace(/'/g, "\\'")}'`);
else if (typeof item === "number" || typeof item === "boolean") items.push(item);
else if (item === null && !node.nullable) items.push("null");
const value = parseSingleSimpleValue(item, node.nullable);
items.push(value);
});
output += tsUnionOf(items);
break;
Expand Down
44 changes: 41 additions & 3 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { OpenAPI2, OpenAPI3, ReferenceObject } from "./types";

type CommentObject = {
title?: string; // not jsdoc
format?: string; // not jsdoc
const?: boolean; // jsdoc without value
default?: string; // jsdoc with value
deprecated?: boolean; // jsdoc without value
description?: string; // jsdoc with value
default?: string; // jsdoc with value
enum?: boolean; // jsdoc without value
example?: string; // jsdoc with value
format?: string; // not jsdoc
nullable?: boolean; // Node information
title?: string; // not jsdoc
type: string; // Type of node
};

/**
Expand All @@ -32,6 +36,15 @@ export function prepareComment(v: CommentObject): string | void {
if (v[field]) commentsArray.push(`@${field} ${v[field]} `);
}

// * JSDOC 'Constant' without value
if (v.const) commentsArray.push(`@constant `);

// * JSDOC 'Enum' with type
if (v.enum) {
const canBeNull = v.nullable ? `|${null}` : "";
commentsArray.push(`@enum {${v.type}${canBeNull}}`);
}

if (!commentsArray.length) return;

return comment(commentsArray.join("\n"));
Expand Down Expand Up @@ -68,6 +81,25 @@ export function isRef(obj: any): obj is ReferenceObject {
return !!obj.$ref;
}

export type ParsedSimpleValue = string | number | boolean;

/**
* For parsing CONST / ENUM single values
* @param value - node.const or node.enum[I] for parsing
* @param isNodeNullable - node.nullable
* @returns parsed value
*/
export function parseSingleSimpleValue(value: unknown, isNodeNullable = false): ParsedSimpleValue {
if (typeof value === "string") return `'${value.replace(/'/g, "\\'")}'`;

if (typeof value === "number" || typeof value === "boolean") return value;

if (value === null && !isNodeNullable) return "null";

// Edge case
return `${value}`;
}

/** Return type of node (works for v2 or v3, as there are no conflicting types) */
type SchemaObjectType =
| "anyOf"
Expand All @@ -79,6 +111,7 @@ type SchemaObjectType =
| "oneOf"
| "ref"
| "string"
| "const"
| "unknown";
export function nodeType(obj: any): SchemaObjectType {
if (!obj || typeof obj !== "object") {
Expand All @@ -89,6 +122,11 @@ export function nodeType(obj: any): SchemaObjectType {
return "ref";
}

// const
if (obj.const) {
return "const";
}

// enum
if (Array.isArray(obj.enum) && obj.enum.length) {
return "enum";
Expand Down
10 changes: 8 additions & 2 deletions test/bin/expected/prettier-js.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ export interface components {
quantity?: number
/** Format: date-time */
shipDate?: string
/** @description Order Status */
/**
* @description Order Status
* @enum {string}
*/
status?: 'placed' | 'approved' | 'delivered'
complete?: boolean
}
Expand Down Expand Up @@ -111,7 +114,10 @@ export interface components {
name: string
photoUrls: string[]
tags?: components['schemas']['Tag'][]
/** @description pet status in the store */
/**
* @description pet status in the store
* @enum {string}
*/
status?: 'available' | 'pending' | 'sold'
}
ApiResponse: {
Expand Down
10 changes: 8 additions & 2 deletions test/bin/expected/prettier-json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ export interface components {
quantity?: number
/** Format: date-time */
shipDate?: string
/** @description Order Status */
/**
* @description Order Status
* @enum {string}
*/
status?: 'placed' | 'approved' | 'delivered'
complete?: boolean
}
Expand Down Expand Up @@ -111,7 +114,10 @@ export interface components {
name: string
photoUrls: string[]
tags?: components['schemas']['Tag'][]
/** @description pet status in the store */
/**
* @description pet status in the store
* @enum {string}
*/
status?: 'available' | 'pending' | 'sold'
}
ApiResponse: {
Expand Down
29 changes: 28 additions & 1 deletion test/bin/expected/specs/manifold.ts
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,9 @@ export interface definitions {
};
Region: {
id: definitions["ID"];
/** @enum {string} */
type: "region";
/** @enum {integer} */
version: 1;
body: definitions["RegionBody"];
};
Expand Down Expand Up @@ -590,7 +592,9 @@ export interface definitions {
};
Provider: {
id: definitions["ID"];
/** @enum {integer} */
version: 1;
/** @enum {string} */
type: "provider";
body: definitions["ProviderBody"];
};
Expand Down Expand Up @@ -633,13 +637,17 @@ export interface definitions {
base_url?: string;
/** Format: url */
sso_url?: string;
/** @enum {string} */
version?: "v1";
/** @default [object Object] */
features?: {
access_code?: boolean;
sso?: boolean;
plan_change?: boolean;
/** @default multiple */
/**
* @default multiple
* @enum {string}
*/
credential?: "none" | "single" | "multiple" | "unknown";
};
};
Expand Down Expand Up @@ -678,6 +686,7 @@ export interface definitions {
FeatureType: {
label: definitions["Label"];
name: definitions["Name"];
/** @enum {string} */
type: "boolean" | "string" | "number";
/** @description This sets whether or not the feature can be customized by a consumer. */
customizable?: boolean;
Expand Down Expand Up @@ -803,6 +812,7 @@ export interface definitions {
ProductImageURL: string;
/** @description List of tags for product categorization and search */
ProductTags: definitions["Label"][];
/** @enum {string} */
ProductState: "available" | "hidden" | "grandfathered" | "new" | "upcoming";
/** @default [object Object] */
ProductListing: {
Expand Down Expand Up @@ -854,6 +864,8 @@ export interface definitions {
* Pre-Order, should not be used yet. But in the future it should allow people to
* pre-provision a resource for when it does go live.
* Public, means the resource is live and everyone should be able to provision it.
*
* @enum {string}
*/
ProductProvisioning: "provider-only" | "pre-order" | "public";
/** @default [object Object] */
Expand All @@ -877,6 +889,8 @@ export interface definitions {
* @description Describes how the region for a resource is specified, if
* unspecified, then regions have no impact on this
* resource.
*
* @enum {string}
*/
region?: "user-specified" | "unspecified";
/**
Expand All @@ -888,6 +902,7 @@ export interface definitions {
* * `unknown`: The credential type is unknown.
*
* @default multiple
* @enum {string}
*/
credential?: "none" | "single" | "multiple" | "unknown";
};
Expand Down Expand Up @@ -921,7 +936,9 @@ export interface definitions {
};
feature_types: definitions["FeatureType"][];
billing: {
/** @enum {string} */
type: "monthly-prorated" | "monthly-anniversary" | "annual-anniversary";
/** @enum {string} */
currency: "usd";
};
integration: {
Expand All @@ -930,14 +947,17 @@ export interface definitions {
base_url: string;
/** Format: url */
sso_url?: string;
/** @enum {string} */
version: "v1";
features: definitions["ProductIntegrationFeatures"];
};
tags?: definitions["ProductTags"];
};
Product: {
id: definitions["ID"];
/** @enum {integer} */
version: 1;
/** @enum {string} */
type: "product";
body: definitions["ProductBody"];
};
Expand Down Expand Up @@ -966,6 +986,7 @@ export interface definitions {
/** @description Dollar value in cents. */
cost: number;
};
/** @enum {string} */
PlanState: "hidden" | "available" | "grandfathered" | "unlisted";
ExpandedPlanBody: definitions["PlanBody"] & {
/** @description An array of feature definitions for the plan, as defined on the Product. */
Expand All @@ -984,13 +1005,17 @@ export interface definitions {
};
Plan: {
id: definitions["ID"];
/** @enum {integer} */
version: 1;
/** @enum {string} */
type: "plan";
body: definitions["PlanBody"];
};
ExpandedPlan: {
id: definitions["ID"];
/** @enum {integer} */
version: 1;
/** @enum {string} */
type: "plan";
body: definitions["ExpandedPlanBody"];
};
Expand Down Expand Up @@ -1030,7 +1055,9 @@ export interface definitions {
PriceFormula: string;
ExpandedProduct: {
id: definitions["ID"];
/** @enum {integer} */
version: 1;
/** @enum {string} */
type: "product";
body: definitions["ProductBody"];
plans?: definitions["ExpandedPlan"][];
Expand Down
10 changes: 8 additions & 2 deletions test/bin/expected/specs/petstore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ export interface components {
quantity?: number;
/** Format: date-time */
shipDate?: string;
/** @description Order Status */
/**
* @description Order Status
* @enum {string}
*/
status?: "placed" | "approved" | "delivered";
complete?: boolean;
};
Expand Down Expand Up @@ -111,7 +114,10 @@ export interface components {
name: string;
photoUrls: string[];
tags?: components["schemas"]["Tag"][];
/** @description pet status in the store */
/**
* @description pet status in the store
* @enum {string}
*/
status?: "available" | "pending" | "sold";
};
ApiResponse: {
Expand Down
10 changes: 8 additions & 2 deletions test/bin/expected/stdout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ export interface components {
quantity?: number;
/** Format: date-time */
shipDate?: string;
/** @description Order Status */
/**
* @description Order Status
* @enum {string}
*/
status?: "placed" | "approved" | "delivered";
complete?: boolean;
};
Expand Down Expand Up @@ -111,7 +114,10 @@ export interface components {
name: string;
photoUrls: string[];
tags?: components["schemas"]["Tag"][];
/** @description pet status in the store */
/**
* @description pet status in the store
* @enum {string}
*/
status?: "available" | "pending" | "sold";
};
ApiResponse: {
Expand Down
Loading