Skip to content

Commit 5b201ad

Browse files
ryanslattenRick SaccoccianewmanwClinton Werth
authored
Fix observation sync issues (#236)
* Convert httpClient calls to @esri/arcgis-rest-request with ArcGISIdentityManager for authentication * Fix addFields and deleteFields * Fixed outFields and returnGeometry for query generation * [service] prepend form name to all event form fields for clarity and to avoid ESRI column name conflicts * [service] Fix check to determine if an observation sync to ESRI is a create or update * [service] OAuth refresh token flow in work * [service] Add IdentityManager to ObservationSender construction * [service] Remove httpClient * [service] fix FeatureQuerier response from request * [service] Refactor ArcGISIdentityManager management * draft changes * [service] ArcGIS field names are lowercase, account for this when adding/removing fields * Undo revert of FeatureQuerier * [service] Remove HttpClient.ts, no longer used * [service] updateConfig inner async methods await before we query * [server] Attribute sync fix upon adding feature server * [web/service] API update to include feature service authentication status * [service] fix regression in token, username/password validation req parameter parsing * [service] Clean up TODO comments * [service] form field mapping need to account for form name and field name to avoid conflicts * Reduce response logging that may cause confusion * [service] include event forms in detecting config changes * Merge develop * [service] sync configured events instead of checking changes of all events. Store event ids instead of names * [server] Fix query syntax error * fix failing errors for observations * [service] fix event deletion and firstRun logic * fix issues with updates * fix deletion issue * fix required field issue * update arcgis read me --------- Co-authored-by: Rick Saccoccia <[email protected]> Co-authored-by: William Newman <[email protected]> Co-authored-by: Clinton Werth <[email protected]>
1 parent cec08a8 commit 5b201ad

13 files changed

+371
-413
lines changed

plugins/arcgis/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Build with arcgis plugin
22

3+
This plugin allows for MAGE observation data to be passed to an Arcgis feature service.
34
Follow the instructions in the root README. After completing the web-app package install and build in the 'Building from source' section:
45

56
Build arcgis plugin:

plugins/arcgis/service/src/AddLayersRequest.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ export interface Field {
2121
alias: string
2222
sqlType: string
2323
length: number
24-
nullable: boolean
2524
editable: boolean
2625
domain?: string
2726
defaultValue?: any

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/FeatureLayerProcessor.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export class FeatureLayerProcessor {
4545
constructor(layerInfo: LayerInfo, config: ArcGISPluginConfig, identityManager: ArcGISIdentityManager, console: Console) {
4646
this.layerInfo = layerInfo;
4747
this.lastTimeStamp = 0;
48-
this.featureQuerier = new FeatureQuerier(layerInfo, config, identityManager,console);
48+
this.featureQuerier = new FeatureQuerier(layerInfo, config, identityManager, console);
4949
this._binner = new ObservationBinner(layerInfo, this.featureQuerier, config);
5050
this.sender = new ObservationsSender(layerInfo, config, identityManager, console);
5151
}
@@ -85,7 +85,7 @@ export class FeatureLayerProcessor {
8585

8686
for (const arcObservation of observations.deletions) {
8787
if (this.layerInfo.geometryType == arcObservation.esriGeometryType) {
88-
this.sender.sendDelete(Number(arcObservation.id));
88+
this.sender.sendDelete(arcObservation.id);
8989
}
9090
}
9191
}
Lines changed: 28 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { ArcGISPluginConfig } from "./ArcGISPluginConfig";
22
import { LayerInfo } from "./LayerInfo";
33
import { QueryObjectResult } from "./QueryObjectResult";
4-
import { ArcGISIdentityManager, request } from "@esri/arcgis-rest-request";
4+
import { ArcGISIdentityManager } from "@esri/arcgis-rest-request";
5+
import { queryFeatures } from '@esri/arcgis-rest-feature-service';
56

67
/**
78
* Performs various queries on observations for a specific arc feature layer.
@@ -52,22 +53,17 @@ export class FeatureQuerier {
5253
* @param geometry query the geometry, default is true
5354
*/
5455
async queryObservation(observationId: string, response: (result: QueryObjectResult) => void, fields?: string[], geometry?: boolean) {
55-
const queryUrl = new URL(this._url)
56-
if (this._config.eventIdField == null) {
57-
queryUrl.searchParams.set('where', `${this._config.observationIdField} LIKE '${observationId}${this._config.idSeparator}%'`);
58-
} else {
59-
queryUrl.searchParams.set('where', `${this._config.observationIdField} = ${observationId}`);
60-
}
61-
queryUrl.searchParams.set('outFields', this.outFields(fields))
62-
queryUrl.searchParams.set('returnGeometry', geometry === false ? 'false' : 'true')
63-
this._console.info('ArcGIS query: ' + queryUrl)
64-
65-
const queryResponse = await request(queryUrl.toString(), {
56+
const where = !this._config.eventIdField
57+
? `${this._config.observationIdField} LIKE '${observationId}${this._config.idSeparator}%'`
58+
: `${this._config.observationIdField} = '${observationId}'`;
59+
this._console.info('ArcGIS query observation: ' + this._url.toString() + where);
60+
await queryFeatures({
61+
url: this._url.toString(),
6662
authentication: this._identityManager,
67-
params: { f: 'json' }
68-
});
69-
70-
response(queryResponse as QueryObjectResult);
63+
where,
64+
returnGeometry: geometry,
65+
outFields: fields?.length ? fields : '*'
66+
}).then((queryResponse) => response(queryResponse as QueryObjectResult)).catch((error) => this._console.error('Error in FeatureQuerier.queryObservation :: ' + error));
7167
}
7268

7369
/**
@@ -77,19 +73,14 @@ export class FeatureQuerier {
7773
* @param geometry query the geometry, default is true
7874
*/
7975
async queryObservations(response: (result: QueryObjectResult) => void, fields?: string[], geometry?: boolean) {
80-
const queryUrl = new URL(this._url)
81-
queryUrl.searchParams.set('where', `${this._config.observationIdField} IS NOT NULL`);
82-
queryUrl.searchParams.set('outFields', this.outFields(fields));
83-
queryUrl.searchParams.set('returnGeometry', geometry === false ? 'false' : 'true');
84-
85-
this._console.info('ArcGIS query: ' + queryUrl)
86-
87-
const queryResponse = await request(queryUrl.toString(), {
76+
this._console.info('ArcGIS query observation: ' + this._url.toString());
77+
await queryFeatures({
78+
url: this._url.toString(),
8879
authentication: this._identityManager,
89-
params: { f: 'json' }
90-
});
91-
92-
response(queryResponse as QueryObjectResult);
80+
where: `${this._config.observationIdField} IS NOT NULL`,
81+
returnGeometry: geometry,
82+
outFields: fields?.length ? fields : '*'
83+
}).then((queryResponse) => response(queryResponse as QueryObjectResult)).catch((error) => this._console.error('Error in FeatureQuerier.queryObservations :: ' + error));
9384
}
9485

9586
/**
@@ -98,37 +89,14 @@ export class FeatureQuerier {
9889
* @param field field to query
9990
*/
10091
async queryDistinct(response: (result: QueryObjectResult) => void, field: string) {
101-
const queryUrl = new URL(this._url);
102-
queryUrl.searchParams.set('where', `${field} IS NOT NULL`);
103-
queryUrl.searchParams.set('returnDistinctValues', 'true');
104-
queryUrl.searchParams.set('outFields', this.outFields([field]));
105-
queryUrl.searchParams.set('returnGeometry', 'false');
106-
this._console.info('ArcGIS query: ' + queryUrl)
107-
108-
try {
109-
const queryResponse = await request(queryUrl.toString(), {
110-
authentication: this._identityManager,
111-
params: { f: 'json' }
112-
113-
});
114-
115-
response(queryResponse as QueryObjectResult);
116-
} catch (err) {
117-
console.error("could not query", err)
118-
}
119-
}
120-
121-
/**
122-
* Build the out fields query parameter
123-
* @param fields query fields
124-
* @returns out fields
125-
*/
126-
private outFields(fields?: string[]): string {
127-
if (fields != null && fields.length > 0) {
128-
return fields.join(',');
129-
} else {
130-
return '*';
131-
}
92+
this._console.info('ArcGIS query observation: ' + this._url.toString());
93+
await queryFeatures({
94+
url: this._url.toString(),
95+
authentication: this._identityManager,
96+
where: `${field} IS NOT NULL`,
97+
returnGeometry: false,
98+
outFields: field ? [field] : '*',
99+
returnDistinctValues: true
100+
}).then((queryResponse) => response(queryResponse as QueryObjectResult)).catch((error) => this._console.error('Error in FeatureQuerier.queryDistinct :: ' + error));
132101
}
133-
134102
}

0 commit comments

Comments
 (0)