Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix observation sync issues #236

Draft
wants to merge 37 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
e2da608
Convert httpClient calls to @esri/arcgis-rest-request with ArcGISIden…
Oct 28, 2024
e40718f
Fix addFields and deleteFields
Oct 31, 2024
07986a6
Fixed outFields and returnGeometry for query generation
Nov 1, 2024
8592110
[service] prepend form name to all event form fields for clarity and …
newmanw Nov 1, 2024
feeea9e
[service] Fix check to determine if an observation sync to ESRI is a …
newmanw Nov 1, 2024
c38a342
Merge branch 'develop' into switch-httpClient-to-esri-library
newmanw Nov 1, 2024
041f96c
[service] OAuth refresh token flow in work
newmanw Nov 1, 2024
45b317b
[service] Add IdentityManager to ObservationSender construction
Nov 4, 2024
e66b6de
[service] Remove httpClient
Nov 4, 2024
7691ad7
[service] fix FeatureQuerier response from request
Nov 4, 2024
ed561a7
[service] Refactor ArcGISIdentityManager management
newmanw Nov 7, 2024
10b5444
draft changes
ryanslatten Nov 7, 2024
86fb706
Merge branch 'switch-httpClient-to-esri-library' of https://github.co…
ryanslatten Nov 7, 2024
157d209
[service] ArcGIS field names are lowercase, account for this when add…
newmanw Nov 7, 2024
6379dae
Undo revert of FeatureQuerier
ryanslatten Nov 8, 2024
a38cf9b
[service] Remove HttpClient.ts, no longer used
Nov 8, 2024
df19f92
[service] updateConfig inner async methods await before we query
Nov 8, 2024
46cdaf4
[server] Attribute sync fix upon adding feature server
Nov 9, 2024
f7cb430
[web/service] API update to include feature service authentication st…
newmanw Nov 13, 2024
c9fe35e
[service] fix regression in token, username/password validation req p…
newmanw Nov 13, 2024
a028aaf
[service] Clean up TODO comments
newmanw Nov 14, 2024
53d392e
[service] form field mapping need to account for form name and field …
newmanw Nov 14, 2024
75a3841
Reduce response logging that may cause confusion
ryanslatten Nov 14, 2024
4d37e6a
Merge branch 'switch-httpClient-to-esri-library' of https://github.co…
ryanslatten Nov 14, 2024
eb31a93
[service] include event forms in detecting config changes
Nov 15, 2024
20410f6
Merge develop
newmanw Nov 18, 2024
03b2297
Merge branch 'develop' into switch-httpClient-to-esri-library
newmanw Nov 18, 2024
ec07047
[service] sync configured events instead of checking changes of all e…
Nov 26, 2024
fdaa660
[server] Fix query syntax error
Nov 27, 2024
5fefc3a
fix failing errors for observations
ryanslatten Nov 27, 2024
8db0006
Merge branch 'develop' of https://github.com/ngageoint/mage-server in…
ryanslatten Nov 27, 2024
e98eb82
[service] fix event deletion and firstRun logic
Nov 30, 2024
4625296
fix issues with updates
ryanslatten Dec 5, 2024
3df2fea
Merge branch 'develop' of https://github.com/ngageoint/mage-server in…
ryanslatten Jan 6, 2025
25416d8
Merge branch 'fix-event-sync' of https://github.com/ngageoint/mage-se…
ryanslatten Jan 15, 2025
81961d5
Merge branch 'develop' of https://github.com/ngageoint/mage-server in…
ryanslatten Jan 15, 2025
490f4ee
Merge branch 'fix-event-sync' of https://github.com/ngageoint/mage-se…
ryanslatten Jan 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 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 @@ -9,8 +11,8 @@ export interface FeatureServiceConfig {
url: string

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

/**
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
6 changes: 5 additions & 1 deletion plugins/arcgis/service/src/EventDeletionHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ export class EventDeletionHandler {
this._config = config;
}

public updateConfig(newConfig: ArcGISPluginConfig): void {
this._config = newConfig;
}

/**
*
* @param activeEvents The current set of active events.
Expand Down Expand Up @@ -83,7 +87,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
2 changes: 1 addition & 1 deletion plugins/arcgis/service/src/FeatureLayerProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export class FeatureLayerProcessor {

for (const arcObservation of observations.deletions) {
if (this.layerInfo.geometryType == arcObservation.esriGeometryType) {
this.sender.sendDelete(Number(arcObservation.id));
this.sender.sendDelete(arcObservation.id);
}
}
}
Expand Down
88 changes: 28 additions & 60 deletions plugins/arcgis/service/src/FeatureQuerier.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { ArcGISPluginConfig } from "./ArcGISPluginConfig";
import { LayerInfo } from "./LayerInfo";
import { QueryObjectResult } from "./QueryObjectResult";
import { ArcGISIdentityManager, request } from "@esri/arcgis-rest-request";
import { ArcGISIdentityManager } from "@esri/arcgis-rest-request";
import { queryFeatures } from '@esri/arcgis-rest-feature-service';

/**
* Performs various queries on observations for a specific arc feature layer.
Expand Down Expand Up @@ -52,22 +53,17 @@ export class FeatureQuerier {
* @param geometry query the geometry, default is true
*/
async queryObservation(observationId: string, response: (result: QueryObjectResult) => void, fields?: string[], geometry?: boolean) {
const queryUrl = new URL(this._url)
if (this._config.eventIdField == null) {
queryUrl.searchParams.set('where', `${this._config.observationIdField} LIKE '${observationId}${this._config.idSeparator}%'`);
} else {
queryUrl.searchParams.set('where', `${this._config.observationIdField} = ${observationId}`);
}
queryUrl.searchParams.set('outFields', this.outFields(fields))
queryUrl.searchParams.set('returnGeometry', geometry === false ? 'false' : 'true')
this._console.info('ArcGIS query: ' + queryUrl)

const queryResponse = await request(queryUrl.toString(), {
const where = !this._config.eventIdField
? `${this._config.observationIdField} LIKE '${observationId}${this._config.idSeparator}%'`
: `${this._config.observationIdField} = '${observationId}'`;
this._console.info('ArcGIS query observation: ' + this._url.toString() + where);
await queryFeatures({
url: this._url.toString(),
authentication: this._identityManager,
params: { f: 'json' }
});

response(queryResponse as QueryObjectResult);
where,
returnGeometry: geometry,
outFields: fields?.length ? fields : '*'
}).then((queryResponse) => response(queryResponse as QueryObjectResult)).catch((error) => this._console.error('Error in FeatureQuerier.queryObservation :: ' + error));
}

/**
Expand All @@ -77,19 +73,14 @@ export class FeatureQuerier {
* @param geometry query the geometry, default is true
*/
async queryObservations(response: (result: QueryObjectResult) => void, fields?: string[], geometry?: boolean) {
const queryUrl = new URL(this._url)
queryUrl.searchParams.set('where', `${this._config.observationIdField} IS NOT NULL`);
queryUrl.searchParams.set('outFields', this.outFields(fields));
queryUrl.searchParams.set('returnGeometry', geometry === false ? 'false' : 'true');

this._console.info('ArcGIS query: ' + queryUrl)

const queryResponse = await request(queryUrl.toString(), {
this._console.info('ArcGIS query observation: ' + this._url.toString());
await queryFeatures({
url: this._url.toString(),
authentication: this._identityManager,
params: { f: 'json' }
});

response(queryResponse as QueryObjectResult);
where: `${this._config.observationIdField} IS NOT NULL`,
returnGeometry: geometry,
outFields: fields?.length ? fields : '*'
}).then((queryResponse) => response(queryResponse as QueryObjectResult)).catch((error) => this._console.error('Error in FeatureQuerier.queryObservations :: ' + error));
}

/**
Expand All @@ -98,37 +89,14 @@ export class FeatureQuerier {
* @param field field to query
*/
async queryDistinct(response: (result: QueryObjectResult) => void, field: string) {
const queryUrl = new URL(this._url);
queryUrl.searchParams.set('where', `${field} IS NOT NULL`);
queryUrl.searchParams.set('returnDistinctValues', 'true');
queryUrl.searchParams.set('outFields', this.outFields([field]));
queryUrl.searchParams.set('returnGeometry', 'false');
this._console.info('ArcGIS query: ' + queryUrl)

try {
const queryResponse = await request(queryUrl.toString(), {
authentication: this._identityManager,
params: { f: 'json' }

});

response(queryResponse as QueryObjectResult);
} catch (err) {
console.error("could not query", err)
}
}

/**
* Build the out fields query parameter
* @param fields query fields
* @returns out fields
*/
private outFields(fields?: string[]): string {
if (fields != null && fields.length > 0) {
return fields.join(',');
} else {
return '*';
}
this._console.info('ArcGIS query observation: ' + this._url.toString());
await queryFeatures({
url: this._url.toString(),
authentication: this._identityManager,
where: `${field} IS NOT NULL`,
returnGeometry: false,
outFields: field ? [field] : '*',
returnDistinctValues: true
}).then((queryResponse) => response(queryResponse as QueryObjectResult)).catch((error) => this._console.error('Error in FeatureQuerier.queryDistinct :: ' + error));
}

}
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)
}

}
3 changes: 2 additions & 1 deletion plugins/arcgis/service/src/ObservationBinner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ export class ObservationBinner {
const bins = new ObservationBins();

for (const arcObservation of observations.observations) {
if (arcObservation.lastModified != arcObservation.createdAt) {
// TODO: Would probably want a better way to determine which observations need to be updated in arcgis
if (observations.firstRun || arcObservation.lastModified != arcObservation.createdAt) {
bins.updates.add(arcObservation);
} else if (!this._addedObs.has(arcObservation.id)) {
bins.adds.add(arcObservation);
Expand Down
Loading
Loading