Skip to content

Commit

Permalink
Random multiplicity (#205)
Browse files Browse the repository at this point in the history
* [plugins/random] support multiple and randomly vanishing feed items for testing support

* [plugins/random] bump major version to 2.0.0

* [plugins/random] remove unnecessary validation error

* [plugins/random] add id to feature title to distinguish points in list view
  • Loading branch information
restjohn committed Apr 19, 2024
1 parent e9ff0c2 commit b4697c4
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 44 deletions.
2 changes: 1 addition & 1 deletion plugins/random/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion plugins/random/package.json
@@ -1,6 +1,6 @@
{
"name": "@ngageoint/mage.random",
"version": "1.0.0",
"version": "2.0.0",
"description": "Feed plugin that returns random geojson points",
"main": "lib/index.js",
"files": [
Expand Down
61 changes: 27 additions & 34 deletions plugins/random/src/random.ts
Expand Up @@ -18,15 +18,6 @@ export class RandomServiceType implements FeedServiceType {
readonly configSchema: JSONSchema4 = {}

async validateServiceConfig(config: Json): Promise<null | InvalidServiceConfigError> {
if (typeof config !== 'string') {
return new FeedsError(ErrInvalidServiceConfig, { invalidKeys: [], config }, 'config must be a url string')
}
try {
new URL(config)
}
catch (err) {
return new FeedsError(ErrInvalidServiceConfig, { invalidKeys: [], config }, 'invalid service url')
}
return null
}

Expand Down Expand Up @@ -60,41 +51,43 @@ export class RandomConnection implements FeedServiceConnection {
}

async fetchTopicContent(topic: string, params?: JsonObject | undefined): Promise<FeedTopicContent> {
let latitude: number = params?.latitude as number || 0.0
let longitude: number = params?.longitude as number || 0.0
const move: boolean = params?.move as boolean || false
if (move) {
latitude += Math.random() * .001
longitude += Math.random() * .001
}
const titlePrefix = typeof params?.titlePrefix === 'string' ? params.titlePrefix : topic
const feature: Feature = {
type: 'Feature',
id: "1",
properties: {
timestamp: new Date(),
title: `${titlePrefix} random point`
},
geometry: {
type: 'Point',
coordinates: [longitude, latitude]
const typedParams = Random.resolveFetchParametersJson(params)
const itemIds: string[] = Array.from({ length: typedParams.maxItems }).map((_, index) => String(index))
const itemCount = Math.trunc(Math.random() * (typedParams.maxItems - typedParams.minItems + 1)) + typedParams.minItems
const { latitude, longitude } = typedParams
const features: Feature[] = Array.from({ length: itemCount }).map(_ => {
const coordinates = [
typedParams.scatterDegrees * (Math.random() * 2 - 1) + longitude,
typedParams.scatterDegrees * (Math.random() * 2 - 1) + latitude
]
const nextId = typedParams.randomOrder ? itemIds.splice(Math.trunc(Math.random() * itemIds.length), 1)[0] : itemIds.shift()
const titlePrefix = typeof params?.titlePrefix === 'string' ? params.titlePrefix : 'Wut'
return {
type: 'Feature',
id: nextId,
properties: {
timestamp: new Date(),
title: `${titlePrefix} random point ${nextId}`
},
geometry: {
type: 'Point',
coordinates
}
}
}

})
const topicModule = this.topics.get(topic)
if (!topicModule) {
throw new Error(`unknown topic: ${topic}`)
}
const res: RandomResponse = {
status: 200,
body: { feature }
body: { features }
}

const delay = parseFloat(String(params?.fetchDelay)) || 0
const delay = typedParams.fetchDelay
return new Promise(resolve => {
setTimeout(() => {
resolve(topicModule.transformResponse(res))
}, delay * 1000)
}, Math.trunc(delay * 1000))
})
}
}
Expand All @@ -109,7 +102,7 @@ export interface RandomRequest {
export interface RandomResponse {
status: number
body: {
feature: Feature
features: Feature[]
}
}

Expand Down
52 changes: 44 additions & 8 deletions plugins/random/src/topics/random.ts
@@ -1,6 +1,7 @@
import { FeedTopic, FeedTopicContent } from '@ngageoint/mage.service/lib/entities/feeds/entities.feeds'
import { RandomResponse } from '../random'
import { PluginResourceUrl } from '@ngageoint/mage.service/lib/entities/entities.global'
import { JsonObject } from '@ngageoint/mage.service/lib/entities/entities.json_types'

export const topicDescriptor: FeedTopic = {
id: 'random',
Expand All @@ -12,15 +13,32 @@ export const topicDescriptor: FeedTopic = {
properties: {
latitude: {
type: 'number',
description: 'Initial position latitude',
default: 0.0
},
longitude: {
type: 'number',
description: 'Initial position longitude',
default: 0.0
},
move: {
minItems: {
type: 'number',
default: 1
},
maxItems: {
type: 'number',
default: 1,
description: ''
},
randomOrder: {
type: 'boolean',
default: false
default: false,
description: 'Randomize the order of the item list between fetches. This will cause different feature IDs to disappear from the list when maxItems is greater than minItems.'
},
scatterDegrees: {
type: 'number',
description: 'Scatter the positions of the items randomly from the initial position within the given threshold of decimal degrees.',
default: 0.001
},
titlePrefix: {
type: 'string',
Expand All @@ -33,7 +51,7 @@ export const topicDescriptor: FeedTopic = {
description: 'Delay for the given number of seconds before returning any data.',
default: 0.0
}
}
},
},
itemsHaveIdentity: true,
itemsHaveSpatialDimension: true,
Expand Down Expand Up @@ -74,18 +92,36 @@ export const topicDescriptor: FeedTopic = {
}

export interface RandomTopicParams {
latitude: number,
longitude: number,
move?: boolean
fetchDelay?: number
latitude: number
longitude: number
minItems: number
maxItems: number
randomOrder: boolean
scatterDegrees: number
titlePrefix: string
fetchDelay: number
}

export function resolveFetchParametersJson(json?: JsonObject): RandomTopicParams {
json = json || {}
return {
latitude: typeof json.latitude === 'number' ? json.latitude : topicDescriptor.paramsSchema!.properties!.latitude.default as number,
longitude: typeof json.longitude === 'number' ? json.longitude : topicDescriptor.paramsSchema!.properties!.longitude.default as number ,
minItems: typeof json.minItems === 'number' ? json.minItems : topicDescriptor.paramsSchema!.properties!.minItems.default as number,
maxItems: typeof json.maxItems === 'number' ? json.maxItems : topicDescriptor.paramsSchema!.properties!.maxItems.default as number,
randomOrder: typeof json.randomOrder === 'boolean' ? json.randomOrder : topicDescriptor.paramsSchema!.properties!.randomOrder.default as boolean,
scatterDegrees: typeof json.scatterDegrees === 'number' ? json.scatterDegrees : topicDescriptor.paramsSchema!.properties!.scatterDegrees.default as number,
titlePrefix: typeof json.titlePrefix === 'string' ? json.titlePrefix : topicDescriptor.id,
fetchDelay: typeof json.fetchDelay === 'number' ? json.fetchDelay : topicDescriptor.paramsSchema!.properties!.fetchDelay.default as number
}
}

export const transformResponse = (res: RandomResponse): FeedTopicContent => {
return {
topic: topicDescriptor.id,
items: {
type: 'FeatureCollection',
features: [res.body.feature]
features: res.body.features
}
}
}

0 comments on commit b4697c4

Please sign in to comment.