Skip to content

Commit 742b235

Browse files
committed
define a schema for relation members
1 parent 781f2f0 commit 742b235

File tree

3 files changed

+109
-6
lines changed

3 files changed

+109
-6
lines changed

lib/build.js

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import chalk from 'chalk';
22
import fs from 'fs';
33
import { globSync } from 'glob';
4-
import jsonschema from 'jsonschema';
4+
import { Validator } from 'jsonschema';
55
import path from 'path';
66
import shell from 'shelljs';
77
import YAML from 'js-yaml';
@@ -21,6 +21,8 @@ const discardedSchema = require('../schemas/discarded.json');
2121

2222
let _currBuild = null;
2323

24+
const jsonschema = new Validator();
25+
2426
function validateData(options) {
2527
const START = '🔬 ' + chalk.yellow('Validating schema...');
2628
const END = '👍 ' + chalk.green('schema okay');
@@ -203,6 +205,9 @@ function read(f) {
203205

204206

205207
function validateSchema(file, instance, schema) {
208+
// add this schema to the cache, so $ref can be resolved faster
209+
jsonschema.addSchema(schema);
210+
206211
let validationErrors = jsonschema.validate(instance, schema).errors;
207212

208213
if (validationErrors.length) {
@@ -362,6 +367,15 @@ function generatePresets(dataDir, tstrings, searchableFieldIDs, listReusedIcons)
362367
if (!icons[icon]) icons[icon] = [];
363368
icons[icon].push(id);
364369
}
370+
371+
if (preset.relation) {
372+
tstrings.presets[id].relation_roles = {};
373+
for (const member of preset.relation.members) {
374+
for (const role in member.roles) {
375+
tstrings.presets[id].relation_roles[role] = member.roles[role];
376+
}
377+
}
378+
}
365379
});
366380

367381
if (listReusedIcons) {
@@ -454,8 +468,10 @@ function generateTranslations(fields, presets, tstrings, searchableFieldIDs) {
454468
let tags = preset.tags || {};
455469
let keys = Object.keys(tags);
456470

471+
const tagsString = keys.map(k => `${k}=${tags[k]}`).join(' + ');
472+
457473
if (keys.length) {
458-
yamlPreset['#name'] = keys.map(k => `${k}=${tags[k]}`).join(' + ');
474+
yamlPreset['#name'] = tagsString;
459475
if (yamlPreset.aliases) {
460476
yamlPreset['#name'] += ' | ' + yamlPreset.aliases.split('\n').join(', ');
461477
}
@@ -466,6 +482,12 @@ function generateTranslations(fields, presets, tstrings, searchableFieldIDs) {
466482
yamlPreset['#name'] += ` | Local preset for countries ${preset.locationSet.include.map(country => `"${country.toUpperCase()}"`).join(', ')}`;
467483
}
468484

485+
if (yamlPreset.relation_roles) {
486+
for (const role in yamlPreset.relation_roles) {
487+
yamlPreset.relation_roles[`#${role}`] = `Relation role “${role}” when used with ${tagsString}`;
488+
}
489+
}
490+
469491
if (preset.searchable !== false) {
470492
if (yamlPreset.terms) {
471493
yamlPreset['#terms'] = 'terms: ' + yamlPreset.terms;

schemas/field.json

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,7 @@
9494
"minItems": 1,
9595
"uniqueItems": true,
9696
"items": {
97-
"type": "string",
98-
"enum": ["point", "vertex", "line", "area", "relation"]
97+
"$ref": "#/$defs/Geometry"
9998
}
10099
},
101100
"default": {
@@ -297,5 +296,11 @@
297296
{ "required": ["keys"] }
298297
]}
299298
]}
300-
]
299+
],
300+
"$defs": {
301+
"Geometry": {
302+
"type": "string",
303+
"enum": ["point", "vertex", "line", "area", "relation"]
304+
}
305+
}
301306
}

schemas/preset.json

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,83 @@
129129
}
130130
},
131131
"additionalProperties": false
132+
},
133+
"relation": {
134+
"$ref": "#/$defs/RelationSchema"
135+
},
136+
"relationCrossReference": {
137+
"description": "A preset can reference the relation schema from another preset",
138+
"type": "string",
139+
"pattern": "^\\{.+\\}$"
132140
}
133141
},
134-
"additionalProperties": false
142+
"additionalProperties": false,
143+
"$defs": {
144+
"RelationSchema": {
145+
"type": "object",
146+
"properties": {
147+
"optionalTags": {
148+
"type": "object",
149+
"description": "Only useful for specifying placeholders which are referenced in members.*.matchTags",
150+
"examples": [{ "route": "$1" }],
151+
"additionalProperties": {
152+
"type": "string"
153+
}
154+
},
155+
"allowDuplicateMembers": {
156+
"type": "boolean",
157+
"default": true
158+
},
159+
"members": {
160+
"type": "array",
161+
"items": {
162+
"type": "object",
163+
"properties": {
164+
"roles": {
165+
"type": "object",
166+
"additionalProperties": {
167+
"type": "string"
168+
},
169+
"description": "Map of roles to their label in the default language. An empty string is allowed as key."
170+
},
171+
"geometry": {
172+
"type": "array",
173+
"items": {
174+
"$ref": "field.json#/$defs/Geometry"
175+
},
176+
"description": "If not specified, any geometry is allowed"
177+
},
178+
"matchTags": {
179+
"type": "array",
180+
"items": {
181+
"type": "object",
182+
"additionalProperties": {
183+
"type": "string"
184+
}
185+
},
186+
"examples": [
187+
[{}],
188+
[{ "a": 1, "b": 2 }],
189+
[{ "a": 1 }, { "b": 2 }]
190+
],
191+
"description": "`*` can be used as a tag value. If multiple array items are specified, only 1 needs to match."
192+
},
193+
"min": {
194+
"type": "integer",
195+
"description": "If unspecified, there is no minimum"
196+
},
197+
"max": {
198+
"type": "integer",
199+
"description": "If unspecified, there is no maximum"
200+
}
201+
},
202+
"required": ["matchTags"],
203+
"additionalProperties": false
204+
}
205+
}
206+
},
207+
"required": ["allowDuplicateMembers", "members"],
208+
"additionalProperties": false
209+
}
210+
}
135211
}

0 commit comments

Comments
 (0)