@ngageoint/mage.arcgis.service
Version:
A mage service plugin that synchronizes mage observations to a configured ArcGIS feature layer.
156 lines • 8.27 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FeatureLayerProcessor = void 0;
const ArcObjects_1 = require("./ArcObjects");
const FeatureQuerier_1 = require("./FeatureQuerier");
const ObservationBins_1 = require("./ObservationBins");
const ObservationsSender_1 = require("./ObservationsSender");
/**
* Processes new, updated, and deleted observations and sends the changes to a specific arc feature layer.
*/
class FeatureLayerProcessor {
/**
* Creates a new instance of FeatureLayerProcessor.
* @param {LayerInfo} layerInfo - Information about the arc feature layer this class sends observations to.
* @param {ArcGISPluginConfig} config - Contains certain parameters that can be configured.
* @param {ArcGISIdentityManager} identityManager - ArcGIS identity manager for authentication.
* @param {Console} console - Used to log messages to the console.
*/
constructor(layerInfo, config, identityManager, console) {
/**
* The last time we checked for new/modified observations.
*/
this.lastTimeStamp = 0;
/**
* The number of existence queries we are still waiting for.
*/
this._existenceQueryCounts = 0;
this._addedObs = new Set();
this.layerInfo = layerInfo;
this._config = config;
this._console = console;
this.featureQuerier = new FeatureQuerier_1.FeatureQuerier(layerInfo, config, identityManager, console);
this.sender = new ObservationsSender_1.ObservationsSender(layerInfo, config, identityManager, console);
this._pendingNewAndUpdates = new ObservationBins_1.ObservationBins;
}
/**
* Indicates if this binner has pending updates still waiting to be processed.
* @returns {boolean} True if it is still waiting for updates to be processed, false otherwise.
*/
hasPendingUpdates() {
return this._existenceQueryCounts > 0;
}
/**
* Checks to see if there are any updates that need to be sent to the feature layer.
*/
processPendingUpdates() {
return __awaiter(this, void 0, void 0, function* () {
const newAndUpdates = new ObservationBins_1.ObservationBins();
for (let i = 0; i < this._pendingNewAndUpdates.adds.count(); i++) {
if (!this._addedObs.has(this._pendingNewAndUpdates.adds.observations[i].id)) {
newAndUpdates.adds.add(this._pendingNewAndUpdates.adds.observations[i]);
this._addedObs.add(this._pendingNewAndUpdates.adds.observations[i].id);
}
}
for (let i = 0; i < this._pendingNewAndUpdates.updates.count(); i++) {
newAndUpdates.updates.add(this._pendingNewAndUpdates.updates.observations[i]);
}
this._pendingNewAndUpdates.clear();
yield this.send(newAndUpdates);
});
}
/**
* Goes through each observation and figures out if the geometry type matches the arc feature layer.
* If so it then separates the adds from the updates and sends them to the arc feature layer.
* @param {ArcObjects} observations - The observations to process.
*/
processArcObjects(observations) {
return __awaiter(this, void 0, void 0, function* () {
const arcObjectsForLayer = new ArcObjects_1.ArcObjects();
arcObjectsForLayer.firstRun = observations.firstRun;
for (const arcObservation of observations.observations) {
if (this.layerInfo.geometryType === arcObservation.esriGeometryType) {
arcObjectsForLayer.add(arcObservation);
}
}
const bins = new ObservationBins_1.ObservationBins();
for (const arcObservation of arcObjectsForLayer.observations) {
// TODO: Would probably want a better way to determine which observations need to be updated in arcgis
if (arcObjectsForLayer.firstRun || arcObservation.lastModified !== arcObservation.createdAt) {
bins.updates.add(arcObservation);
}
else if (!this._addedObs.has(arcObservation.id)) {
bins.adds.add(arcObservation);
this._addedObs.add(arcObservation.id);
}
}
for (const arcObservation of bins.updates.observations) {
this._existenceQueryCounts++;
this.featureQuerier.queryObservation(arcObservation.id, (result) => {
this._existenceQueryCounts--;
if (result.features != null && result.features.length > 0) {
this._addedObs.add(arcObservation.id);
const arcAttributes = result.features[0].attributes;
let lastEdited = null;
if (this._config.lastEditedDateField != null) {
lastEdited = Number(arcAttributes[this._config.lastEditedDateField]);
}
if (!lastEdited || lastEdited < arcObservation.lastModified) {
const objectIdField = result.objectIdFieldName;
const updateAttributes = arcObservation.object.attributes;
updateAttributes[objectIdField] = arcAttributes[objectIdField];
// Determine if any editable attribute values should be deleted
const lowerCaseUpdateAttributes = Object.fromEntries(Object.entries(updateAttributes).map(([k, v]) => [k.toLowerCase(), v]));
for (const arcAttribute of Object.keys(arcAttributes)) {
if (this.layerInfo.isEditable(arcAttribute)
&& arcAttributes[arcAttribute] != null
&& lowerCaseUpdateAttributes[arcAttribute.toLowerCase()] == null) {
updateAttributes[arcAttribute] = null;
}
}
this._pendingNewAndUpdates.updates.add(arcObservation);
}
}
else {
this._pendingNewAndUpdates.adds.add(arcObservation);
}
}, undefined, false);
}
bins.updates = this._pendingNewAndUpdates.updates;
yield this.send(bins);
for (const arcObservation of observations.deletions) {
if (this.layerInfo.geometryType === arcObservation.esriGeometryType && this._addedObs.has(arcObservation.id)) {
yield this.sender.sendDelete(arcObservation.id);
this._addedObs.delete(arcObservation.id);
}
}
});
}
/**
* Sends all the observations to the arc server.
* @param {ObservationBins} bins - The observations to send.
*/
send(bins) {
return __awaiter(this, void 0, void 0, function* () {
const promises = [];
if (!bins.adds.isEmpty()) {
promises.push(this.sender.sendAdds(bins.adds));
}
if (!bins.updates.isEmpty()) {
promises.push(this.sender.sendUpdates(bins.updates));
}
yield Promise.all(promises);
});
}
}
exports.FeatureLayerProcessor = FeatureLayerProcessor;
//# sourceMappingURL=FeatureLayerProcessor.js.map