mirror of
https://github.com/umap-project/umap.git
synced 2025-04-29 11:52:38 +02:00
chore(sync): Ensure properties can be updated before doing it.
When receiving a message, this checks the given properties belong to the "subject" before applying the message.
This commit is contained in:
parent
25ccdde0b0
commit
2fafad714e
5 changed files with 133 additions and 25 deletions
|
@ -99,26 +99,26 @@ export const SCHEMA = {
|
||||||
description: {
|
description: {
|
||||||
type: 'Text',
|
type: 'Text',
|
||||||
impacts: ['ui'],
|
impacts: ['ui'],
|
||||||
belongsTo: ['map', 'datalayer'],
|
belongsTo: ['map', 'datalayer', 'feature'],
|
||||||
label: translate('description'),
|
label: translate('description'),
|
||||||
helpEntries: 'textFormatting',
|
helpEntries: 'textFormatting',
|
||||||
},
|
},
|
||||||
displayOnLoad: {
|
displayOnLoad: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
impacts: [],
|
impacts: [],
|
||||||
belongsTo: ['datalayer'], // XXX ?
|
belongsTo: ['datalayer'],
|
||||||
},
|
},
|
||||||
displayPopupFooter: {
|
displayPopupFooter: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
impacts: ['ui'],
|
impacts: ['ui'],
|
||||||
belongsTo: ['map'], // XXX ?
|
belongsTo: ['map'],
|
||||||
label: translate('Do you want to display popup footer?'),
|
label: translate('Do you want to display popup footer?'),
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
easing: {
|
easing: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
impacts: [],
|
impacts: [],
|
||||||
belongsTo: ['feature'], // XXX ?
|
belongsTo: ['map', 'datalayer', 'feature'],
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
editinosmControl: {
|
editinosmControl: {
|
||||||
|
@ -140,7 +140,7 @@ export const SCHEMA = {
|
||||||
facetKey: {
|
facetKey: {
|
||||||
type: String,
|
type: String,
|
||||||
impacts: ['ui'],
|
impacts: ['ui'],
|
||||||
belongsTo: ['datalayer'],
|
belongsTo: ['map', 'datalayer'],
|
||||||
},
|
},
|
||||||
fill: {
|
fill: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
@ -174,12 +174,12 @@ export const SCHEMA = {
|
||||||
filterKey: {
|
filterKey: {
|
||||||
type: String,
|
type: String,
|
||||||
impacts: [],
|
impacts: [],
|
||||||
belongsTo: ['map', 'datalayer', 'feature'], // XXX ?
|
belongsTo: ['map', 'datalayer', 'feature'],
|
||||||
},
|
},
|
||||||
fromZoom: {
|
fromZoom: {
|
||||||
type: Number,
|
type: Number,
|
||||||
impacts: [], // not needed
|
impacts: [], // not needed
|
||||||
belongsTo: ['map'], // XXX ?
|
belongsTo: ['map', 'datalayer'],
|
||||||
label: translate('From zoom'),
|
label: translate('From zoom'),
|
||||||
helpText: translate('Optional.'),
|
helpText: translate('Optional.'),
|
||||||
},
|
},
|
||||||
|
@ -232,7 +232,7 @@ export const SCHEMA = {
|
||||||
inCaption: {
|
inCaption: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
impacts: ['ui'],
|
impacts: ['ui'],
|
||||||
belongsTo: [], // XXX ?
|
belongsTo: ['datalayer'],
|
||||||
},
|
},
|
||||||
interactive: {
|
interactive: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
@ -268,7 +268,7 @@ export const SCHEMA = {
|
||||||
labelKey: {
|
labelKey: {
|
||||||
type: String,
|
type: String,
|
||||||
impacts: ['data'],
|
impacts: ['data'],
|
||||||
belongsTo: ['map', 'datalayer', 'feature'], // XXX ?
|
belongsTo: ['map', 'datalayer', 'feature'],
|
||||||
helpEntries: 'labelKey',
|
helpEntries: 'labelKey',
|
||||||
placeholder: translate('Default: name'),
|
placeholder: translate('Default: name'),
|
||||||
label: translate('Label key'),
|
label: translate('Label key'),
|
||||||
|
@ -277,7 +277,7 @@ export const SCHEMA = {
|
||||||
licence: {
|
licence: {
|
||||||
type: String,
|
type: String,
|
||||||
impacts: ['ui'],
|
impacts: ['ui'],
|
||||||
belongsTo: ['map', 'datalayer'], // XXX ?
|
belongsTo: ['map', 'datalayer'],
|
||||||
label: translate('licence'),
|
label: translate('licence'),
|
||||||
},
|
},
|
||||||
limitBounds: {
|
limitBounds: {
|
||||||
|
@ -354,7 +354,7 @@ export const SCHEMA = {
|
||||||
outlink: {
|
outlink: {
|
||||||
type: String,
|
type: String,
|
||||||
impacts: ['data'],
|
impacts: ['data'],
|
||||||
belongsTo: ['feature'],
|
belongsTo: ['map', 'datalayer', 'feature'],
|
||||||
label: translate('Link to…'),
|
label: translate('Link to…'),
|
||||||
helpEntries: 'outlink',
|
helpEntries: 'outlink',
|
||||||
placeholder: 'http://...',
|
placeholder: 'http://...',
|
||||||
|
@ -363,7 +363,7 @@ export const SCHEMA = {
|
||||||
outlinkTarget: {
|
outlinkTarget: {
|
||||||
type: String,
|
type: String,
|
||||||
impacts: ['data'],
|
impacts: ['data'],
|
||||||
belongsTo: ['feature'],
|
belongsTo: ['map', 'datalayer', 'feature'],
|
||||||
label: translate('Open link in…'),
|
label: translate('Open link in…'),
|
||||||
inheritable: true,
|
inheritable: true,
|
||||||
default: 'blank',
|
default: 'blank',
|
||||||
|
@ -405,7 +405,7 @@ export const SCHEMA = {
|
||||||
popupShape: {
|
popupShape: {
|
||||||
type: String,
|
type: String,
|
||||||
impacts: [], // not needed
|
impacts: [], // not needed
|
||||||
belongsTo: ['map', 'datalayer', 'feature'], // XXX ?
|
belongsTo: ['map', 'datalayer', 'feature'],
|
||||||
label: translate('Popup shape'),
|
label: translate('Popup shape'),
|
||||||
inheritable: true,
|
inheritable: true,
|
||||||
choices: [
|
choices: [
|
||||||
|
@ -418,7 +418,7 @@ export const SCHEMA = {
|
||||||
popupTemplate: {
|
popupTemplate: {
|
||||||
type: String,
|
type: String,
|
||||||
impacts: [], // not needed
|
impacts: [], // not needed
|
||||||
belongsTo: ['map', 'datalayer', 'feature'], // XXX ?
|
belongsTo: ['map', 'datalayer', 'feature'],
|
||||||
label: translate('Popup content style'),
|
label: translate('Popup content style'),
|
||||||
inheritable: true,
|
inheritable: true,
|
||||||
choices: [
|
choices: [
|
||||||
|
@ -480,7 +480,7 @@ export const SCHEMA = {
|
||||||
slugKey: {
|
slugKey: {
|
||||||
type: String,
|
type: String,
|
||||||
impacts: [],
|
impacts: [],
|
||||||
belongsTo: ['map', 'datalayer', 'feature'], // XXX ?
|
belongsTo: ['map', 'datalayer', 'feature'],
|
||||||
},
|
},
|
||||||
smoothFactor: {
|
smoothFactor: {
|
||||||
type: Number,
|
type: Number,
|
||||||
|
@ -538,19 +538,19 @@ export const SCHEMA = {
|
||||||
toZoom: {
|
toZoom: {
|
||||||
type: Number,
|
type: Number,
|
||||||
impacts: [], // not needed
|
impacts: [], // not needed
|
||||||
belongsTo: ['map'],
|
belongsTo: ['map', 'datalayer'],
|
||||||
label: translate('To zoom'),
|
label: translate('To zoom'),
|
||||||
helpText: translate('Optional.'),
|
helpText: translate('Optional.'),
|
||||||
},
|
},
|
||||||
type: {
|
type: {
|
||||||
type: 'String',
|
type: 'String',
|
||||||
impacts: ['data'],
|
impacts: ['data'],
|
||||||
belongsTo: [], // XXX ?
|
belongsTo: ['datalayer'],
|
||||||
},
|
},
|
||||||
weight: {
|
weight: {
|
||||||
type: Number,
|
type: Number,
|
||||||
impacts: ['data'],
|
impacts: ['data'],
|
||||||
belongsTo: ['feature'], // XXX ?,
|
belongsTo: ['map', 'datalayer', 'feature'],
|
||||||
min: 1,
|
min: 1,
|
||||||
max: 20,
|
max: 20,
|
||||||
step: 1,
|
step: 1,
|
||||||
|
@ -574,10 +574,15 @@ export const SCHEMA = {
|
||||||
zoomTo: {
|
zoomTo: {
|
||||||
type: Number,
|
type: Number,
|
||||||
impacts: [], // not need to update the view
|
impacts: [], // not need to update the view
|
||||||
belongsTo: ['map'],
|
belongsTo: ['map', 'datalayer', 'feature'],
|
||||||
placeholder: translate('Inherit'),
|
placeholder: translate('Inherit'),
|
||||||
helpEntries: 'zoomTo',
|
helpEntries: 'zoomTo',
|
||||||
label: translate('Default zoom level'),
|
label: translate('Default zoom level'),
|
||||||
inheritable: true,
|
inheritable: true,
|
||||||
},
|
},
|
||||||
|
_latlng: {
|
||||||
|
type: Object,
|
||||||
|
impacts: ['data'],
|
||||||
|
belongsTo: ['feature'],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { propertyBelongsTo } from '../utils.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This file contains the updaters: classes that are able to convert messages
|
* This file contains the updaters: classes that are able to convert messages
|
||||||
* received from another party (or the server) to changes on the map.
|
* received from another party (or the server) to changes on the map.
|
||||||
|
@ -31,9 +33,16 @@ class BaseUpdater {
|
||||||
return this.map.defaultEditDataLayer()
|
return this.map.defaultEditDataLayer()
|
||||||
}
|
}
|
||||||
|
|
||||||
applyMessage(message) {
|
applyMessage(payload) {
|
||||||
let { verb } = message
|
let { verb, subject } = payload
|
||||||
return this[verb](message)
|
|
||||||
|
if (verb == 'update') {
|
||||||
|
if (!propertyBelongsTo(payload.key, subject)) {
|
||||||
|
console.error('Invalid message received', payload)
|
||||||
|
return // Do not apply the message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this[verb](payload)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ export class WebSocketTransport {
|
||||||
}
|
}
|
||||||
|
|
||||||
onMessage(wsMessage) {
|
onMessage(wsMessage) {
|
||||||
// XXX validate incoming data.
|
|
||||||
this.receiver.dispatch(JSON.parse(wsMessage.data))
|
this.receiver.dispatch(JSON.parse(wsMessage.data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ export function checkId(string) {
|
||||||
* Return an array of unique impacts.
|
* Return an array of unique impacts.
|
||||||
*
|
*
|
||||||
* @param {fields} list[fields]
|
* @param {fields} list[fields]
|
||||||
|
* @param object schema object. If ommited, global U.SCHEMA will be used.
|
||||||
* @returns Array[string]
|
* @returns Array[string]
|
||||||
*/
|
*/
|
||||||
export function getImpactsFromSchema(fields, schema) {
|
export function getImpactsFromSchema(fields, schema) {
|
||||||
|
@ -53,6 +54,31 @@ export function getImpactsFromSchema(fields, schema) {
|
||||||
return Array.from(impacted)
|
return Array.from(impacted)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the given property belongs to the given subject, according to the schema.
|
||||||
|
*
|
||||||
|
* @param srtring property
|
||||||
|
* @param string subject
|
||||||
|
* @param object schema object. If ommited, global U.SCHEMA will be used.
|
||||||
|
* @returns Bool
|
||||||
|
*/
|
||||||
|
export function propertyBelongsTo(property, subject, schema) {
|
||||||
|
schema = schema || U.SCHEMA
|
||||||
|
if (subject === 'feature') {
|
||||||
|
property = property.replace('properties.', '').replace('_umap_options.', '')
|
||||||
|
}
|
||||||
|
property = property.replace('options.', '')
|
||||||
|
console.log(property)
|
||||||
|
const splits = property.split('.')
|
||||||
|
const nested = splits.length > 1
|
||||||
|
if (nested) property = splits[0]
|
||||||
|
if (!Object.keys(schema).includes(property)) return false
|
||||||
|
if (nested) {
|
||||||
|
if (schema[property].type !== Object) return false
|
||||||
|
}
|
||||||
|
return schema[property].belongsTo.includes(subject)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Import DOM purify, and initialize it.
|
* Import DOM purify, and initialize it.
|
||||||
*
|
*
|
||||||
|
|
|
@ -623,7 +623,76 @@ describe('Utils', function () {
|
||||||
assert.deepEqual(getImpactsFromSchema(['foo', 'bar', 'baz'], schema), ['A', 'B'])
|
assert.deepEqual(getImpactsFromSchema(['foo', 'bar', 'baz'], schema), ['A', 'B'])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
describe('parseNaiveDate', () => {
|
|
||||||
|
describe('#propertyBelongsTo', () => {
|
||||||
|
it('should return false on unexisting property', function () {
|
||||||
|
let schema = {}
|
||||||
|
assert.deepEqual(Utils.propertyBelongsTo('foo', 'map', schema), false)
|
||||||
|
})
|
||||||
|
it('should return false if subject is not listed', function () {
|
||||||
|
let schema = {
|
||||||
|
foo: { belongsTo: ['feature'] },
|
||||||
|
}
|
||||||
|
assert.deepEqual(Utils.propertyBelongsTo('foo', 'map', schema), false)
|
||||||
|
})
|
||||||
|
it('should return true if subject is listed', function () {
|
||||||
|
let schema = {
|
||||||
|
foo: { belongsTo: ['map'] },
|
||||||
|
}
|
||||||
|
assert.deepEqual(Utils.propertyBelongsTo('foo', 'map', schema), true)
|
||||||
|
})
|
||||||
|
it('should remove the `options.` prefix before checking', function () {
|
||||||
|
let schema = {
|
||||||
|
foo: { belongsTo: ['map'] },
|
||||||
|
}
|
||||||
|
assert.deepEqual(Utils.propertyBelongsTo('options.foo', 'map', schema), true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Accepts setting properties on objects', function () {
|
||||||
|
let schema = {
|
||||||
|
foo: {
|
||||||
|
type: Object,
|
||||||
|
belongsTo: ['map'],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
assert.deepEqual(Utils.propertyBelongsTo('options.foo.name', 'map', schema), true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Rejects setting properties on non-objects', function () {
|
||||||
|
let schema = {
|
||||||
|
foo: {
|
||||||
|
type: String,
|
||||||
|
belongsTo: ['map'],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
assert.deepEqual(
|
||||||
|
Utils.propertyBelongsTo('options.foo.name', 'map', schema),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('when subject = feature, should filter the `properties.`', function () {
|
||||||
|
let schema = {
|
||||||
|
foo: { belongsTo: ['feature'] },
|
||||||
|
}
|
||||||
|
assert.deepEqual(
|
||||||
|
Utils.propertyBelongsTo('properties.foo', 'feature', schema),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('On features, should filter the `_umap_options.`', function () {
|
||||||
|
let schema = {
|
||||||
|
foo: { belongsTo: ['feature'] },
|
||||||
|
}
|
||||||
|
assert.deepEqual(
|
||||||
|
Utils.propertyBelongsTo('properties._umap_options.foo', 'feature', schema),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('#parseNaiveDate', () => {
|
||||||
it('should parse a date', () => {
|
it('should parse a date', () => {
|
||||||
assert.equal(
|
assert.equal(
|
||||||
Utils.parseNaiveDate('2024/03/04').toISOString(),
|
Utils.parseNaiveDate('2024/03/04').toISOString(),
|
||||||
|
|
Loading…
Reference in a new issue