@kalisio/kdk 2.6.2 → 2.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core/api/authentication.js +13 -1
- package/core/api/hooks/hooks.query.js +9 -0
- package/core/client/capabilities.js +6 -1
- package/core/client/components/KChip.vue +27 -6
- package/core/client/components/collection/KCard.vue +2 -2
- package/core/client/utils/utils.session.js +5 -1
- package/extras/tours/pane.top.js +9 -0
- package/map/api/hooks/hooks.query.js +60 -0
- package/map/client/components/stickies/KZoomControl.vue +17 -9
- package/map/client/i18n/map_en.json +8 -1
- package/map/client/i18n/map_fr.json +8 -1
- package/map/client/mixins/map/mixin.base-map.js +19 -1
- package/package.json +2 -2
- package/scripts/kash/CHANGELOG.md +4 -0
- package/scripts/kash/LICENSE +21 -0
- package/scripts/kash/README.md +9 -0
- package/scripts/kash/kash.sh +40 -45
- package/scripts/kash/scripts/run_tests.sh +4 -1
- package/test/api/core/config/default.cjs +1 -0
- package/test/api/map/index.test.js +21 -0
|
@@ -30,6 +30,16 @@ export class Authentication extends AuthenticationService {
|
|
|
30
30
|
return {}
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
+
|
|
34
|
+
// Similarly the subject will not be set by Feathers for stateless tokens that do not target existing users.
|
|
35
|
+
async getTokenOptions (authResult, params) {
|
|
36
|
+
const jwtOptions = await super.getTokenOptions(authResult, params)
|
|
37
|
+
if (!jwtOptions.subject) {
|
|
38
|
+
const subject = _.get(authResult, 'authentication.payload.sub')
|
|
39
|
+
if (subject) jwtOptions.subject = subject
|
|
40
|
+
}
|
|
41
|
+
return jwtOptions
|
|
42
|
+
}
|
|
33
43
|
}
|
|
34
44
|
|
|
35
45
|
export class AuthenticationProviderStrategy extends OAuthStrategy {
|
|
@@ -105,6 +115,7 @@ export class JWTAuthenticationStrategy extends JWTStrategy {
|
|
|
105
115
|
const { identityFields } = authConfig
|
|
106
116
|
const { entity } = this.configuration
|
|
107
117
|
const renewJwt = _.get(this.configuration, 'renewJwt', true)
|
|
118
|
+
const { provider, ...paramsWithoutProvider } = params
|
|
108
119
|
|
|
109
120
|
if (!accessToken) {
|
|
110
121
|
throw new NotAuthenticated('No access token')
|
|
@@ -142,7 +153,8 @@ export class JWTAuthenticationStrategy extends JWTStrategy {
|
|
|
142
153
|
$or: _.reduce(identityFields, (or, field) => or.concat([{ [field]: payload.sub }]), []),
|
|
143
154
|
$limit: 1
|
|
144
155
|
}
|
|
145
|
-
|
|
156
|
+
// Avoid sending provider to internal call as it might raises some issues with hooks relying on it otherwise
|
|
157
|
+
const response = await this.entityService.find({ ...paramsWithoutProvider, query })
|
|
146
158
|
const [value = null] = response.data ? response.data : response
|
|
147
159
|
// Otherwise assume a stateless token
|
|
148
160
|
if (value) {
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import _ from 'lodash'
|
|
2
2
|
import { marshallComparisonFields, marshallTime, marshallBooleanFields, marshallNumberFields, marshallDateFields } from '../marshall.js'
|
|
3
3
|
import mongodb from 'mongodb'
|
|
4
|
+
import errors from '@feathersjs/errors'
|
|
4
5
|
import makeDebug from 'debug'
|
|
5
6
|
import { makeDiacriticPattern } from '../../common/utils.js'
|
|
6
7
|
|
|
7
8
|
const { ObjectID } = mongodb
|
|
9
|
+
const { Forbidden } = errors
|
|
8
10
|
const debug = makeDebug('kdk:core:query:hooks')
|
|
9
11
|
|
|
10
12
|
export function marshallTimeQuery (hook) {
|
|
@@ -59,10 +61,17 @@ export function marshallHttpQuery (hook) {
|
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
export async function aggregationQuery (hook) {
|
|
64
|
+
if (hook.type !== 'before') {
|
|
65
|
+
throw new Error('The \'aggregationQuery\' hook should only be used as a \'before\' hook.')
|
|
66
|
+
}
|
|
62
67
|
const query = hook.params.query
|
|
63
68
|
if (!query) return
|
|
64
69
|
const service = hook.service
|
|
65
70
|
if (query.$aggregation) {
|
|
71
|
+
// Generic aggregation request could allow to disclose or update information in DB so that it should only be used through controlled internal calls
|
|
72
|
+
if (hook.params.provider) {
|
|
73
|
+
throw new Forbidden('You are not allowed to perform aggregation')
|
|
74
|
+
}
|
|
66
75
|
const collection = service.Model
|
|
67
76
|
// Set result to avoid service DB call
|
|
68
77
|
hook.result = await collection.aggregate(query.$aggregation.pipeline, query.$aggregation.options).toArray()
|
|
@@ -17,7 +17,12 @@ export const Capabilities = {
|
|
|
17
17
|
const capabilities = await window.fetch(api.getConfig('domain') + _.get(config, 'apiPath') + '/capabilities')
|
|
18
18
|
this.content = await capabilities.json()
|
|
19
19
|
// Store latest capabilities data for offline mode
|
|
20
|
-
|
|
20
|
+
// Avoid blocking on eg QuotaExceededError
|
|
21
|
+
try {
|
|
22
|
+
await LocalCache.setItem('capabilities', this.content)
|
|
23
|
+
} catch (error) {
|
|
24
|
+
logger.error(error)
|
|
25
|
+
}
|
|
21
26
|
}
|
|
22
27
|
logger.debug('[KDK] Capabilities initialized with content:', this.content)
|
|
23
28
|
if (!this.content) return
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<q-chip
|
|
3
3
|
v-model="computedState"
|
|
4
4
|
:selected="selected"
|
|
5
|
-
:icon="
|
|
5
|
+
:icon="computedIcon"
|
|
6
6
|
:iconRight="iconRight"
|
|
7
7
|
:iconRemove="iconRemove"
|
|
8
8
|
:iconSelected="iconSelected"
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
@click="event => emit('click', event)"
|
|
18
18
|
class="k-chip"
|
|
19
19
|
>
|
|
20
|
-
<div v-if="computedLabel"
|
|
20
|
+
<div v-if="!hideLabel && computedLabel"
|
|
21
21
|
:id="id"
|
|
22
22
|
class="ellipsis"
|
|
23
23
|
:class="{ 'q-pl-sm': !dense && (icon || isTruncated ) , 'q-pl-xs': dense && (icon || isTruncated) }"
|
|
@@ -46,10 +46,21 @@ const props = defineProps({
|
|
|
46
46
|
type: [String, Number],
|
|
47
47
|
default: ''
|
|
48
48
|
},
|
|
49
|
+
hideLabel: {
|
|
50
|
+
type: Boolean,
|
|
51
|
+
default: false
|
|
52
|
+
},
|
|
49
53
|
tooltip: {
|
|
50
54
|
type: String,
|
|
51
55
|
default: undefined
|
|
52
56
|
},
|
|
57
|
+
tooltipBehavior: {
|
|
58
|
+
type: String,
|
|
59
|
+
default: 'truncated',
|
|
60
|
+
validator: (value) => {
|
|
61
|
+
return ['always', 'truncated', 'never'].includes(value)
|
|
62
|
+
}
|
|
63
|
+
},
|
|
53
64
|
icon: {
|
|
54
65
|
type: String,
|
|
55
66
|
default: undefined
|
|
@@ -66,6 +77,10 @@ const props = defineProps({
|
|
|
66
77
|
type: String,
|
|
67
78
|
default: 'check'
|
|
68
79
|
},
|
|
80
|
+
hideIcon: {
|
|
81
|
+
type: Boolean,
|
|
82
|
+
default: false
|
|
83
|
+
},
|
|
69
84
|
modelValue: {
|
|
70
85
|
type: Boolean,
|
|
71
86
|
default: true
|
|
@@ -129,11 +144,17 @@ const computedState = computed({
|
|
|
129
144
|
}
|
|
130
145
|
})
|
|
131
146
|
const computedLabel = computed(() => {
|
|
132
|
-
return i18n.tie(props.label)
|
|
147
|
+
if (!props.hideLabel && props.label) return i18n.tie(props.label)
|
|
148
|
+
})
|
|
149
|
+
const computedIcon = computed(() => {
|
|
150
|
+
if (!props.hideIcon) return props.icon
|
|
133
151
|
})
|
|
134
152
|
const computedTooltip = computed(() => {
|
|
135
|
-
if (props.
|
|
136
|
-
if (props.
|
|
153
|
+
if (props.tooltipBehavior === 'never') return
|
|
154
|
+
if (props.tooltipBehavior === 'truncated') {
|
|
155
|
+
if (!isTruncated.value) return
|
|
156
|
+
}
|
|
157
|
+
return props.label ? i18n.tie(props.label) : props.tooltip
|
|
137
158
|
})
|
|
138
159
|
const computedColor = computed(() => {
|
|
139
160
|
return props.outline ? 'transparent' : getHtmlColor(props.color)
|
|
@@ -153,7 +174,7 @@ const computedBorderColor = computed(() => {
|
|
|
153
174
|
function onResize () {
|
|
154
175
|
// check whether the label is truncated
|
|
155
176
|
const chipElement = document.getElementById(id)
|
|
156
|
-
if (chipElement) isTruncated.value = (chipElement && chipElement.offsetWidth < chipElement.scrollWidth)
|
|
177
|
+
if (chipElement) isTruncated.value = (chipElement && (chipElement.offsetWidth < chipElement.scrollWidth))
|
|
157
178
|
}
|
|
158
179
|
</script>
|
|
159
180
|
|
|
@@ -163,10 +163,10 @@ export default {
|
|
|
163
163
|
computedSections () {
|
|
164
164
|
return _.map(this.sections, (value, key) => {
|
|
165
165
|
return {
|
|
166
|
-
...value,
|
|
167
166
|
item: this.item,
|
|
168
167
|
actions: _.filter(this.itemActions, { scope: key }),
|
|
169
|
-
dense: this.dense
|
|
168
|
+
dense: this.dense,
|
|
169
|
+
...value
|
|
170
170
|
}
|
|
171
171
|
})
|
|
172
172
|
},
|
|
@@ -11,7 +11,11 @@ async function authenticate(authentication) {
|
|
|
11
11
|
let user = Store.get('user')
|
|
12
12
|
if (user) return
|
|
13
13
|
// Store latest authentication data for offline mode
|
|
14
|
-
|
|
14
|
+
try {
|
|
15
|
+
await LocalCache.setItem('authentication', authentication)
|
|
16
|
+
} catch (error) {
|
|
17
|
+
logger.error(error)
|
|
18
|
+
}
|
|
15
19
|
// Anonymous user or user account ?
|
|
16
20
|
user = authentication.user ? authentication.user : { name: i18n.t('composables.ANONYMOUS'), anonymous: true }
|
|
17
21
|
Store.set('user', user)
|
package/extras/tours/pane.top.js
CHANGED
|
@@ -105,6 +105,15 @@ module.exports = {
|
|
|
105
105
|
}
|
|
106
106
|
}, options)
|
|
107
107
|
},
|
|
108
|
+
toggleZoomControl: (options) => {
|
|
109
|
+
return Object.assign({
|
|
110
|
+
target: '#toggle-zoom-control-sticky',
|
|
111
|
+
title: 'tours.navigation-bar.ZOOM_CONTROL_LABEL',
|
|
112
|
+
params: {
|
|
113
|
+
placement: 'bottom'
|
|
114
|
+
}
|
|
115
|
+
}, options)
|
|
116
|
+
},
|
|
108
117
|
positionIndicator: (options) => {
|
|
109
118
|
return Object.assign({
|
|
110
119
|
target: '#position-indicator',
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import _ from 'lodash'
|
|
2
|
+
import errors from '@feathersjs/errors'
|
|
2
3
|
import { marshallGeometry } from '../marshall.js'
|
|
3
4
|
import makeDebug from 'debug'
|
|
4
5
|
|
|
5
6
|
const debug = makeDebug('kdk:map:query:hooks')
|
|
7
|
+
const { Forbidden } = errors
|
|
6
8
|
|
|
7
9
|
export function marshallGeometryQuery (hook) {
|
|
8
10
|
const query = hook.params.query
|
|
@@ -238,6 +240,62 @@ export function asGeoJson (options = {}) {
|
|
|
238
240
|
}
|
|
239
241
|
}
|
|
240
242
|
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
// Verifies that only authorized accumulation operators are used
|
|
246
|
+
function validateGroupExpression(expression) {
|
|
247
|
+
// Safe accumulation operators for $group
|
|
248
|
+
const SAFE_GROUP_ACCUMULATORS = new Set([
|
|
249
|
+
'$sum', '$avg', '$first', '$last', '$max', '$min'
|
|
250
|
+
])
|
|
251
|
+
// Safe expression operators usable in $group
|
|
252
|
+
const SAFE_GROUP_EXPRESSIONS = new Set([
|
|
253
|
+
// Arithmetic operators
|
|
254
|
+
'$add', '$subtract', '$multiply', '$divide', '$mod',
|
|
255
|
+
'$abs', '$ceil', '$floor', '$round', '$trunc',
|
|
256
|
+
'$sqrt', '$pow', '$exp', '$ln', '$log', '$log10',
|
|
257
|
+
// Comparison operators
|
|
258
|
+
'$eq', '$ne', '$gt', '$gte', '$lt', '$lte', '$cmp',
|
|
259
|
+
// Logical operators
|
|
260
|
+
'$and', '$or', '$not',
|
|
261
|
+
// Date operators
|
|
262
|
+
'$dateToString', '$year', '$month', '$dayOfMonth', '$hour',
|
|
263
|
+
'$minute', '$second', '$dayOfWeek', '$dayOfYear', '$week',
|
|
264
|
+
// Conditional operators
|
|
265
|
+
'$cond', '$ifNull', '$switch',
|
|
266
|
+
// Type operators
|
|
267
|
+
'$toString', '$toInt', '$toDouble'
|
|
268
|
+
])
|
|
269
|
+
// Dangerous operators to reject in $group (arbitrary code execution)
|
|
270
|
+
const DANGEROUS_GROUP_OPERATORS = new Set([
|
|
271
|
+
'$accumulator', // Custom JavaScript execution
|
|
272
|
+
'$function', // JavaScript function execution
|
|
273
|
+
'$where' // JavaScript code execution
|
|
274
|
+
])
|
|
275
|
+
|
|
276
|
+
if (_.isNil(expression)) {
|
|
277
|
+
return
|
|
278
|
+
} else if (Array.isArray(expression)) {
|
|
279
|
+
expression.forEach(validateGroupExpression)
|
|
280
|
+
return
|
|
281
|
+
} else if (typeof expression === 'object') {
|
|
282
|
+
for (const [key, value] of Object.entries(expression)) {
|
|
283
|
+
// Check if it's an operator
|
|
284
|
+
if (key.startsWith('$')) {
|
|
285
|
+
if (DANGEROUS_GROUP_OPERATORS.has(key)) throw new Forbidden(`You are not allowed to use ${key} operator`)
|
|
286
|
+
|
|
287
|
+
// Check if operator is in the allowed list
|
|
288
|
+
const isAccumulator = SAFE_GROUP_ACCUMULATORS.has(key)
|
|
289
|
+
const isExpression = SAFE_GROUP_EXPRESSIONS.has(key)
|
|
290
|
+
if (!isAccumulator && !isExpression) throw new Forbidden(`You are not allowed to use ${key} operator`)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Recursively validate the value
|
|
294
|
+
validateGroupExpression(value)
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
241
299
|
export async function aggregateFeaturesQuery (hook) {
|
|
242
300
|
const query = hook.params.query
|
|
243
301
|
const service = hook.service
|
|
@@ -294,6 +352,8 @@ export async function aggregateFeaturesQuery (hook) {
|
|
|
294
352
|
}
|
|
295
353
|
// Merge with any additional group expression
|
|
296
354
|
const group = _.get(query, '$group', {})
|
|
355
|
+
// Check for possible injection
|
|
356
|
+
validateGroupExpression(group)
|
|
297
357
|
Object.assign(groupBy, group)
|
|
298
358
|
// The query contains the match stage except options relevent to the aggregation pipeline
|
|
299
359
|
const match = _.omit(query, ['$group', '$groupBy', '$aggregate', '$geoNear', '$sort', '$limit', '$skip'])
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
id="zoom-out"
|
|
5
5
|
icon="remove"
|
|
6
6
|
tooltip="mixins.activity.ZOOM_OUT"
|
|
7
|
-
color="
|
|
8
|
-
text-color="
|
|
7
|
+
:color="color"
|
|
8
|
+
:text-color="textColor"
|
|
9
9
|
:flat="square"
|
|
10
10
|
round
|
|
11
11
|
@click="onZoomOutFn"
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
id="zoom-in"
|
|
18
18
|
icon="add"
|
|
19
19
|
tooltip="mixins.activity.ZOOM_IN"
|
|
20
|
-
color="
|
|
21
|
-
text-color="
|
|
20
|
+
:color="color"
|
|
21
|
+
:text-color="textColor"
|
|
22
22
|
:flat="square"
|
|
23
23
|
round
|
|
24
24
|
@click="onZoomInFn"
|
|
@@ -33,16 +33,24 @@ import { composables as kCoreComposables } from '@kalisio/kdk/core.client'
|
|
|
33
33
|
// Props
|
|
34
34
|
defineProps({
|
|
35
35
|
vertical: {
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
type: Boolean,
|
|
37
|
+
default: true
|
|
38
38
|
},
|
|
39
39
|
square: {
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
type: Boolean,
|
|
41
|
+
default: false
|
|
42
42
|
},
|
|
43
43
|
size: {
|
|
44
|
+
type: String,
|
|
44
45
|
default: '10px',
|
|
45
|
-
|
|
46
|
+
},
|
|
47
|
+
color: {
|
|
48
|
+
type: String,
|
|
49
|
+
default: 'white'
|
|
50
|
+
},
|
|
51
|
+
textColor: {
|
|
52
|
+
type: String,
|
|
53
|
+
default: 'grey-9'
|
|
46
54
|
}
|
|
47
55
|
})
|
|
48
56
|
|
|
@@ -114,6 +114,12 @@
|
|
|
114
114
|
"PRINT_VIEW": "Print view",
|
|
115
115
|
"REMOVE_LABEL": "Remove"
|
|
116
116
|
},
|
|
117
|
+
"gesture": {
|
|
118
|
+
"TOUCH": "Use two fingers to move the map",
|
|
119
|
+
"SCROLL": "Use Ctrl + scroll to zoom the map",
|
|
120
|
+
"SCROLL_MAC": "Use ⌘ + scroll to zoom the map",
|
|
121
|
+
"CTRL": "Use Ctrl + scroll to zoom the map"
|
|
122
|
+
},
|
|
117
123
|
"timeseries": {
|
|
118
124
|
"FORECAST_PROBE": "Forecast"
|
|
119
125
|
},
|
|
@@ -312,7 +318,8 @@
|
|
|
312
318
|
"TOGGLE_VR_LABEL": "Switch to Virtual Reality (VR) mode with this button.",
|
|
313
319
|
"TOGGLE_FULLSCREEN_LABEL": "Switch to fullscreen mode with this button.",
|
|
314
320
|
"TOGGLE_CATALOG_LABEL": "Open the catalog with this button.",
|
|
315
|
-
"NORTH_ARROW_LABEL": "Display or hide the north arrow using this button."
|
|
321
|
+
"NORTH_ARROW_LABEL": "Display or hide the north arrow using this button.",
|
|
322
|
+
"ZOOM_CONTROL_LABEL": "Display or hide the zoom control buttons using this button."
|
|
316
323
|
},
|
|
317
324
|
"catalog-panel": {
|
|
318
325
|
"CATALOG_LABEL": "The <b>catalog</b> allows to manage data displayed on your map.<br/>The catalogd is divided into 3 different tabs.",
|
|
@@ -114,6 +114,12 @@
|
|
|
114
114
|
"PRINT_VIEW": "Imprimer la vue",
|
|
115
115
|
"REMOVE_LABEL": "Supprimer"
|
|
116
116
|
},
|
|
117
|
+
"gesture": {
|
|
118
|
+
"TOUCH": "Utilisez deux doigts pour déplacer la carte",
|
|
119
|
+
"SCROLL": "Utilisez Ctrl + molette pour zoomer",
|
|
120
|
+
"SCROLL_MAC": "Utilisez ⌘ + molette pour zoomer",
|
|
121
|
+
"CTRL": "Utilisez Ctrl + molette pour zoomer"
|
|
122
|
+
},
|
|
117
123
|
"timeseries": {
|
|
118
124
|
"FORECAST_PROBE": "Prévisions"
|
|
119
125
|
},
|
|
@@ -312,7 +318,8 @@
|
|
|
312
318
|
"TOGGLE_VR_LABEL": "Basculez en mode <b>Réalité Virtuelle</b> (VR) avec ce bouton.",
|
|
313
319
|
"TOGGLE_FULLSCREEN_LABEL": "Basculez en mode plein écran avec ce bouton.",
|
|
314
320
|
"TOGGLE_CATALOG_LABEL": "Ouvrez le <b>catalogue</b> avec ce bouton.",
|
|
315
|
-
"NORTH_ARROW_LABEL": "Affichez ou cachez la flèche nord avec ce bouton."
|
|
321
|
+
"NORTH_ARROW_LABEL": "Affichez ou cachez la flèche nord avec ce bouton.",
|
|
322
|
+
"ZOOM_CONTROL_LABEL": "Affichez ou cachez les boutons de zoom avec ce bouton."
|
|
316
323
|
},
|
|
317
324
|
"catalog-panel": {
|
|
318
325
|
"CATALOG_LABEL": "Le <b>catalogue</b> permet de gérer les données affichées sur votre carte.<br/>Le catalogue se décompose en 3 onglets.",
|
|
@@ -3,6 +3,8 @@ import sift from 'sift'
|
|
|
3
3
|
import logger from 'loglevel'
|
|
4
4
|
import moment from 'moment'
|
|
5
5
|
import { EventBus } from 'quasar'
|
|
6
|
+
import { Platform } from '../../../../core/client/platform.js'
|
|
7
|
+
import { i18n } from '../../../../core/client/i18n.js'
|
|
6
8
|
import { point, rhumbDistance, rhumbBearing, rhumbDestination } from '@turf/turf'
|
|
7
9
|
import L from 'leaflet'
|
|
8
10
|
import 'leaflet/dist/leaflet.css'
|
|
@@ -24,6 +26,8 @@ import 'leaflet-timedimension/dist/leaflet.timedimension.control.css'
|
|
|
24
26
|
import '@geoman-io/leaflet-geoman-free'
|
|
25
27
|
import '@geoman-io/leaflet-geoman-free/dist/leaflet-geoman.css'
|
|
26
28
|
import 'leaflet-rotate/dist/leaflet-rotate-src.js'
|
|
29
|
+
import 'leaflet-gesture-handling'
|
|
30
|
+
import 'leaflet-gesture-handling/dist/leaflet-gesture-handling.css'
|
|
27
31
|
|
|
28
32
|
import { Time } from '../../../../core/client/time.js'
|
|
29
33
|
import { Events } from '../../../../core/client/events.js'
|
|
@@ -108,8 +112,22 @@ export const baseMap = {
|
|
|
108
112
|
geolocate: true,
|
|
109
113
|
rotateControl: false // Rotate plugin show this even if rotation is disabled
|
|
110
114
|
})
|
|
115
|
+
const gestureHandlingOptions = {
|
|
116
|
+
text: {
|
|
117
|
+
touch: i18n.t('mixins.gesture.TOUCH'),
|
|
118
|
+
scroll: i18n.t('mixins.gesture.SCROLL'),
|
|
119
|
+
scrollMac: i18n.t('mixins.gesture.SCROLL_MAC'),
|
|
120
|
+
ctrl: i18n.t('mixins.gesture.CTRL')
|
|
121
|
+
},
|
|
122
|
+
duration: 1000
|
|
123
|
+
}
|
|
111
124
|
// Initialize the map
|
|
112
|
-
this.map = L.map(domEl, Object.assign({
|
|
125
|
+
this.map = L.map(domEl, Object.assign({
|
|
126
|
+
zoomControl: false,
|
|
127
|
+
touchZoom: true,
|
|
128
|
+
gestureHandling: Platform.has.touch && Platform.is.desktop && !Platform.is.firefox,
|
|
129
|
+
gestureHandlingOptions
|
|
130
|
+
}, viewerOptions))
|
|
113
131
|
const backgroundColor = _.get(viewerOptions, 'backgroundColor')
|
|
114
132
|
if (backgroundColor) this.map._container.style.backgroundColor = backgroundColor
|
|
115
133
|
// Make sure geoman is initialized on the map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kalisio/kdk",
|
|
3
3
|
"description": "Kalisio Development Kit",
|
|
4
|
-
"version": "2.6.
|
|
4
|
+
"version": "2.6.4",
|
|
5
5
|
"homepage": "https://github.com/kalisio/kdk",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"keywords": [
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"@feathersjs/feathers": "^5.0.8",
|
|
95
95
|
"@feathersjs/schema": "^5.0.8",
|
|
96
96
|
"@feathersjs/socketio": "^5.0.8",
|
|
97
|
-
"@kalisio/feathers-import-export": "^1.
|
|
97
|
+
"@kalisio/feathers-import-export": "^1.4.0",
|
|
98
98
|
"@kalisio/feathers-s3": "^1.5.0",
|
|
99
99
|
"@kalisio/feathers-webpush": "^1.0.2",
|
|
100
100
|
"@turf/bbox": "^6.0.1",
|
|
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
### Added
|
|
11
11
|
|
|
12
|
+
- added `get_toml_value` to extract values from TOML fields
|
|
12
13
|
- support for mongo 8 (`install_mongo8`)
|
|
13
14
|
- added `get_flavor_from_git_ref` `get_version_from_git_ref` `get_custom_from_git_ref` helpers to parse git ref names (tag or branch names).
|
|
14
15
|
- added `install_sona_scanner_cli` to install SonarQube scanner cli tool
|
|
@@ -18,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
18
19
|
- support for mongo 4,5,6
|
|
19
20
|
- helper to install k9s
|
|
20
21
|
- support for node 16,18
|
|
22
|
+
- support for Code Climate
|
|
21
23
|
|
|
22
24
|
### Changed
|
|
23
25
|
|
|
@@ -32,6 +34,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
32
34
|
- node22 bumped to `22.16`
|
|
33
35
|
- mongodb7 bumped to `7.0.21`
|
|
34
36
|
- mongodb8 bumped to `8.0.10`
|
|
37
|
+
- allow `_` character in custom fields (see `get_custom_from_git_ref`)
|
|
38
|
+
- run_app_tests now run lint on whole project repo (using `yarn lint`)
|
|
35
39
|
|
|
36
40
|
### Fixed
|
|
37
41
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2017-20xx Kalisio
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/scripts/kash/README.md
CHANGED
|
@@ -2,3 +2,12 @@
|
|
|
2
2
|
Kalisio's bash snippets repository
|
|
3
3
|
|
|
4
4
|
## HOWTO use
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
## License
|
|
8
|
+
|
|
9
|
+
Licensed under the [MIT license](LICENSE).
|
|
10
|
+
|
|
11
|
+
Copyright (c) 2017-present [Kalisio](https://kalisio.com)
|
|
12
|
+
|
|
13
|
+
[](https://kalisio.com)
|
package/scripts/kash/kash.sh
CHANGED
|
@@ -129,11 +129,11 @@ AGE_VERSION=1.1.1
|
|
|
129
129
|
SOPS_VERSION=3.8.1
|
|
130
130
|
|
|
131
131
|
# https://github.com/kubernetes/kubernetes/tree/master/CHANGELOG
|
|
132
|
-
KUBECTL_VERSION=1.
|
|
132
|
+
KUBECTL_VERSION=1.31.11
|
|
133
133
|
# https://github.com/helm/helm/releases
|
|
134
|
-
HELM_VERSION=3.
|
|
134
|
+
HELM_VERSION=3.18.4
|
|
135
135
|
# https://github.com/helmfile/helmfile/releases
|
|
136
|
-
HELMFILE_VERSION=
|
|
136
|
+
HELMFILE_VERSION=1.1.3
|
|
137
137
|
|
|
138
138
|
# https://github.com/nvm-sh/nvm/releases
|
|
139
139
|
NVM_VERSION=0.40.3
|
|
@@ -246,34 +246,6 @@ ensure_sops() {
|
|
|
246
246
|
fi
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
-
# Install code climate test reporter in ~/.local/bin
|
|
250
|
-
# Arg1: a writable folder where to write downloaded files
|
|
251
|
-
install_cc_test_reporter() {
|
|
252
|
-
local DL_ROOT=$1
|
|
253
|
-
local DL_PATH="$DL_ROOT/cc"
|
|
254
|
-
if [ ! -d "$DL_PATH" ]; then
|
|
255
|
-
mkdir -p "$DL_PATH" && cd "$DL_PATH"
|
|
256
|
-
curl -OLsS https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64
|
|
257
|
-
curl -OLsS https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64.sha256
|
|
258
|
-
sha256sum --ignore-missing --quiet -c test-reporter-latest-linux-amd64.sha256
|
|
259
|
-
cd ~-
|
|
260
|
-
fi
|
|
261
|
-
cd "$DL_PATH"
|
|
262
|
-
cp test-reporter-latest-linux-amd64 ~/.local/bin/cc-test-reporter
|
|
263
|
-
chmod +x ~/.local/bin/cc-test-reporter
|
|
264
|
-
cd ~-
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
# Sends test coverage to code climate
|
|
268
|
-
# Arg1: code climate identifier for authentication
|
|
269
|
-
# Arg2: prefix to use when using format-coverage (can be empty)
|
|
270
|
-
send_coverage_to_cc() {
|
|
271
|
-
local CC_TEST_REPORTER_ID=$1
|
|
272
|
-
local CC_PREFIX=${2:-}
|
|
273
|
-
~/.local/bin/cc-test-reporter format-coverage -t lcov --add-prefix "$CC_PREFIX" coverage/lcov.info
|
|
274
|
-
~/.local/bin/cc-test-reporter upload-coverage -r "$CC_TEST_REPORTER_ID"
|
|
275
|
-
}
|
|
276
|
-
|
|
277
249
|
# Make sure nvm is installed
|
|
278
250
|
# Arg1: a writable folder where to write downloaded files
|
|
279
251
|
# NOTE: also define 'yarn' as a default package, ie. it'll be automatically
|
|
@@ -486,12 +458,28 @@ use_mongo() {
|
|
|
486
458
|
### Utils
|
|
487
459
|
###
|
|
488
460
|
|
|
461
|
+
# Extract a value from a TOML file
|
|
462
|
+
# Expected args:
|
|
463
|
+
# 1. the toml file
|
|
464
|
+
# 2. the field to extract
|
|
465
|
+
get_toml_value() {
|
|
466
|
+
local TOML_SRC="$1"
|
|
467
|
+
local TOML_FIELD="$2"
|
|
468
|
+
|
|
469
|
+
ensure_yq
|
|
470
|
+
yq --input-format=toml --output-format=yaml ".$TOML_FIELD" "$TOML_SRC"
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
# Extract a value from a JSON file
|
|
474
|
+
# Expected args:
|
|
475
|
+
# 1. the toml file
|
|
476
|
+
# 2. the field to extract
|
|
489
477
|
get_json_value() {
|
|
490
478
|
local JSON_SRC="$1"
|
|
491
479
|
local JSON_FIELD="$2"
|
|
492
480
|
|
|
493
481
|
ensure_yq
|
|
494
|
-
yq --output-format=yaml ".$JSON_FIELD" "$JSON_SRC"
|
|
482
|
+
yq --input-format=yaml --output-format=yaml ".$JSON_FIELD" "$JSON_SRC"
|
|
495
483
|
}
|
|
496
484
|
|
|
497
485
|
# Extract version major from a version string.
|
|
@@ -1003,7 +991,7 @@ get_version_from_git_ref() {
|
|
|
1003
991
|
get_custom_from_git_ref() {
|
|
1004
992
|
local GIT_REF=$1
|
|
1005
993
|
|
|
1006
|
-
local CUSTOM_REGEX="(^|-)([a-zA-Z0-
|
|
994
|
+
local CUSTOM_REGEX="(^|-)([a-zA-Z0-9_]+)$"
|
|
1007
995
|
if [[ "$GIT_REF" =~ $CUSTOM_REGEX ]]; then
|
|
1008
996
|
if [[ "${BASH_REMATCH[1]}" == "" ]]; then
|
|
1009
997
|
# If first capture group is empty => that's probably a 'dev' flavor.
|
|
@@ -1284,13 +1272,13 @@ get_app_kli_file() {
|
|
|
1284
1272
|
# Expected arguments:
|
|
1285
1273
|
# 1. the app repository directory
|
|
1286
1274
|
# 2. the directory in which we'll find kli files relative to the 'development' repository root directory
|
|
1287
|
-
# 3.
|
|
1275
|
+
# 3. whether to run SonarQube analysis and publish code quality & coverage results (boolean)
|
|
1288
1276
|
# 4. the node version to use (16, 18, ...)
|
|
1289
1277
|
# 5. the mongo version to use (5, 6, ...). Mongo will not be started if not provided
|
|
1290
1278
|
run_app_tests() {
|
|
1291
1279
|
local REPO_DIR="$1"
|
|
1292
1280
|
local KLI_BASE="$2"
|
|
1293
|
-
local
|
|
1281
|
+
local RUN_SONAR="$3"
|
|
1294
1282
|
local NODE_VER="$4"
|
|
1295
1283
|
local MONGO_VER="$5"
|
|
1296
1284
|
local WORKSPACE_DIR
|
|
@@ -1307,6 +1295,13 @@ run_app_tests() {
|
|
|
1307
1295
|
|
|
1308
1296
|
echo "About to run tests for $APP v$VERSION-$FLAVOR ..."
|
|
1309
1297
|
|
|
1298
|
+
## Lint whole project
|
|
1299
|
+
##
|
|
1300
|
+
|
|
1301
|
+
cd "$REPO_DIR"
|
|
1302
|
+
yarn lint
|
|
1303
|
+
cd ~-
|
|
1304
|
+
|
|
1310
1305
|
## Start mongo
|
|
1311
1306
|
##
|
|
1312
1307
|
|
|
@@ -1322,19 +1317,19 @@ run_app_tests() {
|
|
|
1322
1317
|
## Run tests
|
|
1323
1318
|
##
|
|
1324
1319
|
|
|
1325
|
-
|
|
1320
|
+
cd "$REPO_DIR/api"
|
|
1326
1321
|
|
|
1327
1322
|
use_node "$NODE_VER"
|
|
1328
1323
|
yarn test
|
|
1329
1324
|
|
|
1330
|
-
##
|
|
1325
|
+
## Run SonarQube analysis and publish code quality & coverage reports
|
|
1331
1326
|
##
|
|
1332
1327
|
|
|
1333
|
-
if [ "$
|
|
1334
|
-
|
|
1328
|
+
if [ "$RUN_SONAR" = true ]; then
|
|
1329
|
+
cd "$ROOT_DIR" && sonar-scanner
|
|
1335
1330
|
fi
|
|
1336
1331
|
|
|
1337
|
-
|
|
1332
|
+
cd ~-
|
|
1338
1333
|
}
|
|
1339
1334
|
|
|
1340
1335
|
# Setup the workspace for a lib project.
|
|
@@ -1397,12 +1392,12 @@ get_lib_branch() {
|
|
|
1397
1392
|
# Run tests for a library module
|
|
1398
1393
|
# Expected arguments
|
|
1399
1394
|
# 1. Root directory
|
|
1400
|
-
# 2.
|
|
1395
|
+
# 2. whether to run SonarQube analysis and publish code quality & coverage results (boolean)
|
|
1401
1396
|
# 3. node version to be used
|
|
1402
1397
|
# 4. mongo version to be used if required by tests
|
|
1403
1398
|
run_lib_tests () {
|
|
1404
1399
|
local ROOT_DIR="$1"
|
|
1405
|
-
local
|
|
1400
|
+
local RUN_SONAR="$2"
|
|
1406
1401
|
local NODE_VER="$3"
|
|
1407
1402
|
local MONGO_VER="$4"
|
|
1408
1403
|
local WORKSPACE_DIR
|
|
@@ -1437,11 +1432,11 @@ run_lib_tests () {
|
|
|
1437
1432
|
use_node "$NODE_VER"
|
|
1438
1433
|
yarn && yarn test
|
|
1439
1434
|
|
|
1440
|
-
##
|
|
1435
|
+
## Run SonarQube analysis and publish code quality & coverage reports
|
|
1441
1436
|
##
|
|
1442
1437
|
|
|
1443
|
-
if [ "$
|
|
1444
|
-
|
|
1438
|
+
if [ "$RUN_SONAR" = true ]; then
|
|
1439
|
+
cd "$ROOT_DIR" && sonar-scanner
|
|
1445
1440
|
fi
|
|
1446
1441
|
}
|
|
1447
1442
|
|
|
@@ -11,7 +11,7 @@ ROOT_DIR=$(dirname "$THIS_DIR")
|
|
|
11
11
|
### Github Actions
|
|
12
12
|
|
|
13
13
|
init_github() {
|
|
14
|
-
install_reqs yq age sops nvm node20 node22
|
|
14
|
+
install_reqs yq age sops nvm node20 node22 sonar_scanner_cli
|
|
15
15
|
|
|
16
16
|
# mongo is not available for alpine hosts
|
|
17
17
|
if [ "$OS_ID" != "alpine" ]; then
|
|
@@ -49,6 +49,8 @@ mkdir -p "$TMP_DIR/utils"
|
|
|
49
49
|
cd "$TMP_DIR/utils"
|
|
50
50
|
curl -OLsS "https://raw.githubusercontent.com/kalisio/krawler/master/package.json"
|
|
51
51
|
[ "$(get_json_value "$TMP_DIR/utils/package.json" 'name')" != "@kalisio/krawler" ] && exit 1
|
|
52
|
+
curl -OLsS "https://raw.githubusercontent.com/kalisio/kazarr/refs/heads/master/pyproject.toml"
|
|
53
|
+
[ "$(get_toml_value "$TMP_DIR/utils/pyproject.toml" 'project.name')" != "kazarr" ] && exit 1
|
|
52
54
|
cd ~-
|
|
53
55
|
|
|
54
56
|
[ "$(get_semver_major "1" )" != "1" ] && exit 1
|
|
@@ -129,6 +131,7 @@ git_shallow_clone https://github.com/kalisio/kApp.git "$TMP_DIR/kApp.v1.3.0" pro
|
|
|
129
131
|
[[ "$(get_custom_from_git_ref "test-v4.3-blabla")" != "blabla" ]] && exit 1
|
|
130
132
|
[[ "$(get_custom_from_git_ref "prod-v4.5.3")" != "" ]] && exit 1
|
|
131
133
|
[[ "$(get_custom_from_git_ref "prod-v4.5.3-custom")" != "custom" ]] && exit 1
|
|
134
|
+
[[ "$(get_custom_from_git_ref "prod-v4.5.3-custom_foo")" != "custom_foo" ]] && exit 1
|
|
132
135
|
|
|
133
136
|
# Setup a fake workspace with additional dependencies
|
|
134
137
|
mkdir -p "$TMP_DIR/fake"
|
|
@@ -402,6 +402,27 @@ describe('map:services', () => {
|
|
|
402
402
|
$aggregate: ['H']
|
|
403
403
|
}
|
|
404
404
|
|
|
405
|
+
it('performs unauthorised element aggregation on vigicrues observations service', async () => {
|
|
406
|
+
try {
|
|
407
|
+
await vigicruesObsService.find({
|
|
408
|
+
query: Object.assign({ $sort: { time: -1 }, $limit: 1, $group: { property: { $accumulator: { lang: 'js' } } } }, aggregationQuery)
|
|
409
|
+
})
|
|
410
|
+
} catch (error) {
|
|
411
|
+
expect(error).toExist()
|
|
412
|
+
expect(error.name).to.equal('Forbidden')
|
|
413
|
+
}
|
|
414
|
+
try {
|
|
415
|
+
await vigicruesObsService.find({
|
|
416
|
+
query: Object.assign({ $sort: { time: -1 }, $limit: 1, $group: { property: { $sum: { $function: { lang: 'js' } } } } }, aggregationQuery)
|
|
417
|
+
})
|
|
418
|
+
} catch (error) {
|
|
419
|
+
expect(error).toExist()
|
|
420
|
+
expect(error.name).to.equal('Forbidden')
|
|
421
|
+
}
|
|
422
|
+
})
|
|
423
|
+
// Let enough time to process
|
|
424
|
+
.timeout(5000)
|
|
425
|
+
|
|
405
426
|
it('performs element aggregation on vigicrues observations service', async () => {
|
|
406
427
|
const result = await vigicruesObsService.find({
|
|
407
428
|
query: Object.assign({}, aggregationQuery)
|