Skip to content

Commit

Permalink
[service] sync configured events instead of checking changes of all e…
Browse files Browse the repository at this point in the history
…vents. Store event ids instead of names
  • Loading branch information
Rick Saccoccia committed Nov 26, 2024
1 parent dd74faf commit f9a44e6
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 63 deletions.
14 changes: 8 additions & 6 deletions plugins/arcgis/service/src/ArcGISConfig.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { MageEventId } from "@ngageoint/mage.service/lib/entities/events/entities.events"

/**
* Contains an arc feature service url and layers.
*/
Expand All @@ -8,10 +10,10 @@ export interface FeatureServiceConfig {
*/
url: string

/**
* Serialized ArcGISIdentityManager
*/
identityManager: string
/**
* Serialized ArcGISIdentityManager
*/
identityManager: string

/**
* The feature layers.
Expand All @@ -35,9 +37,9 @@ export interface FeatureLayerConfig {
geometryType?: string

/**
* The event ids or names that sync to this arc feature layer.
* The event ids that sync to this arc feature layer.
*/
events?: (number|string)[]
eventIds?: MageEventId[]
}


Expand Down
2 changes: 1 addition & 1 deletion plugins/arcgis/service/src/EventDeletionHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export class EventDeletionHandler {
}

/**
* Called when the query is finished. It goes through the results and gathers all even Ids currently stored
* Called when the query is finished. It goes through the results and gathers all event Ids currently stored
* in the arc layer. It then will remove any events from the arc layer that do not exist.
* @param layerProcessor The feature layer processor.
* @param result The returned results.
Expand Down
2 changes: 1 addition & 1 deletion plugins/arcgis/service/src/EventLayerProcessorOrganizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export class EventLayerProcessorOrganizer {
for (const event of events) {
let syncProcessors = new Array<FeatureLayerProcessor>();
for (const layerProcessor of layerProcessors) {
if (layerProcessor.layerInfo.hasEvent(event.name)) {
if (layerProcessor.layerInfo.hasEvent(event.id)) {
syncProcessors.push(layerProcessor);
}
}
Expand Down
18 changes: 9 additions & 9 deletions plugins/arcgis/service/src/FeatureServiceAdmin.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ArcGISPluginConfig } from "./ArcGISPluginConfig"
import { FeatureServiceConfig, FeatureLayerConfig } from "./ArcGISConfig"
import { MageEvent, MageEventRepository } from '@ngageoint/mage.service/lib/entities/events/entities.events'
import { MageEvent, MageEventId, MageEventRepository } from '@ngageoint/mage.service/lib/entities/events/entities.events'
import { Layer, Field } from "./AddLayersRequest"
import { Form, FormField, FormFieldType, FormId } from '@ngageoint/mage.service/lib/entities/events/entities.events.forms'
import { ObservationsTransformer } from "./ObservationsTransformer"
Expand Down Expand Up @@ -120,29 +120,29 @@ export class FeatureServiceAdmin {
}

/**
* Get the layer events
* Get the Mage layer events
* @param layer feature layer
* @param eventRepo event repository
* @returns layer events
* @returns Mage layer events
*/
private async layerEvents(layer: FeatureLayerConfig, eventRepo: MageEventRepository): Promise<MageEvent[]> {
const layerEvents: Set<number|string> = new Set()
if (layer.events != null) {
for (const layerEvent of layer.events) {
layerEvents.add(layerEvent)
const layerEventIds: Set<MageEventId> = new Set()
if (layer.eventIds != null) {
for (const layerEventId of layer.eventIds) {
layerEventIds.add(layerEventId)
}
}

let mageEvents
if (layerEvents.size > 0) {
if (layerEventIds.size > 0) {
mageEvents = await eventRepo.findAll()
} else {
mageEvents = await eventRepo.findActiveEvents()
}

const events: MageEvent[] = []
for (const mageEvent of mageEvents) {
if (layerEvents.size == 0 || layerEvents.has(mageEvent.name) || layerEvents.has(mageEvent.id)) {
if (layerEventIds.size == 0 || layerEventIds.has(mageEvent.id)) {
const event = await eventRepo.findById(mageEvent.id)
if (event != null) {
events.push(event)
Expand Down
17 changes: 8 additions & 9 deletions plugins/arcgis/service/src/LayerInfo.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { MageEventId } from "@ngageoint/mage.service/lib/entities/events/entities.events";
import { LayerInfoResult, LayerField } from "./LayerInfoResult";

/**
Expand Down Expand Up @@ -28,7 +29,7 @@ export class LayerInfo {
/**
* The events that are synching to this layer.
*/
events: Set<string> = new Set<string>()
events: Set<MageEventId> = new Set<MageEventId>()

/**
* Constructor.
Expand All @@ -37,12 +38,10 @@ export class LayerInfo {
* @param layerInfo The layer info.
* @param token The access token.
*/
constructor(url: string, events: string[], layerInfo: LayerInfoResult) {
constructor(url: string, events: MageEventId[], layerInfo: LayerInfoResult) {
this.url = url
if (events != undefined && events != null && events.length == 0) {
this.events.add('nothing to sync')
}
if (events != undefined || events != null) {

if (events && events.length > 0) {
for (const event of events) {
this.events.add(event);
}
Expand All @@ -69,11 +68,11 @@ export class LayerInfo {

/**
* Determine if the layer is enabled for the event.
* @param event The event.
* @param eventId The event.
* @return true if enabled
*/
hasEvent(event: string) {
return this.events.size == 0 || this.events.has(event)
hasEvent(eventId: MageEventId) {
return this.events.size == 0 || this.events.has(eventId)
}

}
48 changes: 20 additions & 28 deletions plugins/arcgis/service/src/ObservationProcessor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { PagingParameters } from '@ngageoint/mage.service/lib/entities/entities.global';
import { MageEventId } from "@ngageoint/mage.service/lib/entities/events/entities.events";
import { MageEventRepository } from '@ngageoint/mage.service/lib/entities/events/entities.events';
import { EventScopedObservationRepository, ObservationRepositoryForEvent } from '@ngageoint/mage.service/lib/entities/observations/entities.observations';
import { UserRepository } from '@ngageoint/mage.service/lib/entities/users/entities.users';
Expand Down Expand Up @@ -164,8 +165,13 @@ export class ObservationProcessor {
private async updateConfig(): Promise<ArcGISPluginConfig> {
const config = await this.safeGetConfig()

// Include form definitions while detecting changes in config
const eventForms = await this._eventRepo.findAll();
// Include configured eventform definitions while detecting changes in config
const eventIds = config.featureServices
.flatMap(service => service.layers)
.flatMap(layer => layer.eventIds)
.filter((eventId): eventId is MageEventId => typeof eventId === 'number');

const eventForms = await this._eventRepo.findAllByIds(eventIds);
const fullConfig = { ...config, eventForms };

const configJson = JSON.stringify(fullConfig)
Expand Down Expand Up @@ -207,7 +213,7 @@ export class ObservationProcessor {
try {
const identityManager = await this._identityService.signin(service)
const response = await request(service.url, { authentication: identityManager })
this.handleFeatureService(response, service, config)
await this.handleFeatureService(response, service, config)
} catch (err) {
console.error(err)
}
Expand Down Expand Up @@ -235,25 +241,7 @@ export class ObservationProcessor {
}

for (const featureLayer of featureServiceConfig.layers) {
const eventNames: string[] = []
const events = featureLayer.events
if (events != null) {
for (const event of events) {
const eventId = Number(event);
if (isNaN(eventId)) {
eventNames.push(String(event));
} else {
const mageEvent = await this._eventRepo.findById(eventId)
if (mageEvent != null) {
eventNames.push(mageEvent.name);
}
}
}
}
if (eventNames.length > 0) {
featureLayer.events = eventNames
}

// TODO - this used to convert event ids to names and set back on featureLayer.events. What is impact of not doing?
const layer = serviceLayers.get(featureLayer.layer)

let layerId = undefined
Expand All @@ -270,7 +258,7 @@ export class ObservationProcessor {
const featureService = new FeatureService(console, featureServiceConfig, identityManager)
const layerInfo = await featureService.queryLayerInfo(layerId);
const url = `${featureServiceConfig.url}/${layerId}`;
this.handleLayerInfo(url, featureServiceConfig, featureLayer, layerInfo, config);
await this.handleLayerInfo(url, featureServiceConfig, featureLayer, layerInfo, config);
}
}
}
Expand All @@ -286,10 +274,10 @@ export class ObservationProcessor {
*/
private async handleLayerInfo(url: string, featureServiceConfig: FeatureServiceConfig, featureLayer: FeatureLayerConfig, layerInfo: LayerInfoResult, config: ArcGISPluginConfig) {
if (layerInfo.geometryType != null) {
const events = featureLayer.events as string[]
const admin = new FeatureServiceAdmin(config, this._identityService, this._console)
const eventIds = featureLayer.eventIds || []
await admin.updateLayer(featureServiceConfig, featureLayer, layerInfo, this._eventRepo)
const info = new LayerInfo(url, events, layerInfo)
const info = new LayerInfo(url, eventIds, layerInfo)
const identityManager = await this._identityService.signin(featureServiceConfig)
const layerProcessor = new FeatureLayerProcessor(info, config, identityManager, this._console);
this._layerProcessors.push(layerProcessor);
Expand All @@ -310,9 +298,13 @@ export class ObservationProcessor {
layerProcessor.processPendingUpdates();
}
this._console.info('ArcGIS plugin processing new observations...');
const activeEvents = await this._eventRepo.findActiveEvents();
this._eventDeletionHandler.checkForEventDeletion(activeEvents, this._layerProcessors, this._firstRun);
const eventsToProcessors = this._organizer.organize(activeEvents, this._layerProcessors);
const enabledEvents = (await this._eventRepo.findActiveEvents()).filter(event =>
this._layerProcessors.some(layerProcessor =>
layerProcessor.layerInfo.hasEvent(event.id)
)
);
this._eventDeletionHandler.checkForEventDeletion(enabledEvents, this._layerProcessors, this._firstRun);
const eventsToProcessors = this._organizer.organize(enabledEvents, this._layerProcessors);
const nextQueryTime = Date.now();
for (const pair of eventsToProcessors) {
this._console.info('ArcGIS getting newest observations for event ' + pair.event.name);
Expand Down
52 changes: 43 additions & 9 deletions plugins/arcgis/service/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { ObservationRepositoryToken } from '@ngageoint/mage.service/lib/plugins.
import { MageEventRepositoryToken } from '@ngageoint/mage.service/lib/plugins.api/plugins.api.events'
import { UserRepositoryToken } from '@ngageoint/mage.service/lib/plugins.api/plugins.api.users'
import { SettingPermission } from '@ngageoint/mage.service/lib/entities/authorization/entities.permissions'
import { MageEventId } from '@ngageoint/mage.service/lib/entities/events/entities.events'
import { ObservationProcessor } from './ObservationProcessor'
import { ArcGISIdentityManager, request } from "@esri/arcgis-rest-request"
import { FeatureServiceConfig } from './ArcGISConfig'
import { FeatureServiceConfig, FeatureLayerConfig } from './ArcGISConfig'
import { URL } from "node:url"
import express from 'express'
import { ArcGISIdentityService, createArcGISIdentityService, getPortalUrl } from './ArcGISService'
Expand Down Expand Up @@ -166,16 +167,49 @@ const arcgisPluginHooks: InitPluginHook<typeof InjectedServices> = {
const config = await stateRepo.get()
const { featureServices: updatedServices, ...updateConfig } = req.body

// Map exisiting identityManager, client does not send this
const featureServices: FeatureServiceConfig[] = updatedServices.map((updateService: FeatureServiceConfig) => {
const existingService = config.featureServices.find((featureService: FeatureServiceConfig) => featureService.url === updateService.url)

// Convert event names to event IDs
// Fetch all events and create a mapping of event names to event IDs
const allEvents = await eventRepo.findAll();
const eventNameToIdMap = new Map<string, MageEventId>();
allEvents.forEach(event => {
eventNameToIdMap.set(event.name, event.id);
});

// Process the incoming feature services with eventIds instead of event names
const featureServices: FeatureServiceConfig[] = updatedServices.map((updateService: any) => {
const existingService = config.featureServices.find(
(featureService: FeatureServiceConfig) => featureService.url === updateService.url
);

// Process layers
const layers: FeatureLayerConfig[] = updateService.layers.map((layer: any) => {
// Extract event names from the incoming layer data
const eventNames: string[] = layer.events || [];

// Convert event names to event IDs using the mapping
const eventIds = eventNames
.map(eventName => eventNameToIdMap.get(eventName))
.filter((id): id is MageEventId => id !== undefined);

// Construct the FeatureLayerConfig with eventIds
const featureLayerConfig: FeatureLayerConfig = {
layer: layer.layer,
geometryType: layer.geometryType,
eventIds: eventIds,
};

return featureLayerConfig;
});

return {
url: updateService.url,
layers: updateService.layers,
identityManager: existingService?.identityManager
}
})

layers: layers,
// Map exisiting identityManager, client does not send this
identityManager: existingService?.identityManager,
};
});

await stateRepo.patch({ ...updateConfig, featureServices })

// Sync configuration with feature servers by restarting observation processor
Expand Down

0 comments on commit f9a44e6

Please sign in to comment.