Skip to content

Commit 25416d8

Browse files
committed
Merge branch 'fix-event-sync' of https://github.com/ngageoint/mage-server into fix-observation-sync
2 parents 3df2fea + e98eb82 commit 25416d8

File tree

7 files changed

+98
-69
lines changed

7 files changed

+98
-69
lines changed

plugins/arcgis/service/src/ArcGISConfig.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { MageEventId } from "@ngageoint/mage.service/lib/entities/events/entities.events"
2+
13
/**
24
* Contains an arc feature service url and layers.
35
*/
@@ -9,8 +11,8 @@ export interface FeatureServiceConfig {
911
url: string
1012

1113
/**
12-
* Serialized ArcGISIdentityManager
13-
*/
14+
* Serialized ArcGISIdentityManager
15+
*/
1416
identityManager: string
1517

1618
/**
@@ -35,9 +37,9 @@ export interface FeatureLayerConfig {
3537
geometryType?: string
3638

3739
/**
38-
* The event ids or names that sync to this arc feature layer.
40+
* The event ids that sync to this arc feature layer.
3941
*/
40-
events?: (number|string)[]
42+
eventIds?: MageEventId[]
4143
}
4244

4345

plugins/arcgis/service/src/EventDeletionHandler.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ export class EventDeletionHandler {
3434
this._config = config;
3535
}
3636

37+
public updateConfig(newConfig: ArcGISPluginConfig): void {
38+
this._config = newConfig;
39+
}
40+
3741
/**
3842
*
3943
* @param activeEvents The current set of active events.
@@ -83,7 +87,7 @@ export class EventDeletionHandler {
8387
}
8488

8589
/**
86-
* Called when the query is finished. It goes through the results and gathers all even Ids currently stored
90+
* Called when the query is finished. It goes through the results and gathers all event Ids currently stored
8791
* in the arc layer. It then will remove any events from the arc layer that do not exist.
8892
* @param layerProcessor The feature layer processor.
8993
* @param result The returned results.

plugins/arcgis/service/src/EventLayerProcessorOrganizer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export class EventLayerProcessorOrganizer {
1919
for (const event of events) {
2020
let syncProcessors = new Array<FeatureLayerProcessor>();
2121
for (const layerProcessor of layerProcessors) {
22-
if (layerProcessor.layerInfo.hasEvent(event.name)) {
22+
if (layerProcessor.layerInfo.hasEvent(event.id)) {
2323
syncProcessors.push(layerProcessor);
2424
}
2525
}

plugins/arcgis/service/src/FeatureServiceAdmin.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ArcGISPluginConfig } from "./ArcGISPluginConfig"
22
import { FeatureServiceConfig, FeatureLayerConfig } from "./ArcGISConfig"
3-
import { MageEvent, MageEventRepository } from '@ngageoint/mage.service/lib/entities/events/entities.events'
3+
import { MageEvent, MageEventId, MageEventRepository } from '@ngageoint/mage.service/lib/entities/events/entities.events'
44
import { Layer, Field } from "./AddLayersRequest"
55
import { Form, FormField, FormFieldType, FormId } from '@ngageoint/mage.service/lib/entities/events/entities.events.forms'
66
import { ObservationsTransformer } from "./ObservationsTransformer"
@@ -120,29 +120,29 @@ export class FeatureServiceAdmin {
120120
}
121121

122122
/**
123-
* Get the layer events
123+
* Get the Mage layer events
124124
* @param layer feature layer
125125
* @param eventRepo event repository
126-
* @returns layer events
126+
* @returns Mage layer events
127127
*/
128128
private async layerEvents(layer: FeatureLayerConfig, eventRepo: MageEventRepository): Promise<MageEvent[]> {
129-
const layerEvents: Set<number|string> = new Set()
130-
if (layer.events != null) {
131-
for (const layerEvent of layer.events) {
132-
layerEvents.add(layerEvent)
129+
const layerEventIds: Set<MageEventId> = new Set()
130+
if (layer.eventIds != null) {
131+
for (const layerEventId of layer.eventIds) {
132+
layerEventIds.add(layerEventId)
133133
}
134134
}
135135

136136
let mageEvents
137-
if (layerEvents.size > 0) {
137+
if (layerEventIds.size > 0) {
138138
mageEvents = await eventRepo.findAll()
139139
} else {
140140
mageEvents = await eventRepo.findActiveEvents()
141141
}
142142

143143
const events: MageEvent[] = []
144144
for (const mageEvent of mageEvents) {
145-
if (layerEvents.size == 0 || layerEvents.has(mageEvent.name) || layerEvents.has(mageEvent.id)) {
145+
if (layerEventIds.size == 0 || layerEventIds.has(mageEvent.id)) {
146146
const event = await eventRepo.findById(mageEvent.id)
147147
if (event != null) {
148148
events.push(event)

plugins/arcgis/service/src/LayerInfo.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { MageEventId } from "@ngageoint/mage.service/lib/entities/events/entities.events";
12
import { LayerInfoResult, LayerField } from "./LayerInfoResult";
23

34
/**
@@ -28,7 +29,7 @@ export class LayerInfo {
2829
/**
2930
* The events that are synching to this layer.
3031
*/
31-
events: Set<string> = new Set<string>()
32+
events: Set<MageEventId> = new Set<MageEventId>()
3233

3334
/**
3435
* Constructor.
@@ -37,12 +38,10 @@ export class LayerInfo {
3738
* @param layerInfo The layer info.
3839
* @param token The access token.
3940
*/
40-
constructor(url: string, events: string[], layerInfo: LayerInfoResult) {
41+
constructor(url: string, events: MageEventId[], layerInfo: LayerInfoResult) {
4142
this.url = url
42-
if (events != undefined && events != null && events.length == 0) {
43-
this.events.add('nothing to sync')
44-
}
45-
if (events != undefined || events != null) {
43+
44+
if (events && events.length > 0) {
4645
for (const event of events) {
4746
this.events.add(event);
4847
}
@@ -69,11 +68,11 @@ export class LayerInfo {
6968

7069
/**
7170
* Determine if the layer is enabled for the event.
72-
* @param event The event.
71+
* @param eventId The event.
7372
* @return true if enabled
7473
*/
75-
hasEvent(event: string) {
76-
return this.events.size == 0 || this.events.has(event)
74+
hasEvent(eventId: MageEventId) {
75+
return this.events.size == 0 || this.events.has(eventId)
7776
}
7877

7978
}

plugins/arcgis/service/src/ObservationProcessor.ts

Lines changed: 21 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { PagingParameters } from '@ngageoint/mage.service/lib/entities/entities.global';
2+
import { MageEventId } from "@ngageoint/mage.service/lib/entities/events/entities.events";
23
import { MageEventRepository } from '@ngageoint/mage.service/lib/entities/events/entities.events';
34
import { EventScopedObservationRepository, ObservationRepositoryForEvent } from '@ngageoint/mage.service/lib/entities/observations/entities.observations';
45
import { UserRepository } from '@ngageoint/mage.service/lib/entities/users/entities.users';
@@ -164,19 +165,23 @@ export class ObservationProcessor {
164165
private async updateConfig(): Promise<ArcGISPluginConfig> {
165166
const config = await this.safeGetConfig()
166167

167-
// Include form definitions while detecting changes in config
168-
const eventForms = await this._eventRepo.findAll();
168+
// Include configured eventform definitions while detecting changes in config
169+
const eventIds = config.featureServices
170+
.flatMap(service => service.layers)
171+
.flatMap(layer => layer.eventIds)
172+
.filter((eventId): eventId is MageEventId => typeof eventId === 'number');
173+
174+
const eventForms = await this._eventRepo.findAllByIds(eventIds);
169175
const fullConfig = { ...config, eventForms };
170176

171177
const configJson = JSON.stringify(fullConfig)
172178
if (this._previousConfig == null || this._previousConfig != configJson) {
173179
this._transformer = new ObservationsTransformer(config, console);
174180
this._geometryChangeHandler = new GeometryChangedHandler(this._transformer);
175-
this._eventDeletionHandler = new EventDeletionHandler(this._console, config);
181+
this._eventDeletionHandler.updateConfig(config);
176182
this._layerProcessors = [];
177183
await this.getFeatureServiceLayers(config);
178184
this._previousConfig = configJson
179-
this._firstRun = true;
180185
}
181186
return config
182187
}
@@ -186,7 +191,6 @@ export class ObservationProcessor {
186191
*/
187192
async start() {
188193
this._isRunning = true;
189-
this._firstRun = true;
190194
this.processAndScheduleNext();
191195
}
192196

@@ -207,7 +211,7 @@ export class ObservationProcessor {
207211
try {
208212
const identityManager = await this._identityService.signin(service)
209213
const response = await request(service.url, { authentication: identityManager })
210-
this.handleFeatureService(response, service, config)
214+
await this.handleFeatureService(response, service, config)
211215
} catch (err) {
212216
console.error(err)
213217
}
@@ -235,25 +239,7 @@ export class ObservationProcessor {
235239
}
236240

237241
for (const featureLayer of featureServiceConfig.layers) {
238-
const eventNames: string[] = []
239-
const events = featureLayer.events
240-
if (events != null) {
241-
for (const event of events) {
242-
const eventId = Number(event);
243-
if (isNaN(eventId)) {
244-
eventNames.push(String(event));
245-
} else {
246-
const mageEvent = await this._eventRepo.findById(eventId)
247-
if (mageEvent != null) {
248-
eventNames.push(mageEvent.name);
249-
}
250-
}
251-
}
252-
}
253-
if (eventNames.length > 0) {
254-
featureLayer.events = eventNames
255-
}
256-
242+
// TODO - this used to convert event ids to names and set back on featureLayer.events. What is impact of not doing?
257243
const layer = serviceLayers.get(featureLayer.layer)
258244

259245
let layerId = undefined
@@ -270,7 +256,7 @@ export class ObservationProcessor {
270256
const featureService = new FeatureService(console, featureServiceConfig, identityManager)
271257
const layerInfo = await featureService.queryLayerInfo(layerId);
272258
const url = `${featureServiceConfig.url}/${layerId}`;
273-
this.handleLayerInfo(url, featureServiceConfig, featureLayer, layerInfo, config);
259+
await this.handleLayerInfo(url, featureServiceConfig, featureLayer, layerInfo, config);
274260
}
275261
}
276262
}
@@ -286,10 +272,10 @@ export class ObservationProcessor {
286272
*/
287273
private async handleLayerInfo(url: string, featureServiceConfig: FeatureServiceConfig, featureLayer: FeatureLayerConfig, layerInfo: LayerInfoResult, config: ArcGISPluginConfig) {
288274
if (layerInfo.geometryType != null) {
289-
const events = featureLayer.events as string[]
290275
const admin = new FeatureServiceAdmin(config, this._identityService, this._console)
276+
const eventIds = featureLayer.eventIds || []
291277
await admin.updateLayer(featureServiceConfig, featureLayer, layerInfo, this._eventRepo)
292-
const info = new LayerInfo(url, events, layerInfo)
278+
const info = new LayerInfo(url, eventIds, layerInfo)
293279
const identityManager = await this._identityService.signin(featureServiceConfig)
294280
const layerProcessor = new FeatureLayerProcessor(info, config, identityManager, this._console);
295281
this._layerProcessors.push(layerProcessor);
@@ -310,9 +296,13 @@ export class ObservationProcessor {
310296
layerProcessor.processPendingUpdates();
311297
}
312298
this._console.info('ArcGIS plugin processing new observations...');
313-
const activeEvents = await this._eventRepo.findActiveEvents();
314-
this._eventDeletionHandler.checkForEventDeletion(activeEvents, this._layerProcessors, this._firstRun);
315-
const eventsToProcessors = this._organizer.organize(activeEvents, this._layerProcessors);
299+
const enabledEvents = (await this._eventRepo.findActiveEvents()).filter(event =>
300+
this._layerProcessors.some(layerProcessor =>
301+
layerProcessor.layerInfo.hasEvent(event.id)
302+
)
303+
);
304+
this._eventDeletionHandler.checkForEventDeletion(enabledEvents, this._layerProcessors, this._firstRun);
305+
const eventsToProcessors = this._organizer.organize(enabledEvents, this._layerProcessors);
316306
const nextQueryTime = Date.now();
317307
for (const pair of eventsToProcessors) {
318308
this._console.info('ArcGIS getting newest observations for event ' + pair.event.name);

plugins/arcgis/service/src/index.ts

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ import { ObservationRepositoryToken } from '@ngageoint/mage.service/lib/plugins.
44
import { MageEventRepositoryToken } from '@ngageoint/mage.service/lib/plugins.api/plugins.api.events'
55
import { UserRepositoryToken } from '@ngageoint/mage.service/lib/plugins.api/plugins.api.users'
66
import { SettingPermission } from '@ngageoint/mage.service/lib/entities/authorization/entities.permissions'
7+
import { MageEventId } from '@ngageoint/mage.service/lib/entities/events/entities.events'
78
import { ObservationProcessor } from './ObservationProcessor'
89
import { ArcGISIdentityManager, request } from "@esri/arcgis-rest-request"
9-
import { FeatureServiceConfig } from './ArcGISConfig'
10+
import { FeatureServiceConfig, FeatureLayerConfig } from './ArcGISConfig'
1011
import { URL } from "node:url"
1112
import express from 'express'
1213
import { ArcGISIdentityService, createArcGISIdentityService, getPortalUrl } from './ArcGISService'
@@ -35,12 +36,12 @@ const InjectedServices = {
3536

3637
const pluginWebRoute = "plugins/@ngageoint/mage.arcgis.service"
3738

38-
const sanitizeFeatureService = async (config: FeatureServiceConfig, identityService: ArcGISIdentityService): Promise<Omit<FeatureServiceConfig & { authenticated: boolean }, 'identityManager'>> => {
39+
const sanitizeFeatureService = async (config: FeatureServiceConfig, identityService: ArcGISIdentityService): Promise<Omit<FeatureServiceConfig & { authenticated: boolean }, 'identityManager'>> => {
3940
let authenticated = false
4041
try {
4142
await identityService.signin(config)
4243
authenticated = true
43-
} catch(ignore) {}
44+
} catch (ignore) { }
4445

4546
const { identityManager, ...sanitized } = config;
4647
return { ...sanitized, authenticated }
@@ -132,7 +133,7 @@ const arcgisPluginHooks: InitPluginHook<typeof InjectedServices> = {
132133
</head>
133134
</html>
134135
`);
135-
}).catch((error) => res.status(400).json(error))
136+
}).catch((error) => res.status(400).json(error))
136137
})
137138

138139
return routes
@@ -166,18 +167,51 @@ const arcgisPluginHooks: InitPluginHook<typeof InjectedServices> = {
166167
const config = await stateRepo.get()
167168
const { featureServices: updatedServices, ...updateConfig } = req.body
168169

169-
// Map exisiting identityManager, client does not send this
170-
const featureServices: FeatureServiceConfig[] = updatedServices.map((updateService: FeatureServiceConfig) => {
171-
const existingService = config.featureServices.find((featureService: FeatureServiceConfig) => featureService.url === updateService.url)
170+
171+
// Convert event names to event IDs
172+
// Fetch all events and create a mapping of event names to event IDs
173+
const allEvents = await eventRepo.findAll();
174+
const eventNameToIdMap = new Map<string, MageEventId>();
175+
allEvents.forEach(event => {
176+
eventNameToIdMap.set(event.name, event.id);
177+
});
178+
179+
// Process the incoming feature services with eventIds instead of event names
180+
const featureServices: FeatureServiceConfig[] = updatedServices.map((updateService: any) => {
181+
const existingService = config.featureServices.find(
182+
(featureService: FeatureServiceConfig) => featureService.url === updateService.url
183+
);
184+
185+
// Process layers
186+
const layers: FeatureLayerConfig[] = updateService.layers.map((layer: any) => {
187+
// Extract event names from the incoming layer data
188+
const eventNames: string[] = layer.events || [];
189+
190+
// Convert event names to event IDs using the mapping
191+
const eventIds = eventNames
192+
.map(eventName => eventNameToIdMap.get(eventName))
193+
.filter((id): id is MageEventId => id !== undefined);
194+
195+
// Construct the FeatureLayerConfig with eventIds
196+
const featureLayerConfig: FeatureLayerConfig = {
197+
layer: layer.layer,
198+
geometryType: layer.geometryType,
199+
eventIds: eventIds,
200+
};
201+
202+
return featureLayerConfig;
203+
});
204+
172205
return {
173206
url: updateService.url,
174-
layers: updateService.layers,
175-
identityManager: existingService?.identityManager || ''
176-
}
177-
})
207+
layers: layers,
208+
// Map exisiting identityManager, client does not send this
209+
identityManager: existingService?.identityManager,
210+
};
211+
});
178212

179213
await stateRepo.patch({ ...updateConfig, featureServices })
180-
214+
181215
// Sync configuration with feature servers by restarting observation processor
182216
processor.stop()
183217
processor.start()
@@ -195,7 +229,7 @@ const arcgisPluginHooks: InitPluginHook<typeof InjectedServices> = {
195229
let service: FeatureServiceConfig
196230
let identityManager: ArcGISIdentityManager
197231
if (token) {
198-
identityManager = await ArcGISIdentityManager.fromToken({ token })
232+
identityManager = await ArcGISIdentityManager.fromToken({ token })
199233
service = { url, layers: [], identityManager: identityManager.serialize() }
200234
} else if (username && password) {
201235
identityManager = await ArcGISIdentityManager.signIn({
@@ -221,7 +255,7 @@ const arcgisPluginHooks: InitPluginHook<typeof InjectedServices> = {
221255
return res.send('Invalid credentials provided to communicate with feature service').status(400)
222256
}
223257
})
224-
258+
225259
routes.get('/featureService/layers', async (req, res, next) => {
226260
const url = req.query.featureServiceUrl as string
227261
const config = await processor.safeGetConfig()

0 commit comments

Comments
 (0)