Skip to content
This repository was archived by the owner on Nov 8, 2024. It is now read-only.

Commit 8469181

Browse files
authored
Merge pull request #200 from apiaryio/kylef/valueOf-ref
Support generating body from schema with references
2 parents ac55c73 + 759caec commit 8469181

File tree

11 files changed

+261
-38
lines changed

11 files changed

+261
-38
lines changed

packages/fury-adapter-oas3-parser/CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
- Added primitive support for 'examples' in 'Media Type Object'. The first
88
example value is used for JSON media types.
99

10-
- Added primitive support for generating a JSON message body from a schema for
11-
JSON media types. Referencing is not supported for this feature.
10+
- Added support for generating a JSON message body from a schema for
11+
JSON media types.
1212

1313
- Added support for header parameters.
1414

packages/fury-adapter-oas3-parser/lib/parser/oas/parseComponentsObject.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,14 @@ function parseComponentsObject(context, element) {
119119
validateIsObject,
120120
R.compose(parseObject(context, name, parseMember), getValue),
121121
(object) => {
122-
context.state.components.push(new namespace.elements.Member(member.key, object));
122+
const contextMember = context.state.components.getMember(member.key.toValue());
123+
124+
if (contextMember) {
125+
contextMember.value = object;
126+
} else {
127+
context.state.components.push(new namespace.elements.Member(member.key, object));
128+
}
129+
123130
return object;
124131
})(member);
125132
};
@@ -169,7 +176,8 @@ function parseComponentsObject(context, element) {
169176
[R.T, createInvalidMemberWarning(namespace, name)],
170177
]);
171178

172-
return parseObject(context, name, parseMember)(element);
179+
const order = ['schemas'];
180+
return parseObject(context, name, parseMember, [], order)(element);
173181
}
174182

175183

packages/fury-adapter-oas3-parser/lib/parser/oas/parseMediaTypeObject.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,18 @@ function parseMediaTypeObject(context, MessageBodyClass, element) {
115115
const dataStructure = mediaTypeObject.get('schema');
116116

117117
if (!messageBody && dataStructure && isJSONMediaType(mediaType)) {
118-
const value = dataStructure.content.valueOf();
118+
let elements = [];
119+
const { components } = context.state;
120+
if (components) {
121+
const schemas = components.get('schemas');
122+
if (schemas) {
123+
elements = schemas.content
124+
.filter(e => e.value && e.value.content)
125+
.map(e => e.value.content);
126+
}
127+
}
128+
129+
const value = dataStructure.content.valueOf(undefined, elements);
119130

120131
if (value) {
121132
const body = JSON.stringify(value);

packages/fury-adapter-oas3-parser/test/integration/fixtures/components/media-type-object-schema.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@
101101
"content": "application/json"
102102
}
103103
},
104-
"content": "{}"
104+
"content": "{\"name\":\"\",\"company\":{\"name\":\"\"}}"
105105
},
106106
{
107107
"element": "dataStructure",

packages/fury-adapter-oas3-parser/test/integration/fixtures/components/responses-object-response-with-schema.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,27 @@
8282
}
8383
},
8484
"content": [
85+
{
86+
"element": "asset",
87+
"meta": {
88+
"classes": {
89+
"element": "array",
90+
"content": [
91+
{
92+
"element": "string",
93+
"content": "messageBody"
94+
}
95+
]
96+
}
97+
},
98+
"attributes": {
99+
"contentType": {
100+
"element": "string",
101+
"content": "application/json"
102+
}
103+
},
104+
"content": "{}"
105+
},
85106
{
86107
"element": "dataStructure",
87108
"content": {

packages/fury-adapter-oas3-parser/test/integration/fixtures/petstore.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@
154154
"content": "application/json"
155155
}
156156
},
157-
"content": "[]"
157+
"content": "[{\"id\":0,\"name\":\"\",\"tag\":\"\"}]"
158158
},
159159
{
160160
"element": "dataStructure",
@@ -329,7 +329,7 @@
329329
"content": "application/json"
330330
}
331331
},
332-
"content": "[]"
332+
"content": "[{\"id\":0,\"name\":\"\",\"tag\":\"\"}]"
333333
},
334334
{
335335
"element": "dataStructure",

packages/fury-adapter-oas3-parser/test/integration/fixtures/petstore.sourcemap.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@
404404
"content": "application/json"
405405
}
406406
},
407-
"content": "[]"
407+
"content": "[{\"id\":0,\"name\":\"\",\"tag\":\"\"}]"
408408
},
409409
{
410410
"element": "dataStructure",
@@ -854,7 +854,7 @@
854854
"content": "application/json"
855855
}
856856
},
857-
"content": "[]"
857+
"content": "[{\"id\":0,\"name\":\"\",\"tag\":\"\"}]"
858858
},
859859
{
860860
"element": "dataStructure",

packages/fury-adapter-oas3-parser/test/unit/parser/oas/parseMediaTypeObject-test.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,5 +260,75 @@ describe('Media Type Object', () => {
260260
expect(message.messageBody.toValue()).to.equal('{"name":"doe"}');
261261
expect(message.messageBody.contentType.toValue()).to.equal('application/json');
262262
});
263+
264+
it('generates a messageBody asset for JSON type with referenced schema with no examples', () => {
265+
context.state.components = new namespace.elements.Object({
266+
schemas: {
267+
Name: new namespace.elements.DataStructure(
268+
new namespace.elements.String('doe', {
269+
id: 'Name',
270+
})
271+
),
272+
},
273+
});
274+
275+
const mediaType = new namespace.elements.Member('application/json', {
276+
schema: {
277+
type: 'object',
278+
properties: {
279+
name: {
280+
$ref: '#/components/schemas/Name',
281+
},
282+
},
283+
},
284+
});
285+
286+
const parseResult = parse(context, messageBodyClass, mediaType);
287+
288+
const message = parseResult.get(0);
289+
expect(message).to.be.instanceof(messageBodyClass);
290+
expect(message.messageBody.toValue()).to.equal('{"name":"doe"}');
291+
expect(message.messageBody.contentType.toValue()).to.equal('application/json');
292+
});
293+
294+
it('generates a messageBody asset for JSON type with circular referenced schema with no examples', () => {
295+
const node = new namespace.Element();
296+
node.element = 'Node';
297+
298+
const nodes = new namespace.Element();
299+
nodes.element = 'Nodes';
300+
301+
context.state.components = new namespace.elements.Object({
302+
schemas: {
303+
Nodes: new namespace.elements.DataStructure(
304+
new namespace.elements.Array({
305+
node,
306+
}, {
307+
id: 'Nodes',
308+
})
309+
),
310+
Node: new namespace.elements.DataStructure(
311+
new namespace.elements.Object({
312+
parents: nodes,
313+
}, {
314+
id: 'Node',
315+
})
316+
),
317+
},
318+
});
319+
320+
const mediaType = new namespace.elements.Member('application/json', {
321+
schema: {
322+
$ref: '#/components/schemas/Node',
323+
},
324+
});
325+
326+
const parseResult = parse(context, messageBodyClass, mediaType);
327+
328+
const message = parseResult.get(0);
329+
expect(message).to.be.instanceof(messageBodyClass);
330+
expect(message.messageBody.toValue()).to.equal('{"parents":[]}');
331+
expect(message.messageBody.contentType.toValue()).to.equal('application/json');
332+
});
263333
});
264334
});

packages/fury-adapter-oas3-parser/test/unit/parser/oas/parseResponseObject-test.js

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -175,19 +175,14 @@ describe('Response Object', () => {
175175
},
176176
});
177177

178-
const dataStructure = new namespace.elements.DataStructure();
179-
dataStructure.id = 'Node';
180-
178+
const pets = new namespace.elements.Array();
179+
pets.id = 'Pets';
181180
context.state.components = new namespace.elements.Object({
182181
schemas: {
183-
Node: dataStructure,
182+
Pets: new namespace.elements.DataStructure(pets),
184183
},
185184
});
186185

187-
context.state.components.set('schemas', new namespace.elements.Object([
188-
new namespace.elements.Member('Pets', new namespace.elements.Array()),
189-
]));
190-
191186
const parseResult = parse(context, response);
192187

193188
expect(parseResult.get(0).headers.length).to.be.equal(2);

packages/minim-api-description/lib/define-value-of.js

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -98,61 +98,81 @@ module.exports = (namespace) => {
9898
const isEnumElement = e => (e.element === 'enum' || e instanceof EnumElement);
9999
const isPlural = e => (e instanceof ArrayElement) || (e instanceof ObjectElement);
100100

101-
function mapValue(e, options, f) {
101+
function mapValue(e, options, f, elements) {
102102
const opts = updateTypeAttributes(e, options);
103103
if (e.content && (!isPlural(e) || e.content.length > 0)) {
104-
const result = f(e, opts, 'content');
104+
const result = f(e, opts, elements, 'content');
105105
if (undefined !== result) {
106106
return result;
107107
}
108108
}
109109
const sample = findFirstSample(e);
110110
if (sample) {
111-
const result = f(sample, opts, 'sample');
111+
const result = f(sample, opts, elements, 'sample');
112112
if (undefined !== result) {
113113
return result;
114114
}
115115
}
116116
const dflt = findDefault(e);
117117
if (dflt) {
118-
const result = f(dflt, opts, 'default');
118+
const result = f(dflt, opts, elements, 'default');
119119
if (undefined !== result) {
120120
return result;
121121
}
122122
}
123123
if (isFlag(NULLABLE_FLAG, opts)) {
124-
const result = f(new NullElement(), opts, 'nullable');
124+
const result = f(new NullElement(), opts, elements, 'nullable');
125125
if (undefined !== result) {
126126
return result;
127127
}
128128
}
129+
130+
if (elements) {
131+
if (e.element === 'ref') {
132+
const result = elements.filter(el => el.id.equals(e.content))[0];
133+
const inheritedElements = elements.filter(el => !el.id.equals(e.content));
134+
135+
if (e.path && e.path.toValue() === 'content') {
136+
return mapValue(result.content, opts, f, inheritedElements);
137+
}
138+
139+
return mapValue(result, opts, f, inheritedElements);
140+
}
141+
142+
const result = elements.filter(el => el.id.equals(e.element))[0];
143+
if (result) {
144+
const inheritedElements = elements.filter(el => !el.id.equals(e.element));
145+
return mapValue(result, opts, f, inheritedElements);
146+
}
147+
}
148+
129149
if (isEnumElement(e)) {
130150
const enums = e.enumerations;
131151
if (enums && enums.content && enums.content[0]) {
132-
const result = f(enums.content[0], opts, 'generated');
152+
const result = f(enums.content[0], opts, elements, 'generated');
133153
if (undefined !== result) {
134154
return result;
135155
}
136156
}
137157
}
138158
const trivial = trivialValue(e);
139159
if (trivial) {
140-
const result = f(trivial, opts, 'generated');
160+
const result = f(trivial, opts, elements, 'generated');
141161
if (undefined !== result) {
142162
return result;
143163
}
144164
}
145165
if (isPlural(e) && e.content.length === 0) {
146-
return f(e, opts, 'generated');
166+
return f(e, opts, elements, 'generated');
147167
}
148168

149169
return undefined;
150170
}
151171

152-
function reduceValue(e, options) {
172+
function reduceValue(e, options, elements) {
153173
const opts = updateTypeAttributes(e, options);
154174
if (undefined === e.content) {
155-
return mapValue(e, opts, e => e.content);
175+
return mapValue(e, opts, e => e.content, elements);
156176
}
157177
if (isPrimitive(e)) {
158178
return e.content;
@@ -161,7 +181,7 @@ module.exports = (namespace) => {
161181
return null;
162182
}
163183
if (isEnumElement(e)) {
164-
return mapValue(e.content, inheritFlags(opts), reduceValue);
184+
return mapValue(e.content, inheritFlags(opts), reduceValue, elements);
165185
}
166186
if (e instanceof ObjectElement) {
167187
let result = {};
@@ -171,7 +191,7 @@ module.exports = (namespace) => {
171191
&& !isFlag(FIXED_TYPE_FLAG, opts)
172192
&& !hasTypeAttribute(item, 'required'));
173193

174-
const k = mapValue(item.key, inheritFlags(opts), reduceValue);
194+
const k = mapValue(item.key, inheritFlags(opts), reduceValue, elements);
175195

176196
if (undefined === k) {
177197
if (skippable) {
@@ -181,7 +201,7 @@ module.exports = (namespace) => {
181201
return true;
182202
}
183203

184-
const v = mapValue(item.value, inheritFlags(opts), reduceValue);
204+
const v = mapValue(item.value, inheritFlags(opts), reduceValue, elements);
185205
if (undefined === v) {
186206
if (skippable) {
187207
return false;
@@ -196,7 +216,7 @@ module.exports = (namespace) => {
196216
return result;
197217
}
198218
if (e instanceof ArrayElement) {
199-
const result = e.map(item => mapValue(item, inheritFlags(opts), reduceValue));
219+
const result = e.map(item => mapValue(item, inheritFlags(opts), reduceValue, elements));
200220
if (!isFlag(FIXED_FLAG, opts) && !isFlag(FIXED_TYPE_FLAG, opts)) {
201221
return result.filter(item => item !== undefined);
202222
}
@@ -210,17 +230,17 @@ module.exports = (namespace) => {
210230

211231
if (!Object.getOwnPropertyNames(Element.prototype).includes('valueOf')) {
212232
Object.defineProperty(Element.prototype, 'valueOf', {
213-
value(flags) {
233+
value(flags, elements) {
214234
if (flags !== undefined && flags.source) {
215-
return mapValue(this, 0, (value, opts, source) => {
216-
const result = reduceValue(value, opts);
235+
return mapValue(this, 0, (value, opts, elements, source) => {
236+
const result = reduceValue(value, opts, elements);
217237
if (undefined === result) {
218238
return undefined;
219239
}
220-
return [reduceValue(value, opts), source];
221-
});
240+
return [reduceValue(value, opts, elements), source];
241+
}, elements);
222242
}
223-
return mapValue(this, 0, (value, opts) => reduceValue(value, opts));
243+
return mapValue(this, 0, (value, opts) => reduceValue(value, opts, elements), elements);
224244
},
225245
});
226246
}

0 commit comments

Comments
 (0)