Skip to content

Commit 448a099

Browse files
authored
feat(reference): attach parse result as meta to Arazzo source descriptions (#76)
1 parent 3b1e343 commit 448a099

File tree

6 files changed

+257
-0
lines changed

6 files changed

+257
-0
lines changed

packages/apidom-reference/README.md

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,70 @@ for (const element of parseResult) {
351351
}
352352
```
353353

354+
##### Accessing parsed documents via SourceDescriptionElement
355+
356+
When source descriptions parsing is enabled, a `ParseResultElement` is attached to each
357+
`SourceDescriptionElement`'s meta as `'parseResult'`. This attachment always happens,
358+
regardless of success or failure:
359+
360+
- **On success**: The parseResult contains the parsed API document
361+
- **On failure**: The parseResult contains error/warning annotations explaining what went wrong
362+
363+
```js
364+
import { parse } from '@speclynx/apidom-reference';
365+
366+
const parseResult = await parse('/path/to/arazzo.json', {
367+
parse: { parserOpts: { sourceDescriptions: true } },
368+
});
369+
370+
// Access parsed document from source description element
371+
const api = parseResult.api;
372+
const sourceDesc = api.sourceDescriptions.get(0);
373+
const parsedDoc = sourceDesc.meta.get('parseResult');
374+
375+
// Check for errors before using
376+
if (parsedDoc.errors.length > 0) {
377+
console.log('Parsing failed:', parsedDoc.errors);
378+
} else {
379+
console.log(parsedDoc.api.element); // e.g., 'openApi3_1'
380+
}
381+
```
382+
383+
##### Low-level API
384+
385+
For advanced use cases where you need to parse source descriptions from an already-parsed Arazzo document
386+
(e.g., when using naked parser adapters directly), the `parseSourceDescriptions` function is exported:
387+
388+
```js
389+
import { parse } from '@speclynx/apidom-parser-adapter-arazzo-json-1';
390+
import { parseSourceDescriptions } from '@speclynx/apidom-reference/parse/parsers/arazzo-json-1';
391+
import { options, mergeOptions } from '@speclynx/apidom-reference';
392+
393+
// Parse using naked parser adapter
394+
const parseResult = await parse(arazzoJsonString);
395+
396+
// Parse source descriptions separately
397+
const sourceDescriptions = await parseSourceDescriptions(
398+
parseResult,
399+
'/path/to/arazzo.json',
400+
mergeOptions(options, { parse: { parserOpts: { sourceDescriptions: true } } }),
401+
);
402+
403+
// Access parsed document from source description element
404+
const sourceDesc = parseResult.api.sourceDescriptions.get(0);
405+
const parsedDoc = sourceDesc.meta.get('parseResult');
406+
```
407+
408+
The function signature is:
409+
```typescript
410+
parseSourceDescriptions(
411+
parseResult: ParseResultElement,
412+
parseResultRetrievalURI: string,
413+
options: ReferenceOptions,
414+
parserName?: string, // defaults to 'arazzo-json-1'
415+
): Promise<ParseResultElement[]>
416+
```
417+
354418
#### [arazzo-yaml-1](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/parse/parsers/arazzo-yaml-1)
355419

356420
Wraps [@speclynx/apidom-parser-adapter-arazzo-yaml-1](https://github.com/speclynx/apidom/tree/main/packages/apidom-parser-adapter-arazzo-yaml-1) package
@@ -385,6 +449,45 @@ Parser-specific options take precedence over global options. See [arazzo-json-1]
385449

386450
See [arazzo-json-1 Error handling](#error-handling) - the behavior is identical for both JSON and YAML parsers.
387451

452+
##### Accessing parsed documents via SourceDescriptionElement
453+
454+
See [arazzo-json-1 Accessing parsed documents](#accessing-parsed-documents-via-sourcedescriptionelement) - the behavior is identical for both JSON and YAML parsers.
455+
456+
##### Low-level API
457+
458+
For advanced use cases where you need to parse source descriptions from an already-parsed Arazzo document
459+
(e.g., when using naked parser adapters directly), the `parseSourceDescriptions` function is exported:
460+
461+
```js
462+
import { parse } from '@speclynx/apidom-parser-adapter-arazzo-yaml-1';
463+
import { parseSourceDescriptions } from '@speclynx/apidom-reference/parse/parsers/arazzo-yaml-1';
464+
import { options, mergeOptions } from '@speclynx/apidom-reference';
465+
466+
// Parse using naked parser adapter
467+
const parseResult = await parse(arazzoYamlString);
468+
469+
// Parse source descriptions separately
470+
const sourceDescriptions = await parseSourceDescriptions(
471+
parseResult,
472+
'/path/to/arazzo.yaml',
473+
mergeOptions(options, { parse: { parserOpts: { sourceDescriptions: true } } }),
474+
);
475+
476+
// Access parsed document from source description element
477+
const sourceDesc = parseResult.api.sourceDescriptions.get(0);
478+
const parsedDoc = sourceDesc.meta.get('parseResult');
479+
```
480+
481+
The function signature is:
482+
```typescript
483+
parseSourceDescriptions(
484+
parseResult: ParseResultElement,
485+
parseResultRetrievalURI: string,
486+
options: ReferenceOptions,
487+
parserName?: string, // defaults to 'arazzo-yaml-1'
488+
): Promise<ParseResultElement[]>
489+
```
490+
388491
#### [json](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/parse/parsers/json)
389492

390493
Wraps [@speclynx/apidom-parser-adapter-json](https://github.com/speclynx/apidom/tree/main/packages/apidom-parser-adapter-json) package
@@ -1677,6 +1780,70 @@ const resultFiltered = await dereference('/path/to/arazzo.json', {
16771780
});
16781781
```
16791782

1783+
###### Accessing dereferenced documents via SourceDescriptionElement
1784+
1785+
When source descriptions dereferencing is enabled, a `ParseResultElement` is attached to each
1786+
`SourceDescriptionElement`'s meta as `'parseResult'`. This attachment always happens,
1787+
regardless of success or failure:
1788+
1789+
- **On success**: The parseResult contains the dereferenced API document
1790+
- **On failure**: The parseResult contains error/warning annotations explaining what went wrong
1791+
1792+
```js
1793+
import { dereference } from '@speclynx/apidom-reference';
1794+
1795+
const parseResult = await dereference('/path/to/arazzo.json', {
1796+
dereference: { strategyOpts: { sourceDescriptions: true } },
1797+
});
1798+
1799+
// Access dereferenced document from source description element
1800+
const api = parseResult.api;
1801+
const sourceDesc = api.sourceDescriptions.get(0);
1802+
const dereferencedDoc = sourceDesc.meta.get('parseResult');
1803+
1804+
// Check for errors before using
1805+
if (dereferencedDoc.errors.length > 0) {
1806+
console.log('Dereferencing failed:', dereferencedDoc.errors);
1807+
} else {
1808+
console.log(dereferencedDoc.api.element); // e.g., 'openApi3_1'
1809+
}
1810+
```
1811+
1812+
###### Low-level API
1813+
1814+
For advanced use cases where you need to dereference source descriptions from an already-parsed (optionally dereferenced)
1815+
Arazzo document (e.g., when using naked parser adapters directly), the `dereferenceSourceDescriptions` function is exported:
1816+
1817+
```js
1818+
import { parse } from '@speclynx/apidom-parser-adapter-arazzo-json-1';
1819+
import { dereferenceSourceDescriptions } from '@speclynx/apidom-reference/dereference/strategies/arazzo-1';
1820+
import { options, mergeOptions } from '@speclynx/apidom-reference';
1821+
1822+
// Parse using naked parser adapter
1823+
const parseResult = await parse(arazzoJsonString);
1824+
1825+
// Dereference source descriptions separately
1826+
const sourceDescriptions = await dereferenceSourceDescriptions(
1827+
parseResult,
1828+
'/path/to/arazzo.json',
1829+
mergeOptions(options, { dereference: { strategyOpts: { sourceDescriptions: true } } }),
1830+
);
1831+
1832+
// Access dereferenced document from source description element
1833+
const sourceDesc = parseResult.api.sourceDescriptions.get(0);
1834+
const dereferencedDoc = sourceDesc.meta.get('parseResult');
1835+
```
1836+
1837+
The function signature is:
1838+
```typescript
1839+
dereferenceSourceDescriptions(
1840+
parseResult: ParseResultElement,
1841+
parseResultRetrievalURI: string,
1842+
options: ReferenceOptions,
1843+
strategyName?: string, // defaults to 'arazzo-1'
1844+
): Promise<ParseResultElement[]>
1845+
```
1846+
16801847
##### [openapi-2](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/dereference/strategies/openapi-2)
16811848

16821849
Dereference strategy for dereferencing [OpenApi 2.0](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md) definitions.

packages/apidom-reference/src/dereference/strategies/arazzo-1/source-descriptions.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,11 @@ async function dereferenceSourceDescription(
152152
/**
153153
* Dereferences source descriptions from an Arazzo document.
154154
*
155+
* Each source description result is attached to its corresponding
156+
* SourceDescriptionElement's meta as 'parseResult' for easy access,
157+
* regardless of success or failure. On failure, the ParseResultElement
158+
* contains annotations explaining what went wrong.
159+
*
155160
* @param parseResult - ParseResult containing a parsed (optionally dereferenced) Arazzo specification
156161
* @param parseResultRetrievalURI - URI from which the parseResult was retrieved
157162
* @param options - Full ReferenceOptions (caller responsibility to construct)
@@ -164,6 +169,13 @@ async function dereferenceSourceDescription(
164169
* - Maximum dereference depth is exceeded (error annotation)
165170
* Returns an empty array when sourceDescriptions option is disabled or no names match.
166171
*
172+
* @example
173+
* ```typescript
174+
* // Access dereferenced document from source description element
175+
* const sourceDesc = parseResult.api.sourceDescriptions.get(0);
176+
* const dereferencedDoc = sourceDesc.meta.get('parseResult');
177+
* ```
178+
*
167179
* @public
168180
*/
169181
export async function dereferenceSourceDescriptions(
@@ -252,6 +264,8 @@ export async function dereferenceSourceDescriptions(
252264
sourceDescription,
253265
ctx,
254266
);
267+
// always attach result (even on failure - contains annotations)
268+
sourceDescription.meta.set('parseResult', sourceDescriptionDereferenceResult);
255269
results.push(sourceDescriptionDereferenceResult);
256270
}
257271

packages/apidom-reference/src/parse/parsers/arazzo-json-1/source-descriptions.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,11 @@ async function parseSourceDescription(
151151
/**
152152
* Parses source descriptions from an Arazzo document's ParseResult.
153153
*
154+
* Each source description result is attached to its corresponding
155+
* SourceDescriptionElement's meta as 'parseResult' for easy access,
156+
* regardless of success or failure. On failure, the ParseResultElement
157+
* contains annotations explaining what went wrong.
158+
*
154159
* @param parseResult - ParseResult containing an Arazzo specification
155160
* @param parseResultRetrievalURI - URI from which the parseResult was retrieved
156161
* @param options - Full ReferenceOptions (caller responsibility to construct)
@@ -172,6 +177,10 @@ async function parseSourceDescription(
172177
* parse: { parserOpts: { sourceDescriptions: true } }
173178
* });
174179
* const results = await parseSourceDescriptions(parseResult, uri, fullOptions);
180+
*
181+
* // Access parsed document from source description element
182+
* const sourceDesc = parseResult.api.sourceDescriptions.get(0);
183+
* const parsedDoc = sourceDesc.meta.get('parseResult');
175184
* ```
176185
*
177186
* @public
@@ -257,6 +266,8 @@ export async function parseSourceDescriptions(
257266
// process sequentially to ensure proper cycle detection with shared visitedUrls
258267
for (const sourceDescription of sourceDescriptions) {
259268
const sourceDescriptionParseResult = await parseSourceDescription(sourceDescription, ctx);
269+
// always attach result (even on failure - contains annotations)
270+
sourceDescription.meta.set('parseResult', sourceDescriptionParseResult);
260271
results.push(sourceDescriptionParseResult);
261272
}
262273

packages/apidom-reference/test/dereference/strategies/arazzo-1/source-descriptions/source-descriptions.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,29 @@ describe('dereference', function () {
145145
assert.isTrue(sdParseResult.classes.includes('source-description'));
146146
assert.strictEqual(sdParseResult.meta.get('name')!.toValue(), 'petStore');
147147
});
148+
149+
specify('should attach parseResult to source description element meta', async function () {
150+
const uri = path.join(__dirname, 'fixtures', 'root.json');
151+
const data = fs.readFileSync(uri).toString();
152+
const parseResult = await parse(data);
153+
154+
await dereferenceSourceDescriptions(
155+
parseResult,
156+
uri,
157+
mergeOptions(options, {
158+
dereference: { strategyOpts: { sourceDescriptions: true } },
159+
}),
160+
);
161+
162+
// verify meta is set on source description element
163+
const api: any = parseResult.api!;
164+
const sourceDescriptions = api.get('sourceDescriptions');
165+
const sourceDesc = sourceDescriptions.get(0);
166+
const attachedParseResult = sourceDesc.meta.get('parseResult') as ParseResultElement;
167+
168+
assert.isTrue(isParseResultElement(attachedParseResult));
169+
assert.strictEqual(attachedParseResult.api?.element, 'openApi3_1');
170+
});
148171
});
149172
});
150173
});

packages/apidom-reference/test/parse/parsers/arazzo-json-1/source-descriptions.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,27 @@ describe('parsers', function () {
143143
assert.isTrue(sdParseResult.classes.includes('source-description'));
144144
assert.strictEqual(sdParseResult.meta.get('name')!.toValue(), 'petStore');
145145
});
146+
147+
specify('should attach parseResult to source description element meta', async function () {
148+
const uri = path.join(__dirname, 'fixtures', 'source-descriptions', 'root.json');
149+
const data = fs.readFileSync(uri).toString();
150+
const parseResult = await parse(data);
151+
152+
await parseSourceDescriptions(
153+
parseResult,
154+
uri,
155+
mergeOptions(options, { parse: { parserOpts: { sourceDescriptions: true } } }),
156+
);
157+
158+
// verify meta is set on source description element
159+
const api: any = parseResult.api!;
160+
const sourceDescriptions = api.get('sourceDescriptions');
161+
const sourceDesc = sourceDescriptions.get(0);
162+
const attachedParseResult = sourceDesc.meta.get('parseResult') as ParseResultElement;
163+
164+
assert.isTrue(isParseResultElement(attachedParseResult));
165+
assert.strictEqual(attachedParseResult.api?.element, 'openApi3_1');
166+
});
146167
});
147168
});
148169
});

packages/apidom-reference/test/parse/parsers/arazzo-yaml-1/source-descriptions.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,27 @@ describe('parsers', function () {
143143
assert.isTrue(sdParseResult.classes.includes('source-description'));
144144
assert.strictEqual(sdParseResult.meta.get('name')!.toValue(), 'petStore');
145145
});
146+
147+
specify('should attach parseResult to source description element meta', async function () {
148+
const uri = path.join(__dirname, 'fixtures', 'source-descriptions', 'root.yaml');
149+
const data = fs.readFileSync(uri).toString();
150+
const parseResult = await parse(data);
151+
152+
await parseSourceDescriptions(
153+
parseResult,
154+
uri,
155+
mergeOptions(options, { parse: { parserOpts: { sourceDescriptions: true } } }),
156+
);
157+
158+
// verify meta is set on source description element
159+
const api: any = parseResult.api!;
160+
const sourceDescriptions = api.get('sourceDescriptions');
161+
const sourceDesc = sourceDescriptions.get(0);
162+
const attachedParseResult = sourceDesc.meta.get('parseResult') as ParseResultElement;
163+
164+
assert.isTrue(isParseResultElement(attachedParseResult));
165+
assert.strictEqual(attachedParseResult.api?.element, 'openApi3_1');
166+
});
146167
});
147168
});
148169
});

0 commit comments

Comments
 (0)