Skip to content

Commit 41542d1

Browse files
authored
Merge pull request #4 from manifoldco/dan/improvements/swagger-additionalProperties
Create interfaces for additionalProperties objects
2 parents 12e7858 + be775ce commit 41542d1

File tree

4 files changed

+103
-22
lines changed

4 files changed

+103
-22
lines changed

example/input.yaml

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1544,4 +1544,21 @@ definitions:
15441544
- version
15451545
- type
15461546
- body
1547-
- provider
1547+
- provider
1548+
1549+
Credentials:
1550+
type: object
1551+
description: |
1552+
Map of configuration variable names to values. Names must match
1553+
`^[a-zA-Z][a-zA-Z0-9_]{0,999}$`.
1554+
additionalProperties:
1555+
type: string
1556+
FeatureMap:
1557+
type: object
1558+
description: A map of feature labels to selected values for customizable features
1559+
additionalProperties: true
1560+
x-go-type:
1561+
type: FeatureMap
1562+
import:
1563+
package: github.com/manifoldco/go-manifold
1564+
alias: manifold

example/output.ts

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ namespace OpenAPI2 {
4343
features?: ProductIntegrationFeatures;
4444
}
4545
export enum UpdateProductBodyIntegrationVersion {
46-
V1 = "v1"
46+
V1 = 'v1'
4747
}
4848
export interface UpdateProduct {
4949
id: string;
@@ -87,7 +87,7 @@ namespace OpenAPI2 {
8787
Version1 = 1
8888
}
8989
export enum RegionType {
90-
Region = "region"
90+
Region = 'region'
9191
}
9292
export interface ProviderBody {
9393
teamId: string;
@@ -104,11 +104,12 @@ namespace OpenAPI2 {
104104
body: ProviderBody;
105105
}
106106
export enum ProviderType {
107-
Provider = "provider"
107+
Provider = 'provider'
108108
}
109109
export enum ProviderVersion {
110110
Version1 = 1
111111
}
112+
export interface ProductTags {}
112113
export interface ProductListing {
113114
// When true, everyone can see the product when requested. When false it will
114115
// not be visible to anyone except those on the provider team.
@@ -155,8 +156,8 @@ namespace OpenAPI2 {
155156
region?: ProductIntegrationFeaturesRegion;
156157
}
157158
export enum ProductIntegrationFeaturesRegion {
158-
UserSpecified = "user-specified",
159-
Unspecified = "unspecified"
159+
UserSpecified = 'user-specified',
160+
Unspecified = 'unspecified'
160161
}
161162
export interface ProductBody {
162163
providerId: string;
@@ -189,19 +190,19 @@ namespace OpenAPI2 {
189190
features: ProductIntegrationFeatures;
190191
}
191192
export enum ProductBodyIntegrationVersion {
192-
V1 = "v1"
193+
V1 = 'v1'
193194
}
194195
export interface ProductBodyBilling {
195196
type: ProductBodyBillingType;
196197
currency: ProductBodyBillingCurrency;
197198
}
198199
export enum ProductBodyBillingCurrency {
199-
Usd = "usd"
200+
Usd = 'usd'
200201
}
201202
export enum ProductBodyBillingType {
202-
MonthlyProrated = "monthly-prorated",
203-
MonthlyAnniversary = "monthly-anniversary",
204-
AnnualAnniversary = "annual-anniversary"
203+
MonthlyProrated = 'monthly-prorated',
204+
MonthlyAnniversary = 'monthly-anniversary',
205+
AnnualAnniversary = 'annual-anniversary'
205206
}
206207
export interface ProductBodyTerms {
207208
url?: string;
@@ -214,11 +215,12 @@ namespace OpenAPI2 {
214215
body: ProductBody;
215216
}
216217
export enum ProductType {
217-
Product = "product"
218+
Product = 'product'
218219
}
219220
export enum ProductVersion {
220221
Version1 = 1
221222
}
223+
export interface PlanResizeList {}
222224
export interface PlanBody {
223225
providerId: string;
224226
productId: string;
@@ -244,11 +246,12 @@ namespace OpenAPI2 {
244246
body: PlanBody;
245247
}
246248
export enum PlanType {
247-
Plan = "plan"
249+
Plan = 'plan'
248250
}
249251
export enum PlanVersion {
250252
Version1 = 1
251253
}
254+
export interface FeatureValuesList {}
252255
export interface FeatureValueDetails {
253256
label: string;
254257
name: string;
@@ -299,9 +302,9 @@ namespace OpenAPI2 {
299302
values?: FeatureValueDetails[];
300303
}
301304
export enum FeatureTypeType {
302-
Boolean = "boolean",
303-
String = "string",
304-
Number = "number"
305+
Boolean = 'boolean',
306+
String = 'string',
307+
Number = 'number'
305308
}
306309
export interface FeatureNumericRange {
307310
// Defines the end of the range ( inclusive ), from the previous, or 0;
@@ -329,6 +332,9 @@ namespace OpenAPI2 {
329332
suffix?: string;
330333
costRanges?: FeatureNumericRange[];
331334
}
335+
export interface FeatureMap {
336+
[name: string]: any;
337+
}
332338
export interface ExpandedProduct {
333339
id: string;
334340
version: ExpandedProductVersion;
@@ -338,7 +344,7 @@ namespace OpenAPI2 {
338344
provider: Provider;
339345
}
340346
export enum ExpandedProductType {
341-
Product = "product"
347+
Product = 'product'
342348
}
343349
export enum ExpandedProductVersion {
344350
Version1 = 1
@@ -360,7 +366,7 @@ namespace OpenAPI2 {
360366
body: ExpandedPlanBody;
361367
}
362368
export enum ExpandedPlanType {
363-
Plan = "plan"
369+
Plan = 'plan'
364370
}
365371
export enum ExpandedPlanVersion {
366372
Version1 = 1
@@ -376,6 +382,9 @@ namespace OpenAPI2 {
376382
// Explanation of the errors
377383
message: string[];
378384
}
385+
export interface Credentials {
386+
[name: string]: string;
387+
}
379388
export interface CreateRegion {
380389
body: RegionBody;
381390
}

src/swagger-2.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export interface Swagger2Definition {
99
items?: Swagger2Definition;
1010
oneOf?: Swagger2Definition[];
1111
properties?: { [index: string]: Swagger2Definition };
12+
additionalProperties?: boolean | Swagger2Definition;
1213
required?: string[];
1314
type?: 'array' | 'boolean' | 'integer' | 'number' | 'object' | 'string';
1415
}
@@ -113,7 +114,7 @@ function parse(spec: Swagger2, namespace: string) {
113114
function buildNextInterface() {
114115
const nextObject = queue.pop();
115116
if (!nextObject) return; // Geez TypeScript it’s going to be OK
116-
const [ID, { allOf, properties, required }] = nextObject;
117+
const [ID, { allOf, properties, required, additionalProperties, type }] = nextObject;
117118

118119
let allProperties = properties || {};
119120
const includes: string[] = [];
@@ -132,7 +133,12 @@ function parse(spec: Swagger2, namespace: string) {
132133
}
133134

134135
// If nothing’s here, let’s skip this one.
135-
if (!Object.keys(allProperties).length) {
136+
if (
137+
!Object.keys(allProperties).length &&
138+
additionalProperties !== true &&
139+
type &&
140+
TYPES[type]
141+
) {
136142
return;
137143
}
138144
// Open interface
@@ -162,6 +168,17 @@ function parse(spec: Swagger2, namespace: string) {
162168
output.push(`${name}: ${type};`);
163169
});
164170

171+
if (additionalProperties) {
172+
if (<boolean>additionalProperties === true) {
173+
output.push(`[name: string]: any`);
174+
}
175+
176+
if ((<Swagger2Definition>additionalProperties).type) {
177+
const type = getType(<Swagger2Definition>additionalProperties, '');
178+
output.push(`[name: string]: ${type}`);
179+
}
180+
}
181+
165182
// Close interface
166183
output.push('}');
167184

@@ -181,7 +198,7 @@ function parse(spec: Swagger2, namespace: string) {
181198

182199
output.push('}'); // Close namespace
183200

184-
return prettier.format(output.join('\n'), { parser: 'typescript' });
201+
return prettier.format(output.join('\n'), { parser: 'typescript', singleQuote: true });
185202
}
186203

187204
export default parse;

tests/swagger-2.test.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { Swagger2 } from '../src/swagger-2';
88
// Let Prettier handle formatting, not the test expectations
99
function format(spec: string, namespaced?: boolean) {
1010
const wrapped = namespaced === false ? spec : `namespace OpenAPI2 { ${spec} }`;
11-
return prettier.format(wrapped, { parser: 'typescript' });
11+
return prettier.format(wrapped, { parser: 'typescript', singleQuote: true });
1212
}
1313

1414
describe('Swagger 2 spec', () => {
@@ -249,6 +249,44 @@ describe('Swagger 2 spec', () => {
249249
});
250250
});
251251

252+
it('can deal with additionalProperties: true', () => {
253+
const swagger: Swagger2 = {
254+
definitions: {
255+
FeatureMap: {
256+
type: 'object',
257+
additionalProperties: true,
258+
},
259+
},
260+
};
261+
262+
const ts = format(`
263+
export interface FeatureMap {
264+
[name: string]: any;
265+
}`);
266+
267+
expect(swaggerToTS(swagger)).toBe(ts);
268+
});
269+
270+
it('can deal with additionalProperties of type', () => {
271+
const swagger: Swagger2 = {
272+
definitions: {
273+
Credentials: {
274+
type: 'object',
275+
additionalProperties: {
276+
type: 'string',
277+
},
278+
},
279+
},
280+
};
281+
282+
const ts = format(`
283+
export interface Credentials {
284+
[name: string]: string;
285+
}`);
286+
287+
expect(swaggerToTS(swagger)).toBe(ts);
288+
});
289+
252290
describe('other output', () => {
253291
it('generates the example output correctly', () => {
254292
const input = yaml.safeLoad(

0 commit comments

Comments
 (0)