Skip to content

Commit 09f7a37

Browse files
committed
Interpret @range {json} as rdf:JSON params
1 parent 9727dc8 commit 09f7a37

File tree

6 files changed

+153
-7
lines changed

6 files changed

+153
-7
lines changed

README.md

+54-1
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ Using comment tags, arguments can be customized.
222222
|---|---
223223
| `@ignored` | This field will be ignored.
224224
| `@default {<value>}` | The `default` attribute of the parameter will be set to `<value>`
225-
| `@range {<type>}` | The `range` attribute of the parameter will be set to `<type>`. You can only use values that fit the type of field. Options: `boolean, int, integer, number, byte, long, float, decimal, double, string`. For example, if your field has the type `number`, you could explicitly mark it as a `float` by using `@range {float}`. See [the documentation](https://componentsjs.readthedocs.io/en/latest/configuration/components/parameters/).
225+
| `@range {<type>}` | The `range` attribute of the parameter will be set to `<type>`. You can only use values that fit the type of field. Options: `json, boolean, int, integer, number, byte, long, float, decimal, double, string`. For example, if your field has the type `number`, you could explicitly mark it as a `float` by using `@range {float}`. See [the documentation](https://componentsjs.readthedocs.io/en/latest/configuration/components/parameters/).
226226

227227
#### Examples
228228

@@ -265,6 +265,59 @@ Component file:
265265
}
266266
```
267267

268+
**Tagging constructor fields as raw JSON:**
269+
270+
TypeScript class:
271+
```typescript
272+
export class MyActor {
273+
/**
274+
* @param myValue - Values will be passed as parsed JSON @range {json}
275+
* @param ignoredArg - @ignored
276+
*/
277+
constructor(myValue: any, ignoredArg: string) {
278+
279+
}
280+
}
281+
```
282+
283+
Component file:
284+
```json
285+
{
286+
"components": [
287+
{
288+
"parameters": [
289+
{
290+
"@id": "my-actor#TestClass#myValue",
291+
"range": "rdf:JSON",
292+
"required": false,
293+
"unique": false,
294+
"comment": "Values will be passed as parsed JSON"
295+
}
296+
],
297+
"constructorArguments": [
298+
{
299+
"@id": "my-actor#TestClass#myValue"
300+
}
301+
]
302+
}
303+
]
304+
}
305+
```
306+
307+
When instantiating TestClass as follows, its JSON value will be passed directly into the constructor:
308+
```json
309+
{
310+
"@id": "ex:myInstance",
311+
"@type": "TestClass",
312+
"myValue": {
313+
"someKey": {
314+
"someOtherKey1": 1,
315+
"someOtherKey2": "abc"
316+
}
317+
}
318+
}
319+
```
320+
268321
**Tagging interface fields:**
269322

270323
TypeScript class:

lib/serialize/ComponentConstructor.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ export class ComponentConstructor {
488488
// Fill in required fields
489489
const definition: ParameterDefinition = {
490490
'@id': fieldId,
491-
range: `xsd:${range}`,
491+
range: range === 'json' ? 'rdf:JSON' : `xsd:${range}`,
492492
};
493493

494494
// Fill in optional fields

lib/serialize/ContextConstructor.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,12 @@ export class ContextConstructor {
7070

7171
// Generate type-scoped context when enabled
7272
if (this.typeScopedContexts) {
73-
const typeScopedContext: Record<string, string> = {};
73+
const typeScopedContext: Record<string, Record<string, string>> = {};
7474
for (const parameter of component.parameters) {
75-
typeScopedContext[parameter['@id'].slice(Math.max(0, component['@id'].length + 1))] = parameter['@id'];
75+
typeScopedContext[parameter['@id'].slice(Math.max(0, component['@id'].length + 1))] = {
76+
'@id': parameter['@id'],
77+
...parameter.range === 'rdf:JSON' ? { '@type': '@json' } : {},
78+
};
7679
}
7780
(<any> shortcuts[match[0]])['@context'] = typeScopedContext;
7881
}

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
"@types/semver": "^7.3.4",
7070
"@typescript-eslint/typescript-estree": "^4.6.1",
7171
"comment-parser": "^0.7.6",
72-
"componentsjs": "^4.2.0",
72+
"componentsjs": "^4.3.0",
7373
"jsonld-context-parser": "^2.0.2",
7474
"lru-cache": "^6.0.0",
7575
"minimist": "^1.2.5",

test/serialize/ComponentConstructor.test.ts

+18
Original file line numberDiff line numberDiff line change
@@ -1732,6 +1732,24 @@ describe('ComponentConstructor', () => {
17321732
unique: true,
17331733
});
17341734
});
1735+
1736+
it('should construct a JSON parameter definition', () => {
1737+
const rangeValue = 'json';
1738+
expect(ctor.constructParameterRaw(context, <ClassLoaded> classReference, {
1739+
type: 'field',
1740+
name: 'field',
1741+
range: { type: 'raw', value: 'string' },
1742+
required: true,
1743+
unique: true,
1744+
comment: 'Hi',
1745+
}, rangeValue, 'mp:a/b/file-param#MyClass_field')).toEqual({
1746+
'@id': 'mp:a/b/file-param#MyClass_field',
1747+
comment: 'Hi',
1748+
range: 'rdf:JSON',
1749+
required: true,
1750+
unique: true,
1751+
});
1752+
});
17351753
});
17361754

17371755
describe('constructParameterClass', () => {

test/serialize/ContextConstructor.test.ts

+74-2
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,80 @@ describe('ContextConstructor', () => {
203203
'@id': 'mp:file1#MyClass1',
204204
'@prefix': true,
205205
'@context': {
206-
param1: 'mp:file1#MyClass1_param1',
207-
param2: 'mp:file1#MyClass1_param2',
206+
param1: {
207+
'@id': 'mp:file1#MyClass1_param1',
208+
},
209+
param2: {
210+
'@id': 'mp:file1#MyClass1_param2',
211+
},
212+
},
213+
},
214+
MyClass2: {
215+
'@id': 'mp:b/file2#MyClass2',
216+
'@prefix': true,
217+
'@context': {},
218+
},
219+
});
220+
});
221+
222+
it('should handle non-empty component definitions when typeScopedContexts is true for JSON ranges', () => {
223+
ctor = new ContextConstructor({
224+
packageMetadata,
225+
typeScopedContexts: true,
226+
});
227+
expect(ctor.constructComponentShortcuts({
228+
'/docs/package/components/file1': {
229+
'@context': [
230+
'https://linkedsoftwaredependencies.org/bundles/npm/my-package/context.jsonld',
231+
],
232+
'@id': 'npmd:my-package',
233+
components: [
234+
{
235+
'@id': 'mp:file1#MyClass1',
236+
'@type': 'Class',
237+
constructorArguments: [],
238+
parameters: [
239+
{
240+
'@id': 'mp:file1#MyClass1_param1',
241+
range: 'rdf:JSON',
242+
},
243+
{
244+
'@id': 'mp:file1#MyClass1_param2',
245+
range: 'rdf:JSON',
246+
},
247+
],
248+
requireElement: 'MyClass1',
249+
},
250+
],
251+
},
252+
'/docs/package/components/b/file2': {
253+
'@context': [
254+
'https://linkedsoftwaredependencies.org/bundles/npm/my-package/context.jsonld',
255+
],
256+
'@id': 'npmd:my-package',
257+
components: [
258+
{
259+
'@id': 'mp:b/file2#MyClass2',
260+
'@type': 'Class',
261+
requireElement: 'MyClass2',
262+
constructorArguments: [],
263+
parameters: [],
264+
},
265+
],
266+
},
267+
})).toEqual({
268+
MyClass1: {
269+
'@id': 'mp:file1#MyClass1',
270+
'@prefix': true,
271+
'@context': {
272+
param1: {
273+
'@id': 'mp:file1#MyClass1_param1',
274+
'@type': '@json',
275+
},
276+
param2: {
277+
'@id': 'mp:file1#MyClass1_param2',
278+
'@type': '@json',
279+
},
208280
},
209281
},
210282
MyClass2: {

0 commit comments

Comments
 (0)