From 1d1f3f744b5e7a99c83162b6a7fe9ca0d57ef6cf Mon Sep 17 00:00:00 2001 From: Rich Gwozdz Date: Wed, 24 Jan 2024 10:32:26 -0800 Subject: [PATCH 1/9] feat: support pbf feature result --- demo/provider-data/square.geojson | 23 + package-lock.json | 168 +- packages/featureserver/.nycrc | 2 +- packages/featureserver/coverage-unit.svg | 8 +- packages/featureserver/coverage.svg | 14 +- packages/featureserver/package.json | 1 + .../featureserver/src/geoservice-error.js | 13 - .../helpers/feature-layer-metadata.spec.js | 1338 +++++++- packages/featureserver/src/helpers/index.js | 1 - .../src/helpers/server-metadata.js | 1 - .../src/helpers/server-metadata.spec.js | 94 +- .../src/helpers/table-layer-metadata.js | 6 +- .../src/helpers/table-layer-metadata.spec.js | 2227 +++++++++++-- .../featureserver/src/metadata-defaults.js | 153 +- packages/featureserver/src/query/index.js | 38 +- .../featureserver/src/query/log-warnings.js | 12 +- .../validate-query-request-parameters.js | 48 + .../validate-query-request-parameters.spec.js | 32 + .../featureserver/src/response-handler.js | 16 - .../src/response-handler.spec.js | 64 - .../general-response-handler.js | 16 + .../general-response-handler.spec.js | 81 + .../src/response-handlers/helpers/index.js | 5 + .../helpers/send-callback.js | 9 + .../helpers/send-callback.spec.js | 23 + .../send-pbf/FeatureCollection.proto.js | 2964 +++++++++++++++++ .../send-pbf/get-geometry-transform.js | 65 + .../send-pbf/get-geometry-transform.spec.js | 194 ++ .../helpers/send-pbf/index.js | 60 + .../helpers/send-pbf/index.spec.js | 144 + .../send-pbf/transform-features-for-pbf.js | 52 + .../transform-features-for-pbf.spec.js | 223 ++ .../send-pbf/transform-to-pbf-attributes.js | 33 + .../transform-to-pbf-attributes.spec.js | 108 + .../send-pbf/transform-to-pbf-geometry.js | 174 + .../transform-to-pbf-geometry.spec.js | 244 ++ .../helpers/send-pretty-json.js | 10 + .../helpers/send-pretty-json.spec.js | 25 + .../src/response-handlers/index.js | 4 + .../query-response-handler.js | 24 + .../query-response-handler.spec.js | 103 + packages/featureserver/src/route.js | 62 +- packages/featureserver/src/route.spec.js | 147 +- .../src/server-info-route-handler.js | 2 +- .../src/server-info-route-handler.spec.js | 18 +- .../test/integration/info.spec.js | 170 +- .../test/integration/layers.spec.js | 278 +- ...geoservice-client-requests-no-geom.spec.js | 176 +- test/helpers/client-response-fixtures.js | 102 +- 49 files changed, 8961 insertions(+), 814 deletions(-) create mode 100644 demo/provider-data/square.geojson delete mode 100644 packages/featureserver/src/geoservice-error.js create mode 100644 packages/featureserver/src/query/validate-query-request-parameters.js create mode 100644 packages/featureserver/src/query/validate-query-request-parameters.spec.js delete mode 100644 packages/featureserver/src/response-handler.js delete mode 100644 packages/featureserver/src/response-handler.spec.js create mode 100644 packages/featureserver/src/response-handlers/general-response-handler.js create mode 100644 packages/featureserver/src/response-handlers/general-response-handler.spec.js create mode 100644 packages/featureserver/src/response-handlers/helpers/index.js create mode 100644 packages/featureserver/src/response-handlers/helpers/send-callback.js create mode 100644 packages/featureserver/src/response-handlers/helpers/send-callback.spec.js create mode 100644 packages/featureserver/src/response-handlers/helpers/send-pbf/FeatureCollection.proto.js create mode 100644 packages/featureserver/src/response-handlers/helpers/send-pbf/get-geometry-transform.js create mode 100644 packages/featureserver/src/response-handlers/helpers/send-pbf/get-geometry-transform.spec.js create mode 100644 packages/featureserver/src/response-handlers/helpers/send-pbf/index.js create mode 100644 packages/featureserver/src/response-handlers/helpers/send-pbf/index.spec.js create mode 100644 packages/featureserver/src/response-handlers/helpers/send-pbf/transform-features-for-pbf.js create mode 100644 packages/featureserver/src/response-handlers/helpers/send-pbf/transform-features-for-pbf.spec.js create mode 100644 packages/featureserver/src/response-handlers/helpers/send-pbf/transform-to-pbf-attributes.js create mode 100644 packages/featureserver/src/response-handlers/helpers/send-pbf/transform-to-pbf-attributes.spec.js create mode 100644 packages/featureserver/src/response-handlers/helpers/send-pbf/transform-to-pbf-geometry.js create mode 100644 packages/featureserver/src/response-handlers/helpers/send-pbf/transform-to-pbf-geometry.spec.js create mode 100644 packages/featureserver/src/response-handlers/helpers/send-pretty-json.js create mode 100644 packages/featureserver/src/response-handlers/helpers/send-pretty-json.spec.js create mode 100644 packages/featureserver/src/response-handlers/index.js create mode 100644 packages/featureserver/src/response-handlers/query-response-handler.js create mode 100644 packages/featureserver/src/response-handlers/query-response-handler.spec.js diff --git a/demo/provider-data/square.geojson b/demo/provider-data/square.geojson new file mode 100644 index 000000000..c8bccc9cb --- /dev/null +++ b/demo/provider-data/square.geojson @@ -0,0 +1,23 @@ +{ + "type" : "FeatureCollection", + "features" : [ + { + "type" : "Feature", + "geometry" : + { + "type" : "Polygon", + "coordinates" : + [ + [ + [-121, 48], + [-121, 47], + [-122, 47], + [-122, 48], + [-121, 48] + ] + ] + }, + "properties" : null + } + ] +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 743f9bbfb..da3a27981 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4326,6 +4326,60 @@ "node": ">=14" } }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, "node_modules/@sentry-internal/tracing": { "version": "7.64.0", "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.64.0.tgz", @@ -4887,8 +4941,7 @@ "node_modules/@types/node": { "version": "20.5.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.1.tgz", - "integrity": "sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg==", - "dev": true + "integrity": "sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg==" }, "node_modules/@types/normalize-package-data": { "version": "2.4.1", @@ -14006,6 +14059,11 @@ "triple-beam": "^1.3.0" } }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, "node_modules/longest": { "version": "2.0.1", "dev": true, @@ -17605,6 +17663,29 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/protobufjs": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", + "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/protocols": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz", @@ -21492,6 +21573,7 @@ "joi": "^17.12.0", "lodash": "^4.17.21", "postgres-date": "^2.1.0", + "protobufjs": "^7.2.5", "wkt-parser": "^1.3.3" }, "devDependencies": { @@ -23746,6 +23828,7 @@ "mocha": "^10.0.0", "nyc": "^15.1.0", "postgres-date": "^2.1.0", + "protobufjs": "^7.2.5", "proxyquire": "^2.1.3", "should": "^13.2.3", "should-sinon": "0.0.6", @@ -24800,6 +24883,60 @@ "dev": true, "optional": true }, + "@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "requires": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, "@sentry-internal/tracing": { "version": "7.64.0", "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.64.0.tgz", @@ -25260,8 +25397,7 @@ "@types/node": { "version": "20.5.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.1.tgz", - "integrity": "sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg==", - "dev": true + "integrity": "sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg==" }, "@types/normalize-package-data": { "version": "2.4.1", @@ -31509,6 +31645,11 @@ "triple-beam": "^1.3.0" } }, + "long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, "longest": { "version": "2.0.1", "dev": true @@ -34010,6 +34151,25 @@ "read": "^2.0.0" } }, + "protobufjs": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", + "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + } + }, "protocols": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz", diff --git a/packages/featureserver/.nycrc b/packages/featureserver/.nycrc index fc4463304..d414ed55b 100644 --- a/packages/featureserver/.nycrc +++ b/packages/featureserver/.nycrc @@ -3,6 +3,6 @@ "include": [ "src/**/*.js" ], - "exclude": ["**/*.spec.js"], + "exclude": ["**/*.spec.js", "**/FeatureCollection.proto.js"], "includeAllSources": true } \ No newline at end of file diff --git a/packages/featureserver/coverage-unit.svg b/packages/featureserver/coverage-unit.svg index fd76fef13..fbc83b818 100644 --- a/packages/featureserver/coverage-unit.svg +++ b/packages/featureserver/coverage-unit.svg @@ -1,5 +1,5 @@ - - coverage: 93.51% + + coverage: 94.88% @@ -13,8 +13,8 @@ \ No newline at end of file diff --git a/packages/featureserver/coverage.svg b/packages/featureserver/coverage.svg index 265b06386..2e4ee4aba 100644 --- a/packages/featureserver/coverage.svg +++ b/packages/featureserver/coverage.svg @@ -1,20 +1,20 @@ - - coverage: 97.1% + + coverage: 98.03% - + - - + + \ No newline at end of file diff --git a/packages/featureserver/package.json b/packages/featureserver/package.json index 512cd5614..dc4ea25a8 100644 --- a/packages/featureserver/package.json +++ b/packages/featureserver/package.json @@ -36,6 +36,7 @@ "joi": "^17.12.0", "lodash": "^4.17.21", "postgres-date": "^2.1.0", + "protobufjs": "^7.2.5", "wkt-parser": "^1.3.3" }, "devDependencies": { diff --git a/packages/featureserver/src/geoservice-error.js b/packages/featureserver/src/geoservice-error.js deleted file mode 100644 index 39152c394..000000000 --- a/packages/featureserver/src/geoservice-error.js +++ /dev/null @@ -1,13 +0,0 @@ - -class GeoServiceError extends Error { - constructor (code, message, details = []) { - super(message); - this.code = code; - - this.details = Array.isArray(details) ? details : [details]; - - Error.captureStackTrace(this, GeoServiceError); - } -} - -module.exports = { GeoServiceError }; \ No newline at end of file diff --git a/packages/featureserver/src/helpers/feature-layer-metadata.spec.js b/packages/featureserver/src/helpers/feature-layer-metadata.spec.js index f5b72881a..430c45c20 100644 --- a/packages/featureserver/src/helpers/feature-layer-metadata.spec.js +++ b/packages/featureserver/src/helpers/feature-layer-metadata.spec.js @@ -2,17 +2,23 @@ const should = require('should'); should.config.checkProtoEql = false; const sinon = require('sinon'); const proxyquire = require('proxyquire'); -const { PointRenderer } = require('./renderers'); -const CURRENT_VERSION = 11.1; -const FULL_VERSION = '11.1.0'; const calculateBoundsSpy = sinon.spy(function () { - return [0, 1, 2, 3]; + return { + spatialReference: { + latestWkid: 4326, + wkid: 4326, + }, + xmax: 180, + xmin: -180, + ymax: 90, + ymin: -90, + }; }); const getSpatialReferenceSpy = sinon.spy(function () { return { wkid: 4326, - latestWkid: 4326 + latestWkid: 4326, }; }); const getGeometryTypeFromGeojsonSpy = sinon.spy(function () { @@ -22,96 +28,13 @@ const normalizeExtentSpy = sinon.spy(function () { return 'normalized-extent'; }); -const defaultFixture = { - id: 0, - name: 'Not Set', - description: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', - copyrightText: 'Copyright information varies by provider. For more information please contact the source of this data.', - parentLayer: null, - subLayers: null, - defaultVisibility: true, - hasAttachments: false, - htmlPopupType: 'esriServerHTMLPopupTypeNone', - displayField: 'OBJECTID', - typeIdField: null, - fields: [ - { - alias: 'OBJECTID', - defaultValue: null, - domain: null, - editable: false, - name: 'OBJECTID', - nullable: false, - sqlType: 'sqlTypeInteger', - type: 'esriFieldTypeOID' - } - ], - relationships: [], - capabilities: 'Query', - maxRecordCount: 2000, - supportsStatistics: true, - supportsAdvancedQueries: true, - supportedQueryFormats: 'JSON', - ownershipBasedAccessControlForFeatures: { - allowOthersToQuery: true - }, - useStandardizedQueries: true, - advancedQueryCapabilities: { - useStandardizedQueries: true, - supportsStatistics: true, - supportsOrderBy: true, - supportsDistinct: true, - supportsPagination: true, - supportsTrueCurve: false, - supportsReturningQueryExtent: true, - supportsQueryWithDistance: true - }, - canModifyLayer: false, - dateFieldsTimeReference: null, - isDataVersioned: false, - supportsRollbackOnFailureParameter: true, - hasM: false, - hasZ: false, - allowGeometryUpdates: true, - objectIdField: 'OBJECTID', - globalIdField: '', - types: [], - templates: [], - hasStaticData: false, - timeInfo: {}, - uniqueIdField: { - name: 'OBJECTID', - isSystemMaintained: true - }, - type: 'Feature Layer', - minScale: 0, - maxScale: 0, - canScaleSymbols: false, - drawingInfo: { - renderer: {}, - labelingInfo: null - }, - extent: { - xmin: -180, - ymin: -90, - xmax: 180, - ymax: 90, - spatialReference: { - wkid: 4326, - latestWkid: 4326 - } - }, - supportsCoordinatesQuantization: false, - hasLabels: false, - currentVersion: CURRENT_VERSION, - fullVersion: FULL_VERSION -}; - const FeatureLayerMetadata = proxyquire('./feature-layer-metadata', { - '@terraformer/spatial': { calculateBounds: calculateBoundsSpy }, + '@terraformer/spatial': { + calculateBounds: calculateBoundsSpy, + }, './get-spatial-reference': getSpatialReferenceSpy, './get-geometry-type-from-geojson': getGeometryTypeFromGeojsonSpy, - './normalize-extent': normalizeExtentSpy + './normalize-extent': normalizeExtentSpy, }); describe('FeatureLayerMetadata', () => { @@ -124,73 +47,603 @@ describe('FeatureLayerMetadata', () => { it('calling with new should return default metadata', () => { const featureLayerMetadata = new FeatureLayerMetadata(); + featureLayerMetadata.should.deepEqual({ - ...defaultFixture, - fields: [] + currentVersion: 11.1, + id: 0, + name: 'Not Set', + type: 'Feature Layer', + displayField: '', + description: + 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'OBJECTID', + uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, + fields: [], + relationships: [], + capabilities: 'Query', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], + timeInfo: {}, + minScale: 0, + maxScale: 0, + drawingInfo: { renderer: {}, labelingInfo: null }, + extent: { + xmin: -180, + ymin: -90, + xmax: 180, + ymax: 90, + spatialReference: { wkid: 4326, latestWkid: 4326 }, + }, + supportsCoordinatesQuantization: true, + hasLabels: false, }); }); describe('mixinOverrides', () => { it('should set point geometryType and renderer', () => { const featureLayerMetadata = new FeatureLayerMetadata(); - featureLayerMetadata.mixinOverrides({ - features: [] - }, { foo: 'bar' }); + featureLayerMetadata.mixinOverrides( + { + features: [], + }, + { foo: 'bar' }, + ); featureLayerMetadata.should.deepEqual({ - ...defaultFixture, - geometryType: 'esriGeometryPoint', + currentVersion: 11.1, + id: 0, + name: 'Not Set', + type: 'Feature Layer', + displayField: '', + description: + 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'OBJECTID', + uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, + fields: [ + { + name: 'OBJECTID', + type: 'esriFieldTypeOID', + alias: 'OBJECTID', + sqlType: 'sqlTypeInteger', + domain: null, + defaultValue: null, + editable: false, + nullable: false, + }, + ], + relationships: [], + capabilities: 'Query', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], + timeInfo: {}, + minScale: 0, + maxScale: 0, drawingInfo: { + renderer: { + type: 'simple', + symbol: { + color: [45, 172, 128, 161], + outline: { + color: [190, 190, 190, 105], + width: 0.5, + type: 'esriSLS', + style: 'esriSLSSolid', + }, + size: 7.5, + type: 'esriSMS', + style: 'esriSMSCircle', + }, + }, labelingInfo: null, - renderer: new PointRenderer() - } + }, + extent: { + xmin: -180, + ymin: -90, + xmax: 180, + ymax: 90, + spatialReference: { wkid: 4326, latestWkid: 4326 }, + }, + supportsCoordinatesQuantization: false, + hasLabels: false, + geometryType: 'esriGeometryPoint', }); getGeometryTypeFromGeojsonSpy.callCount.should.equal(1); - getGeometryTypeFromGeojsonSpy.firstCall.args.should.deepEqual([{ - features: [], - foo: 'bar' - }]); + getGeometryTypeFromGeojsonSpy.firstCall.args.should.deepEqual([ + { + features: [], + foo: 'bar', + }, + ]); }); it('should set quanitization if capabilities.quantization === true', () => { const featureLayerMetadata = new FeatureLayerMetadata(); - featureLayerMetadata.mixinOverrides({ - features: [] - }, { - capabilities: { - quantization: true - } - }); - + featureLayerMetadata.mixinOverrides( + { + features: [], + }, + { + capabilities: { + quantization: true, + }, + }, + ); featureLayerMetadata.should.deepEqual({ - ...defaultFixture, - geometryType: 'esriGeometryPoint', + currentVersion: 11.1, + id: 0, + name: 'Not Set', + type: 'Feature Layer', + displayField: '', + description: + 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'OBJECTID', + uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, + fields: [ + { + name: 'OBJECTID', + type: 'esriFieldTypeOID', + alias: 'OBJECTID', + sqlType: 'sqlTypeInteger', + domain: null, + defaultValue: null, + editable: false, + nullable: false, + }, + ], + relationships: [], + capabilities: 'Query', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], + timeInfo: {}, + minScale: 0, + maxScale: 0, drawingInfo: { + renderer: { + type: 'simple', + symbol: { + color: [45, 172, 128, 161], + outline: { + color: [190, 190, 190, 105], + width: 0.5, + type: 'esriSLS', + style: 'esriSLSSolid', + }, + size: 7.5, + type: 'esriSMS', + style: 'esriSMSCircle', + }, + }, labelingInfo: null, - renderer: new PointRenderer() }, - supportsCoordinatesQuantization: true + extent: { + xmin: -180, + ymin: -90, + xmax: 180, + ymax: 90, + spatialReference: { wkid: 4326, latestWkid: 4326 }, + }, + supportsCoordinatesQuantization: true, + hasLabels: false, + geometryType: 'esriGeometryPoint', }); }); it('should set extent from options', () => { const featureLayerMetadata = new FeatureLayerMetadata(); - featureLayerMetadata.mixinOverrides({ - features: ['feature'] - }, { - extent: 'dataset-extent' - }); + featureLayerMetadata.mixinOverrides( + { + features: ['feature'], + }, + { + extent: 'dataset-extent', + }, + ); featureLayerMetadata.should.deepEqual({ - ...defaultFixture, - geometryType: 'esriGeometryPoint', + currentVersion: 11.1, + id: 0, + name: 'Not Set', + type: 'Feature Layer', + displayField: '', + description: + 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'OBJECTID', + uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, + fields: [ + { + name: 'OBJECTID', + type: 'esriFieldTypeOID', + alias: 'OBJECTID', + sqlType: 'sqlTypeInteger', + domain: null, + defaultValue: null, + editable: false, + nullable: false, + }, + ], + relationships: [], + capabilities: 'Query', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], + timeInfo: {}, + minScale: 0, + maxScale: 0, drawingInfo: { + renderer: { + type: 'simple', + symbol: { + color: [45, 172, 128, 161], + outline: { + color: [190, 190, 190, 105], + width: 0.5, + type: 'esriSLS', + style: 'esriSLSSolid', + }, + size: 7.5, + type: 'esriSMS', + style: 'esriSMSCircle', + }, + }, labelingInfo: null, - renderer: new PointRenderer() }, - extent: 'normalized-extent' + extent: 'normalized-extent', + supportsCoordinatesQuantization: false, + hasLabels: false, + geometryType: 'esriGeometryPoint', }); getSpatialReferenceSpy.callCount.should.equal(1); @@ -201,27 +654,158 @@ describe('FeatureLayerMetadata', () => { it('should set extent from features', () => { const featureLayerMetadata = new FeatureLayerMetadata(); - featureLayerMetadata.mixinOverrides({ - features: ['feature'] - }, {}); + featureLayerMetadata.mixinOverrides( + { + features: ['feature'], + }, + {}, + ); featureLayerMetadata.should.deepEqual({ - ...defaultFixture, - geometryType: 'esriGeometryPoint', + currentVersion: 11.1, + id: 0, + name: 'Not Set', + type: 'Feature Layer', + displayField: '', + description: + 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'OBJECTID', + uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, + fields: [ + { + name: 'OBJECTID', + type: 'esriFieldTypeOID', + alias: 'OBJECTID', + sqlType: 'sqlTypeInteger', + domain: null, + defaultValue: null, + editable: false, + nullable: false, + }, + ], + relationships: [], + capabilities: 'Query', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], + timeInfo: {}, + minScale: 0, + maxScale: 0, drawingInfo: { + renderer: { + type: 'simple', + symbol: { + color: [45, 172, 128, 161], + outline: { + color: [190, 190, 190, 105], + width: 0.5, + type: 'esriSLS', + style: 'esriSLSSolid', + }, + size: 7.5, + type: 'esriSMS', + style: 'esriSMSCircle', + }, + }, labelingInfo: null, - renderer: new PointRenderer() }, extent: { - spatialReference: { - wkid: 4326, - latestWkid: 4326 - }, - xmax: 2, - xmin: 0, - ymax: 3, - ymin: 1 - } + xmin: -180, + ymin: -90, + xmax: 180, + ymax: 90, + spatialReference: { wkid: 4326, latestWkid: 4326 }, + }, + supportsCoordinatesQuantization: false, + hasLabels: false, + geometryType: 'esriGeometryPoint', }); getSpatialReferenceSpy.callCount.should.equal(1); @@ -232,66 +816,466 @@ describe('FeatureLayerMetadata', () => { it('should set renderer from options', () => { const featureLayerMetadata = new FeatureLayerMetadata(); - featureLayerMetadata.mixinOverrides({ - features: [] - }, { renderer: 'custom-renderer' }); + featureLayerMetadata.mixinOverrides( + { + features: [], + }, + { renderer: 'custom-renderer' }, + ); featureLayerMetadata.should.deepEqual({ - ...defaultFixture, + currentVersion: 11.1, + id: 0, + name: 'Not Set', + type: 'Feature Layer', + displayField: '', + description: + 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'OBJECTID', + uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, + fields: [ + { + name: 'OBJECTID', + type: 'esriFieldTypeOID', + alias: 'OBJECTID', + sqlType: 'sqlTypeInteger', + domain: null, + defaultValue: null, + editable: false, + nullable: false, + }, + ], + relationships: [], + capabilities: 'Query', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], + timeInfo: {}, + minScale: 0, + maxScale: 0, + drawingInfo: { renderer: 'custom-renderer', labelingInfo: null }, + extent: { + xmin: -180, + ymin: -90, + xmax: 180, + ymax: 90, + spatialReference: { wkid: 4326, latestWkid: 4326 }, + }, + supportsCoordinatesQuantization: false, + hasLabels: false, geometryType: 'esriGeometryPoint', - drawingInfo: { - labelingInfo: null, - renderer: 'custom-renderer' - } }); }); it('should set other direct overrides', () => { const featureLayerMetadata = new FeatureLayerMetadata(); - featureLayerMetadata.mixinOverrides({ - features: [] - }, { minScale: 1, maxScale: 10 }); + featureLayerMetadata.mixinOverrides( + { + features: [], + }, + { minScale: 1, maxScale: 10 }, + ); featureLayerMetadata.should.deepEqual({ - ...defaultFixture, - geometryType: 'esriGeometryPoint', - drawingInfo: { - labelingInfo: null, - renderer: new PointRenderer() + currentVersion: 11.1, + id: 0, + name: 'Not Set', + type: 'Feature Layer', + displayField: '', + description: + 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'OBJECTID', + uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, + fields: [ + { + name: 'OBJECTID', + type: 'esriFieldTypeOID', + alias: 'OBJECTID', + sqlType: 'sqlTypeInteger', + domain: null, + defaultValue: null, + editable: false, + nullable: false, + }, + ], + relationships: [], + capabilities: 'Query', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], + timeInfo: {}, minScale: 1, maxScale: 10, - currentVersion: CURRENT_VERSION, - fullVersion: FULL_VERSION + drawingInfo: { + renderer: { + type: 'simple', + symbol: { + color: [45, 172, 128, 161], + outline: { + color: [190, 190, 190, 105], + width: 0.5, + type: 'esriSLS', + style: 'esriSLSSolid', + }, + size: 7.5, + type: 'esriSMS', + style: 'esriSMSCircle', + }, + }, + labelingInfo: null, + }, + extent: { + xmin: -180, + ymin: -90, + xmax: 180, + ymax: 90, + spatialReference: { wkid: 4326, latestWkid: 4326 }, + }, + supportsCoordinatesQuantization: false, + hasLabels: false, + geometryType: 'esriGeometryPoint', }); }); }); it('static method "create" should normalize input, call constructor, and mixin-overrides ', () => { - const featureLayerMetadata = FeatureLayerMetadata.create({ - features: [], - metadata: { - foo: 'bar', - displayField: 'myField', - capabilities: 'list,of,stuff' + const featureLayerMetadata = FeatureLayerMetadata.create( + { + features: [], + metadata: { + foo: 'bar', + displayField: 'myField', + capabilities: 'list,of,stuff', + }, + capabilities: { + world: 'hellow', + }, }, - capabilities: { - world: 'hellow' - } - }, { - params: { layer: '99' } - }); + { + params: { layer: '99' }, + }, + ); + featureLayerMetadata.should.deepEqual({ - ...defaultFixture, - geometryType: 'esriGeometryPoint', + currentVersion: 11.1, + id: 99, + name: 'Not Set', + type: 'Feature Layer', + displayField: 'myField', + description: + 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'OBJECTID', + uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, + fields: [ + { + name: 'OBJECTID', + type: 'esriFieldTypeOID', + alias: 'OBJECTID', + sqlType: 'sqlTypeInteger', + domain: null, + defaultValue: null, + editable: false, + nullable: false, + }, + ], + relationships: [], + capabilities: 'list,of,stuff', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], + timeInfo: {}, + minScale: 0, + maxScale: 0, drawingInfo: { + renderer: { + type: 'simple', + symbol: { + color: [45, 172, 128, 161], + outline: { + color: [190, 190, 190, 105], + width: 0.5, + type: 'esriSLS', + style: 'esriSLSSolid', + }, + size: 7.5, + type: 'esriSMS', + style: 'esriSMSCircle', + }, + }, labelingInfo: null, - renderer: new PointRenderer() }, - capabilities: 'list,of,stuff', - displayField: 'myField', - id: 99 + extent: { + xmin: -180, + ymin: -90, + xmax: 180, + ymax: 90, + spatialReference: { wkid: 4326, latestWkid: 4326 }, + }, + supportsCoordinatesQuantization: false, + hasLabels: false, + geometryType: 'esriGeometryPoint', }); }); }); diff --git a/packages/featureserver/src/helpers/index.js b/packages/featureserver/src/helpers/index.js index dc983053e..bda69c0b3 100644 --- a/packages/featureserver/src/helpers/index.js +++ b/packages/featureserver/src/helpers/index.js @@ -5,7 +5,6 @@ module.exports = { getCollectionCrs: require('./get-collection-crs'), getGeometryTypeFromGeojson: require('./get-geometry-type-from-geojson'), isTable: require('./is-geojson-table'), - calculateExtent: require('./calculate-extent'), getSpatialReference: require('./get-spatial-reference'), TableLayerMetadata: require('./table-layer-metadata'), FeatureLayerMetadata: require('./feature-layer-metadata'), diff --git a/packages/featureserver/src/helpers/server-metadata.js b/packages/featureserver/src/helpers/server-metadata.js index 70c101dfc..855ab4748 100644 --- a/packages/featureserver/src/helpers/server-metadata.js +++ b/packages/featureserver/src/helpers/server-metadata.js @@ -7,7 +7,6 @@ const defaults = require('../metadata-defaults'); const OVERRIDABLE_DEFAULTS = [ 'currentVersion', - 'fullVersion', 'serviceDescription', 'description', 'maxRecordCount', diff --git a/packages/featureserver/src/helpers/server-metadata.spec.js b/packages/featureserver/src/helpers/server-metadata.spec.js index 6ae5acf0f..102f1a0cd 100644 --- a/packages/featureserver/src/helpers/server-metadata.spec.js +++ b/packages/featureserver/src/helpers/server-metadata.spec.js @@ -6,12 +6,22 @@ const ServerMetadata = require('./server-metadata'); describe('ServerMetadata', () => { it('should use defaults when no overrides', () => { const result = ServerMetadata.create(); + + console.log(JSON.stringify(result)); + result.should.deepEqual({ currentVersion: 11.1, - fullVersion: '11.1.0', serviceDescription: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + hasVersionedData: false, + supportsDisconnectedEditing: false, + hasStaticData: false, + hasSharedDomains: false, maxRecordCount: 2000, + supportedQueryFormats: 'JSON', + supportsVCSProjection: false, + supportedExportFormats: '', + capabilities: 'Query', description: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', copyrightText: @@ -31,17 +41,24 @@ describe('ServerMetadata', () => { ymax: 90, spatialReference: { wkid: 4326, latestWkid: 4326 }, }, - hasStaticData: false, + allowGeometryUpdates: false, units: 'esriDecimalDegrees', + supportsAppend: false, + supportsSharedDomains: false, + supportsWebHooks: false, + supportsTemporalLayers: false, + layerOverridesEnabled: false, + syncEnabled: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturnDeleteResults: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsQueryContingentValues: false, + supportedContingentValuesFormats: '', + supportsContingentValuesJson: null, tables: [], layers: [], - supportedQueryFormats: 'JSON', - capabilities: 'Query', - syncEnabled: false, - hasVersionedData: false, - supportsDisconnectedEditing: false, supportsRelationshipsResource: false, - allowGeometryUpdates: false, }); }); @@ -58,11 +75,19 @@ describe('ServerMetadata', () => { spatialReference: { wkid: 4326, latestWkid: 4326 }, }, }); + result.should.deepEqual({ currentVersion: 11.1, - fullVersion: '11.1.0', serviceDescription: 'goodbye', + hasVersionedData: false, + supportsDisconnectedEditing: false, + hasStaticData: false, + hasSharedDomains: false, maxRecordCount: 2000, + supportedQueryFormats: 'JSON', + supportsVCSProjection: false, + supportedExportFormats: '', + capabilities: 'Query', description: 'hello world', copyrightText: 'override default', spatialReference: { wkid: 4326, latestWkid: 4326 }, @@ -80,17 +105,24 @@ describe('ServerMetadata', () => { ymax: 90, spatialReference: { wkid: 4326, latestWkid: 4326 }, }, - hasStaticData: false, + allowGeometryUpdates: false, units: 'esriDecimalDegrees', + supportsAppend: false, + supportsSharedDomains: false, + supportsWebHooks: false, + supportsTemporalLayers: false, + layerOverridesEnabled: false, + syncEnabled: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturnDeleteResults: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsQueryContingentValues: false, + supportedContingentValuesFormats: '', + supportsContingentValuesJson: null, tables: [], layers: [], - supportedQueryFormats: 'JSON', - capabilities: 'Query', - syncEnabled: false, - hasVersionedData: false, - supportsDisconnectedEditing: false, supportsRelationshipsResource: false, - allowGeometryUpdates: false, }); }); @@ -101,10 +133,17 @@ describe('ServerMetadata', () => { result.should.deepEqual({ currentVersion: 11.1, - fullVersion: '11.1.0', serviceDescription: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + hasVersionedData: false, + supportsDisconnectedEditing: false, + hasStaticData: false, + hasSharedDomains: false, maxRecordCount: 2000, + supportedQueryFormats: 'JSON', + supportsVCSProjection: false, + supportedExportFormats: '', + capabilities: 'Query', description: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', copyrightText: @@ -124,17 +163,24 @@ describe('ServerMetadata', () => { ymax: 90, spatialReference: { wkid: 4326, latestWkid: 4326 }, }, - hasStaticData: false, + allowGeometryUpdates: false, units: 'esriDecimalDegrees', + supportsAppend: false, + supportsSharedDomains: false, + supportsWebHooks: false, + supportsTemporalLayers: false, + layerOverridesEnabled: false, + syncEnabled: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturnDeleteResults: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsQueryContingentValues: false, + supportedContingentValuesFormats: '', + supportsContingentValuesJson: null, tables: [], layers: [], - supportedQueryFormats: 'JSON', - capabilities: 'Query', - syncEnabled: false, - hasVersionedData: false, - supportsDisconnectedEditing: false, supportsRelationshipsResource: false, - allowGeometryUpdates: false, }); }); diff --git a/packages/featureserver/src/helpers/table-layer-metadata.js b/packages/featureserver/src/helpers/table-layer-metadata.js index ddc272e91..817f68c6a 100644 --- a/packages/featureserver/src/helpers/table-layer-metadata.js +++ b/packages/featureserver/src/helpers/table-layer-metadata.js @@ -33,13 +33,11 @@ class TableLayerMetadata { // TODO: deprecate req.app.locals.config usage const { currentVersion, - fullVersion, description } = _.get(req, 'app.locals.config.featureServer', {}); const normalizedOptions = _.pickBy({ currentVersion, - fullVersion, description, layerId, ...query, @@ -64,7 +62,7 @@ class TableLayerMetadata { mixinOverrides (geojson = {}, options = {}) { const { id, - idField, + idField = 'OBJECTID', displayField, capabilities, layerId, @@ -169,7 +167,6 @@ class TableLayerMetadata { maxRecordCount, defaultVisibility, currentVersion, - fullVersion, hasZ } = options; @@ -184,7 +181,6 @@ class TableLayerMetadata { maxRecordCount, defaultVisibility, currentVersion, - fullVersion, hasZ }); } diff --git a/packages/featureserver/src/helpers/table-layer-metadata.spec.js b/packages/featureserver/src/helpers/table-layer-metadata.spec.js index 1c27ffe91..4bcafeda2 100644 --- a/packages/featureserver/src/helpers/table-layer-metadata.spec.js +++ b/packages/featureserver/src/helpers/table-layer-metadata.spec.js @@ -2,77 +2,20 @@ const should = require('should'); should.config.checkProtoEql = false; const sinon = require('sinon'); const proxyquire = require('proxyquire'); -const CURRENT_VERSION = 11.1; -const FULL_VERSION = '11.1.0'; const createLayerMetadataFieldsSpy = sinon.spy(function () { return ['fields']; }); const fields = { LayerFields: { - create: createLayerMetadataFieldsSpy - } + create: createLayerMetadataFieldsSpy, + }, }; const TableLayerMetadata = proxyquire('./table-layer-metadata', { - '../helpers/fields': fields + '../helpers/fields': fields, }); -const defaultFixture = { - id: 0, - name: 'Not Set', - type: 'Table', - description: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', - copyrightText: 'Copyright information varies by provider. For more information please contact the source of this data.', - parentLayer: null, - subLayers: null, - defaultVisibility: true, - hasAttachments: false, - htmlPopupType: 'esriServerHTMLPopupTypeNone', - displayField: 'OBJECTID', - typeIdField: null, - fields: [], - relationships: [], - capabilities: 'Query', - maxRecordCount: 2000, - supportsStatistics: true, - supportsAdvancedQueries: true, - supportedQueryFormats: 'JSON', - ownershipBasedAccessControlForFeatures: { - allowOthersToQuery: true - }, - useStandardizedQueries: true, - advancedQueryCapabilities: { - useStandardizedQueries: true, - supportsStatistics: true, - supportsOrderBy: true, - supportsDistinct: true, - supportsPagination: true, - supportsTrueCurve: false, - supportsReturningQueryExtent: true, - supportsQueryWithDistance: true - }, - canModifyLayer: false, - dateFieldsTimeReference: null, - isDataVersioned: false, - supportsRollbackOnFailureParameter: true, - hasM: false, - hasZ: false, - allowGeometryUpdates: true, - objectIdField: 'OBJECTID', - globalIdField: '', - types: [], - templates: [], - hasStaticData: false, - timeInfo: {}, - uniqueIdField: { - name: 'OBJECTID', - isSystemMaintained: true - }, - currentVersion: CURRENT_VERSION, - fullVersion: FULL_VERSION -}; - describe('TableLayerMetadata', () => { beforeEach(() => { createLayerMetadataFieldsSpy.resetHistory(); @@ -80,23 +23,230 @@ describe('TableLayerMetadata', () => { it('calling with new should return default metadata', () => { const tableLayerMetadata = new TableLayerMetadata(); - tableLayerMetadata.should.deepEqual(defaultFixture); + tableLayerMetadata.should.deepEqual({ + currentVersion: 11.1, + id: 0, + name: 'Not Set', + type: 'Table', + displayField: '', + description: + 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'OBJECTID', + uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, + fields: [], + relationships: [], + capabilities: 'Query', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], + timeInfo: {}, + }); }); describe('mixinOverrides', () => { it('uses fields creator helper', () => { const tableLayerMetadata = new TableLayerMetadata(); const geojson = { - features: [{ - type: 'FeatureCollection', - properties: {} - }] + features: [ + { + type: 'FeatureCollection', + properties: {}, + }, + ], }; tableLayerMetadata.mixinOverrides(geojson); tableLayerMetadata.should.deepEqual({ - ...defaultFixture, - fields: ['fields'] + currentVersion: 11.1, + id: 0, + name: 'Not Set', + type: 'Table', + displayField: '', + description: + 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'OBJECTID', + uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, + fields: ['fields'], + relationships: [], + capabilities: 'Query', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], + timeInfo: {}, }); createLayerMetadataFieldsSpy.callCount.should.equal(1); createLayerMetadataFieldsSpy.firstCall.args.should.deepEqual([geojson]); @@ -107,9 +257,109 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides({}, { layerId: '2' }); tableLayerMetadata.should.deepEqual({ - ...defaultFixture, + currentVersion: 11.1, + id: 2, + name: 'Not Set', + type: 'Table', + displayField: '', + description: + 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'OBJECTID', + uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, fields: ['fields'], - id: 2 + relationships: [], + capabilities: 'Query', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], + timeInfo: {}, }); }); @@ -118,9 +368,109 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides({}, { id: 3 }); tableLayerMetadata.should.deepEqual({ - ...defaultFixture, + currentVersion: 11.1, + id: 3, + name: 'Not Set', + type: 'Table', + displayField: '', + description: + 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'OBJECTID', + uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, fields: ['fields'], - id: 3 + relationships: [], + capabilities: 'Query', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], + timeInfo: {}, }); }); @@ -129,9 +479,109 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides({}, { displayField: 'hellow' }); tableLayerMetadata.should.deepEqual({ - ...defaultFixture, + currentVersion: 11.1, + id: 0, + name: 'Not Set', + type: 'Table', + displayField: 'hellow', + description: + 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'OBJECTID', + uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, fields: ['fields'], - displayField: 'hellow' + relationships: [], + capabilities: 'Query', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], + timeInfo: {}, }); }); @@ -140,14 +590,109 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides({}, { idField: 'fluffy' }); tableLayerMetadata.should.deepEqual({ - ...defaultFixture, - fields: ['fields'], - objectIdField: 'fluffy', + currentVersion: 11.1, + id: 0, + name: 'Not Set', + type: 'Table', displayField: 'fluffy', - uniqueIdField: { - isSystemMaintained: true, - name: 'fluffy' - } + description: + 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'fluffy', + uniqueIdField: { name: 'fluffy', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, + fields: ['fields'], + relationships: [], + capabilities: 'Query', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], + timeInfo: {}, }); }); @@ -156,9 +701,109 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides({}, { hasZ: true }); tableLayerMetadata.should.deepEqual({ - ...defaultFixture, + currentVersion: 11.1, + id: 0, + name: 'Not Set', + type: 'Table', + displayField: '', + description: + 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: true, + objectIdField: 'OBJECTID', + uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, fields: ['fields'], - hasZ: true + relationships: [], + capabilities: 'Query', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], + timeInfo: {}, }); }); @@ -167,9 +812,109 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides({}, { hasStaticData: true }); tableLayerMetadata.should.deepEqual({ - ...defaultFixture, + currentVersion: 11.1, + id: 0, + name: 'Not Set', + type: 'Table', + displayField: '', + description: + 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'OBJECTID', + uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: true, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, fields: ['fields'], - hasStaticData: true + relationships: [], + capabilities: 'Query', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], + timeInfo: {}, }); }); @@ -178,9 +923,109 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides({}, { hasStaticData: 'true' }); tableLayerMetadata.should.deepEqual({ - ...defaultFixture, + currentVersion: 11.1, + id: 0, + name: 'Not Set', + type: 'Table', + displayField: '', + description: + 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'OBJECTID', + uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, fields: ['fields'], - hasStaticData: false + relationships: [], + capabilities: 'Query', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], + timeInfo: {}, }); }); @@ -189,30 +1034,337 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides({}, { capabilities: {} }); tableLayerMetadata.should.deepEqual({ - ...defaultFixture, - fields: ['fields'] + currentVersion: 11.1, + id: 0, + name: 'Not Set', + type: 'Table', + displayField: '', + description: + 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'OBJECTID', + uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, + fields: ['fields'], + relationships: [], + capabilities: 'Query', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], + timeInfo: {}, }); }); it('defer to "capabilities.list" option if defined ', () => { const tableLayerMetadata = new TableLayerMetadata(); - tableLayerMetadata.mixinOverrides({}, { capabilities: { list: 'hello,world' } }); + tableLayerMetadata.mixinOverrides( + {}, + { capabilities: { list: 'hello,world' } }, + ); tableLayerMetadata.should.deepEqual({ - ...defaultFixture, + currentVersion: 11.1, + id: 0, + name: 'Not Set', + type: 'Table', + displayField: '', + description: + 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'OBJECTID', + uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, fields: ['fields'], - capabilities: 'hello,world' + relationships: [], + capabilities: 'hello,world', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], + timeInfo: {}, }); }); it('add "Extract" to default "capabilities" if capabilities.extract is truthy', () => { const tableLayerMetadata = new TableLayerMetadata(); - tableLayerMetadata.mixinOverrides({}, { capabilities: { extract: true } }); + tableLayerMetadata.mixinOverrides( + {}, + { capabilities: { extract: true } }, + ); tableLayerMetadata.should.deepEqual({ - ...defaultFixture, + currentVersion: 11.1, + id: 0, + name: 'Not Set', + type: 'Table', + displayField: '', + description: + 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'OBJECTID', + uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, fields: ['fields'], - capabilities: 'Query,Extract' + relationships: [], + capabilities: 'Query,Extract', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], + timeInfo: {}, }); }); @@ -221,9 +1373,109 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides({}, { hasStaticData: true }); tableLayerMetadata.should.deepEqual({ - ...defaultFixture, + currentVersion: 11.1, + id: 0, + name: 'Not Set', + type: 'Table', + displayField: '', + description: + 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'OBJECTID', + uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: true, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, fields: ['fields'], - hasStaticData: true + relationships: [], + capabilities: 'Query', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], + timeInfo: {}, }); }); @@ -232,8 +1484,109 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides({}, { supportsPagination: 'false' }); tableLayerMetadata.should.deepEqual({ - ...defaultFixture, - fields: ['fields'] + currentVersion: 11.1, + id: 0, + name: 'Not Set', + type: 'Table', + displayField: '', + description: + 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'OBJECTID', + uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, + fields: ['fields'], + relationships: [], + capabilities: 'Query', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], + timeInfo: {}, }); }); @@ -242,12 +1595,109 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides({}, { supportsPagination: false }); tableLayerMetadata.should.deepEqual({ - ...defaultFixture, - fields: ['fields'], + currentVersion: 11.1, + id: 0, + name: 'Not Set', + type: 'Table', + displayField: '', + description: + 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, advancedQueryCapabilities: { - ...defaultFixture.advancedQueryCapabilities, - supportsPagination: false - } + supportsPagination: false, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'OBJECTID', + uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, + fields: ['fields'], + relationships: [], + capabilities: 'Query', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], + timeInfo: {}, }); }); @@ -256,9 +1706,109 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides({}, { hasAttachments: true }); tableLayerMetadata.should.deepEqual({ - ...defaultFixture, + currentVersion: 11.1, + id: 0, + name: 'Not Set', + type: 'Table', + displayField: '', + description: + 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: true, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'OBJECTID', + uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, fields: ['fields'], - hasAttachments: true + relationships: [], + capabilities: 'Query', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], + timeInfo: {}, }); }); @@ -267,139 +1817,436 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides({}, { hasAttachments: 'true' }); tableLayerMetadata.should.deepEqual({ - ...defaultFixture, + currentVersion: 11.1, + id: 0, + name: 'Not Set', + type: 'Table', + displayField: '', + description: + 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'OBJECTID', + uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, fields: ['fields'], - hasAttachments: false + relationships: [], + capabilities: 'Query', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], + timeInfo: {}, }); }); it('use supported options for direct overrides', () => { const tableLayerMetadata = new TableLayerMetadata(); - tableLayerMetadata.mixinOverrides({}, { + tableLayerMetadata.mixinOverrides( + {}, + { + name: 'Hank Williams', + relationships: ['something'], + description: 'There is a tear in my beer.', + copyrightText: 'copyright-text', + templates: ['templates'], + timeInfo: { time: 'June of 44' }, + maxRecordCount: 9999, + defaultVisibility: false, + currentVersion: 90.99, + }, + ); + + tableLayerMetadata.should.deepEqual({ + currentVersion: 90.99, + id: 0, name: 'Hank Williams', - relationships: ['something'], + type: 'Table', + displayField: '', description: 'There is a tear in my beer.', copyrightText: 'copyright-text', + defaultVisibility: false, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'OBJECTID', + uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, templates: ['templates'], - timeInfo: { time: 'June of 44' }, + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, maxRecordCount: 9999, - defaultVisibility: false, - currentVersion: 90.99, - fullVersion: '90.9.9' - }); - - tableLayerMetadata.should.deepEqual({ - ...defaultFixture, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, fields: ['fields'], - name: 'Hank Williams', relationships: ['something'], - description: 'There is a tear in my beer.', - copyrightText: 'copyright-text', - templates: ['templates'], + capabilities: 'Query', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], timeInfo: { time: 'June of 44' }, - maxRecordCount: 9999, - defaultVisibility: false, - currentVersion: 90.99, - fullVersion: '90.9.9' }); }); }); it('static method "normalizeInput" should create expected geojson and default options', () => { - const { geojson, options } = TableLayerMetadata.normalizeInput({ - features: ['feature'] - }, { - params: { layer: '99' } - }); + const { geojson, options } = TableLayerMetadata.normalizeInput( + { + features: ['feature'], + }, + { + params: { layer: '99' }, + }, + ); geojson.should.deepEqual({ features: ['feature'] }); options.should.deepEqual({ capabilities: {}, - layerId: '99' + layerId: '99', }); }); it('static method "normalizeInput" should merge capabilities', () => { - const { geojson, options } = TableLayerMetadata.normalizeInput({ - features: ['feature'], - metadata: { - capabilities: 'list,of,stuff' + const { geojson, options } = TableLayerMetadata.normalizeInput( + { + features: ['feature'], + metadata: { + capabilities: 'list,of,stuff', + }, + capabilities: { + world: 'hellow', + }, }, - capabilities: { - world: 'hellow' - } - }, { - params: { layer: '99' } - }); + { + params: { layer: '99' }, + }, + ); geojson.should.deepEqual({ features: ['feature'] }); options.should.deepEqual({ layerId: '99', capabilities: { list: 'list,of,stuff', - world: 'hellow' - } + world: 'hellow', + }, }); }); it('static method "normalizeInput" should defer to metadata description', () => { - const { geojson, options } = TableLayerMetadata.normalizeInput({ - features: ['feature'], - metadata: { - description: 'Metadata description' - } - }, { - params: { layer: '99' }, - app: { - locals: { - config: { - featureServer: { - description: 'Overrides default layer description.' - } - } - } - } - }); + const { geojson, options } = TableLayerMetadata.normalizeInput( + { + features: ['feature'], + metadata: { + description: 'Metadata description', + }, + }, + { + params: { layer: '99' }, + app: { + locals: { + config: { + featureServer: { + description: 'Overrides default layer description.', + }, + }, + }, + }, + }, + ); geojson.should.deepEqual({ features: ['feature'] }); options.should.deepEqual({ layerId: '99', description: 'Metadata description', - capabilities: {} + capabilities: {}, }); }); it('static method "create" should normalize input, call constructor, and mixin-overrides ', () => { - const tableLayerMetadata = TableLayerMetadata.create({ - features: ['feature'], - metadata: { - foo: 'bar', - displayField: 'myField', - capabilities: 'list,of,stuff' + const tableLayerMetadata = TableLayerMetadata.create( + { + features: ['feature'], + metadata: { + foo: 'bar', + displayField: 'myField', + capabilities: 'list,of,stuff', + }, + capabilities: { + world: 'hellow', + }, }, - capabilities: { - world: 'hellow' - } - }, { - params: { layer: '99' }, - app: { - locals: { - config: { - featureServer: { - currentVersion: 90.99, - fullVersion: '90.9.9', - description: 'Overrides default layer description.' - } - } - } - } - }); + { + params: { layer: '99' }, + app: { + locals: { + config: { + featureServer: { + currentVersion: 90.99, + description: 'Overrides default layer description.', + }, + }, + }, + }, + }, + ); + tableLayerMetadata.should.deepEqual({ - ...defaultFixture, - capabilities: 'list,of,stuff', + currentVersion: 90.99, + id: 99, + name: 'Not Set', + type: 'Table', displayField: 'myField', + description: 'Overrides default layer description.', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'OBJECTID', + uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, fields: ['fields'], - id: 99, - currentVersion: 90.99, - fullVersion: '90.9.9', - description: 'Overrides default layer description.' + relationships: [], + capabilities: 'list,of,stuff', + ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, + types: [], + timeInfo: {}, }); }); }); diff --git a/packages/featureserver/src/metadata-defaults.js b/packages/featureserver/src/metadata-defaults.js index e9b17bd09..399447f50 100644 --- a/packages/featureserver/src/metadata-defaults.js +++ b/packages/featureserver/src/metadata-defaults.js @@ -81,82 +81,150 @@ class MetadataDefaults { serverDefaults() { return { currentVersion: this.#overridables.currentVersion, - fullVersion: this.#overridables.fullVersion, - maxRecordCount: this.#overridables.maxRecordCount, serviceDescription: this.#overridables.server.serviceDescription, + hasVersionedData: false, + supportsDisconnectedEditing: false, + hasStaticData: this.#overridables.server.hasStaticData, + hasSharedDomains: false, + maxRecordCount: this.#overridables.maxRecordCount, + supportedQueryFormats: 'JSON', + supportsVCSProjection: false, + supportedExportFormats: '', + capabilities: 'Query', description: this.#overridables.server.description, copyrightText: this.#overridables.server.copyrightText, spatialReference: this.#overridables.server.spatialReference, fullExtent: this.#overridables.server.fullExtent, initialExtent: this.#overridables.server.initialExtent, - hasStaticData: this.#overridables.server.hasStaticData, + allowGeometryUpdates: false, units: 'esriDecimalDegrees', + supportsAppend: false, + supportsSharedDomains: false, + supportsWebHooks: false, + supportsTemporalLayers: false, + layerOverridesEnabled: false, + syncEnabled: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturnDeleteResults: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsQueryContingentValues: false, + supportedContingentValuesFormats: '', + supportsContingentValuesJson: null, tables: [], layers: [], - supportedQueryFormats: 'JSON', - capabilities: 'Query', - syncEnabled: false, - hasVersionedData: false, - supportsDisconnectedEditing: false, - supportsRelationshipsResource: false, - allowGeometryUpdates: false, }; } tableLayerDefaults() { return { currentVersion: this.#overridables.currentVersion, - fullVersion: this.#overridables.fullVersion, id: 0, name: 'Not Set', type: 'Table', + displayField: '', description: this.#overridables.layer.description, copyrightText: this.#overridables.layer.copyrightText, - parentLayer: null, - subLayers: null, defaultVisibility: true, - hasAttachments: false, - htmlPopupType: 'esriServerHTMLPopupTypeNone', - displayField: 'OBJECTID', - typeIdField: null, - fields: [], - relationships: [], - capabilities: 'Query', - maxRecordCount: this.#overridables.maxRecordCount, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, supportsStatistics: true, + supportsExceedsLimitStatistics: false, supportsAdvancedQueries: true, - supportedQueryFormats: 'JSON', - ownershipBasedAccessControlForFeatures: { - allowOthersToQuery: true, - }, - useStandardizedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, advancedQueryCapabilities: { - useStandardizedQueries: true, + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, supportsStatistics: true, supportsOrderBy: true, supportsDistinct: true, - supportsPagination: true, - supportsTrueCurve: false, - supportsReturningQueryExtent: true, - supportsQueryWithDistance: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, }, - canModifyLayer: false, - dateFieldsTimeReference: null, - isDataVersioned: false, - supportsRollbackOnFailureParameter: true, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', hasM: false, hasZ: false, - allowGeometryUpdates: true, objectIdField: 'OBJECTID', - globalIdField: '', - types: [], - templates: [], - hasStaticData: false, - timeInfo: {}, uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true, }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, + maxRecordCount: this.#overridables.maxRecordCount, + standardMaxRecordCount: this.#overridables.maxRecordCount, + standardMaxRecordCountNoGeometry: this.#overridables.maxRecordCount, + tileMaxRecordCount: this.#overridables.maxRecordCount, + maxRecordCountFactor: 1, + fields: [], + relationships: [], + capabilities: 'Query', + ownershipBasedAccessControlForFeatures: { + allowOthersToQuery: true, + }, + types: [], + timeInfo: {}, }; } @@ -166,13 +234,12 @@ class MetadataDefaults { type: 'Feature Layer', minScale: 0, maxScale: 0, - canScaleSymbols: false, drawingInfo: { renderer: {}, labelingInfo: null, }, extent: this.#overridables.layer.extent, - supportsCoordinatesQuantization: false, + supportsCoordinatesQuantization: true, // TODO hasLabels: false, }; } diff --git a/packages/featureserver/src/query/index.js b/packages/featureserver/src/query/index.js index 2cbecd7fc..caed9fb70 100644 --- a/packages/featureserver/src/query/index.js +++ b/packages/featureserver/src/query/index.js @@ -6,6 +6,7 @@ const { renderStatisticsResponse } = require('./render-statistics'); const { renderPrecalculatedStatisticsResponse } = require('./render-precalculated-statistics'); const { renderCountAndExtentResponse } = require('./render-count-and-extent'); const { getGeometryTypeFromGeojson } = require('../helpers'); +const { validate } = require('./validate-query-request-parameters'); function query (json, requestParams = {}) { const { @@ -15,16 +16,24 @@ function query (json, requestParams = {}) { } = {} } = json; + validate(requestParams); + const { f: requestedFormat } = requestParams; + // TODO: if format PBF, need to send pbf if only count requested if (shouldRenderPrecalculatedData(json, requestParams)) { return renderPrecalculatedData(json, requestParams); } const data = (skipFiltering || !features) ? json : filterAndTransform(json, requestParams); - logWarnings(data, requestParams.f); + if(shouldLogWarnings(requestParams)) { + logWarnings(data, requestedFormat, requestParams.outFields); + } + + // TODO: Bug when count or extent requested. + // QUESTION: Is this problematic if its an aggregation with stats? if (requestedFormat === 'geojson') { return { type: 'FeatureCollection', @@ -39,24 +48,17 @@ function query (json, requestParams = {}) { }); } -function shouldRenderPrecalculatedData ({ statistics, count, extent }, { returnCountOnly, returnExtentOnly }) { - if (statistics) { - return true; - } - - if (returnCountOnly === true && count !== undefined && returnExtentOnly === true && extent) { - return true; - } - - if (returnCountOnly === true && count !== undefined && !returnExtentOnly) { - return true; - } +function shouldLogWarnings(requestParams) { + const { returnCountOnly, returnExtentOnly, returnIdsOnly } = requestParams; + + return !(returnCountOnly || returnExtentOnly || returnIdsOnly); +} - if (returnExtentOnly === true && extent && !returnCountOnly) { - return true; - } +function shouldRenderPrecalculatedData (json, requestParameters) { + const { statistics, count, extent } = json; + const { returnCountOnly, returnExtentOnly } = requestParameters; - return false; + return !!statistics || (returnCountOnly === true && count !== undefined) || (returnExtentOnly === true && extent && !returnCountOnly); } function renderPrecalculatedData (data, { @@ -73,6 +75,7 @@ function renderPrecalculatedData (data, { const retVal = {}; + // TODO: if only count, and f=pbf need to encode response if (returnCountOnly) { retVal.count = count; } @@ -92,6 +95,7 @@ function renderGeoservicesResponse (data, params = {}) { outSR } = params; + // TODO: if only count, and f=pbf need to encode response if (returnCountOnly || returnExtentOnly) { return renderCountAndExtentResponse(data, { returnCountOnly, diff --git a/packages/featureserver/src/query/log-warnings.js b/packages/featureserver/src/query/log-warnings.js index 053785b42..54f13d7b8 100644 --- a/packages/featureserver/src/query/log-warnings.js +++ b/packages/featureserver/src/query/log-warnings.js @@ -2,10 +2,10 @@ const _ = require('lodash'); const { getDataTypeFromValue } = require('../helpers'); const logManager = require('../log-manager'); -function logWarnings(geojson, format) { +function logWarnings(geojson, format, outFields = '*') { const { metadata = {}, features } = geojson; const esriFormat = format !== geojson; - + const outFieldsSet = outFields === '*' || outFields === '' ? null : outFields.split(','); const properties = _.get(features, '[0].properties') || _.get(features, '[0].attributes'); if (esriFormat && !metadata.idField && properties?.OBJECTID === undefined) { @@ -21,9 +21,13 @@ function logWarnings(geojson, format) { } if (metadata.fields && properties) { - compareFieldDefintionsToFeature(metadata.fields, properties); + const fields = outFieldsSet ? metadata.fields.filter(field => { + return outFieldsSet.includes(field.name || field.alias); + }) : metadata.fields; + + compareFieldDefintionsToFeature(fields, properties); - compareFeatureToFieldDefinitions(properties, metadata.fields); + compareFeatureToFieldDefinitions(properties, fields); } } diff --git a/packages/featureserver/src/query/validate-query-request-parameters.js b/packages/featureserver/src/query/validate-query-request-parameters.js new file mode 100644 index 000000000..6d66c025e --- /dev/null +++ b/packages/featureserver/src/query/validate-query-request-parameters.js @@ -0,0 +1,48 @@ +const joi = require('joi'); + +const spatialReferenceSchema = joi.object({ + wkid: joi.number().integer().required(), + latestWkid: joi.number().integer(), +}).unknown(); + +const esriExtentSchema = joi.object({ + xmin: joi.number().required(), + xmax: joi.number().required(), + ymin: joi.number().required(), + ymax: joi.number().required(), + spatialReference: spatialReferenceSchema.optional(), +}); + +const quantizationParametersSchema = joi.object({ + originPosition: joi.string().optional(), + tolerance: joi.number().optional(), + extent: esriExtentSchema.optional() +}); + + +const queryRequestSchema = joi.object({ + quantizationParameters: quantizationParametersSchema +}).unknown(); + +function validate(queryRequestParams) { + const { error } = queryRequestSchema.validate(queryRequestParams); + + if (error) { + handleError(error); + } +} + +function handleError(error) { + // TODO: right now the only possible error is for quantizationParams + // const [param] = error.details[0].path; + // if (param === 'quantizationParameters') { + const err = new Error('\'quantizationParameters\' parameter is invalid'); + err.code = 400; + err.details = [error.details[0].message]; + throw err; + // } + // throw error; +} +module.exports = { + validate +}; diff --git a/packages/featureserver/src/query/validate-query-request-parameters.spec.js b/packages/featureserver/src/query/validate-query-request-parameters.spec.js new file mode 100644 index 000000000..d87628c64 --- /dev/null +++ b/packages/featureserver/src/query/validate-query-request-parameters.spec.js @@ -0,0 +1,32 @@ +const should = require('should'); // eslint-disable-line +const { validate } = require('./validate-query-request-parameters'); + +describe('validate-query-request-parameters', () => { + describe('quantizationParameters', () => { + it('should invalidate with 400 due to missing extent param', () => { + try { + validate({ + quantizationParameters: { + extent: {}, + }, + }); + } catch (error) { + error.message.should.deepEqual( + '\'quantizationParameters\' parameter is invalid', + ); + error.details.should.deepEqual([ + '"quantizationParameters.extent.xmin" is required' + ]); + error.code.should.equal(400); + } + }); + + it('should validate if missing (optional)', () => { + try { + validate({}); + } catch (err) { + err.should.deepEqual(undefined); + } + }); + }); +}); diff --git a/packages/featureserver/src/response-handler.js b/packages/featureserver/src/response-handler.js deleted file mode 100644 index b58a7e608..000000000 --- a/packages/featureserver/src/response-handler.js +++ /dev/null @@ -1,16 +0,0 @@ -module.exports = function responseHandler (req, res, statusCode, payload) { - if (typeof req.query.callback === 'string') { - let sanitizedCallback = req.query.callback.replace(/[^\w\d\.\(\)\[\]]/g, '') // eslint-disable-line - res.set('Content-Type', 'application/javascript'); - res.status(statusCode); - return res.send(`${sanitizedCallback}(${JSON.stringify(payload)})`); - } - - if (req.query?.f === 'pjson') { - res.set('Content-type', 'application/json; charset=utf-8'); - res.status(statusCode); - return res.send(JSON.stringify(payload, null, 2)); - } - - return res.status(statusCode).json(payload); -}; diff --git a/packages/featureserver/src/response-handler.spec.js b/packages/featureserver/src/response-handler.spec.js deleted file mode 100644 index b8d2dfd27..000000000 --- a/packages/featureserver/src/response-handler.spec.js +++ /dev/null @@ -1,64 +0,0 @@ -const should = require('should') // eslint-disable-line -const sinon = require('sinon'); -const responseHandler = require('./response-handler'); - -describe('request handler', () => { - const res = { - json: () => { return res; }, - send: () => { return res; }, - set: () => { return res; }, - status: () => { return res; } - }; - - const req = { - query: {} - }; - - beforeEach(() => { - sinon.spy(res); - }); - - afterEach(() => { - sinon.restore(); - }); - - it('return 200 and json', () => { - responseHandler(req, res, 200, { test: true }); - res.send.notCalled.should.equal(true); - res.status.firstCall.args[0].should.be.exactly(200); - res.json.firstCall.args[0].should.deepEqual({ test: true }); - }); - - it('return 500 and json', () => { - responseHandler(req, res, 500, { test: true }); - res.send.notCalled.should.equal(true); - res.status.firstCall.args[0].should.be.exactly(500); - res.json.firstCall.args[0].should.deepEqual({ test: true }); - }); - - it('return 200 and callback', () => { - const reqCallback = { - query: { - callback: 'test' - } - }; - responseHandler(reqCallback, res, 200, { test: true }); - res.send.firstCall.args[0].should.equal('test({"test":true})'); - res.status.firstCall.args[0].should.be.exactly(200); - res.json.notCalled.should.equal(true); - }); - - it('return 200 and pjson', () => { - const reqPretty = { - query: { - f: 'pjson' - } - }; - responseHandler(reqPretty, res, 200, { test: true }); - res.send.firstCall.args[0].should.equal(`{ - "test": true -}`); - res.status.firstCall.args[0].should.be.exactly(200); - res.json.notCalled.should.equal(true); - }); -}); diff --git a/packages/featureserver/src/response-handlers/general-response-handler.js b/packages/featureserver/src/response-handlers/general-response-handler.js new file mode 100644 index 000000000..9117c505d --- /dev/null +++ b/packages/featureserver/src/response-handlers/general-response-handler.js @@ -0,0 +1,16 @@ +const { sendCallbackResponse, sendPrettyJson } = require('./helpers'); + +module.exports = function generalResponseHandler(res, payload, requestParameters) { + const { f, callback } = requestParameters; + + if (typeof callback === 'string') { + return sendCallbackResponse(res, payload, callback); + } + + if (f === 'pjson') { + return sendPrettyJson(res, payload); + } + + // payload here might be Esri JSON or GeoJSON + return res.status(200).json(payload); +}; diff --git a/packages/featureserver/src/response-handlers/general-response-handler.spec.js b/packages/featureserver/src/response-handlers/general-response-handler.spec.js new file mode 100644 index 000000000..c21597c1b --- /dev/null +++ b/packages/featureserver/src/response-handlers/general-response-handler.spec.js @@ -0,0 +1,81 @@ +const should = require('should'); // eslint-disable-line +const sinon = require('sinon'); +const proxyquire = require('proxyquire'); + +const sendPrettyJsonSpy = sinon.spy(() => { + return { pretty: 'json' }; +}); + +const sendCallbackResponseSpy = sinon.spy(() => { + return 'callback-string'; +}); + +const responseHandler = proxyquire('./general-response-handler', { + './helpers': { + sendPrettyJson: sendPrettyJsonSpy, + sendCallbackResponse: sendCallbackResponseSpy, + }, +}); + +describe('general response handler', () => { + const res = { + json: () => { + return res; + }, + send: () => { + return res; + }, + set: () => { + return res; + }, + status: () => { + return res; + }, + }; + + beforeEach(() => { + sinon.spy(res); + }); + + afterEach(() => { + sinon.restore(); + }); + + it('return 200 and json', () => { + responseHandler(res, { test: true }, {}); + res.send.notCalled.should.equal(true); + res.status.firstCall.args[0].should.be.exactly(200); + res.json.firstCall.args[0].should.deepEqual({ test: true }); + }); + + it('send as callback-string', () => { + responseHandler( + res, + { test: true }, + { + callback: 'test-callback', + }, + ); + + sendCallbackResponseSpy.firstCall.args.should.deepEqual([ + res, + { test: true }, + 'test-callback', + ]); + }); + + it('send as pretty json', () => { + responseHandler( + res, + { test: true }, + { + f: 'pjson', + }, + ); + + sendPrettyJsonSpy.firstCall.args.should.deepEqual([ + res, + { test: true } + ]); + }); +}); diff --git a/packages/featureserver/src/response-handlers/helpers/index.js b/packages/featureserver/src/response-handlers/helpers/index.js new file mode 100644 index 000000000..138c6286e --- /dev/null +++ b/packages/featureserver/src/response-handlers/helpers/index.js @@ -0,0 +1,5 @@ +module.exports = { + ...require('./send-pbf/index.js'), + ...require('./send-callback.js'), + ...require('./send-pretty-json.js') +}; diff --git a/packages/featureserver/src/response-handlers/helpers/send-callback.js b/packages/featureserver/src/response-handlers/helpers/send-callback.js new file mode 100644 index 000000000..c8933cb13 --- /dev/null +++ b/packages/featureserver/src/response-handlers/helpers/send-callback.js @@ -0,0 +1,9 @@ +function sendCallbackResponse(res, payload, callbackString) { + const sanitizedCallback = callbackString.replace(/[^\w\d\.\(\)\[\]]/g, '') // eslint-disable-line + res.set('Content-Type', 'application/javascript'); + res.status(200); + return res.send(`${sanitizedCallback}(${JSON.stringify(payload)})`); +} +module.exports = { + sendCallbackResponse +}; diff --git a/packages/featureserver/src/response-handlers/helpers/send-callback.spec.js b/packages/featureserver/src/response-handlers/helpers/send-callback.spec.js new file mode 100644 index 000000000..60fb923ed --- /dev/null +++ b/packages/featureserver/src/response-handlers/helpers/send-callback.spec.js @@ -0,0 +1,23 @@ +const should = require('should'); // eslint-disable-line +const sinon = require('sinon'); +const { sendCallbackResponse } = require('./'); + + +const res = { + set: sinon.spy(() => { + return res; + }), + status: sinon.spy(() => { + return res; + }), + send: sinon.spy() +}; + +describe('sendCallbackResponse', () => { + it('should respond with callback string', () => { + sendCallbackResponse(res, { hello: 'world'}, 'what'); + res.set.firstCall.args.should.deepEqual(['Content-Type', 'application/javascript']); + res.send.firstCall.args[0].should.equal('what({"hello":"world"})'); + res.status.firstCall.args[0].should.be.exactly(200); + }); +}); \ No newline at end of file diff --git a/packages/featureserver/src/response-handlers/helpers/send-pbf/FeatureCollection.proto.js b/packages/featureserver/src/response-handlers/helpers/send-pbf/FeatureCollection.proto.js new file mode 100644 index 000000000..168eccd13 --- /dev/null +++ b/packages/featureserver/src/response-handlers/helpers/send-pbf/FeatureCollection.proto.js @@ -0,0 +1,2964 @@ +// TODO: move this to a npm +/* eslint-disable */ +/*eslint-disable block-scoped-var, id-length, no-control-regex, no-magic-numbers, no-prototype-builtins, no-redeclare, no-shadow, no-var, sort-vars*/ +"use strict"; + +var $protobuf = require("protobufjs/minimal"); + +var $Writer = $protobuf.Writer, $util = $protobuf.util; + +var $root = $protobuf.roots["default"] || ($protobuf.roots["default"] = {}); + +$root.esriPBuffer = (function() { + + var esriPBuffer = {}; + + esriPBuffer.FeatureCollectionPBuffer = (function() { + + function FeatureCollectionPBuffer(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + FeatureCollectionPBuffer.prototype.version = ""; + FeatureCollectionPBuffer.prototype.queryResult = null; + + FeatureCollectionPBuffer.create = function create(properties) { + return new FeatureCollectionPBuffer(properties); + }; + + FeatureCollectionPBuffer.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.version != null && Object.hasOwnProperty.call(message, "version")) + writer.uint32(10).string(message.version); + if (message.queryResult != null && Object.hasOwnProperty.call(message, "queryResult")) + $root.esriPBuffer.FeatureCollectionPBuffer.QueryResult.encode(message.queryResult, writer.uint32(18).fork()).ldelim(); + return writer; + }; + + FeatureCollectionPBuffer.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + FeatureCollectionPBuffer.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.version != null && message.hasOwnProperty("version")) + if (!$util.isString(message.version)) + return "version: string expected"; + if (message.queryResult != null && message.hasOwnProperty("queryResult")) { + var error = $root.esriPBuffer.FeatureCollectionPBuffer.QueryResult.verify(message.queryResult); + if (error) + return "queryResult." + error; + } + return null; + }; + + FeatureCollectionPBuffer.fromObject = function fromObject(object) { + if (object instanceof $root.esriPBuffer.FeatureCollectionPBuffer) + return object; + var message = new $root.esriPBuffer.FeatureCollectionPBuffer(); + if (object.version != null) + message.version = String(object.version); + if (object.queryResult != null) { + if (typeof object.queryResult !== "object") + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.queryResult: object expected"); + message.queryResult = $root.esriPBuffer.FeatureCollectionPBuffer.QueryResult.fromObject(object.queryResult); + } + return message; + }; + + FeatureCollectionPBuffer.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.version = ""; + object.queryResult = null; + } + if (message.version != null && message.hasOwnProperty("version")) + object.version = message.version; + if (message.queryResult != null && message.hasOwnProperty("queryResult")) + object.queryResult = $root.esriPBuffer.FeatureCollectionPBuffer.QueryResult.toObject(message.queryResult, options); + return object; + }; + + FeatureCollectionPBuffer.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + FeatureCollectionPBuffer.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/esriPBuffer.FeatureCollectionPBuffer"; + }; + + FeatureCollectionPBuffer.GeometryType = (function() { + var valuesById = {}, values = Object.create(valuesById); + values[valuesById[0] = "esriGeometryTypePoint"] = 0; + values[valuesById[1] = "esriGeometryTypeMultipoint"] = 1; + values[valuesById[2] = "esriGeometryTypePolyline"] = 2; + values[valuesById[3] = "esriGeometryTypePolygon"] = 3; + values[valuesById[4] = "esriGeometryTypeMultipatch"] = 4; + values[valuesById[127] = "esriGeometryTypeNone"] = 127; + values[valuesById[5] = "esriGeometryTypeEnvelope"] = 5; + return values; + })(); + + FeatureCollectionPBuffer.FieldType = (function() { + var valuesById = {}, values = Object.create(valuesById); + values[valuesById[0] = "esriFieldTypeSmallInteger"] = 0; + values[valuesById[1] = "esriFieldTypeInteger"] = 1; + values[valuesById[2] = "esriFieldTypeSingle"] = 2; + values[valuesById[3] = "esriFieldTypeDouble"] = 3; + values[valuesById[4] = "esriFieldTypeString"] = 4; + values[valuesById[5] = "esriFieldTypeDate"] = 5; + values[valuesById[6] = "esriFieldTypeOID"] = 6; + values[valuesById[7] = "esriFieldTypeGeometry"] = 7; + values[valuesById[8] = "esriFieldTypeBlob"] = 8; + values[valuesById[9] = "esriFieldTypeRaster"] = 9; + values[valuesById[10] = "esriFieldTypeGUID"] = 10; + values[valuesById[11] = "esriFieldTypeGlobalID"] = 11; + values[valuesById[12] = "esriFieldTypeXML"] = 12; + values[valuesById[13] = "esriFieldTypeBigInteger"] = 13; + values[valuesById[14] = "esriFieldTypeDateOnly"] = 14; + values[valuesById[15] = "esriFieldTypeTimeOnly"] = 15; + values[valuesById[16] = "esriFieldTypeTimestampOffset"] = 16; + return values; + })(); + + FeatureCollectionPBuffer.SQLType = (function() { + var valuesById = {}, values = Object.create(valuesById); + values[valuesById[0] = "sqlTypeBigInt"] = 0; + values[valuesById[1] = "sqlTypeBinary"] = 1; + values[valuesById[2] = "sqlTypeBit"] = 2; + values[valuesById[3] = "sqlTypeChar"] = 3; + values[valuesById[4] = "sqlTypeDate"] = 4; + values[valuesById[5] = "sqlTypeDecimal"] = 5; + values[valuesById[6] = "sqlTypeDouble"] = 6; + values[valuesById[7] = "sqlTypeFloat"] = 7; + values[valuesById[8] = "sqlTypeGeometry"] = 8; + values[valuesById[9] = "sqlTypeGUID"] = 9; + values[valuesById[10] = "sqlTypeInteger"] = 10; + values[valuesById[11] = "sqlTypeLongNVarchar"] = 11; + values[valuesById[12] = "sqlTypeLongVarbinary"] = 12; + values[valuesById[13] = "sqlTypeLongVarchar"] = 13; + values[valuesById[14] = "sqlTypeNChar"] = 14; + values[valuesById[15] = "sqlTypeNVarchar"] = 15; + values[valuesById[16] = "sqlTypeOther"] = 16; + values[valuesById[17] = "sqlTypeReal"] = 17; + values[valuesById[18] = "sqlTypeSmallInt"] = 18; + values[valuesById[19] = "sqlTypeSqlXml"] = 19; + values[valuesById[20] = "sqlTypeTime"] = 20; + values[valuesById[21] = "sqlTypeTimestamp"] = 21; + values[valuesById[22] = "sqlTypeTimestamp2"] = 22; + values[valuesById[23] = "sqlTypeTinyInt"] = 23; + values[valuesById[24] = "sqlTypeVarbinary"] = 24; + values[valuesById[25] = "sqlTypeVarchar"] = 25; + values[valuesById[26] = "sqlTypeTimestampWithTimezone"] = 26; + return values; + })(); + + FeatureCollectionPBuffer.QuantizeOriginPostion = (function() { + var valuesById = {}, values = Object.create(valuesById); + values[valuesById[0] = "upperLeft"] = 0; + values[valuesById[1] = "lowerLeft"] = 1; + return values; + })(); + + FeatureCollectionPBuffer.SpatialReference = (function() { + + function SpatialReference(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + SpatialReference.prototype.wkid = 0; + SpatialReference.prototype.lastestWkid = 0; + SpatialReference.prototype.vcsWkid = 0; + SpatialReference.prototype.latestVcsWkid = 0; + SpatialReference.prototype.wkt = ""; + + SpatialReference.create = function create(properties) { + return new SpatialReference(properties); + }; + + SpatialReference.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.wkid != null && Object.hasOwnProperty.call(message, "wkid")) + writer.uint32(8).uint32(message.wkid); + if (message.lastestWkid != null && Object.hasOwnProperty.call(message, "lastestWkid")) + writer.uint32(16).uint32(message.lastestWkid); + if (message.vcsWkid != null && Object.hasOwnProperty.call(message, "vcsWkid")) + writer.uint32(24).uint32(message.vcsWkid); + if (message.latestVcsWkid != null && Object.hasOwnProperty.call(message, "latestVcsWkid")) + writer.uint32(32).uint32(message.latestVcsWkid); + if (message.wkt != null && Object.hasOwnProperty.call(message, "wkt")) + writer.uint32(42).string(message.wkt); + return writer; + }; + + SpatialReference.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + SpatialReference.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.wkid != null && message.hasOwnProperty("wkid")) + if (!$util.isInteger(message.wkid)) + return "wkid: integer expected"; + if (message.lastestWkid != null && message.hasOwnProperty("lastestWkid")) + if (!$util.isInteger(message.lastestWkid)) + return "lastestWkid: integer expected"; + if (message.vcsWkid != null && message.hasOwnProperty("vcsWkid")) + if (!$util.isInteger(message.vcsWkid)) + return "vcsWkid: integer expected"; + if (message.latestVcsWkid != null && message.hasOwnProperty("latestVcsWkid")) + if (!$util.isInteger(message.latestVcsWkid)) + return "latestVcsWkid: integer expected"; + if (message.wkt != null && message.hasOwnProperty("wkt")) + if (!$util.isString(message.wkt)) + return "wkt: string expected"; + return null; + }; + + SpatialReference.fromObject = function fromObject(object) { + if (object instanceof $root.esriPBuffer.FeatureCollectionPBuffer.SpatialReference) + return object; + var message = new $root.esriPBuffer.FeatureCollectionPBuffer.SpatialReference(); + if (object.wkid != null) + message.wkid = object.wkid >>> 0; + if (object.lastestWkid != null) + message.lastestWkid = object.lastestWkid >>> 0; + if (object.vcsWkid != null) + message.vcsWkid = object.vcsWkid >>> 0; + if (object.latestVcsWkid != null) + message.latestVcsWkid = object.latestVcsWkid >>> 0; + if (object.wkt != null) + message.wkt = String(object.wkt); + return message; + }; + + SpatialReference.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.wkid = 0; + object.lastestWkid = 0; + object.vcsWkid = 0; + object.latestVcsWkid = 0; + object.wkt = ""; + } + if (message.wkid != null && message.hasOwnProperty("wkid")) + object.wkid = message.wkid; + if (message.lastestWkid != null && message.hasOwnProperty("lastestWkid")) + object.lastestWkid = message.lastestWkid; + if (message.vcsWkid != null && message.hasOwnProperty("vcsWkid")) + object.vcsWkid = message.vcsWkid; + if (message.latestVcsWkid != null && message.hasOwnProperty("latestVcsWkid")) + object.latestVcsWkid = message.latestVcsWkid; + if (message.wkt != null && message.hasOwnProperty("wkt")) + object.wkt = message.wkt; + return object; + }; + + SpatialReference.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + SpatialReference.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/esriPBuffer.FeatureCollectionPBuffer.SpatialReference"; + }; + + return SpatialReference; + })(); + + FeatureCollectionPBuffer.Field = (function() { + + function Field(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + Field.prototype.name = ""; + Field.prototype.fieldType = 0; + Field.prototype.alias = ""; + Field.prototype.sqlType = 0; + Field.prototype.domain = ""; + Field.prototype.defaultValue = ""; + + Field.create = function create(properties) { + return new Field(properties); + }; + + Field.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.name != null && Object.hasOwnProperty.call(message, "name")) + writer.uint32(10).string(message.name); + if (message.fieldType != null && Object.hasOwnProperty.call(message, "fieldType")) + writer.uint32(16).int32(message.fieldType); + if (message.alias != null && Object.hasOwnProperty.call(message, "alias")) + writer.uint32(26).string(message.alias); + if (message.sqlType != null && Object.hasOwnProperty.call(message, "sqlType")) + writer.uint32(32).int32(message.sqlType); + if (message.domain != null && Object.hasOwnProperty.call(message, "domain")) + writer.uint32(42).string(message.domain); + if (message.defaultValue != null && Object.hasOwnProperty.call(message, "defaultValue")) + writer.uint32(50).string(message.defaultValue); + return writer; + }; + + Field.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + Field.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.name != null && message.hasOwnProperty("name")) + if (!$util.isString(message.name)) + return "name: string expected"; + if (message.fieldType != null && message.hasOwnProperty("fieldType")) + switch (message.fieldType) { + default: + return "fieldType: enum value expected"; + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + break; + } + if (message.alias != null && message.hasOwnProperty("alias")) + if (!$util.isString(message.alias)) + return "alias: string expected"; + if (message.sqlType != null && message.hasOwnProperty("sqlType")) + switch (message.sqlType) { + default: + return "sqlType: enum value expected"; + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + break; + } + if (message.domain != null && message.hasOwnProperty("domain")) + if (!$util.isString(message.domain)) + return "domain: string expected"; + if (message.defaultValue != null && message.hasOwnProperty("defaultValue")) + if (!$util.isString(message.defaultValue)) + return "defaultValue: string expected"; + return null; + }; + + Field.fromObject = function fromObject(object) { + if (object instanceof $root.esriPBuffer.FeatureCollectionPBuffer.Field) + return object; + var message = new $root.esriPBuffer.FeatureCollectionPBuffer.Field(); + if (object.name != null) + message.name = String(object.name); + switch (object.fieldType) { + default: + if (typeof object.fieldType === "number") { + message.fieldType = object.fieldType; + break; + } + break; + case "esriFieldTypeSmallInteger": + case 0: + message.fieldType = 0; + break; + case "esriFieldTypeInteger": + case 1: + message.fieldType = 1; + break; + case "esriFieldTypeSingle": + case 2: + message.fieldType = 2; + break; + case "esriFieldTypeDouble": + case 3: + message.fieldType = 3; + break; + case "esriFieldTypeString": + case 4: + message.fieldType = 4; + break; + case "esriFieldTypeDate": + case 5: + message.fieldType = 5; + break; + case "esriFieldTypeOID": + case 6: + message.fieldType = 6; + break; + case "esriFieldTypeGeometry": + case 7: + message.fieldType = 7; + break; + case "esriFieldTypeBlob": + case 8: + message.fieldType = 8; + break; + case "esriFieldTypeRaster": + case 9: + message.fieldType = 9; + break; + case "esriFieldTypeGUID": + case 10: + message.fieldType = 10; + break; + case "esriFieldTypeGlobalID": + case 11: + message.fieldType = 11; + break; + case "esriFieldTypeXML": + case 12: + message.fieldType = 12; + break; + case "esriFieldTypeBigInteger": + case 13: + message.fieldType = 13; + break; + case "esriFieldTypeDateOnly": + case 14: + message.fieldType = 14; + break; + case "esriFieldTypeTimeOnly": + case 15: + message.fieldType = 15; + break; + case "esriFieldTypeTimestampOffset": + case 16: + message.fieldType = 16; + break; + } + if (object.alias != null) + message.alias = String(object.alias); + switch (object.sqlType) { + default: + if (typeof object.sqlType === "number") { + message.sqlType = object.sqlType; + break; + } + break; + case "sqlTypeBigInt": + case 0: + message.sqlType = 0; + break; + case "sqlTypeBinary": + case 1: + message.sqlType = 1; + break; + case "sqlTypeBit": + case 2: + message.sqlType = 2; + break; + case "sqlTypeChar": + case 3: + message.sqlType = 3; + break; + case "sqlTypeDate": + case 4: + message.sqlType = 4; + break; + case "sqlTypeDecimal": + case 5: + message.sqlType = 5; + break; + case "sqlTypeDouble": + case 6: + message.sqlType = 6; + break; + case "sqlTypeFloat": + case 7: + message.sqlType = 7; + break; + case "sqlTypeGeometry": + case 8: + message.sqlType = 8; + break; + case "sqlTypeGUID": + case 9: + message.sqlType = 9; + break; + case "sqlTypeInteger": + case 10: + message.sqlType = 10; + break; + case "sqlTypeLongNVarchar": + case 11: + message.sqlType = 11; + break; + case "sqlTypeLongVarbinary": + case 12: + message.sqlType = 12; + break; + case "sqlTypeLongVarchar": + case 13: + message.sqlType = 13; + break; + case "sqlTypeNChar": + case 14: + message.sqlType = 14; + break; + case "sqlTypeNVarchar": + case 15: + message.sqlType = 15; + break; + case "sqlTypeOther": + case 16: + message.sqlType = 16; + break; + case "sqlTypeReal": + case 17: + message.sqlType = 17; + break; + case "sqlTypeSmallInt": + case 18: + message.sqlType = 18; + break; + case "sqlTypeSqlXml": + case 19: + message.sqlType = 19; + break; + case "sqlTypeTime": + case 20: + message.sqlType = 20; + break; + case "sqlTypeTimestamp": + case 21: + message.sqlType = 21; + break; + case "sqlTypeTimestamp2": + case 22: + message.sqlType = 22; + break; + case "sqlTypeTinyInt": + case 23: + message.sqlType = 23; + break; + case "sqlTypeVarbinary": + case 24: + message.sqlType = 24; + break; + case "sqlTypeVarchar": + case 25: + message.sqlType = 25; + break; + case "sqlTypeTimestampWithTimezone": + case 26: + message.sqlType = 26; + break; + } + if (object.domain != null) + message.domain = String(object.domain); + if (object.defaultValue != null) + message.defaultValue = String(object.defaultValue); + return message; + }; + + Field.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.name = ""; + object.fieldType = options.enums === String ? "esriFieldTypeSmallInteger" : 0; + object.alias = ""; + object.sqlType = options.enums === String ? "sqlTypeBigInt" : 0; + object.domain = ""; + object.defaultValue = ""; + } + if (message.name != null && message.hasOwnProperty("name")) + object.name = message.name; + if (message.fieldType != null && message.hasOwnProperty("fieldType")) + object.fieldType = options.enums === String ? $root.esriPBuffer.FeatureCollectionPBuffer.FieldType[message.fieldType] === undefined ? message.fieldType : $root.esriPBuffer.FeatureCollectionPBuffer.FieldType[message.fieldType] : message.fieldType; + if (message.alias != null && message.hasOwnProperty("alias")) + object.alias = message.alias; + if (message.sqlType != null && message.hasOwnProperty("sqlType")) + object.sqlType = options.enums === String ? $root.esriPBuffer.FeatureCollectionPBuffer.SQLType[message.sqlType] === undefined ? message.sqlType : $root.esriPBuffer.FeatureCollectionPBuffer.SQLType[message.sqlType] : message.sqlType; + if (message.domain != null && message.hasOwnProperty("domain")) + object.domain = message.domain; + if (message.defaultValue != null && message.hasOwnProperty("defaultValue")) + object.defaultValue = message.defaultValue; + return object; + }; + + Field.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + Field.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/esriPBuffer.FeatureCollectionPBuffer.Field"; + }; + + return Field; + })(); + + FeatureCollectionPBuffer.GeometryField = (function() { + + function GeometryField(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + GeometryField.prototype.field = null; + GeometryField.prototype.geometryType = 0; + + GeometryField.create = function create(properties) { + return new GeometryField(properties); + }; + + GeometryField.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.field != null && Object.hasOwnProperty.call(message, "field")) + $root.esriPBuffer.FeatureCollectionPBuffer.Field.encode(message.field, writer.uint32(10).fork()).ldelim(); + if (message.geometryType != null && Object.hasOwnProperty.call(message, "geometryType")) + writer.uint32(16).int32(message.geometryType); + return writer; + }; + + GeometryField.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + GeometryField.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.field != null && message.hasOwnProperty("field")) { + var error = $root.esriPBuffer.FeatureCollectionPBuffer.Field.verify(message.field); + if (error) + return "field." + error; + } + if (message.geometryType != null && message.hasOwnProperty("geometryType")) + switch (message.geometryType) { + default: + return "geometryType: enum value expected"; + case 0: + case 1: + case 2: + case 3: + case 4: + case 127: + case 5: + break; + } + return null; + }; + + GeometryField.fromObject = function fromObject(object) { + if (object instanceof $root.esriPBuffer.FeatureCollectionPBuffer.GeometryField) + return object; + var message = new $root.esriPBuffer.FeatureCollectionPBuffer.GeometryField(); + if (object.field != null) { + if (typeof object.field !== "object") + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.GeometryField.field: object expected"); + message.field = $root.esriPBuffer.FeatureCollectionPBuffer.Field.fromObject(object.field); + } + switch (object.geometryType) { + default: + if (typeof object.geometryType === "number") { + message.geometryType = object.geometryType; + break; + } + break; + case "esriGeometryTypePoint": + case 0: + message.geometryType = 0; + break; + case "esriGeometryTypeMultipoint": + case 1: + message.geometryType = 1; + break; + case "esriGeometryTypePolyline": + case 2: + message.geometryType = 2; + break; + case "esriGeometryTypePolygon": + case 3: + message.geometryType = 3; + break; + case "esriGeometryTypeMultipatch": + case 4: + message.geometryType = 4; + break; + case "esriGeometryTypeNone": + case 127: + message.geometryType = 127; + break; + case "esriGeometryTypeEnvelope": + case 5: + message.geometryType = 5; + break; + } + return message; + }; + + GeometryField.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.field = null; + object.geometryType = options.enums === String ? "esriGeometryTypePoint" : 0; + } + if (message.field != null && message.hasOwnProperty("field")) + object.field = $root.esriPBuffer.FeatureCollectionPBuffer.Field.toObject(message.field, options); + if (message.geometryType != null && message.hasOwnProperty("geometryType")) + object.geometryType = options.enums === String ? $root.esriPBuffer.FeatureCollectionPBuffer.GeometryType[message.geometryType] === undefined ? message.geometryType : $root.esriPBuffer.FeatureCollectionPBuffer.GeometryType[message.geometryType] : message.geometryType; + return object; + }; + + GeometryField.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + GeometryField.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/esriPBuffer.FeatureCollectionPBuffer.GeometryField"; + }; + + return GeometryField; + })(); + + FeatureCollectionPBuffer.Envelope = (function() { + + function Envelope(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + Envelope.prototype.XMin = 0; + Envelope.prototype.YMin = 0; + Envelope.prototype.XMax = 0; + Envelope.prototype.YMax = 0; + Envelope.prototype.SpatialReference = null; + + Envelope.create = function create(properties) { + return new Envelope(properties); + }; + + Envelope.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.XMin != null && Object.hasOwnProperty.call(message, "XMin")) + writer.uint32(9).double(message.XMin); + if (message.YMin != null && Object.hasOwnProperty.call(message, "YMin")) + writer.uint32(17).double(message.YMin); + if (message.XMax != null && Object.hasOwnProperty.call(message, "XMax")) + writer.uint32(25).double(message.XMax); + if (message.YMax != null && Object.hasOwnProperty.call(message, "YMax")) + writer.uint32(33).double(message.YMax); + if (message.SpatialReference != null && Object.hasOwnProperty.call(message, "SpatialReference")) + $root.esriPBuffer.FeatureCollectionPBuffer.SpatialReference.encode(message.SpatialReference, writer.uint32(42).fork()).ldelim(); + return writer; + }; + + Envelope.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + Envelope.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.XMin != null && message.hasOwnProperty("XMin")) + if (typeof message.XMin !== "number") + return "XMin: number expected"; + if (message.YMin != null && message.hasOwnProperty("YMin")) + if (typeof message.YMin !== "number") + return "YMin: number expected"; + if (message.XMax != null && message.hasOwnProperty("XMax")) + if (typeof message.XMax !== "number") + return "XMax: number expected"; + if (message.YMax != null && message.hasOwnProperty("YMax")) + if (typeof message.YMax !== "number") + return "YMax: number expected"; + if (message.SpatialReference != null && message.hasOwnProperty("SpatialReference")) { + var error = $root.esriPBuffer.FeatureCollectionPBuffer.SpatialReference.verify(message.SpatialReference); + if (error) + return "SpatialReference." + error; + } + return null; + }; + + Envelope.fromObject = function fromObject(object) { + if (object instanceof $root.esriPBuffer.FeatureCollectionPBuffer.Envelope) + return object; + var message = new $root.esriPBuffer.FeatureCollectionPBuffer.Envelope(); + if (object.XMin != null) + message.XMin = Number(object.XMin); + if (object.YMin != null) + message.YMin = Number(object.YMin); + if (object.XMax != null) + message.XMax = Number(object.XMax); + if (object.YMax != null) + message.YMax = Number(object.YMax); + if (object.SpatialReference != null) { + if (typeof object.SpatialReference !== "object") + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.Envelope.SpatialReference: object expected"); + message.SpatialReference = $root.esriPBuffer.FeatureCollectionPBuffer.SpatialReference.fromObject(object.SpatialReference); + } + return message; + }; + + Envelope.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.XMin = 0; + object.YMin = 0; + object.XMax = 0; + object.YMax = 0; + object.SpatialReference = null; + } + if (message.XMin != null && message.hasOwnProperty("XMin")) + object.XMin = options.json && !isFinite(message.XMin) ? String(message.XMin) : message.XMin; + if (message.YMin != null && message.hasOwnProperty("YMin")) + object.YMin = options.json && !isFinite(message.YMin) ? String(message.YMin) : message.YMin; + if (message.XMax != null && message.hasOwnProperty("XMax")) + object.XMax = options.json && !isFinite(message.XMax) ? String(message.XMax) : message.XMax; + if (message.YMax != null && message.hasOwnProperty("YMax")) + object.YMax = options.json && !isFinite(message.YMax) ? String(message.YMax) : message.YMax; + if (message.SpatialReference != null && message.hasOwnProperty("SpatialReference")) + object.SpatialReference = $root.esriPBuffer.FeatureCollectionPBuffer.SpatialReference.toObject(message.SpatialReference, options); + return object; + }; + + Envelope.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + Envelope.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/esriPBuffer.FeatureCollectionPBuffer.Envelope"; + }; + + return Envelope; + })(); + + FeatureCollectionPBuffer.Value = (function() { + + function Value(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + Value.prototype.stringValue = null; + Value.prototype.floatValue = null; + Value.prototype.doubleValue = null; + Value.prototype.sintValue = null; + Value.prototype.uintValue = null; + Value.prototype.int64Value = null; + Value.prototype.uint64Value = null; + Value.prototype.sint64Value = null; + Value.prototype.boolValue = null; + + var $oneOfFields; + + Object.defineProperty(Value.prototype, "valueType", { + get: $util.oneOfGetter($oneOfFields = ["stringValue", "floatValue", "doubleValue", "sintValue", "uintValue", "int64Value", "uint64Value", "sint64Value", "boolValue"]), + set: $util.oneOfSetter($oneOfFields) + }); + + Value.create = function create(properties) { + return new Value(properties); + }; + + Value.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.stringValue != null && Object.hasOwnProperty.call(message, "stringValue")) + writer.uint32(10).string(message.stringValue); + if (message.floatValue != null && Object.hasOwnProperty.call(message, "floatValue")) + writer.uint32(21).float(message.floatValue); + if (message.doubleValue != null && Object.hasOwnProperty.call(message, "doubleValue")) + writer.uint32(25).double(message.doubleValue); + if (message.sintValue != null && Object.hasOwnProperty.call(message, "sintValue")) + writer.uint32(32).sint32(message.sintValue); + if (message.uintValue != null && Object.hasOwnProperty.call(message, "uintValue")) + writer.uint32(40).uint32(message.uintValue); + if (message.int64Value != null && Object.hasOwnProperty.call(message, "int64Value")) + writer.uint32(48).int64(message.int64Value); + if (message.uint64Value != null && Object.hasOwnProperty.call(message, "uint64Value")) + writer.uint32(56).uint64(message.uint64Value); + if (message.sint64Value != null && Object.hasOwnProperty.call(message, "sint64Value")) + writer.uint32(64).sint64(message.sint64Value); + if (message.boolValue != null && Object.hasOwnProperty.call(message, "boolValue")) + writer.uint32(72).bool(message.boolValue); + return writer; + }; + + Value.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + Value.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + var properties = {}; + if (message.stringValue != null && message.hasOwnProperty("stringValue")) { + properties.valueType = 1; + if (!$util.isString(message.stringValue)) + return "stringValue: string expected"; + } + if (message.floatValue != null && message.hasOwnProperty("floatValue")) { + if (properties.valueType === 1) + return "valueType: multiple values"; + properties.valueType = 1; + if (typeof message.floatValue !== "number") + return "floatValue: number expected"; + } + if (message.doubleValue != null && message.hasOwnProperty("doubleValue")) { + if (properties.valueType === 1) + return "valueType: multiple values"; + properties.valueType = 1; + if (typeof message.doubleValue !== "number") + return "doubleValue: number expected"; + } + if (message.sintValue != null && message.hasOwnProperty("sintValue")) { + if (properties.valueType === 1) + return "valueType: multiple values"; + properties.valueType = 1; + if (!$util.isInteger(message.sintValue)) + return "sintValue: integer expected"; + } + if (message.uintValue != null && message.hasOwnProperty("uintValue")) { + if (properties.valueType === 1) + return "valueType: multiple values"; + properties.valueType = 1; + if (!$util.isInteger(message.uintValue)) + return "uintValue: integer expected"; + } + if (message.int64Value != null && message.hasOwnProperty("int64Value")) { + if (properties.valueType === 1) + return "valueType: multiple values"; + properties.valueType = 1; + if (!$util.isInteger(message.int64Value) && !(message.int64Value && $util.isInteger(message.int64Value.low) && $util.isInteger(message.int64Value.high))) + return "int64Value: integer|Long expected"; + } + if (message.uint64Value != null && message.hasOwnProperty("uint64Value")) { + if (properties.valueType === 1) + return "valueType: multiple values"; + properties.valueType = 1; + if (!$util.isInteger(message.uint64Value) && !(message.uint64Value && $util.isInteger(message.uint64Value.low) && $util.isInteger(message.uint64Value.high))) + return "uint64Value: integer|Long expected"; + } + if (message.sint64Value != null && message.hasOwnProperty("sint64Value")) { + if (properties.valueType === 1) + return "valueType: multiple values"; + properties.valueType = 1; + if (!$util.isInteger(message.sint64Value) && !(message.sint64Value && $util.isInteger(message.sint64Value.low) && $util.isInteger(message.sint64Value.high))) + return "sint64Value: integer|Long expected"; + } + if (message.boolValue != null && message.hasOwnProperty("boolValue")) { + if (properties.valueType === 1) + return "valueType: multiple values"; + properties.valueType = 1; + if (typeof message.boolValue !== "boolean") + return "boolValue: boolean expected"; + } + return null; + }; + + Value.fromObject = function fromObject(object) { + if (object instanceof $root.esriPBuffer.FeatureCollectionPBuffer.Value) + return object; + var message = new $root.esriPBuffer.FeatureCollectionPBuffer.Value(); + if (object.stringValue != null) + message.stringValue = String(object.stringValue); + if (object.floatValue != null) + message.floatValue = Number(object.floatValue); + if (object.doubleValue != null) + message.doubleValue = Number(object.doubleValue); + if (object.sintValue != null) + message.sintValue = object.sintValue | 0; + if (object.uintValue != null) + message.uintValue = object.uintValue >>> 0; + if (object.int64Value != null) + if ($util.Long) + (message.int64Value = $util.Long.fromValue(object.int64Value)).unsigned = false; + else if (typeof object.int64Value === "string") + message.int64Value = parseInt(object.int64Value, 10); + else if (typeof object.int64Value === "number") + message.int64Value = object.int64Value; + else if (typeof object.int64Value === "object") + message.int64Value = new $util.LongBits(object.int64Value.low >>> 0, object.int64Value.high >>> 0).toNumber(); + if (object.uint64Value != null) + if ($util.Long) + (message.uint64Value = $util.Long.fromValue(object.uint64Value)).unsigned = true; + else if (typeof object.uint64Value === "string") + message.uint64Value = parseInt(object.uint64Value, 10); + else if (typeof object.uint64Value === "number") + message.uint64Value = object.uint64Value; + else if (typeof object.uint64Value === "object") + message.uint64Value = new $util.LongBits(object.uint64Value.low >>> 0, object.uint64Value.high >>> 0).toNumber(true); + if (object.sint64Value != null) + if ($util.Long) + (message.sint64Value = $util.Long.fromValue(object.sint64Value)).unsigned = false; + else if (typeof object.sint64Value === "string") + message.sint64Value = parseInt(object.sint64Value, 10); + else if (typeof object.sint64Value === "number") + message.sint64Value = object.sint64Value; + else if (typeof object.sint64Value === "object") + message.sint64Value = new $util.LongBits(object.sint64Value.low >>> 0, object.sint64Value.high >>> 0).toNumber(); + if (object.boolValue != null) + message.boolValue = Boolean(object.boolValue); + return message; + }; + + Value.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (message.stringValue != null && message.hasOwnProperty("stringValue")) { + object.stringValue = message.stringValue; + if (options.oneofs) + object.valueType = "stringValue"; + } + if (message.floatValue != null && message.hasOwnProperty("floatValue")) { + object.floatValue = options.json && !isFinite(message.floatValue) ? String(message.floatValue) : message.floatValue; + if (options.oneofs) + object.valueType = "floatValue"; + } + if (message.doubleValue != null && message.hasOwnProperty("doubleValue")) { + object.doubleValue = options.json && !isFinite(message.doubleValue) ? String(message.doubleValue) : message.doubleValue; + if (options.oneofs) + object.valueType = "doubleValue"; + } + if (message.sintValue != null && message.hasOwnProperty("sintValue")) { + object.sintValue = message.sintValue; + if (options.oneofs) + object.valueType = "sintValue"; + } + if (message.uintValue != null && message.hasOwnProperty("uintValue")) { + object.uintValue = message.uintValue; + if (options.oneofs) + object.valueType = "uintValue"; + } + if (message.int64Value != null && message.hasOwnProperty("int64Value")) { + if (typeof message.int64Value === "number") + object.int64Value = options.longs === String ? String(message.int64Value) : message.int64Value; + else + object.int64Value = options.longs === String ? $util.Long.prototype.toString.call(message.int64Value) : options.longs === Number ? new $util.LongBits(message.int64Value.low >>> 0, message.int64Value.high >>> 0).toNumber() : message.int64Value; + if (options.oneofs) + object.valueType = "int64Value"; + } + if (message.uint64Value != null && message.hasOwnProperty("uint64Value")) { + if (typeof message.uint64Value === "number") + object.uint64Value = options.longs === String ? String(message.uint64Value) : message.uint64Value; + else + object.uint64Value = options.longs === String ? $util.Long.prototype.toString.call(message.uint64Value) : options.longs === Number ? new $util.LongBits(message.uint64Value.low >>> 0, message.uint64Value.high >>> 0).toNumber(true) : message.uint64Value; + if (options.oneofs) + object.valueType = "uint64Value"; + } + if (message.sint64Value != null && message.hasOwnProperty("sint64Value")) { + if (typeof message.sint64Value === "number") + object.sint64Value = options.longs === String ? String(message.sint64Value) : message.sint64Value; + else + object.sint64Value = options.longs === String ? $util.Long.prototype.toString.call(message.sint64Value) : options.longs === Number ? new $util.LongBits(message.sint64Value.low >>> 0, message.sint64Value.high >>> 0).toNumber() : message.sint64Value; + if (options.oneofs) + object.valueType = "sint64Value"; + } + if (message.boolValue != null && message.hasOwnProperty("boolValue")) { + object.boolValue = message.boolValue; + if (options.oneofs) + object.valueType = "boolValue"; + } + return object; + }; + + Value.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + Value.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/esriPBuffer.FeatureCollectionPBuffer.Value"; + }; + + return Value; + })(); + + FeatureCollectionPBuffer.Geometry = (function() { + + function Geometry(properties) { + this.lengths = []; + this.coords = []; + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + Geometry.prototype.geometryType = 0; + Geometry.prototype.lengths = $util.emptyArray; + Geometry.prototype.coords = $util.emptyArray; + + Geometry.create = function create(properties) { + return new Geometry(properties); + }; + + Geometry.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.geometryType != null && Object.hasOwnProperty.call(message, "geometryType")) + writer.uint32(8).int32(message.geometryType); + if (message.lengths != null && message.lengths.length) { + writer.uint32(18).fork(); + for (var i = 0; i < message.lengths.length; ++i) + writer.uint32(message.lengths[i]); + writer.ldelim(); + } + if (message.coords != null && message.coords.length) { + writer.uint32(26).fork(); + for (var i = 0; i < message.coords.length; ++i) + writer.sint64(message.coords[i]); + writer.ldelim(); + } + return writer; + }; + + Geometry.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + Geometry.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.geometryType != null && message.hasOwnProperty("geometryType")) + switch (message.geometryType) { + default: + return "geometryType: enum value expected"; + case 0: + case 1: + case 2: + case 3: + case 4: + case 127: + case 5: + break; + } + if (message.lengths != null && message.hasOwnProperty("lengths")) { + if (!Array.isArray(message.lengths)) + return "lengths: array expected"; + for (var i = 0; i < message.lengths.length; ++i) + if (!$util.isInteger(message.lengths[i])) + return "lengths: integer[] expected"; + } + if (message.coords != null && message.hasOwnProperty("coords")) { + if (!Array.isArray(message.coords)) + return "coords: array expected"; + for (var i = 0; i < message.coords.length; ++i) + if (!$util.isInteger(message.coords[i]) && !(message.coords[i] && $util.isInteger(message.coords[i].low) && $util.isInteger(message.coords[i].high))) + return "coords: integer|Long[] expected"; + } + return null; + }; + + Geometry.fromObject = function fromObject(object) { + if (object instanceof $root.esriPBuffer.FeatureCollectionPBuffer.Geometry) + return object; + var message = new $root.esriPBuffer.FeatureCollectionPBuffer.Geometry(); + switch (object.geometryType) { + default: + if (typeof object.geometryType === "number") { + message.geometryType = object.geometryType; + break; + } + break; + case "esriGeometryTypePoint": + case 0: + message.geometryType = 0; + break; + case "esriGeometryTypeMultipoint": + case 1: + message.geometryType = 1; + break; + case "esriGeometryTypePolyline": + case 2: + message.geometryType = 2; + break; + case "esriGeometryTypePolygon": + case 3: + message.geometryType = 3; + break; + case "esriGeometryTypeMultipatch": + case 4: + message.geometryType = 4; + break; + case "esriGeometryTypeNone": + case 127: + message.geometryType = 127; + break; + case "esriGeometryTypeEnvelope": + case 5: + message.geometryType = 5; + break; + } + if (object.lengths) { + if (!Array.isArray(object.lengths)) + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.Geometry.lengths: array expected"); + message.lengths = []; + for (var i = 0; i < object.lengths.length; ++i) + message.lengths[i] = object.lengths[i] >>> 0; + } + if (object.coords) { + if (!Array.isArray(object.coords)) + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.Geometry.coords: array expected"); + message.coords = []; + for (var i = 0; i < object.coords.length; ++i) + if ($util.Long) + (message.coords[i] = $util.Long.fromValue(object.coords[i])).unsigned = false; + else if (typeof object.coords[i] === "string") + message.coords[i] = parseInt(object.coords[i], 10); + else if (typeof object.coords[i] === "number") + message.coords[i] = object.coords[i]; + else if (typeof object.coords[i] === "object") + message.coords[i] = new $util.LongBits(object.coords[i].low >>> 0, object.coords[i].high >>> 0).toNumber(); + } + return message; + }; + + Geometry.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) { + object.lengths = []; + object.coords = []; + } + if (options.defaults) + object.geometryType = options.enums === String ? "esriGeometryTypePoint" : 0; + if (message.geometryType != null && message.hasOwnProperty("geometryType")) + object.geometryType = options.enums === String ? $root.esriPBuffer.FeatureCollectionPBuffer.GeometryType[message.geometryType] === undefined ? message.geometryType : $root.esriPBuffer.FeatureCollectionPBuffer.GeometryType[message.geometryType] : message.geometryType; + if (message.lengths && message.lengths.length) { + object.lengths = []; + for (var j = 0; j < message.lengths.length; ++j) + object.lengths[j] = message.lengths[j]; + } + if (message.coords && message.coords.length) { + object.coords = []; + for (var j = 0; j < message.coords.length; ++j) + if (typeof message.coords[j] === "number") + object.coords[j] = options.longs === String ? String(message.coords[j]) : message.coords[j]; + else + object.coords[j] = options.longs === String ? $util.Long.prototype.toString.call(message.coords[j]) : options.longs === Number ? new $util.LongBits(message.coords[j].low >>> 0, message.coords[j].high >>> 0).toNumber() : message.coords[j]; + } + return object; + }; + + Geometry.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + Geometry.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/esriPBuffer.FeatureCollectionPBuffer.Geometry"; + }; + + return Geometry; + })(); + + FeatureCollectionPBuffer.esriShapeBuffer = (function() { + + function esriShapeBuffer(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + esriShapeBuffer.prototype.bytes = $util.newBuffer([]); + + esriShapeBuffer.create = function create(properties) { + return new esriShapeBuffer(properties); + }; + + esriShapeBuffer.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.bytes != null && Object.hasOwnProperty.call(message, "bytes")) + writer.uint32(10).bytes(message.bytes); + return writer; + }; + + esriShapeBuffer.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + esriShapeBuffer.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.bytes != null && message.hasOwnProperty("bytes")) + if (!(message.bytes && typeof message.bytes.length === "number" || $util.isString(message.bytes))) + return "bytes: buffer expected"; + return null; + }; + + esriShapeBuffer.fromObject = function fromObject(object) { + if (object instanceof $root.esriPBuffer.FeatureCollectionPBuffer.esriShapeBuffer) + return object; + var message = new $root.esriPBuffer.FeatureCollectionPBuffer.esriShapeBuffer(); + if (object.bytes != null) + if (typeof object.bytes === "string") + $util.base64.decode(object.bytes, message.bytes = $util.newBuffer($util.base64.length(object.bytes)), 0); + else if (object.bytes.length >= 0) + message.bytes = object.bytes; + return message; + }; + + esriShapeBuffer.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) + if (options.bytes === String) + object.bytes = ""; + else { + object.bytes = []; + if (options.bytes !== Array) + object.bytes = $util.newBuffer(object.bytes); + } + if (message.bytes != null && message.hasOwnProperty("bytes")) + object.bytes = options.bytes === String ? $util.base64.encode(message.bytes, 0, message.bytes.length) : options.bytes === Array ? Array.prototype.slice.call(message.bytes) : message.bytes; + return object; + }; + + esriShapeBuffer.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + esriShapeBuffer.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/esriPBuffer.FeatureCollectionPBuffer.esriShapeBuffer"; + }; + + return esriShapeBuffer; + })(); + + FeatureCollectionPBuffer.Feature = (function() { + + function Feature(properties) { + this.attributes = []; + this.aggregateGeometries = []; + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + Feature.prototype.attributes = $util.emptyArray; + Feature.prototype.geometry = null; + Feature.prototype.shapeBuffer = null; + Feature.prototype.centroid = null; + Feature.prototype.aggregateGeometries = $util.emptyArray; + Feature.prototype.envelope = null; + + var $oneOfFields; + + Object.defineProperty(Feature.prototype, "compressedGeometry", { + get: $util.oneOfGetter($oneOfFields = ["geometry", "shapeBuffer"]), + set: $util.oneOfSetter($oneOfFields) + }); + + Feature.create = function create(properties) { + return new Feature(properties); + }; + + Feature.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.attributes != null && message.attributes.length) + for (var i = 0; i < message.attributes.length; ++i) + $root.esriPBuffer.FeatureCollectionPBuffer.Value.encode(message.attributes[i], writer.uint32(10).fork()).ldelim(); + if (message.geometry != null && Object.hasOwnProperty.call(message, "geometry")) + $root.esriPBuffer.FeatureCollectionPBuffer.Geometry.encode(message.geometry, writer.uint32(18).fork()).ldelim(); + if (message.shapeBuffer != null && Object.hasOwnProperty.call(message, "shapeBuffer")) + $root.esriPBuffer.FeatureCollectionPBuffer.esriShapeBuffer.encode(message.shapeBuffer, writer.uint32(26).fork()).ldelim(); + if (message.centroid != null && Object.hasOwnProperty.call(message, "centroid")) + $root.esriPBuffer.FeatureCollectionPBuffer.Geometry.encode(message.centroid, writer.uint32(34).fork()).ldelim(); + if (message.aggregateGeometries != null && message.aggregateGeometries.length) + for (var i = 0; i < message.aggregateGeometries.length; ++i) + $root.esriPBuffer.FeatureCollectionPBuffer.Geometry.encode(message.aggregateGeometries[i], writer.uint32(42).fork()).ldelim(); + if (message.envelope != null && Object.hasOwnProperty.call(message, "envelope")) + $root.esriPBuffer.FeatureCollectionPBuffer.Envelope.encode(message.envelope, writer.uint32(50).fork()).ldelim(); + return writer; + }; + + Feature.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + Feature.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + var properties = {}; + if (message.attributes != null && message.hasOwnProperty("attributes")) { + if (!Array.isArray(message.attributes)) + return "attributes: array expected"; + for (var i = 0; i < message.attributes.length; ++i) { + var error = $root.esriPBuffer.FeatureCollectionPBuffer.Value.verify(message.attributes[i]); + if (error) + return "attributes." + error; + } + } + if (message.geometry != null && message.hasOwnProperty("geometry")) { + properties.compressedGeometry = 1; + { + var error = $root.esriPBuffer.FeatureCollectionPBuffer.Geometry.verify(message.geometry); + if (error) + return "geometry." + error; + } + } + if (message.shapeBuffer != null && message.hasOwnProperty("shapeBuffer")) { + if (properties.compressedGeometry === 1) + return "compressedGeometry: multiple values"; + properties.compressedGeometry = 1; + { + var error = $root.esriPBuffer.FeatureCollectionPBuffer.esriShapeBuffer.verify(message.shapeBuffer); + if (error) + return "shapeBuffer." + error; + } + } + if (message.centroid != null && message.hasOwnProperty("centroid")) { + var error = $root.esriPBuffer.FeatureCollectionPBuffer.Geometry.verify(message.centroid); + if (error) + return "centroid." + error; + } + if (message.aggregateGeometries != null && message.hasOwnProperty("aggregateGeometries")) { + if (!Array.isArray(message.aggregateGeometries)) + return "aggregateGeometries: array expected"; + for (var i = 0; i < message.aggregateGeometries.length; ++i) { + var error = $root.esriPBuffer.FeatureCollectionPBuffer.Geometry.verify(message.aggregateGeometries[i]); + if (error) + return "aggregateGeometries." + error; + } + } + if (message.envelope != null && message.hasOwnProperty("envelope")) { + var error = $root.esriPBuffer.FeatureCollectionPBuffer.Envelope.verify(message.envelope); + if (error) + return "envelope." + error; + } + return null; + }; + + Feature.fromObject = function fromObject(object) { + if (object instanceof $root.esriPBuffer.FeatureCollectionPBuffer.Feature) + return object; + var message = new $root.esriPBuffer.FeatureCollectionPBuffer.Feature(); + if (object.attributes) { + if (!Array.isArray(object.attributes)) + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.Feature.attributes: array expected"); + message.attributes = []; + for (var i = 0; i < object.attributes.length; ++i) { + if (typeof object.attributes[i] !== "object") + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.Feature.attributes: object expected"); + message.attributes[i] = $root.esriPBuffer.FeatureCollectionPBuffer.Value.fromObject(object.attributes[i]); + } + } + if (object.geometry != null) { + if (typeof object.geometry !== "object") + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.Feature.geometry: object expected"); + message.geometry = $root.esriPBuffer.FeatureCollectionPBuffer.Geometry.fromObject(object.geometry); + } + if (object.shapeBuffer != null) { + if (typeof object.shapeBuffer !== "object") + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.Feature.shapeBuffer: object expected"); + message.shapeBuffer = $root.esriPBuffer.FeatureCollectionPBuffer.esriShapeBuffer.fromObject(object.shapeBuffer); + } + if (object.centroid != null) { + if (typeof object.centroid !== "object") + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.Feature.centroid: object expected"); + message.centroid = $root.esriPBuffer.FeatureCollectionPBuffer.Geometry.fromObject(object.centroid); + } + if (object.aggregateGeometries) { + if (!Array.isArray(object.aggregateGeometries)) + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.Feature.aggregateGeometries: array expected"); + message.aggregateGeometries = []; + for (var i = 0; i < object.aggregateGeometries.length; ++i) { + if (typeof object.aggregateGeometries[i] !== "object") + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.Feature.aggregateGeometries: object expected"); + message.aggregateGeometries[i] = $root.esriPBuffer.FeatureCollectionPBuffer.Geometry.fromObject(object.aggregateGeometries[i]); + } + } + if (object.envelope != null) { + if (typeof object.envelope !== "object") + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.Feature.envelope: object expected"); + message.envelope = $root.esriPBuffer.FeatureCollectionPBuffer.Envelope.fromObject(object.envelope); + } + return message; + }; + + Feature.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) { + object.attributes = []; + object.aggregateGeometries = []; + } + if (options.defaults) { + object.centroid = null; + object.envelope = null; + } + if (message.attributes && message.attributes.length) { + object.attributes = []; + for (var j = 0; j < message.attributes.length; ++j) + object.attributes[j] = $root.esriPBuffer.FeatureCollectionPBuffer.Value.toObject(message.attributes[j], options); + } + if (message.geometry != null && message.hasOwnProperty("geometry")) { + object.geometry = $root.esriPBuffer.FeatureCollectionPBuffer.Geometry.toObject(message.geometry, options); + if (options.oneofs) + object.compressedGeometry = "geometry"; + } + if (message.shapeBuffer != null && message.hasOwnProperty("shapeBuffer")) { + object.shapeBuffer = $root.esriPBuffer.FeatureCollectionPBuffer.esriShapeBuffer.toObject(message.shapeBuffer, options); + if (options.oneofs) + object.compressedGeometry = "shapeBuffer"; + } + if (message.centroid != null && message.hasOwnProperty("centroid")) + object.centroid = $root.esriPBuffer.FeatureCollectionPBuffer.Geometry.toObject(message.centroid, options); + if (message.aggregateGeometries && message.aggregateGeometries.length) { + object.aggregateGeometries = []; + for (var j = 0; j < message.aggregateGeometries.length; ++j) + object.aggregateGeometries[j] = $root.esriPBuffer.FeatureCollectionPBuffer.Geometry.toObject(message.aggregateGeometries[j], options); + } + if (message.envelope != null && message.hasOwnProperty("envelope")) + object.envelope = $root.esriPBuffer.FeatureCollectionPBuffer.Envelope.toObject(message.envelope, options); + return object; + }; + + Feature.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + Feature.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/esriPBuffer.FeatureCollectionPBuffer.Feature"; + }; + + return Feature; + })(); + + FeatureCollectionPBuffer.UniqueIdField = (function() { + + function UniqueIdField(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + UniqueIdField.prototype.name = ""; + UniqueIdField.prototype.isSystemMaintained = false; + + UniqueIdField.create = function create(properties) { + return new UniqueIdField(properties); + }; + + UniqueIdField.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.name != null && Object.hasOwnProperty.call(message, "name")) + writer.uint32(10).string(message.name); + if (message.isSystemMaintained != null && Object.hasOwnProperty.call(message, "isSystemMaintained")) + writer.uint32(16).bool(message.isSystemMaintained); + return writer; + }; + + UniqueIdField.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + UniqueIdField.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.name != null && message.hasOwnProperty("name")) + if (!$util.isString(message.name)) + return "name: string expected"; + if (message.isSystemMaintained != null && message.hasOwnProperty("isSystemMaintained")) + if (typeof message.isSystemMaintained !== "boolean") + return "isSystemMaintained: boolean expected"; + return null; + }; + + UniqueIdField.fromObject = function fromObject(object) { + if (object instanceof $root.esriPBuffer.FeatureCollectionPBuffer.UniqueIdField) + return object; + var message = new $root.esriPBuffer.FeatureCollectionPBuffer.UniqueIdField(); + if (object.name != null) + message.name = String(object.name); + if (object.isSystemMaintained != null) + message.isSystemMaintained = Boolean(object.isSystemMaintained); + return message; + }; + + UniqueIdField.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.name = ""; + object.isSystemMaintained = false; + } + if (message.name != null && message.hasOwnProperty("name")) + object.name = message.name; + if (message.isSystemMaintained != null && message.hasOwnProperty("isSystemMaintained")) + object.isSystemMaintained = message.isSystemMaintained; + return object; + }; + + UniqueIdField.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + UniqueIdField.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/esriPBuffer.FeatureCollectionPBuffer.UniqueIdField"; + }; + + return UniqueIdField; + })(); + + FeatureCollectionPBuffer.GeometryProperties = (function() { + + function GeometryProperties(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + GeometryProperties.prototype.shapeAreaFieldName = ""; + GeometryProperties.prototype.shapeLengthFieldName = ""; + GeometryProperties.prototype.units = ""; + + GeometryProperties.create = function create(properties) { + return new GeometryProperties(properties); + }; + + GeometryProperties.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.shapeAreaFieldName != null && Object.hasOwnProperty.call(message, "shapeAreaFieldName")) + writer.uint32(10).string(message.shapeAreaFieldName); + if (message.shapeLengthFieldName != null && Object.hasOwnProperty.call(message, "shapeLengthFieldName")) + writer.uint32(18).string(message.shapeLengthFieldName); + if (message.units != null && Object.hasOwnProperty.call(message, "units")) + writer.uint32(26).string(message.units); + return writer; + }; + + GeometryProperties.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + GeometryProperties.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.shapeAreaFieldName != null && message.hasOwnProperty("shapeAreaFieldName")) + if (!$util.isString(message.shapeAreaFieldName)) + return "shapeAreaFieldName: string expected"; + if (message.shapeLengthFieldName != null && message.hasOwnProperty("shapeLengthFieldName")) + if (!$util.isString(message.shapeLengthFieldName)) + return "shapeLengthFieldName: string expected"; + if (message.units != null && message.hasOwnProperty("units")) + if (!$util.isString(message.units)) + return "units: string expected"; + return null; + }; + + GeometryProperties.fromObject = function fromObject(object) { + if (object instanceof $root.esriPBuffer.FeatureCollectionPBuffer.GeometryProperties) + return object; + var message = new $root.esriPBuffer.FeatureCollectionPBuffer.GeometryProperties(); + if (object.shapeAreaFieldName != null) + message.shapeAreaFieldName = String(object.shapeAreaFieldName); + if (object.shapeLengthFieldName != null) + message.shapeLengthFieldName = String(object.shapeLengthFieldName); + if (object.units != null) + message.units = String(object.units); + return message; + }; + + GeometryProperties.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.shapeAreaFieldName = ""; + object.shapeLengthFieldName = ""; + object.units = ""; + } + if (message.shapeAreaFieldName != null && message.hasOwnProperty("shapeAreaFieldName")) + object.shapeAreaFieldName = message.shapeAreaFieldName; + if (message.shapeLengthFieldName != null && message.hasOwnProperty("shapeLengthFieldName")) + object.shapeLengthFieldName = message.shapeLengthFieldName; + if (message.units != null && message.hasOwnProperty("units")) + object.units = message.units; + return object; + }; + + GeometryProperties.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + GeometryProperties.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/esriPBuffer.FeatureCollectionPBuffer.GeometryProperties"; + }; + + return GeometryProperties; + })(); + + FeatureCollectionPBuffer.ServerGens = (function() { + + function ServerGens(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + ServerGens.prototype.minServerGen = $util.Long ? $util.Long.fromBits(0,0,true) : 0; + ServerGens.prototype.serverGen = $util.Long ? $util.Long.fromBits(0,0,true) : 0; + + ServerGens.create = function create(properties) { + return new ServerGens(properties); + }; + + ServerGens.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.minServerGen != null && Object.hasOwnProperty.call(message, "minServerGen")) + writer.uint32(8).uint64(message.minServerGen); + if (message.serverGen != null && Object.hasOwnProperty.call(message, "serverGen")) + writer.uint32(16).uint64(message.serverGen); + return writer; + }; + + ServerGens.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + ServerGens.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.minServerGen != null && message.hasOwnProperty("minServerGen")) + if (!$util.isInteger(message.minServerGen) && !(message.minServerGen && $util.isInteger(message.minServerGen.low) && $util.isInteger(message.minServerGen.high))) + return "minServerGen: integer|Long expected"; + if (message.serverGen != null && message.hasOwnProperty("serverGen")) + if (!$util.isInteger(message.serverGen) && !(message.serverGen && $util.isInteger(message.serverGen.low) && $util.isInteger(message.serverGen.high))) + return "serverGen: integer|Long expected"; + return null; + }; + + ServerGens.fromObject = function fromObject(object) { + if (object instanceof $root.esriPBuffer.FeatureCollectionPBuffer.ServerGens) + return object; + var message = new $root.esriPBuffer.FeatureCollectionPBuffer.ServerGens(); + if (object.minServerGen != null) + if ($util.Long) + (message.minServerGen = $util.Long.fromValue(object.minServerGen)).unsigned = true; + else if (typeof object.minServerGen === "string") + message.minServerGen = parseInt(object.minServerGen, 10); + else if (typeof object.minServerGen === "number") + message.minServerGen = object.minServerGen; + else if (typeof object.minServerGen === "object") + message.minServerGen = new $util.LongBits(object.minServerGen.low >>> 0, object.minServerGen.high >>> 0).toNumber(true); + if (object.serverGen != null) + if ($util.Long) + (message.serverGen = $util.Long.fromValue(object.serverGen)).unsigned = true; + else if (typeof object.serverGen === "string") + message.serverGen = parseInt(object.serverGen, 10); + else if (typeof object.serverGen === "number") + message.serverGen = object.serverGen; + else if (typeof object.serverGen === "object") + message.serverGen = new $util.LongBits(object.serverGen.low >>> 0, object.serverGen.high >>> 0).toNumber(true); + return message; + }; + + ServerGens.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + if ($util.Long) { + var long = new $util.Long(0, 0, true); + object.minServerGen = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.minServerGen = options.longs === String ? "0" : 0; + if ($util.Long) { + var long = new $util.Long(0, 0, true); + object.serverGen = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.serverGen = options.longs === String ? "0" : 0; + } + if (message.minServerGen != null && message.hasOwnProperty("minServerGen")) + if (typeof message.minServerGen === "number") + object.minServerGen = options.longs === String ? String(message.minServerGen) : message.minServerGen; + else + object.minServerGen = options.longs === String ? $util.Long.prototype.toString.call(message.minServerGen) : options.longs === Number ? new $util.LongBits(message.minServerGen.low >>> 0, message.minServerGen.high >>> 0).toNumber(true) : message.minServerGen; + if (message.serverGen != null && message.hasOwnProperty("serverGen")) + if (typeof message.serverGen === "number") + object.serverGen = options.longs === String ? String(message.serverGen) : message.serverGen; + else + object.serverGen = options.longs === String ? $util.Long.prototype.toString.call(message.serverGen) : options.longs === Number ? new $util.LongBits(message.serverGen.low >>> 0, message.serverGen.high >>> 0).toNumber(true) : message.serverGen; + return object; + }; + + ServerGens.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + ServerGens.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/esriPBuffer.FeatureCollectionPBuffer.ServerGens"; + }; + + return ServerGens; + })(); + + FeatureCollectionPBuffer.Scale = (function() { + + function Scale(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + Scale.prototype.xScale = 0; + Scale.prototype.yScale = 0; + Scale.prototype.mScale = 0; + Scale.prototype.zScale = 0; + + Scale.create = function create(properties) { + return new Scale(properties); + }; + + Scale.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.xScale != null && Object.hasOwnProperty.call(message, "xScale")) + writer.uint32(9).double(message.xScale); + if (message.yScale != null && Object.hasOwnProperty.call(message, "yScale")) + writer.uint32(17).double(message.yScale); + if (message.mScale != null && Object.hasOwnProperty.call(message, "mScale")) + writer.uint32(25).double(message.mScale); + if (message.zScale != null && Object.hasOwnProperty.call(message, "zScale")) + writer.uint32(33).double(message.zScale); + return writer; + }; + + Scale.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + Scale.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.xScale != null && message.hasOwnProperty("xScale")) + if (typeof message.xScale !== "number") + return "xScale: number expected"; + if (message.yScale != null && message.hasOwnProperty("yScale")) + if (typeof message.yScale !== "number") + return "yScale: number expected"; + if (message.mScale != null && message.hasOwnProperty("mScale")) + if (typeof message.mScale !== "number") + return "mScale: number expected"; + if (message.zScale != null && message.hasOwnProperty("zScale")) + if (typeof message.zScale !== "number") + return "zScale: number expected"; + return null; + }; + + Scale.fromObject = function fromObject(object) { + if (object instanceof $root.esriPBuffer.FeatureCollectionPBuffer.Scale) + return object; + var message = new $root.esriPBuffer.FeatureCollectionPBuffer.Scale(); + if (object.xScale != null) + message.xScale = Number(object.xScale); + if (object.yScale != null) + message.yScale = Number(object.yScale); + if (object.mScale != null) + message.mScale = Number(object.mScale); + if (object.zScale != null) + message.zScale = Number(object.zScale); + return message; + }; + + Scale.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.xScale = 0; + object.yScale = 0; + object.mScale = 0; + object.zScale = 0; + } + if (message.xScale != null && message.hasOwnProperty("xScale")) + object.xScale = options.json && !isFinite(message.xScale) ? String(message.xScale) : message.xScale; + if (message.yScale != null && message.hasOwnProperty("yScale")) + object.yScale = options.json && !isFinite(message.yScale) ? String(message.yScale) : message.yScale; + if (message.mScale != null && message.hasOwnProperty("mScale")) + object.mScale = options.json && !isFinite(message.mScale) ? String(message.mScale) : message.mScale; + if (message.zScale != null && message.hasOwnProperty("zScale")) + object.zScale = options.json && !isFinite(message.zScale) ? String(message.zScale) : message.zScale; + return object; + }; + + Scale.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + Scale.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/esriPBuffer.FeatureCollectionPBuffer.Scale"; + }; + + return Scale; + })(); + + FeatureCollectionPBuffer.Translate = (function() { + + function Translate(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + Translate.prototype.xTranslate = 0; + Translate.prototype.yTranslate = 0; + Translate.prototype.mTranslate = 0; + Translate.prototype.zTranslate = 0; + + Translate.create = function create(properties) { + return new Translate(properties); + }; + + Translate.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.xTranslate != null && Object.hasOwnProperty.call(message, "xTranslate")) + writer.uint32(9).double(message.xTranslate); + if (message.yTranslate != null && Object.hasOwnProperty.call(message, "yTranslate")) + writer.uint32(17).double(message.yTranslate); + if (message.mTranslate != null && Object.hasOwnProperty.call(message, "mTranslate")) + writer.uint32(25).double(message.mTranslate); + if (message.zTranslate != null && Object.hasOwnProperty.call(message, "zTranslate")) + writer.uint32(33).double(message.zTranslate); + return writer; + }; + + Translate.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + Translate.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.xTranslate != null && message.hasOwnProperty("xTranslate")) + if (typeof message.xTranslate !== "number") + return "xTranslate: number expected"; + if (message.yTranslate != null && message.hasOwnProperty("yTranslate")) + if (typeof message.yTranslate !== "number") + return "yTranslate: number expected"; + if (message.mTranslate != null && message.hasOwnProperty("mTranslate")) + if (typeof message.mTranslate !== "number") + return "mTranslate: number expected"; + if (message.zTranslate != null && message.hasOwnProperty("zTranslate")) + if (typeof message.zTranslate !== "number") + return "zTranslate: number expected"; + return null; + }; + + Translate.fromObject = function fromObject(object) { + if (object instanceof $root.esriPBuffer.FeatureCollectionPBuffer.Translate) + return object; + var message = new $root.esriPBuffer.FeatureCollectionPBuffer.Translate(); + if (object.xTranslate != null) + message.xTranslate = Number(object.xTranslate); + if (object.yTranslate != null) + message.yTranslate = Number(object.yTranslate); + if (object.mTranslate != null) + message.mTranslate = Number(object.mTranslate); + if (object.zTranslate != null) + message.zTranslate = Number(object.zTranslate); + return message; + }; + + Translate.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.xTranslate = 0; + object.yTranslate = 0; + object.mTranslate = 0; + object.zTranslate = 0; + } + if (message.xTranslate != null && message.hasOwnProperty("xTranslate")) + object.xTranslate = options.json && !isFinite(message.xTranslate) ? String(message.xTranslate) : message.xTranslate; + if (message.yTranslate != null && message.hasOwnProperty("yTranslate")) + object.yTranslate = options.json && !isFinite(message.yTranslate) ? String(message.yTranslate) : message.yTranslate; + if (message.mTranslate != null && message.hasOwnProperty("mTranslate")) + object.mTranslate = options.json && !isFinite(message.mTranslate) ? String(message.mTranslate) : message.mTranslate; + if (message.zTranslate != null && message.hasOwnProperty("zTranslate")) + object.zTranslate = options.json && !isFinite(message.zTranslate) ? String(message.zTranslate) : message.zTranslate; + return object; + }; + + Translate.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + Translate.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/esriPBuffer.FeatureCollectionPBuffer.Translate"; + }; + + return Translate; + })(); + + FeatureCollectionPBuffer.Transform = (function() { + + function Transform(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + Transform.prototype.quantizeOriginPostion = 0; + Transform.prototype.scale = null; + Transform.prototype.translate = null; + + Transform.create = function create(properties) { + return new Transform(properties); + }; + + Transform.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.quantizeOriginPostion != null && Object.hasOwnProperty.call(message, "quantizeOriginPostion")) + writer.uint32(8).int32(message.quantizeOriginPostion); + if (message.scale != null && Object.hasOwnProperty.call(message, "scale")) + $root.esriPBuffer.FeatureCollectionPBuffer.Scale.encode(message.scale, writer.uint32(18).fork()).ldelim(); + if (message.translate != null && Object.hasOwnProperty.call(message, "translate")) + $root.esriPBuffer.FeatureCollectionPBuffer.Translate.encode(message.translate, writer.uint32(26).fork()).ldelim(); + return writer; + }; + + Transform.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + Transform.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.quantizeOriginPostion != null && message.hasOwnProperty("quantizeOriginPostion")) + switch (message.quantizeOriginPostion) { + default: + return "quantizeOriginPostion: enum value expected"; + case 0: + case 1: + break; + } + if (message.scale != null && message.hasOwnProperty("scale")) { + var error = $root.esriPBuffer.FeatureCollectionPBuffer.Scale.verify(message.scale); + if (error) + return "scale." + error; + } + if (message.translate != null && message.hasOwnProperty("translate")) { + var error = $root.esriPBuffer.FeatureCollectionPBuffer.Translate.verify(message.translate); + if (error) + return "translate." + error; + } + return null; + }; + + Transform.fromObject = function fromObject(object) { + if (object instanceof $root.esriPBuffer.FeatureCollectionPBuffer.Transform) + return object; + var message = new $root.esriPBuffer.FeatureCollectionPBuffer.Transform(); + switch (object.quantizeOriginPostion) { + default: + if (typeof object.quantizeOriginPostion === "number") { + message.quantizeOriginPostion = object.quantizeOriginPostion; + break; + } + break; + case "upperLeft": + case 0: + message.quantizeOriginPostion = 0; + break; + case "lowerLeft": + case 1: + message.quantizeOriginPostion = 1; + break; + } + if (object.scale != null) { + if (typeof object.scale !== "object") + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.Transform.scale: object expected"); + message.scale = $root.esriPBuffer.FeatureCollectionPBuffer.Scale.fromObject(object.scale); + } + if (object.translate != null) { + if (typeof object.translate !== "object") + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.Transform.translate: object expected"); + message.translate = $root.esriPBuffer.FeatureCollectionPBuffer.Translate.fromObject(object.translate); + } + return message; + }; + + Transform.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + object.quantizeOriginPostion = options.enums === String ? "upperLeft" : 0; + object.scale = null; + object.translate = null; + } + if (message.quantizeOriginPostion != null && message.hasOwnProperty("quantizeOriginPostion")) + object.quantizeOriginPostion = options.enums === String ? $root.esriPBuffer.FeatureCollectionPBuffer.QuantizeOriginPostion[message.quantizeOriginPostion] === undefined ? message.quantizeOriginPostion : $root.esriPBuffer.FeatureCollectionPBuffer.QuantizeOriginPostion[message.quantizeOriginPostion] : message.quantizeOriginPostion; + if (message.scale != null && message.hasOwnProperty("scale")) + object.scale = $root.esriPBuffer.FeatureCollectionPBuffer.Scale.toObject(message.scale, options); + if (message.translate != null && message.hasOwnProperty("translate")) + object.translate = $root.esriPBuffer.FeatureCollectionPBuffer.Translate.toObject(message.translate, options); + return object; + }; + + Transform.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + Transform.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/esriPBuffer.FeatureCollectionPBuffer.Transform"; + }; + + return Transform; + })(); + + FeatureCollectionPBuffer.FeatureResult = (function() { + + function FeatureResult(properties) { + this.fields = []; + this.values = []; + this.features = []; + this.geometryFields = []; + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + FeatureResult.prototype.objectIdFieldName = ""; + FeatureResult.prototype.uniqueIdField = null; + FeatureResult.prototype.globalIdFieldName = ""; + FeatureResult.prototype.geohashFieldName = ""; + FeatureResult.prototype.geometryProperties = null; + FeatureResult.prototype.serverGens = null; + FeatureResult.prototype.geometryType = 0; + FeatureResult.prototype.spatialReference = null; + FeatureResult.prototype.exceededTransferLimit = false; + FeatureResult.prototype.hasZ = false; + FeatureResult.prototype.hasM = false; + FeatureResult.prototype.transform = null; + FeatureResult.prototype.fields = $util.emptyArray; + FeatureResult.prototype.values = $util.emptyArray; + FeatureResult.prototype.features = $util.emptyArray; + FeatureResult.prototype.geometryFields = $util.emptyArray; + + FeatureResult.create = function create(properties) { + return new FeatureResult(properties); + }; + + FeatureResult.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.objectIdFieldName != null && Object.hasOwnProperty.call(message, "objectIdFieldName")) + writer.uint32(10).string(message.objectIdFieldName); + if (message.uniqueIdField != null && Object.hasOwnProperty.call(message, "uniqueIdField")) + $root.esriPBuffer.FeatureCollectionPBuffer.UniqueIdField.encode(message.uniqueIdField, writer.uint32(18).fork()).ldelim(); + if (message.globalIdFieldName != null && Object.hasOwnProperty.call(message, "globalIdFieldName")) + writer.uint32(26).string(message.globalIdFieldName); + if (message.geohashFieldName != null && Object.hasOwnProperty.call(message, "geohashFieldName")) + writer.uint32(34).string(message.geohashFieldName); + if (message.geometryProperties != null && Object.hasOwnProperty.call(message, "geometryProperties")) + $root.esriPBuffer.FeatureCollectionPBuffer.GeometryProperties.encode(message.geometryProperties, writer.uint32(42).fork()).ldelim(); + if (message.serverGens != null && Object.hasOwnProperty.call(message, "serverGens")) + $root.esriPBuffer.FeatureCollectionPBuffer.ServerGens.encode(message.serverGens, writer.uint32(50).fork()).ldelim(); + if (message.geometryType != null && Object.hasOwnProperty.call(message, "geometryType")) + writer.uint32(56).int32(message.geometryType); + if (message.spatialReference != null && Object.hasOwnProperty.call(message, "spatialReference")) + $root.esriPBuffer.FeatureCollectionPBuffer.SpatialReference.encode(message.spatialReference, writer.uint32(66).fork()).ldelim(); + if (message.exceededTransferLimit != null && Object.hasOwnProperty.call(message, "exceededTransferLimit")) + writer.uint32(72).bool(message.exceededTransferLimit); + if (message.hasZ != null && Object.hasOwnProperty.call(message, "hasZ")) + writer.uint32(80).bool(message.hasZ); + if (message.hasM != null && Object.hasOwnProperty.call(message, "hasM")) + writer.uint32(88).bool(message.hasM); + if (message.transform != null && Object.hasOwnProperty.call(message, "transform")) + $root.esriPBuffer.FeatureCollectionPBuffer.Transform.encode(message.transform, writer.uint32(98).fork()).ldelim(); + if (message.fields != null && message.fields.length) + for (var i = 0; i < message.fields.length; ++i) + $root.esriPBuffer.FeatureCollectionPBuffer.Field.encode(message.fields[i], writer.uint32(106).fork()).ldelim(); + if (message.values != null && message.values.length) + for (var i = 0; i < message.values.length; ++i) + $root.esriPBuffer.FeatureCollectionPBuffer.Value.encode(message.values[i], writer.uint32(114).fork()).ldelim(); + if (message.features != null && message.features.length) + for (var i = 0; i < message.features.length; ++i) + $root.esriPBuffer.FeatureCollectionPBuffer.Feature.encode(message.features[i], writer.uint32(122).fork()).ldelim(); + if (message.geometryFields != null && message.geometryFields.length) + for (var i = 0; i < message.geometryFields.length; ++i) + $root.esriPBuffer.FeatureCollectionPBuffer.GeometryField.encode(message.geometryFields[i], writer.uint32(130).fork()).ldelim(); + return writer; + }; + + FeatureResult.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + FeatureResult.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.objectIdFieldName != null && message.hasOwnProperty("objectIdFieldName")) + if (!$util.isString(message.objectIdFieldName)) + return "objectIdFieldName: string expected"; + if (message.uniqueIdField != null && message.hasOwnProperty("uniqueIdField")) { + var error = $root.esriPBuffer.FeatureCollectionPBuffer.UniqueIdField.verify(message.uniqueIdField); + if (error) + return "uniqueIdField." + error; + } + if (message.globalIdFieldName != null && message.hasOwnProperty("globalIdFieldName")) + if (!$util.isString(message.globalIdFieldName)) + return "globalIdFieldName: string expected"; + if (message.geohashFieldName != null && message.hasOwnProperty("geohashFieldName")) + if (!$util.isString(message.geohashFieldName)) + return "geohashFieldName: string expected"; + if (message.geometryProperties != null && message.hasOwnProperty("geometryProperties")) { + var error = $root.esriPBuffer.FeatureCollectionPBuffer.GeometryProperties.verify(message.geometryProperties); + if (error) + return "geometryProperties." + error; + } + if (message.serverGens != null && message.hasOwnProperty("serverGens")) { + var error = $root.esriPBuffer.FeatureCollectionPBuffer.ServerGens.verify(message.serverGens); + if (error) + return "serverGens." + error; + } + if (message.geometryType != null && message.hasOwnProperty("geometryType")) + switch (message.geometryType) { + default: + return "geometryType: enum value expected"; + case 0: + case 1: + case 2: + case 3: + case 4: + case 127: + case 5: + break; + } + if (message.spatialReference != null && message.hasOwnProperty("spatialReference")) { + var error = $root.esriPBuffer.FeatureCollectionPBuffer.SpatialReference.verify(message.spatialReference); + if (error) + return "spatialReference." + error; + } + if (message.exceededTransferLimit != null && message.hasOwnProperty("exceededTransferLimit")) + if (typeof message.exceededTransferLimit !== "boolean") + return "exceededTransferLimit: boolean expected"; + if (message.hasZ != null && message.hasOwnProperty("hasZ")) + if (typeof message.hasZ !== "boolean") + return "hasZ: boolean expected"; + if (message.hasM != null && message.hasOwnProperty("hasM")) + if (typeof message.hasM !== "boolean") + return "hasM: boolean expected"; + if (message.transform != null && message.hasOwnProperty("transform")) { + var error = $root.esriPBuffer.FeatureCollectionPBuffer.Transform.verify(message.transform); + if (error) + return "transform." + error; + } + if (message.fields != null && message.hasOwnProperty("fields")) { + if (!Array.isArray(message.fields)) + return "fields: array expected"; + for (var i = 0; i < message.fields.length; ++i) { + var error = $root.esriPBuffer.FeatureCollectionPBuffer.Field.verify(message.fields[i]); + if (error) + return "fields." + error; + } + } + if (message.values != null && message.hasOwnProperty("values")) { + if (!Array.isArray(message.values)) + return "values: array expected"; + for (var i = 0; i < message.values.length; ++i) { + var error = $root.esriPBuffer.FeatureCollectionPBuffer.Value.verify(message.values[i]); + if (error) + return "values." + error; + } + } + if (message.features != null && message.hasOwnProperty("features")) { + if (!Array.isArray(message.features)) + return "features: array expected"; + for (var i = 0; i < message.features.length; ++i) { + var error = $root.esriPBuffer.FeatureCollectionPBuffer.Feature.verify(message.features[i]); + if (error) + return "features." + error; + } + } + if (message.geometryFields != null && message.hasOwnProperty("geometryFields")) { + if (!Array.isArray(message.geometryFields)) + return "geometryFields: array expected"; + for (var i = 0; i < message.geometryFields.length; ++i) { + var error = $root.esriPBuffer.FeatureCollectionPBuffer.GeometryField.verify(message.geometryFields[i]); + if (error) + return "geometryFields." + error; + } + } + return null; + }; + + FeatureResult.fromObject = function fromObject(object) { + if (object instanceof $root.esriPBuffer.FeatureCollectionPBuffer.FeatureResult) + return object; + var message = new $root.esriPBuffer.FeatureCollectionPBuffer.FeatureResult(); + if (object.objectIdFieldName != null) + message.objectIdFieldName = String(object.objectIdFieldName); + if (object.uniqueIdField != null) { + if (typeof object.uniqueIdField !== "object") + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.FeatureResult.uniqueIdField: object expected"); + message.uniqueIdField = $root.esriPBuffer.FeatureCollectionPBuffer.UniqueIdField.fromObject(object.uniqueIdField); + } + if (object.globalIdFieldName != null) + message.globalIdFieldName = String(object.globalIdFieldName); + if (object.geohashFieldName != null) + message.geohashFieldName = String(object.geohashFieldName); + if (object.geometryProperties != null) { + if (typeof object.geometryProperties !== "object") + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.FeatureResult.geometryProperties: object expected"); + message.geometryProperties = $root.esriPBuffer.FeatureCollectionPBuffer.GeometryProperties.fromObject(object.geometryProperties); + } + if (object.serverGens != null) { + if (typeof object.serverGens !== "object") + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.FeatureResult.serverGens: object expected"); + message.serverGens = $root.esriPBuffer.FeatureCollectionPBuffer.ServerGens.fromObject(object.serverGens); + } + switch (object.geometryType) { + default: + if (typeof object.geometryType === "number") { + message.geometryType = object.geometryType; + break; + } + break; + case "esriGeometryTypePoint": + case 0: + message.geometryType = 0; + break; + case "esriGeometryTypeMultipoint": + case 1: + message.geometryType = 1; + break; + case "esriGeometryTypePolyline": + case 2: + message.geometryType = 2; + break; + case "esriGeometryTypePolygon": + case 3: + message.geometryType = 3; + break; + case "esriGeometryTypeMultipatch": + case 4: + message.geometryType = 4; + break; + case "esriGeometryTypeNone": + case 127: + message.geometryType = 127; + break; + case "esriGeometryTypeEnvelope": + case 5: + message.geometryType = 5; + break; + } + if (object.spatialReference != null) { + if (typeof object.spatialReference !== "object") + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.FeatureResult.spatialReference: object expected"); + message.spatialReference = $root.esriPBuffer.FeatureCollectionPBuffer.SpatialReference.fromObject(object.spatialReference); + } + if (object.exceededTransferLimit != null) + message.exceededTransferLimit = Boolean(object.exceededTransferLimit); + if (object.hasZ != null) + message.hasZ = Boolean(object.hasZ); + if (object.hasM != null) + message.hasM = Boolean(object.hasM); + if (object.transform != null) { + if (typeof object.transform !== "object") + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.FeatureResult.transform: object expected"); + message.transform = $root.esriPBuffer.FeatureCollectionPBuffer.Transform.fromObject(object.transform); + } + if (object.fields) { + if (!Array.isArray(object.fields)) + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.FeatureResult.fields: array expected"); + message.fields = []; + for (var i = 0; i < object.fields.length; ++i) { + if (typeof object.fields[i] !== "object") + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.FeatureResult.fields: object expected"); + message.fields[i] = $root.esriPBuffer.FeatureCollectionPBuffer.Field.fromObject(object.fields[i]); + } + } + if (object.values) { + if (!Array.isArray(object.values)) + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.FeatureResult.values: array expected"); + message.values = []; + for (var i = 0; i < object.values.length; ++i) { + if (typeof object.values[i] !== "object") + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.FeatureResult.values: object expected"); + message.values[i] = $root.esriPBuffer.FeatureCollectionPBuffer.Value.fromObject(object.values[i]); + } + } + if (object.features) { + if (!Array.isArray(object.features)) + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.FeatureResult.features: array expected"); + message.features = []; + for (var i = 0; i < object.features.length; ++i) { + if (typeof object.features[i] !== "object") + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.FeatureResult.features: object expected"); + message.features[i] = $root.esriPBuffer.FeatureCollectionPBuffer.Feature.fromObject(object.features[i]); + } + } + if (object.geometryFields) { + if (!Array.isArray(object.geometryFields)) + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.FeatureResult.geometryFields: array expected"); + message.geometryFields = []; + for (var i = 0; i < object.geometryFields.length; ++i) { + if (typeof object.geometryFields[i] !== "object") + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.FeatureResult.geometryFields: object expected"); + message.geometryFields[i] = $root.esriPBuffer.FeatureCollectionPBuffer.GeometryField.fromObject(object.geometryFields[i]); + } + } + return message; + }; + + FeatureResult.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) { + object.fields = []; + object.values = []; + object.features = []; + object.geometryFields = []; + } + if (options.defaults) { + object.objectIdFieldName = ""; + object.uniqueIdField = null; + object.globalIdFieldName = ""; + object.geohashFieldName = ""; + object.geometryProperties = null; + object.serverGens = null; + object.geometryType = options.enums === String ? "esriGeometryTypePoint" : 0; + object.spatialReference = null; + object.exceededTransferLimit = false; + object.hasZ = false; + object.hasM = false; + object.transform = null; + } + if (message.objectIdFieldName != null && message.hasOwnProperty("objectIdFieldName")) + object.objectIdFieldName = message.objectIdFieldName; + if (message.uniqueIdField != null && message.hasOwnProperty("uniqueIdField")) + object.uniqueIdField = $root.esriPBuffer.FeatureCollectionPBuffer.UniqueIdField.toObject(message.uniqueIdField, options); + if (message.globalIdFieldName != null && message.hasOwnProperty("globalIdFieldName")) + object.globalIdFieldName = message.globalIdFieldName; + if (message.geohashFieldName != null && message.hasOwnProperty("geohashFieldName")) + object.geohashFieldName = message.geohashFieldName; + if (message.geometryProperties != null && message.hasOwnProperty("geometryProperties")) + object.geometryProperties = $root.esriPBuffer.FeatureCollectionPBuffer.GeometryProperties.toObject(message.geometryProperties, options); + if (message.serverGens != null && message.hasOwnProperty("serverGens")) + object.serverGens = $root.esriPBuffer.FeatureCollectionPBuffer.ServerGens.toObject(message.serverGens, options); + if (message.geometryType != null && message.hasOwnProperty("geometryType")) + object.geometryType = options.enums === String ? $root.esriPBuffer.FeatureCollectionPBuffer.GeometryType[message.geometryType] === undefined ? message.geometryType : $root.esriPBuffer.FeatureCollectionPBuffer.GeometryType[message.geometryType] : message.geometryType; + if (message.spatialReference != null && message.hasOwnProperty("spatialReference")) + object.spatialReference = $root.esriPBuffer.FeatureCollectionPBuffer.SpatialReference.toObject(message.spatialReference, options); + if (message.exceededTransferLimit != null && message.hasOwnProperty("exceededTransferLimit")) + object.exceededTransferLimit = message.exceededTransferLimit; + if (message.hasZ != null && message.hasOwnProperty("hasZ")) + object.hasZ = message.hasZ; + if (message.hasM != null && message.hasOwnProperty("hasM")) + object.hasM = message.hasM; + if (message.transform != null && message.hasOwnProperty("transform")) + object.transform = $root.esriPBuffer.FeatureCollectionPBuffer.Transform.toObject(message.transform, options); + if (message.fields && message.fields.length) { + object.fields = []; + for (var j = 0; j < message.fields.length; ++j) + object.fields[j] = $root.esriPBuffer.FeatureCollectionPBuffer.Field.toObject(message.fields[j], options); + } + if (message.values && message.values.length) { + object.values = []; + for (var j = 0; j < message.values.length; ++j) + object.values[j] = $root.esriPBuffer.FeatureCollectionPBuffer.Value.toObject(message.values[j], options); + } + if (message.features && message.features.length) { + object.features = []; + for (var j = 0; j < message.features.length; ++j) + object.features[j] = $root.esriPBuffer.FeatureCollectionPBuffer.Feature.toObject(message.features[j], options); + } + if (message.geometryFields && message.geometryFields.length) { + object.geometryFields = []; + for (var j = 0; j < message.geometryFields.length; ++j) + object.geometryFields[j] = $root.esriPBuffer.FeatureCollectionPBuffer.GeometryField.toObject(message.geometryFields[j], options); + } + return object; + }; + + FeatureResult.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + FeatureResult.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/esriPBuffer.FeatureCollectionPBuffer.FeatureResult"; + }; + + return FeatureResult; + })(); + + FeatureCollectionPBuffer.CountResult = (function() { + + function CountResult(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + CountResult.prototype.count = $util.Long ? $util.Long.fromBits(0,0,true) : 0; + + CountResult.create = function create(properties) { + return new CountResult(properties); + }; + + CountResult.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.count != null && Object.hasOwnProperty.call(message, "count")) + writer.uint32(8).uint64(message.count); + return writer; + }; + + CountResult.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + CountResult.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.count != null && message.hasOwnProperty("count")) + if (!$util.isInteger(message.count) && !(message.count && $util.isInteger(message.count.low) && $util.isInteger(message.count.high))) + return "count: integer|Long expected"; + return null; + }; + + CountResult.fromObject = function fromObject(object) { + if (object instanceof $root.esriPBuffer.FeatureCollectionPBuffer.CountResult) + return object; + var message = new $root.esriPBuffer.FeatureCollectionPBuffer.CountResult(); + if (object.count != null) + if ($util.Long) + (message.count = $util.Long.fromValue(object.count)).unsigned = true; + else if (typeof object.count === "string") + message.count = parseInt(object.count, 10); + else if (typeof object.count === "number") + message.count = object.count; + else if (typeof object.count === "object") + message.count = new $util.LongBits(object.count.low >>> 0, object.count.high >>> 0).toNumber(true); + return message; + }; + + CountResult.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) + if ($util.Long) { + var long = new $util.Long(0, 0, true); + object.count = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.count = options.longs === String ? "0" : 0; + if (message.count != null && message.hasOwnProperty("count")) + if (typeof message.count === "number") + object.count = options.longs === String ? String(message.count) : message.count; + else + object.count = options.longs === String ? $util.Long.prototype.toString.call(message.count) : options.longs === Number ? new $util.LongBits(message.count.low >>> 0, message.count.high >>> 0).toNumber(true) : message.count; + return object; + }; + + CountResult.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + CountResult.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/esriPBuffer.FeatureCollectionPBuffer.CountResult"; + }; + + return CountResult; + })(); + + FeatureCollectionPBuffer.ObjectIdsResult = (function() { + + function ObjectIdsResult(properties) { + this.objectIds = []; + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + ObjectIdsResult.prototype.objectIdFieldName = ""; + ObjectIdsResult.prototype.serverGens = null; + ObjectIdsResult.prototype.objectIds = $util.emptyArray; + + ObjectIdsResult.create = function create(properties) { + return new ObjectIdsResult(properties); + }; + + ObjectIdsResult.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.objectIdFieldName != null && Object.hasOwnProperty.call(message, "objectIdFieldName")) + writer.uint32(10).string(message.objectIdFieldName); + if (message.serverGens != null && Object.hasOwnProperty.call(message, "serverGens")) + $root.esriPBuffer.FeatureCollectionPBuffer.ServerGens.encode(message.serverGens, writer.uint32(18).fork()).ldelim(); + if (message.objectIds != null && message.objectIds.length) { + writer.uint32(26).fork(); + for (var i = 0; i < message.objectIds.length; ++i) + writer.uint64(message.objectIds[i]); + writer.ldelim(); + } + return writer; + }; + + ObjectIdsResult.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + ObjectIdsResult.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.objectIdFieldName != null && message.hasOwnProperty("objectIdFieldName")) + if (!$util.isString(message.objectIdFieldName)) + return "objectIdFieldName: string expected"; + if (message.serverGens != null && message.hasOwnProperty("serverGens")) { + var error = $root.esriPBuffer.FeatureCollectionPBuffer.ServerGens.verify(message.serverGens); + if (error) + return "serverGens." + error; + } + if (message.objectIds != null && message.hasOwnProperty("objectIds")) { + if (!Array.isArray(message.objectIds)) + return "objectIds: array expected"; + for (var i = 0; i < message.objectIds.length; ++i) + if (!$util.isInteger(message.objectIds[i]) && !(message.objectIds[i] && $util.isInteger(message.objectIds[i].low) && $util.isInteger(message.objectIds[i].high))) + return "objectIds: integer|Long[] expected"; + } + return null; + }; + + ObjectIdsResult.fromObject = function fromObject(object) { + if (object instanceof $root.esriPBuffer.FeatureCollectionPBuffer.ObjectIdsResult) + return object; + var message = new $root.esriPBuffer.FeatureCollectionPBuffer.ObjectIdsResult(); + if (object.objectIdFieldName != null) + message.objectIdFieldName = String(object.objectIdFieldName); + if (object.serverGens != null) { + if (typeof object.serverGens !== "object") + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.ObjectIdsResult.serverGens: object expected"); + message.serverGens = $root.esriPBuffer.FeatureCollectionPBuffer.ServerGens.fromObject(object.serverGens); + } + if (object.objectIds) { + if (!Array.isArray(object.objectIds)) + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.ObjectIdsResult.objectIds: array expected"); + message.objectIds = []; + for (var i = 0; i < object.objectIds.length; ++i) + if ($util.Long) + (message.objectIds[i] = $util.Long.fromValue(object.objectIds[i])).unsigned = true; + else if (typeof object.objectIds[i] === "string") + message.objectIds[i] = parseInt(object.objectIds[i], 10); + else if (typeof object.objectIds[i] === "number") + message.objectIds[i] = object.objectIds[i]; + else if (typeof object.objectIds[i] === "object") + message.objectIds[i] = new $util.LongBits(object.objectIds[i].low >>> 0, object.objectIds[i].high >>> 0).toNumber(true); + } + return message; + }; + + ObjectIdsResult.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.arrays || options.defaults) + object.objectIds = []; + if (options.defaults) { + object.objectIdFieldName = ""; + object.serverGens = null; + } + if (message.objectIdFieldName != null && message.hasOwnProperty("objectIdFieldName")) + object.objectIdFieldName = message.objectIdFieldName; + if (message.serverGens != null && message.hasOwnProperty("serverGens")) + object.serverGens = $root.esriPBuffer.FeatureCollectionPBuffer.ServerGens.toObject(message.serverGens, options); + if (message.objectIds && message.objectIds.length) { + object.objectIds = []; + for (var j = 0; j < message.objectIds.length; ++j) + if (typeof message.objectIds[j] === "number") + object.objectIds[j] = options.longs === String ? String(message.objectIds[j]) : message.objectIds[j]; + else + object.objectIds[j] = options.longs === String ? $util.Long.prototype.toString.call(message.objectIds[j]) : options.longs === Number ? new $util.LongBits(message.objectIds[j].low >>> 0, message.objectIds[j].high >>> 0).toNumber(true) : message.objectIds[j]; + } + return object; + }; + + ObjectIdsResult.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + ObjectIdsResult.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/esriPBuffer.FeatureCollectionPBuffer.ObjectIdsResult"; + }; + + return ObjectIdsResult; + })(); + + FeatureCollectionPBuffer.QueryResult = (function() { + + function QueryResult(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + QueryResult.prototype.featureResult = null; + QueryResult.prototype.countResult = null; + QueryResult.prototype.idsResult = null; + + var $oneOfFields; + + Object.defineProperty(QueryResult.prototype, "Results", { + get: $util.oneOfGetter($oneOfFields = ["featureResult", "countResult", "idsResult"]), + set: $util.oneOfSetter($oneOfFields) + }); + + QueryResult.create = function create(properties) { + return new QueryResult(properties); + }; + + QueryResult.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.featureResult != null && Object.hasOwnProperty.call(message, "featureResult")) + $root.esriPBuffer.FeatureCollectionPBuffer.FeatureResult.encode(message.featureResult, writer.uint32(10).fork()).ldelim(); + if (message.countResult != null && Object.hasOwnProperty.call(message, "countResult")) + $root.esriPBuffer.FeatureCollectionPBuffer.CountResult.encode(message.countResult, writer.uint32(18).fork()).ldelim(); + if (message.idsResult != null && Object.hasOwnProperty.call(message, "idsResult")) + $root.esriPBuffer.FeatureCollectionPBuffer.ObjectIdsResult.encode(message.idsResult, writer.uint32(26).fork()).ldelim(); + return writer; + }; + + QueryResult.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + QueryResult.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + var properties = {}; + if (message.featureResult != null && message.hasOwnProperty("featureResult")) { + properties.Results = 1; + { + var error = $root.esriPBuffer.FeatureCollectionPBuffer.FeatureResult.verify(message.featureResult); + if (error) + return "featureResult." + error; + } + } + if (message.countResult != null && message.hasOwnProperty("countResult")) { + if (properties.Results === 1) + return "Results: multiple values"; + properties.Results = 1; + { + var error = $root.esriPBuffer.FeatureCollectionPBuffer.CountResult.verify(message.countResult); + if (error) + return "countResult." + error; + } + } + if (message.idsResult != null && message.hasOwnProperty("idsResult")) { + if (properties.Results === 1) + return "Results: multiple values"; + properties.Results = 1; + { + var error = $root.esriPBuffer.FeatureCollectionPBuffer.ObjectIdsResult.verify(message.idsResult); + if (error) + return "idsResult." + error; + } + } + return null; + }; + + QueryResult.fromObject = function fromObject(object) { + if (object instanceof $root.esriPBuffer.FeatureCollectionPBuffer.QueryResult) + return object; + var message = new $root.esriPBuffer.FeatureCollectionPBuffer.QueryResult(); + if (object.featureResult != null) { + if (typeof object.featureResult !== "object") + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.QueryResult.featureResult: object expected"); + message.featureResult = $root.esriPBuffer.FeatureCollectionPBuffer.FeatureResult.fromObject(object.featureResult); + } + if (object.countResult != null) { + if (typeof object.countResult !== "object") + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.QueryResult.countResult: object expected"); + message.countResult = $root.esriPBuffer.FeatureCollectionPBuffer.CountResult.fromObject(object.countResult); + } + if (object.idsResult != null) { + if (typeof object.idsResult !== "object") + throw TypeError(".esriPBuffer.FeatureCollectionPBuffer.QueryResult.idsResult: object expected"); + message.idsResult = $root.esriPBuffer.FeatureCollectionPBuffer.ObjectIdsResult.fromObject(object.idsResult); + } + return message; + }; + + QueryResult.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (message.featureResult != null && message.hasOwnProperty("featureResult")) { + object.featureResult = $root.esriPBuffer.FeatureCollectionPBuffer.FeatureResult.toObject(message.featureResult, options); + if (options.oneofs) + object.Results = "featureResult"; + } + if (message.countResult != null && message.hasOwnProperty("countResult")) { + object.countResult = $root.esriPBuffer.FeatureCollectionPBuffer.CountResult.toObject(message.countResult, options); + if (options.oneofs) + object.Results = "countResult"; + } + if (message.idsResult != null && message.hasOwnProperty("idsResult")) { + object.idsResult = $root.esriPBuffer.FeatureCollectionPBuffer.ObjectIdsResult.toObject(message.idsResult, options); + if (options.oneofs) + object.Results = "idsResult"; + } + return object; + }; + + QueryResult.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + QueryResult.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/esriPBuffer.FeatureCollectionPBuffer.QueryResult"; + }; + + return QueryResult; + })(); + + return FeatureCollectionPBuffer; + })(); + + return esriPBuffer; +})(); + +module.exports = $root; diff --git a/packages/featureserver/src/response-handlers/helpers/send-pbf/get-geometry-transform.js b/packages/featureserver/src/response-handlers/helpers/send-pbf/get-geometry-transform.js new file mode 100644 index 000000000..4d14206d3 --- /dev/null +++ b/packages/featureserver/src/response-handlers/helpers/send-pbf/get-geometry-transform.js @@ -0,0 +1,65 @@ +const esriProjCodes = require('@esri/proj-codes'); +const wktParser = require('wkt-parser'); + +function getGeometryTransform(spatialReference, quantizationParameters) { + if (quantizationParameters) { + return parseQuantizationParameters(quantizationParameters); + } + + let scale = getSpatialReferenceScaleFactor(spatialReference); + + return { + scale: {xScale: scale, yScale: scale}, + translate: { xTranslate: 0, yTranslate: 0 } // [-20037700, -30241100], + }; +} + +function getSpatialReferenceScaleFactor({ wkt, wkid }) { + wkt = wkt || esriProjCodes.lookup(wkid)?.wkt; + + if (!wkt) { + return 1; + } + + const wktInfo = wktParser(wkt); + + if (wktInfo?.UNIT?.name === 'degree') { + return 1e-9; + } + + if (wktInfo?.UNIT?.convert) { + return 1 / wktInfo.UNIT.convert / 10000; + } + + return 1; +} + +function parseQuantizationParameters(q) { + const { + tolerance = null + } = q; + const scale = tolerance !== null ? q.tolerance: 1; + const [ xTranslate, yTranslate ] = q.extent != null ? [q.extent.xmin, q.extent.ymax] : [0, 0]; + + return { + originPosition: normalizeOriginPosition(q.originPosition), + scale: { xScale: scale, yScale: scale }, + translate: { xTranslate, yTranslate } + }; +} + +function normalizeOriginPosition(originPosition) { + if (originPosition === 'upper-left') { + return 'upperLeft'; + } + + if (originPosition === 'lower-left') { + return 'lowerLeft'; + } + + return originPosition; +} + +module.exports = { + getGeometryTransform +}; diff --git a/packages/featureserver/src/response-handlers/helpers/send-pbf/get-geometry-transform.spec.js b/packages/featureserver/src/response-handlers/helpers/send-pbf/get-geometry-transform.spec.js new file mode 100644 index 000000000..bf120380f --- /dev/null +++ b/packages/featureserver/src/response-handlers/helpers/send-pbf/get-geometry-transform.spec.js @@ -0,0 +1,194 @@ +const should = require('should'); // eslint-disable-line +const { getGeometryTransform } = require('./get-geometry-transform'); + +describe('getGeometryTransform', () => { + describe('from quantizationParameters', () => { + it('from typical ArcGIS client quantizationParameters', () => { + const quantizationParameters = { + mode: 'view', + originPosition: 'upperLeft', + tolerance: 1.0583354500042335, + extent: { + type: 'extent', + xmin: 18341377.47954369, + ymin: 2979920.6113554947, + xmax: 7546517.393554582, + ymax: 11203512.89298139, + spatialReference: { wkid: 102100, latestWkid: 3857 }, + }, + }; + const result = getGeometryTransform({ wkid: 4326 }, quantizationParameters); + result.should.deepEqual({ + originPosition: 'upperLeft', + scale: { + xScale: 1.0583354500042335, + yScale: 1.0583354500042335, + }, + translate: { + xTranslate: 18341377.47954369, + yTranslate: 11203512.89298139, + }, + }); + }); + + it('upper-left origin', () => { + const quantizationParameters = { + mode: 'view', + originPosition: 'upper-left', + tolerance: 1.0583354500042335, + extent: { + type: 'extent', + xmin: 18341377.47954369, + ymin: 2979920.6113554947, + xmax: 7546517.393554582, + ymax: 11203512.89298139, + spatialReference: { wkid: 102100, latestWkid: 3857 }, + }, + }; + const result = getGeometryTransform({ wkid: 4326 }, quantizationParameters); + result.should.deepEqual({ + originPosition: 'upperLeft', + scale: { + xScale: 1.0583354500042335, + yScale: 1.0583354500042335, + }, + translate: { + xTranslate: 18341377.47954369, + yTranslate: 11203512.89298139, + }, + }); + }); + + it('lower-left origin', () => { + const quantizationParameters = { + mode: 'view', + originPosition: 'lower-left', + tolerance: 1.0583354500042335, + extent: { + type: 'extent', + xmin: 18341377.47954369, + ymin: 2979920.6113554947, + xmax: 7546517.393554582, + ymax: 11203512.89298139, + spatialReference: { wkid: 102100, latestWkid: 3857 }, + }, + }; + const result = getGeometryTransform({ wkid: 4326 }, quantizationParameters); + result.should.deepEqual({ + originPosition: 'lowerLeft', + scale: { + xScale: 1.0583354500042335, + yScale: 1.0583354500042335, + }, + translate: { + xTranslate: 18341377.47954369, + yTranslate: 11203512.89298139, + }, + }); + }); + + it('missing tolerance', () => { + const quantizationParameters = { + mode: 'view', + originPosition: 'upperLeft', + extent: { + type: 'extent', + xmin: 18341377.47954369, + ymin: 2979920.6113554947, + xmax: 7546517.393554582, + ymax: 11203512.89298139, + spatialReference: { wkid: 102100, latestWkid: 3857 }, + }, + }; + const result = getGeometryTransform({ wkid: 4326 }, quantizationParameters); + result.should.deepEqual({ + originPosition: 'upperLeft', + scale: { + xScale: 1, + yScale: 1, + }, + translate: { + xTranslate: 18341377.47954369, + yTranslate: 11203512.89298139, + }, + }); + }); + it('missing extent', () => { + const quantizationParameters = { + mode: 'view', + originPosition: 'upperLeft', + tolerance: 1.0583354500042335, + }; + const result = getGeometryTransform({ wkid: 4326 }, quantizationParameters); + result.should.deepEqual({ + originPosition: 'upperLeft', + scale: { + xScale: 1.0583354500042335, + yScale: 1.0583354500042335, + }, + translate: { + xTranslate: 0, + yTranslate: 0, + }, + }); + }); + }); + + it('from spatialReference, with UNIT.name = "degree"', () => { + const result = getGeometryTransform({ wkid: 4326 }); + result.should.deepEqual({ + scale: { + xScale: 1e-9, + yScale: 1e-9, + }, + translate: { + xTranslate: 0, + yTranslate: 0, + }, + }); + }); + + it('from spatialReference, with UNIT.convert', () => { + const result = getGeometryTransform({ wkid: 3857 }); + result.should.deepEqual({ + scale: { + xScale: 0.0001, + yScale: 0.0001, + }, + translate: { + xTranslate: 0, + yTranslate: 0, + }, + }); + }); + + it('from unknown spatial reference', () => { + const result = getGeometryTransform({ wkid: 999 }); + result.should.deepEqual({ + scale: { + xScale: 1, + yScale: 1, + }, + translate: { + xTranslate: 0, + yTranslate: 0, + }, + }); + }); + + it('from custom spatial reference', () => { + const result = getGeometryTransform({ + wkt: 'PROJCRS["NAD83 / California zone 5 (ftUS)",BASEGEOGCRS["NAD83",DATUM["North American Datum 1983",ELLIPSOID["GRS 1980",6378137,298.257222101,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4269]],CONVERSION["SPCS83 California zone 5 (US Survey feet)",METHOD["Lambert Conic Conformal (2SP)",ID["EPSG",9802]],PARAMETER["Latitude of false origin",33.5,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8821]],PARAMETER["Longitude of false origin",-118,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8822]],PARAMETER["Latitude of 1st standard parallel",35.4666666666667,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8823]],PARAMETER["Latitude of 2nd standard parallel",34.0333333333333,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8824]],PARAMETER["Easting at false origin",6561666.667,LENGTHUNIT["US survey foot",0.304800609601219],ID["EPSG",8826]],PARAMETER["Northing at false origin",1640416.667,LENGTHUNIT["US survey foot",0.304800609601219],ID["EPSG",8827]]],CS[Cartesian,2],AXIS["easting (X)",east,ORDER[1],LENGTHUNIT["US survey foot",0.304800609601219]],AXIS["northing (Y)",north,ORDER[2],LENGTHUNIT["US survey foot",0.304800609601219]],USAGE[SCOPE["Engineering survey, topographic mapping."],AREA["United States (USA) - California - counties Kern; Los Angeles; San Bernardino; San Luis Obispo; Santa Barbara; Ventura."],BBOX[32.76,-121.42,35.81,-114.12]],ID["EPSG",2229]]', + }); + result.should.deepEqual({ + scale: { + xScale: 1, + yScale: 1, + }, + translate: { + xTranslate: 0, + yTranslate: 0, + }, + }); + }); +}); diff --git a/packages/featureserver/src/response-handlers/helpers/send-pbf/index.js b/packages/featureserver/src/response-handlers/helpers/send-pbf/index.js new file mode 100644 index 000000000..0918ef621 --- /dev/null +++ b/packages/featureserver/src/response-handlers/helpers/send-pbf/index.js @@ -0,0 +1,60 @@ +const { esriPBuffer: { + FeatureCollectionPBuffer: FeatureCollectionProto +}} = require('./FeatureCollection.proto.js'); +const { transformFeaturesForPbf } = require('./transform-features-for-pbf.js'); + +const FILENAME = 'results.pbf'; + +function sendPbf(res, payload, requestParameters) { + const { returnExtentOnly } = requestParameters; + + if (returnExtentOnly === true) { + const error = new Error('Bad Request'); + error.code = 400; + throw error; + } + + + const pbfPayload = setPbfPayload(payload, requestParameters); + const buffer = FeatureCollectionProto.encode(pbfPayload).finish(); + res.writeHead(200, [ + ['content-type', 'application/x-protobuf'], + ['content-length', buffer.length], + ['content-disposition', `inline;filename=${FILENAME}`], + ]); + + return res.end(buffer); +} + +function setPbfPayload(payload, requestParameters) { + const { returnCountOnly, returnIdsOnly, quantizationParameters } = requestParameters; + + if (returnCountOnly === true) { + return { + queryResult: { + Results: 'countResult', + countResult: payload, + }, + }; + } + + if (returnIdsOnly === true) { + return { + queryResult: { + Results: 'idsResult', + idsResult: payload, + }, + }; + } + + return { + queryResult: { + Results: 'featureResult', + featureResult: transformFeaturesForPbf(payload, quantizationParameters), + }, + }; +} + +module.exports = { + sendPbf, +}; diff --git a/packages/featureserver/src/response-handlers/helpers/send-pbf/index.spec.js b/packages/featureserver/src/response-handlers/helpers/send-pbf/index.spec.js new file mode 100644 index 000000000..cac79b2b8 --- /dev/null +++ b/packages/featureserver/src/response-handlers/helpers/send-pbf/index.spec.js @@ -0,0 +1,144 @@ +const should = require('should'); // eslint-disable-line +const sinon = require('sinon'); +const proxyquire = require('proxyquire'); + +const transformFeaturesForPbfSpy = sinon.spy(() => { + return { pbf: 'json' }; +}); + +const protoSpy = { + encode: sinon.spy(() => { + return protoSpy; + }), + finish: sinon.spy(() => { + return { + esri: 'pbf', + length: 99, + }; + }), +}; + +const { sendPbf } = proxyquire('./', { + './transform-features-for-pbf.js': { + transformFeaturesForPbf: transformFeaturesForPbfSpy, + }, + './FeatureCollection.proto.js': { + esriPBuffer: { + FeatureCollectionPBuffer: protoSpy, + }, + }, +}); + +const res = { + writeHead: sinon.spy(() => { + return res; + }), + end: sinon.spy(), +}; + +describe('sendPbf', () => { + afterEach(() => { + protoSpy.encode.resetHistory(); + protoSpy.finish.resetHistory(); + transformFeaturesForPbfSpy.resetHistory(); + }); + + it('return pbf of featureResult', () => { + sendPbf(res, { esri: 'json' }, {}); + res.end.firstCall.args[0].should.deepEqual({ + esri: 'pbf', + length: 99, + }); + res.writeHead.firstCall.args.should.deepEqual([ + 200, + [ + ['content-type', 'application/x-protobuf'], + ['content-length', 99], + ['content-disposition', 'inline;filename=results.pbf'], + ], + ]); + transformFeaturesForPbfSpy.firstCall.args.should.deepEqual([ + { esri: 'json' }, + undefined, + ]); + protoSpy.encode.firstCall.args.should.deepEqual([ + { + queryResult: { + Results: 'featureResult', + featureResult: { + pbf: 'json', + }, + }, + }, + ]); + protoSpy.finish.called.should.equal(true); + }); + + it('return pbf of countResult', () => { + sendPbf(res, { esri: 'json' }, { returnCountOnly: true }); + res.end.firstCall.args[0].should.deepEqual({ + esri: 'pbf', + length: 99, + }); + res.writeHead.firstCall.args.should.deepEqual([ + 200, + [ + ['content-type', 'application/x-protobuf'], + ['content-length', 99], + ['content-disposition', 'inline;filename=results.pbf'], + ], + ]); + transformFeaturesForPbfSpy.called.should.equal(false); + + protoSpy.encode.firstCall.args.should.deepEqual([ + { + queryResult: { + Results: 'countResult', + countResult: { + esri: 'json', + }, + }, + }, + ]); + protoSpy.finish.called.should.equal(true); + }); + + it('return pbf of idsResult', () => { + sendPbf(res, { esri: 'json' }, { returnIdsOnly: true }); + res.end.firstCall.args[0].should.deepEqual({ + esri: 'pbf', + length: 99, + }); + res.writeHead.firstCall.args.should.deepEqual([ + 200, + [ + ['content-type', 'application/x-protobuf'], + ['content-length', 99], + ['content-disposition', 'inline;filename=results.pbf'], + ], + ]); + transformFeaturesForPbfSpy.called.should.equal(false); + + protoSpy.encode.firstCall.args.should.deepEqual([ + { + queryResult: { + Results: 'idsResult', + idsResult: { + esri: 'json', + }, + }, + }, + ]); + protoSpy.finish.called.should.equal(true); + }); + + it('fail on returnExtentOnly', () => { + try { + sendPbf(res, { esri: 'json' }, { returnExtentOnly: true }); + throw new Error('should have thrown'); + } catch (error) { + error.message.should.equal('Bad Request'); + error.code.should.equal(400); + } + }); +}); diff --git a/packages/featureserver/src/response-handlers/helpers/send-pbf/transform-features-for-pbf.js b/packages/featureserver/src/response-handlers/helpers/send-pbf/transform-features-for-pbf.js new file mode 100644 index 000000000..3686f4a19 --- /dev/null +++ b/packages/featureserver/src/response-handlers/helpers/send-pbf/transform-features-for-pbf.js @@ -0,0 +1,52 @@ +const _ = require('lodash'); +const { getGeometryTransform } = require('./get-geometry-transform'); +const { transformToPbfAttributes } = require('./transform-to-pbf-attributes'); +const { transformToPbfGeometry } = require('./transform-to-pbf-geometry'); + + + +function transformFeaturesForPbf(json, quantizationParameters) { + const fields = _.orderBy(json.fields, ['name'], ['asc']); + const { objectIdFieldName, uniqueIdField, geometryType, spatialReference } = json; + + const geometryTransform = getGeometryTransform(spatialReference, quantizationParameters); + + const features = json.features.map( + transformFunction(fields, geometryTransform), + ); + + return { + objectIdFieldName, + uniqueIdField, + geometryType, + spatialReference, + fields, + features, + transform: geometryTransform + }; +} + +function transformFunction(fields, geometryTransform) { + const fieldMap = fields.reduce((acc, cur) => { + acc[cur.name] = cur.type; + return acc; + }, {}); + + return (feature) => { + const attributes = transformToPbfAttributes(feature.attributes, fieldMap); + + const geometry = transformToPbfGeometry( + feature.geometry, + geometryTransform + ); + + return { + attributes, + geometry + }; + }; +} + +module.exports = { + transformFeaturesForPbf, +}; diff --git a/packages/featureserver/src/response-handlers/helpers/send-pbf/transform-features-for-pbf.spec.js b/packages/featureserver/src/response-handlers/helpers/send-pbf/transform-features-for-pbf.spec.js new file mode 100644 index 000000000..38ceab81c --- /dev/null +++ b/packages/featureserver/src/response-handlers/helpers/send-pbf/transform-features-for-pbf.spec.js @@ -0,0 +1,223 @@ +const should = require('should'); // eslint-disable-line +const { transformFeaturesForPbf } = require('./transform-features-for-pbf'); + +const transformedFixture = { + objectIdFieldName: 'FID', + uniqueIdField: { + name: 'FID', + isSystemMaintained: true, + }, + geometryType: 'esriGeometryPoint', + spatialReference: { + wkid: 102100, + latestWkid: 3857, + }, + fields: [ + { + name: 'CONDITIODT', + type: 'esriFieldTypeDate', + alias: 'CONDITIODT', + sqlType: 'sqlTypeTimestamp2', + length: 8, + domain: null, + defaultValue: null, + }, + { + name: 'FACILITYID', + type: 'esriFieldTypeString', + alias: 'FACILITYID', + sqlType: 'sqlTypeNVarchar', + length: 30, + domain: null, + defaultValue: null, + }, + { + name: 'FID', + type: 'esriFieldTypeOID', + alias: 'FID', + sqlType: 'sqlTypeInteger', + domain: null, + defaultValue: null, + }, + { + name: 'GlobalID_2', + type: 'esriFieldTypeGlobalID', + alias: 'GlobalID_2', + sqlType: 'sqlTypeOther', + length: 38, + domain: null, + defaultValue: 'NEWID() WITH VALUES', + }, + { + name: 'OBJECTID', + type: 'esriFieldTypeInteger', + alias: 'OBJECTID', + sqlType: 'sqlTypeInteger', + domain: null, + defaultValue: null, + }, + { + name: 'TBOX_L', + type: 'esriFieldTypeDouble', + alias: 'TBOX_L', + sqlType: 'sqlTypeFloat', + domain: null, + defaultValue: null, + }, + { + name: 'WARD', + type: 'esriFieldTypeSmallInteger', + alias: 'WARD', + sqlType: 'sqlTypeSmallInt', + domain: null, + defaultValue: null, + }, + ], + transform: { + scale: { + xScale: 0.0001, + yScale: 0.0001, + }, + translate: { + xTranslate: 0, + yTranslate: 0, + }, + }, + features: [ + { + attributes: [ + { + sint64Value: 1421798400000, + }, + { + stringValue: '30040-085-3001-0126-000', + }, + { + uintValue: 1, + }, + { + stringValue: '9279699f-5ece-4ca6-8a3b-b559da37bc5e', + }, + { + sintValue: 46104019, + }, + { + doubleValue: 99, + }, + { + sintValue: 6, + }, + ], + geometry: { + coords: ['-85716573542', '-47044113943'], + }, + }, + ], +}; + +describe('transform features for PBF', () => { + it('convert Esri FeatureCollection to PBF-ready JSON', () => { + const fixture = createFixture(); + const result = transformFeaturesForPbf(fixture); + result.should.deepEqual(transformedFixture); + }); +}); + +function createFixture() { + return { + objectIdFieldName: 'FID', + uniqueIdField: { + name: 'FID', + isSystemMaintained: true, + }, + globalIdFieldName: 'GlobalID_2', + serverGens: { + minServerGen: 3485630, + serverGen: 3485630, + }, + geometryType: 'esriGeometryPoint', + spatialReference: { + wkid: 102100, + latestWkid: 3857, + }, + fields: [ + { + name: 'FID', + type: 'esriFieldTypeOID', + alias: 'FID', + sqlType: 'sqlTypeInteger', + domain: null, + defaultValue: null, + }, + { + name: 'OBJECTID', + type: 'esriFieldTypeInteger', + alias: 'OBJECTID', + sqlType: 'sqlTypeInteger', + domain: null, + defaultValue: null, + }, + { + name: 'WARD', + type: 'esriFieldTypeSmallInteger', + alias: 'WARD', + sqlType: 'sqlTypeSmallInt', + domain: null, + defaultValue: null, + }, + { + name: 'FACILITYID', + type: 'esriFieldTypeString', + alias: 'FACILITYID', + sqlType: 'sqlTypeNVarchar', + length: 30, + domain: null, + defaultValue: null, + }, + { + name: 'TBOX_L', + type: 'esriFieldTypeDouble', + alias: 'TBOX_L', + sqlType: 'sqlTypeFloat', + domain: null, + defaultValue: null, + }, + { + name: 'CONDITIODT', + type: 'esriFieldTypeDate', + alias: 'CONDITIODT', + sqlType: 'sqlTypeTimestamp2', + length: 8, + domain: null, + defaultValue: null, + }, + { + name: 'GlobalID_2', + type: 'esriFieldTypeGlobalID', + alias: 'GlobalID_2', + sqlType: 'sqlTypeOther', + length: 38, + domain: null, + defaultValue: 'NEWID() WITH VALUES', + }, + ], + exceededTransferLimit: true, + features: [ + { + attributes: { + FID: 1, + OBJECTID: 46104019, + WARD: 6, + FACILITYID: '30040-085-3001-0126-000', + TBOX_L: 99, + CONDITIODT: 1421798400000, + GlobalID_2: '9279699f-5ece-4ca6-8a3b-b559da37bc5e', + }, + geometry: { + x: -8571657.3541762847, + y: 4704411.394312229, + }, + }, + ], + }; +} diff --git a/packages/featureserver/src/response-handlers/helpers/send-pbf/transform-to-pbf-attributes.js b/packages/featureserver/src/response-handlers/helpers/send-pbf/transform-to-pbf-attributes.js new file mode 100644 index 000000000..62109a2b0 --- /dev/null +++ b/packages/featureserver/src/response-handlers/helpers/send-pbf/transform-to-pbf-attributes.js @@ -0,0 +1,33 @@ +const _ = require('lodash'); + +const typeLookup = { + esriFieldTypeOID: 'uintValue', + esriFieldTypeInteger: 'sintValue', + esriFieldTypeSmallInteger: 'sintValue', + esriFieldTypeString: 'stringValue', + esriFieldTypeDouble: 'doubleValue', + esriFieldTypeDate: 'sint64Value', + esriFieldTypeGlobalID: 'stringValue', +}; + +function transformToPbfAttributes(attributes, fieldMap) { + return _.chain(attributes) + .entries(attributes) + .map(([key, value]) => ({ + name: key, + value, + })) + .orderBy(['name'],['asc']) + .map(({ name, value }) => { + const type = fieldMap[name]; + const pbfType = typeLookup[type]; + return { [pbfType]: value }; + }) + .value(); +} + + + +module.exports = { + transformToPbfAttributes +}; diff --git a/packages/featureserver/src/response-handlers/helpers/send-pbf/transform-to-pbf-attributes.spec.js b/packages/featureserver/src/response-handlers/helpers/send-pbf/transform-to-pbf-attributes.spec.js new file mode 100644 index 000000000..a9c7352a2 --- /dev/null +++ b/packages/featureserver/src/response-handlers/helpers/send-pbf/transform-to-pbf-attributes.spec.js @@ -0,0 +1,108 @@ +const should = require('should'); // eslint-disable-line +const { transformToPbfAttributes } = require('./transform-to-pbf-attributes'); + +const fields = [ + { + name: 'FID', + type: 'esriFieldTypeOID', + alias: 'FID', + sqlType: 'sqlTypeInteger', + domain: null, + defaultValue: null, + }, + { + name: 'OBJECTID', + type: 'esriFieldTypeInteger', + alias: 'OBJECTID', + sqlType: 'sqlTypeInteger', + domain: null, + defaultValue: null, + }, + { + name: 'WARD', + type: 'esriFieldTypeSmallInteger', + alias: 'WARD', + sqlType: 'sqlTypeSmallInt', + domain: null, + defaultValue: null, + }, + { + name: 'FACILITYID', + type: 'esriFieldTypeString', + alias: 'FACILITYID', + sqlType: 'sqlTypeNVarchar', + length: 30, + domain: null, + defaultValue: null, + }, + { + name: 'TBOX_L', + type: 'esriFieldTypeDouble', + alias: 'TBOX_L', + sqlType: 'sqlTypeFloat', + domain: null, + defaultValue: null, + }, + { + name: 'CONDITIODT', + type: 'esriFieldTypeDate', + alias: 'CONDITIODT', + sqlType: 'sqlTypeTimestamp2', + length: 8, + domain: null, + defaultValue: null, + }, + { + name: 'GlobalID_2', + type: 'esriFieldTypeGlobalID', + alias: 'GlobalID_2', + sqlType: 'sqlTypeOther', + length: 38, + domain: null, + defaultValue: 'NEWID() WITH VALUES', + }, +]; + +const fieldMap = fields.reduce((acc, cur) => { + acc[cur.name] = cur.type; + return acc; +}, {}); + +const attributes = { + FID: 1, + OBJECTID: 46104019, + WARD: 6, + FACILITYID: '30040-085-3001-0126-000', + TBOX_L: 99, + CONDITIODT: 1421798400000, + GlobalID_2: '9279699f-5ece-4ca6-8a3b-b559da37bc5e', +}; + +describe('transformToPbfAttributes', () => { + it('should transform Esri JSON to Esri PBF JSON', () => { + const result = transformToPbfAttributes(attributes ,fieldMap); + result.should.deepEqual([ + { + sint64Value: 1421798400000, + }, + { + stringValue: '30040-085-3001-0126-000', + }, + { + uintValue: 1, + }, + { + stringValue: '9279699f-5ece-4ca6-8a3b-b559da37bc5e', + }, + { + sintValue: 46104019, + }, + { + doubleValue: 99, + }, + { + sintValue: 6, + }, + ]); + }); +}); diff --git a/packages/featureserver/src/response-handlers/helpers/send-pbf/transform-to-pbf-geometry.js b/packages/featureserver/src/response-handlers/helpers/send-pbf/transform-to-pbf-geometry.js new file mode 100644 index 000000000..2f8cf4f91 --- /dev/null +++ b/packages/featureserver/src/response-handlers/helpers/send-pbf/transform-to-pbf-geometry.js @@ -0,0 +1,174 @@ +function transformToPbfGeometry(geometry, transform) { + if (!geometry) { + return null; + } + + if (isPoint(geometry)) { + return transformPoint(geometry, transform); + } + + if (isPolyline(geometry)) { + return transformPolyline(geometry, transform, false, false); + } + + if (isPolygon(geometry)) { + return transformPolygon(geometry, transform, false, false); + } + + if (isMultipoint(geometry)) { + return transformMultipoint(geometry, transform, false, false); + } + + throw new Error('Unknown geometry type'); +} + +function isMultipoint(json) { + return json.points !== undefined; +} + +function isPoint(json) { + return json.x !== undefined && json.y !== undefined; +} + +function isPolyline(json) { + return json.paths !== undefined; +} + +function isPolygon(json) { + return json.rings !== undefined; +} + +function transformMultipoint(geometry, transform) { + const points = transformPoints(geometry.points, transform); + return { + lengths: [points.length], + coords: points.flat().map((int) => int.toString()), + }; +} + +function transformPoint(geometry, transform) { + const { + scale: { xScale, yScale }, + translate: { xTranslate, yTranslate } + } = transform; + + const x = quantizeX(geometry.x, xScale, xTranslate); + const y = quantizeY(geometry.y, yScale, yTranslate); + + return { + coords: createCoordinatesArray(x, y, geometry.z, geometry.m).map((int) => + int.toString(), + ), + }; +} + +function transformPolygon(geometry, transform, hasZ, hasM) { + const rings = transformRings(geometry.rings, transform, hasZ, hasM); + + return { + lengths: rings.map((ring) => ring.length), + coords: rings + .flat() + .flat() + .map((int) => int.toString()), + }; +} + +function transformPolyline(geometry, transform) { + const paths = transformPaths(geometry.paths, transform); + + return { + lengths: paths.map((path) => path.length), + coords: paths + .flat() + .flat() + .map((int) => int.toString()), + }; +} + +function createCoordinatesArray(x, y, placeholder1, placeholder2) { + const result = [x, y]; + + if (placeholder1) { + result.push(placeholder1); + } + + if (placeholder2) { + result.push(placeholder2); + } + return result; +} + +function quantizeX(x, scale, translate) { + return Math.round((x - translate) / scale); +} + +function quantizeY(y, scale, translate) { + return Math.round((translate - y) / scale); +} + +function transformCoordsArray(coordsArray, transform) { + const { + scale: { xScale, yScale }, + translate: { xTranslate, yTranslate } + } = transform; + const result = []; + let prevX; + let prevY; + let x; + let y; + + for (let i = 0; i < coordsArray.length; i++) { + const coords = coordsArray[i]; + + if (i > 0) { + x = quantizeX(coords[0], xScale, xTranslate); + y = quantizeY(coords[1], yScale, yTranslate); + + if (x !== prevX || y !== prevY) { + result.push( + createCoordinatesArray(x - prevX, y - prevY, coords[2], coords[3]), + ); + prevX = x; + prevY = y; + } + } else { + prevX = quantizeX(coords[0], xScale, xTranslate); + prevY = quantizeY(coords[1], yScale, yTranslate); + result.push(createCoordinatesArray(prevX, prevY, coords[2], coords[3])); + } + } + return result; +} + +function transformPoints(points, transform) { + return transformCoordsArray(points, transform); +} + +function transformRings(rings, transform) { + const result = []; + + for (let i = 0; i < rings.length; i++) { + const ring = transformCoordsArray(rings[i], transform); + + result.push(ring); + } + + return result; +} + +function transformPaths(paths, transform) { + const result = []; + + for (let i = 0; i < paths.length; i++) { + const path = transformCoordsArray(paths[i], transform); + + result.push(path); + } + + return result; +} + +module.exports = { + transformToPbfGeometry, +}; diff --git a/packages/featureserver/src/response-handlers/helpers/send-pbf/transform-to-pbf-geometry.spec.js b/packages/featureserver/src/response-handlers/helpers/send-pbf/transform-to-pbf-geometry.spec.js new file mode 100644 index 000000000..ad38d8670 --- /dev/null +++ b/packages/featureserver/src/response-handlers/helpers/send-pbf/transform-to-pbf-geometry.spec.js @@ -0,0 +1,244 @@ +const should = require('should'); // eslint-disable-line +const { transformToPbfGeometry } = require('./transform-to-pbf-geometry'); + +const defaultTransform = { + scale: { + xScale: 0.0001, + yScale: 0.0001 + },//[0.0001, 0.0001], + translate: { + xTranslate: -20037700, + yTranslate: -30241100 + } // [-20037700, -30241100], +}; + +describe('transformToPbfGeometry', () => { + describe('null geometry', () => { + it('should return null', () => { + const fixture = null; + const result = transformToPbfGeometry(fixture, defaultTransform); + should.equal(result, null); + }); + }); + + describe('unknown geometry', () => { + it('should throw', () => { + const fixture = {}; + try { + const result = transformToPbfGeometry(fixture, defaultTransform); + should.equal(result, null); + } catch (error) { + error.message.should.equal('Unknown geometry type'); + } + }); + }); + + describe('point geometry', () => { + it('should quantize and format for pbf buffer', () => { + const fixture = { + x: -8571657.3541762847, + y: 4704411.394312229, + }; + const result = transformToPbfGeometry(fixture, defaultTransform); + result.should.deepEqual({ + coords: ['114660426458', '-349455113943'], + }); + }); + + it('should quantize and format for pbf buffer with Z and M', () => { + const fixture = { + x: -8571657.3541762847, + y: 4704411.394312229, + z: 100, + m: 25.3 + }; + const result = transformToPbfGeometry(fixture, defaultTransform); + result.should.deepEqual({ + coords: ['114660426458', '-349455113943', '100', '25.3'], + }); + }); + + }); + + describe('line geometry', () => { + it('should quantize and format (minimizing required coords) for pbf buffer', () => { + const fixture = { + paths: [ + [ + [-13580977.8767794, 5465442.18332275], + [-13580977.8767794, 5465442.18332275], + [-13247019.4043996, 5465442.18332275], + [-13024380.422813, 5311971.84694547], + ], + ], + }; + const result = transformToPbfGeometry(fixture, defaultTransform); + result.should.deepEqual({ + lengths: [3], + coords: [ + '64567221232', + '-357065421833', + '3339584724', + '0', + '2226389816', + '1534703364', + ], + }); + }); + + it('should quantize and format for pbf buffer', () => { + const fixture = { + paths: [ + [ + [-13463923.910264086, 5922952.447761737], + [-13522627.547987102, 6029352.791134704], + [-13476153.834789714, 6047697.677923147], + ], + ], + }; + const result = transformToPbfGeometry(fixture, defaultTransform); + result.should.deepEqual({ + lengths: [3], + coords: [ + '65737760897', + '-361640524478', + '-587036377', + '-1064003433', + '464737132', + '-183448868', + ], + }); + }); + }); + + describe('polygon geometry', () => { + it('should quantize and format (minimizing required coords) for pbf buffer', () => { + const fixture = { + rings: [ + [ + [-13469658.3859861, 6106854.83488507], + [-13469658.3859861, 5942074.07243111], + [-13580977.8767794, 5942074.07243111], + [-13580977.8767794, 6106854.83488507], + [-13469658.3859861, 6106854.83488507], + ], + ], + }; + const result = transformToPbfGeometry(fixture, defaultTransform); + result.should.deepEqual({ + lengths: [5], + coords: [ + '65680416140', + '-363479548349', + '0', + '1647807625', + '-1113194908', + '0', + '0', + '-1647807625', + '1113194908', + '0', + ], + }); + }); + }); + + describe('multipoint geometry', () => { + it('should quantize and format for pbf buffer', () => { + const fixture = { + points: [ + [-13358338.8951928, 5621521.48619207], + [-13247019.4043996, 5780349.22025635], + ], + }; + const result = transformToPbfGeometry(fixture, defaultTransform); + result.should.deepEqual({ + lengths: [2], + coords: ['66793611048', '-358626214862', '1113194908', '-1588277341'], + }); + }); + }); + + describe('multiline geometry', () => { + it('should quantize and format for pbf buffer', () => { + const fixture = { + paths: [ + [ + [-13803616.8583659, 5621521.48619207], + [-13358338.8951928, 5621521.48619207], + [-13358338.8951928, 5942074.07243111], + ], + [ + [-13803616.8583659, 5160979.44404978], + [-13358338.8951928, 5160979.44404978], + ], + ], + }; + const result = transformToPbfGeometry(fixture, defaultTransform); + result.should.deepEqual({ + lengths: [3, 2], + coords: [ + '62340831416', + '-358626214862', + '4452779632', + '0', + '0', + '-3205525862', + '62340831416', + '-354020794440', + '4452779632', + '0', + ], + }); + }); + }); + + describe('multipolygon geometry', () => { + it('should quantize and format for pbf buffer', () => { + const fixture = { + rings: [ + [ + [-11243268.5701206, 4439106.78725058], + [-11243268.5701206, 4579425.8128701], + [-11020629.5885341, 4579425.8128701], + [-11020629.5885341, 4439106.78725058], + [-11243268.5701206, 4439106.78725058], + ], + [ + [-10797990.6069475, 4163881.14406429], + [-10797990.6069475, 4300621.37204427], + [-10575351.625361, 4300621.37204427], + [-10575351.625361, 4163881.14406429], + [-10797990.6069475, 4163881.14406429], + ], + ], + }; + const result = transformToPbfGeometry(fixture, defaultTransform); + result.should.deepEqual({ + lengths: [5, 5], + coords: [ + '87944314299', + '-346802067873', + '0', + '-1403190256', + '2226389816', + '0', + '0', + '1403190256', + '-2226389816', + '0', + '92397093931', + '-344049811441', + '0', + '-1367402279', + '2226389815', + '0', + '0', + '1367402279', + '-2226389815', + '0', + ], + }); + }); + }); +}); diff --git a/packages/featureserver/src/response-handlers/helpers/send-pretty-json.js b/packages/featureserver/src/response-handlers/helpers/send-pretty-json.js new file mode 100644 index 000000000..ee4c1e0ec --- /dev/null +++ b/packages/featureserver/src/response-handlers/helpers/send-pretty-json.js @@ -0,0 +1,10 @@ + +function sendPrettyJson(res, payload) { + res.set('Content-Type', 'application/json; charset=utf-8'); + res.status(200); + return res.send(JSON.stringify(payload, null, 2)); +} + +module.exports = { + sendPrettyJson +}; diff --git a/packages/featureserver/src/response-handlers/helpers/send-pretty-json.spec.js b/packages/featureserver/src/response-handlers/helpers/send-pretty-json.spec.js new file mode 100644 index 000000000..5ddb67d57 --- /dev/null +++ b/packages/featureserver/src/response-handlers/helpers/send-pretty-json.spec.js @@ -0,0 +1,25 @@ +const should = require('should'); // eslint-disable-line +const sinon = require('sinon'); +const { sendPrettyJson } = require('.'); + +const res = { + set: sinon.spy(() => { + return res; + }), + status: sinon.spy(() => { + return res; + }), + send: sinon.spy(), +}; + +describe('sendPrettyJson', () => { + it('should respond with callback string', () => { + sendPrettyJson(res, { hello: 'world' }); + res.set.firstCall.args.should.deepEqual([ + 'Content-Type', + 'application/json; charset=utf-8', + ]); + res.send.firstCall.args[0].should.deepEqual(JSON.stringify({ hello: 'world' }, null, 2)); + res.status.firstCall.args[0].should.be.exactly(200); + }); +}); diff --git a/packages/featureserver/src/response-handlers/index.js b/packages/featureserver/src/response-handlers/index.js new file mode 100644 index 000000000..719c7b057 --- /dev/null +++ b/packages/featureserver/src/response-handlers/index.js @@ -0,0 +1,4 @@ +module.exports = { + queryResponseHandler: require('./query-response-handler'), + generalResponseHandler: require('./general-response-handler') +}; diff --git a/packages/featureserver/src/response-handlers/query-response-handler.js b/packages/featureserver/src/response-handlers/query-response-handler.js new file mode 100644 index 000000000..59eadcee5 --- /dev/null +++ b/packages/featureserver/src/response-handlers/query-response-handler.js @@ -0,0 +1,24 @@ +const { + sendCallbackResponse, + sendPrettyJson, + sendPbf +} = require('./helpers'); + +module.exports = function queryResponseHandler (res, payload, requestParameters) { + const {f, callback } = requestParameters; + + if (typeof callback === 'string') { + return sendCallbackResponse(res, payload, callback); + } + + if (f === 'pjson') { + return sendPrettyJson(res, payload); + } + + if (f === 'pbf') { + return sendPbf(res, payload, requestParameters); + } + + // payload here might be Esri JSON or GeoJSON + return res.status(200).json(payload); +}; diff --git a/packages/featureserver/src/response-handlers/query-response-handler.spec.js b/packages/featureserver/src/response-handlers/query-response-handler.spec.js new file mode 100644 index 000000000..9b1b1cd66 --- /dev/null +++ b/packages/featureserver/src/response-handlers/query-response-handler.spec.js @@ -0,0 +1,103 @@ +const should = require('should'); // eslint-disable-line +const sinon = require('sinon'); +const proxyquire = require('proxyquire'); + +const sendPrettyJsonSpy = sinon.spy(() => { + return { pretty: 'json' }; +}); + +const sendCallbackResponseSpy = sinon.spy(() => { + return 'callback-string'; +}); + +const sendPbfSpy = sinon.spy(() => { + return 'pbf'; +}); +const responseHandler = proxyquire('./query-response-handler', { + './helpers': { + sendPrettyJson: sendPrettyJsonSpy, + sendCallbackResponse: sendCallbackResponseSpy, + sendPbf: sendPbfSpy + }, +}); + +describe('query response handler', () => { + const res = { + json: () => { + return res; + }, + send: () => { + return res; + }, + set: () => { + return res; + }, + status: () => { + return res; + }, + }; + + beforeEach(() => { + sinon.spy(res); + }); + + afterEach(() => { + sinon.restore(); + }); + + it('return 200 and json', () => { + responseHandler(res, { test: true }, {}); + res.send.notCalled.should.equal(true); + res.status.firstCall.args[0].should.be.exactly(200); + res.json.firstCall.args[0].should.deepEqual({ test: true }); + }); + + it('send as callback-string', () => { + responseHandler( + res, + { test: true }, + { + callback: 'test-callback', + }, + ); + + sendCallbackResponseSpy.firstCall.args.should.deepEqual([ + res, + { test: true }, + 'test-callback', + ]); + }); + + it('send as pretty json', () => { + responseHandler( + res, + { test: true }, + { + f: 'pjson', + }, + ); + + sendPrettyJsonSpy.firstCall.args.should.deepEqual([ + res, + { test: true } + ]); + }); + + it('send as pbf', () => { + responseHandler( + res, + { test: true }, + { + f: 'pbf', + }, + ); + + sendPbfSpy.firstCall.args.should.deepEqual([ + res, + { test: true }, + { + f: 'pbf', + }, + ]); + }); +}); diff --git a/packages/featureserver/src/route.js b/packages/featureserver/src/route.js index 61e58a678..a739406a7 100644 --- a/packages/featureserver/src/route.js +++ b/packages/featureserver/src/route.js @@ -7,7 +7,7 @@ const generateRenderer = require('./generate-renderer'); const restInfo = require('./rest-info-route-handler'); const serverInfo = require('./server-info-route-handler'); const layersInfo = require('./layers-metadata'); -const responseHandler = require('./response-handler'); +const { generalResponseHandler, queryResponseHandler } = require('./response-handlers'); const { validateInputs, normalizeRequestParameters } = require('./helpers'); module.exports = function route(req, res, geojson = {}) { @@ -26,38 +26,53 @@ module.exports = function route(req, res, geojson = {}) { _.get(geojson, 'metadata.maxRecordCount'), ); + // TODO move to each handler, as params and data will vary a lot validateInputs(params, geojson); req = { ...req, query: params }; geojson.metadata = geojson.metadata || { maxRecordCount: 2000 }; - - let result; - + + if (isRestInfoRequest(route)) { + const result = restInfo(geojson, req); + return generalResponseHandler(res, result, req.query); + } + + if (isServerMetadataRequest(route)) { + const result = serverInfo(geojson, req); + return generalResponseHandler(res, result, req.query); + } + + if (isLayersMetadataRequest(route) || isRelationshipsMetadataRequest(route)) { + const result = layersInfo(geojson, params); + return generalResponseHandler(res, result, req.query); + } + + if (isLayerMetadataRequest(method, route)) { + const result = layerInfo(geojson, req); + return generalResponseHandler(res, result, req.query); + } + if (method) { - result = handleMethodRequest({ method, geojson, req }); - } else if (isRestInfoRequest(route)) { - result = restInfo(geojson, req); - } else if (isServerMetadataRequest(route)) { - result = serverInfo(geojson, req); - } else if (isLayersMetadataRequest(route) || isRelationshipsMetadataRequest(route)) { - result = layersInfo(geojson, params); - } else if (isLayerMetadataRequest(route)) { - result = layerInfo(geojson, req); - } else { - const error = new Error('Not Found'); - error.code = 404; - throw error; + const operationResult = handleMethodRequest({ method, geojson, req }); + + if (method === 'query') { + return queryResponseHandler(res, operationResult, req.query); + } + + return generalResponseHandler(res, operationResult, req.query); } - return responseHandler(req, res, 200, result); + const error = new Error('Not Found'); + error.code = 404; + throw error; } catch (error) { logManager.logger.debug(error); const { code = 500 , message, details = [message] } = error; // Geoservice spec wraps all errors in a 200 response (!) - return responseHandler(req, res, 200, { + return generalResponseHandler(res, { error: { code, message, details } - }); + }, req.query ); } }; @@ -74,10 +89,6 @@ function handleMethodRequest({ method, geojson, req }) { return generateRenderer(geojson, req.query); } - if (method === 'info') { - return layerInfo(geojson, req); - } - const error = new Error('Method not supported'); error.code = 400; throw error; @@ -104,8 +115,9 @@ function isRelationshipsMetadataRequest(url) { return /\/FeatureServer\/relationships$/i.test(url); } -function isLayerMetadataRequest(url) { +function isLayerMetadataRequest(method, url) { return ( + method === 'info' || /\/FeatureServer\/\d+$/i.test(url) || /\/FeatureServer\/\d+\/info$/i.test(url) || /\/FeatureServer\/\d+\/$/.test(url) diff --git a/packages/featureserver/src/route.spec.js b/packages/featureserver/src/route.spec.js index 61e5ef1bd..acc3e589e 100644 --- a/packages/featureserver/src/route.spec.js +++ b/packages/featureserver/src/route.spec.js @@ -15,7 +15,7 @@ describe('Route module unit tests', () => { const route = proxyquire('./route', { './query': querySpy, - './response-handler': responseHandlerSpy, + './response-handlers': { generalResponseHandler: responseHandlerSpy, queryResponseHandler: responseHandlerSpy }, }); it('should use query handler and return 200', () => { @@ -39,14 +39,9 @@ describe('Route module unit tests', () => { ]); responseHandlerSpy.calledOnce.should.equal(true); responseHandlerSpy.firstCall.args.should.deepEqual([ - { - params: { method: 'query' }, - query: { resultRecordCount: 2000 }, - url: '/FeatureServer/0/query', - }, {}, - 200, { features: [] }, + { resultRecordCount: 2000 } ]); }); @@ -57,7 +52,7 @@ describe('Route module unit tests', () => { const route = proxyquire('./route', { './query': querySpy, - './response-handler': responseHandlerSpy, + './response-handlers': { generalResponseHandler: responseHandlerSpy }, }); route( @@ -72,13 +67,7 @@ describe('Route module unit tests', () => { querySpy.calledOnce.should.equal(true); responseHandlerSpy.calledOnce.should.equal(true); responseHandlerSpy.firstCall.args.should.deepEqual([ - { - params: { method: 'query' }, - query: { resultRecordCount: 2000 }, - url: '/FeatureServer/0/query', - }, {}, - 200, { error: { code: 500, @@ -86,6 +75,7 @@ describe('Route module unit tests', () => { details: ['Fool bar'], }, }, + { resultRecordCount: 2000 } ]); }); afterEach(() => { @@ -103,7 +93,7 @@ describe('Route module unit tests', () => { const route = proxyquire('./route', { './rest-info-route-handler': restInfoSpy, - './response-handler': responseHandlerSpy, + './response-handlers': { generalResponseHandler: responseHandlerSpy }, }); it('should use restInfo handler and return 200', () => { @@ -130,14 +120,9 @@ describe('Route module unit tests', () => { responseHandlerSpy.calledOnce.should.equal(true); responseHandlerSpy.firstCall.args.should.deepEqual([ - { - params: {}, - query: { resultRecordCount: 2000 }, - url: '/rest/info', - }, {}, - 200, { restInfo: true }, + { resultRecordCount: 2000 } ]); }); @@ -147,7 +132,7 @@ describe('Route module unit tests', () => { }); const route = proxyquire('./route', { './rest-info-route-handler': restInfoSpy, - './response-handler': responseHandlerSpy, + './response-handlers': { generalResponseHandler: responseHandlerSpy }, }); route( @@ -173,13 +158,7 @@ describe('Route module unit tests', () => { responseHandlerSpy.calledOnce.should.equal(true); responseHandlerSpy.firstCall.args.should.deepEqual([ - { - params: {}, - query: { resultRecordCount: 2000 }, - url: '/rest/info', - }, {}, - 200, { error: { code: 500, @@ -187,6 +166,7 @@ describe('Route module unit tests', () => { details: ['Fool bar'], }, }, + { resultRecordCount: 2000 } ]); }); @@ -205,7 +185,7 @@ describe('Route module unit tests', () => { const route = proxyquire('./route', { './server-info-route-handler': serverInfoSpy, - './response-handler': responseHandlerSpy, + './response-handlers': { generalResponseHandler: responseHandlerSpy }, }); it('should use serverInfo handler and return 200', () => { @@ -232,14 +212,9 @@ describe('Route module unit tests', () => { responseHandlerSpy.calledOnce.should.equal(true); responseHandlerSpy.firstCall.args.should.deepEqual([ - { - params: {}, - query: { resultRecordCount: 2000 }, - url: '/rest/services/test/FeatureServer', - }, {}, - 200, { serverInfo: true }, + { resultRecordCount: 2000 } ]); }); @@ -249,7 +224,7 @@ describe('Route module unit tests', () => { }); const route = proxyquire('./route', { './server-info-route-handler': serverInfoSpy, - './response-handler': responseHandlerSpy, + './response-handlers': { generalResponseHandler: responseHandlerSpy }, }); route( @@ -275,13 +250,7 @@ describe('Route module unit tests', () => { responseHandlerSpy.calledOnce.should.equal(true); responseHandlerSpy.firstCall.args.should.deepEqual([ - { - params: {}, - query: { resultRecordCount: 2000 }, - url: '/rest/services/test/FeatureServer', - }, {}, - 200, { error: { code: 500, @@ -289,6 +258,7 @@ describe('Route module unit tests', () => { details: ['Fool bar'], }, }, + { resultRecordCount: 2000 } ]); }); @@ -307,7 +277,7 @@ describe('Route module unit tests', () => { const route = proxyquire('./route', { './layers-metadata': layersInfoSpy, - './response-handler': responseHandlerSpy, + './response-handlers': { generalResponseHandler: responseHandlerSpy }, }); it('should use layersInfo handler and return 200', () => { @@ -332,14 +302,9 @@ describe('Route module unit tests', () => { responseHandlerSpy.calledOnce.should.equal(true); responseHandlerSpy.firstCall.args.should.deepEqual([ - { - params: {}, - query: { resultRecordCount: 2000 }, - url: '/rest/services/test/FeatureServer/layers', - }, {}, - 200, { layersInfo: true }, + { resultRecordCount: 2000 } ]); }); @@ -349,7 +314,7 @@ describe('Route module unit tests', () => { }); const route = proxyquire('./route', { './layers-metadata': layersInfoSpy, - './response-handler': responseHandlerSpy, + './response-handlers': { generalResponseHandler: responseHandlerSpy }, }); route( @@ -373,13 +338,7 @@ describe('Route module unit tests', () => { responseHandlerSpy.calledOnce.should.equal(true); responseHandlerSpy.firstCall.args.should.deepEqual([ - { - params: {}, - query: { resultRecordCount: 2000 }, - url: '/rest/services/test/FeatureServer/layers', - }, {}, - 200, { error: { code: 500, @@ -387,6 +346,7 @@ describe('Route module unit tests', () => { details: ['Fool bar'], }, }, + { resultRecordCount: 2000 } ]); }); @@ -405,7 +365,7 @@ describe('Route module unit tests', () => { const route = proxyquire('./route', { './layer-metadata': layerInfoSpy, - './response-handler': responseHandlerSpy, + './response-handlers': { generalResponseHandler: responseHandlerSpy }, }); it('should use layerInfo handler and return 200', () => { @@ -432,14 +392,9 @@ describe('Route module unit tests', () => { responseHandlerSpy.calledOnce.should.equal(true); responseHandlerSpy.firstCall.args.should.deepEqual([ - { - params: {}, - query: { resultRecordCount: 2000 }, - url: '/rest/services/test/FeatureServer/0', - }, {}, - 200, 'layer-metadata', + { resultRecordCount: 2000 } ]); }); @@ -449,7 +404,7 @@ describe('Route module unit tests', () => { }); const route = proxyquire('./route', { './layer-metadata': layerInfoSpy, - './response-handler': responseHandlerSpy, + './response-handlers': { generalResponseHandler: responseHandlerSpy }, }); route( @@ -475,13 +430,7 @@ describe('Route module unit tests', () => { responseHandlerSpy.calledOnce.should.equal(true); responseHandlerSpy.firstCall.args.should.deepEqual([ - { - params: {}, - query: { resultRecordCount: 2000 }, - url: '/rest/services/test/FeatureServer/0', - }, {}, - 200, { error: { code: 500, @@ -489,6 +438,55 @@ describe('Route module unit tests', () => { details: ['Fool bar'], }, }, + { resultRecordCount: 2000 } + ]); + }); + + afterEach(() => { + layerInfoSpy.resetHistory(); + responseHandlerSpy.resetHistory(); + }); + }); + + describe('/FeatureServer/0/info route', () => { + const layerInfoSpy = sinon.spy(function () { + return 'layer-metadata'; + }); + + const responseHandlerSpy = sinon.spy(); + + const route = proxyquire('./route', { + './layer-metadata': layerInfoSpy, + './response-handlers': { generalResponseHandler: responseHandlerSpy }, + }); + + it('should use layerInfo handler and return 200', () => { + route( + { + params: {}, + query: {}, + url: '/rest/services/test/FeatureServer/0/info', + }, + {}, + {}, + ); + layerInfoSpy.calledOnce.should.equal(true); + layerInfoSpy.firstCall.args.should.deepEqual([ + { + metadata: { maxRecordCount: 2000 }, + }, + { + params: {}, + query: { resultRecordCount: 2000 }, + url: '/rest/services/test/FeatureServer/0/info', + }, + ]); + + responseHandlerSpy.calledOnce.should.equal(true); + responseHandlerSpy.firstCall.args.should.deepEqual([ + {}, + 'layer-metadata', + { resultRecordCount: 2000 } ]); }); @@ -502,7 +500,7 @@ describe('Route module unit tests', () => { it('should handle unknown route', () => { const responseHandlerSpy = sinon.spy(); const route = proxyquire('./route', { - './response-handler': responseHandlerSpy, + './response-handlers': { generalResponseHandler: responseHandlerSpy }, }); route( @@ -516,13 +514,7 @@ describe('Route module unit tests', () => { ); responseHandlerSpy.calledOnce.should.equal(true); responseHandlerSpy.firstCall.args.should.deepEqual([ - { - params: {}, - query: { resultRecordCount: 2000 }, - url: '/hello/world', - }, {}, - 200, { error: { code: 404, @@ -530,6 +522,7 @@ describe('Route module unit tests', () => { details: ['Not Found'], }, }, + { resultRecordCount: 2000 } ]); }); }); diff --git a/packages/featureserver/src/server-info-route-handler.js b/packages/featureserver/src/server-info-route-handler.js index df336544f..dbe2bacb9 100644 --- a/packages/featureserver/src/server-info-route-handler.js +++ b/packages/featureserver/src/server-info-route-handler.js @@ -25,7 +25,6 @@ function serverMetadataResponse(data, req = {}) { ...appConfig, ...providerMetadata, currentVersion: appConfig.currentVersion, - fullVersion: appConfig.fullVersion, }); } @@ -149,6 +148,7 @@ function formatServerItemInfo(json, defaultId) { const retVal = { id: id || defaultId, name: name || defaultName, + type: geometryType ? 'Feature Layer' : 'Table', parentLayerId: -1, defaultVisibility: defaultVisibility !== false, subLayerIds: null, diff --git a/packages/featureserver/src/server-info-route-handler.spec.js b/packages/featureserver/src/server-info-route-handler.spec.js index 1790ea236..849f2d8a6 100644 --- a/packages/featureserver/src/server-info-route-handler.spec.js +++ b/packages/featureserver/src/server-info-route-handler.spec.js @@ -27,7 +27,6 @@ describe('server info', () => { serverMetadataCreateSpy.firstCall.args.should.deepEqual([ { currentVersion: undefined, - fullVersion: undefined, initialExtent: undefined, fullExtent: undefined, layers: [], @@ -46,7 +45,6 @@ describe('server info', () => { serverMetadataCreateSpy.firstCall.args.should.deepEqual([ { currentVersion: 101.1, - fullVersion: undefined, initialExtent: undefined, fullExtent: undefined, layers: [], @@ -65,7 +63,6 @@ describe('server info', () => { serverMetadataCreateSpy.firstCall.args.should.deepEqual([ { currentVersion: undefined, - fullVersion: undefined, initialExtent: undefined, fullExtent: undefined, layers: [], @@ -78,6 +75,7 @@ describe('server info', () => { subLayerIds: null, minScale: 0, maxScale: 0, + type: 'Table' }, ], relationships: [], @@ -104,7 +102,6 @@ describe('server info', () => { serverMetadataCreateSpy.firstCall.args.should.deepEqual([ { currentVersion: undefined, - fullVersion: undefined, initialExtent: undefined, fullExtent: undefined, layers: [], @@ -117,6 +114,7 @@ describe('server info', () => { subLayerIds: null, minScale: 0, maxScale: 0, + type: 'Table' }, ], relationships: [], @@ -159,7 +157,6 @@ describe('server info', () => { serverMetadataCreateSpy.firstCall.args.should.deepEqual([ { currentVersion: undefined, - fullVersion: undefined, crs: { type: 'name', properties: { name: 'urn:ogc:def:crs:OGC:1.3:CRS84' }, @@ -188,6 +185,7 @@ describe('server info', () => { minScale: 0, maxScale: 0, geometryType: 'esriGeometryPoint', + type: 'Feature Layer' }, ], tables: [], @@ -282,7 +280,6 @@ describe('server info', () => { serverMetadataCreateSpy.firstCall.args.should.deepEqual([ { currentVersion: undefined, - fullVersion: undefined, crs: { type: 'name', properties: { name: 'urn:ogc:def:crs:OGC:1.3:CRS84' }, @@ -312,6 +309,7 @@ describe('server info', () => { minScale: 0, maxScale: 0, geometryType: 'esriGeometryPoint', + type: 'Feature Layer' }, { id: 1, @@ -322,6 +320,7 @@ describe('server info', () => { minScale: 0, maxScale: 0, geometryType: 'esriGeometryPoint', + type: 'Feature Layer' }, ], tables: [ @@ -333,6 +332,7 @@ describe('server info', () => { subLayerIds: null, minScale: 0, maxScale: 0, + type: 'Table' }, ], fullExtent: { @@ -438,7 +438,6 @@ describe('server info', () => { serverMetadataCreateSpy.firstCall.args.should.deepEqual([ { currentVersion: undefined, - fullVersion: undefined, crs: { type: 'name', properties: { name: 'urn:ogc:def:crs:OGC:1.3:CRS84' }, @@ -468,6 +467,7 @@ describe('server info', () => { minScale: 0, maxScale: 0, geometryType: 'esriGeometryPoint', + type: 'Feature Layer' }, { id: 1, @@ -478,6 +478,7 @@ describe('server info', () => { minScale: 0, maxScale: 0, geometryType: 'esriGeometryPoint', + type: 'Feature Layer' }, ], tables: [ @@ -489,6 +490,7 @@ describe('server info', () => { subLayerIds: null, minScale: 0, maxScale: 0, + type: 'Table' }, ], fullExtent: { @@ -537,7 +539,6 @@ describe('server info', () => { serverMetadataCreateSpy.firstCall.args.should.deepEqual([ { currentVersion: undefined, - fullVersion: undefined, crs: { type: 'name', properties: { name: 'urn:ogc:def:crs:OGC:1.3:CRS84' }, @@ -554,6 +555,7 @@ describe('server info', () => { minScale: 0, maxScale: 0, geometryType: 'esriGeometryPoint', + type: 'Feature Layer' }, ], tables: [], diff --git a/packages/featureserver/test/integration/info.spec.js b/packages/featureserver/test/integration/info.spec.js index 9470660dd..2bba0b8be 100644 --- a/packages/featureserver/test/integration/info.spec.js +++ b/packages/featureserver/test/integration/info.spec.js @@ -11,52 +11,91 @@ describe('Info operations', () => { it('should conform to the prescribed schema', () => { const req = { app: { - locals: {} - } + locals: {}, + }, }; const supplementalRestInfo = { authInfo: { isTokenBasedSecurity: true, - tokenServicesUrl: 'http://localhost/provider/generateToken' - } + tokenServicesUrl: 'http://localhost/provider/generateToken', + }, }; const restInfo = FeatureServer.restInfo(supplementalRestInfo, req); restInfo.should.have.property('currentVersion', 11.1); restInfo.should.have.property('authInfo'); restInfo.authInfo.should.have.property('isTokenBasedSecurity', true); - restInfo.authInfo.should.have.property('tokenServicesUrl').be.type('string'); + restInfo.authInfo.should.have + .property('tokenServicesUrl') + .be.type('string'); }); }); describe('server info', () => { it('should conform to the prescribed schema', () => { - const server = FeatureServer.serverInfo(data); - const serverSchemaOverride = serverTemplateSchema.append({ - initialExtent: Joi.object().keys({ - xmin: Joi.number().valid(-108.9395), - ymin: Joi.number().valid(37.084968), - xmax: Joi.number().valid(-102), - ymax: Joi.number().valid(40.8877), - spatialReference: Joi.object().keys({ - wkid: Joi.number().valid(4326), - latestWkid: Joi.number().valid(4326) - }) - }), - fullExtent: Joi.object().keys({ - xmin: Joi.number().valid(-108.9395), - ymin: Joi.number().valid(37.084968), - xmax: Joi.number().valid(-102), - ymax: Joi.number().valid(40.8877), - spatialReference: Joi.object().keys({ - wkid: Joi.number().valid(4326), - latestWkid: Joi.number().valid(4326) - }) - }) + const result = FeatureServer.serverInfo(data); + result.should.deepEqual({ + currentVersion: 11.1, + serviceDescription: 'MyTestDesc', + hasVersionedData: false, + supportsDisconnectedEditing: false, + hasStaticData: false, + hasSharedDomains: false, + maxRecordCount: 2000, + supportedQueryFormats: 'JSON', + supportsVCSProjection: false, + supportedExportFormats: '', + capabilities: 'Query', + description: 'MyTestDesc', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + spatialReference: { wkid: 4326, latestWkid: 4326 }, + fullExtent: { + spatialReference: { wkid: 4326, latestWkid: 4326 }, + xmin: -108.9395, + xmax: -102, + ymin: 37.084968, + ymax: 40.8877, + }, + initialExtent: { + spatialReference: { wkid: 4326, latestWkid: 4326 }, + xmin: -108.9395, + xmax: -102, + ymin: 37.084968, + ymax: 40.8877, + }, + allowGeometryUpdates: false, + units: 'esriDecimalDegrees', + supportsAppend: false, + supportsSharedDomains: false, + supportsWebHooks: false, + supportsTemporalLayers: false, + layerOverridesEnabled: false, + syncEnabled: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturnDeleteResults: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsQueryContingentValues: false, + supportedContingentValuesFormats: '', + supportsContingentValuesJson: null, + tables: [], + layers: [ + { + id: 0, + name: 'Snow', + type: 'Feature Layer', + parentLayerId: -1, + defaultVisibility: true, + subLayerIds: null, + minScale: 0, + maxScale: 0, + geometryType: 'esriGeometryPoint', + }, + ], + relationships: [], + supportsRelationshipsResource: false, }); - - // Test response body schema - serverSchemaOverride.validate(server, { presence: 'required' }).should.not.have.property('error'); }); it('should work with geojson passed in', () => { @@ -71,8 +110,11 @@ describe('Info operations', () => { hasStaticData: true, maxRecordCount: 100, description: 'test', - extent: [[11, 12], [13, 14]], - layers: [_.cloneDeep(data)] + extent: [ + [11, 12], + [13, 14], + ], + layers: [_.cloneDeep(data)], }; const server = FeatureServer.serverInfo(input); server.hasStaticData.should.equal(true); @@ -85,7 +127,7 @@ describe('Info operations', () => { it('should not bomb out on this thing', () => { const input = { - layers: [require('./fixtures/polygon-metadata-error.json')] + layers: [require('./fixtures/polygon-metadata-error.json')], }; const server = FeatureServer.serverInfo(input); server.layers.length.should.equal(1); @@ -97,7 +139,7 @@ describe('Info operations', () => { hasStaticData: true, maxRecordCount: 100, description: 'test', - layers: [_.cloneDeep(data)] + layers: [_.cloneDeep(data)], }; const server = FeatureServer.serverInfo(input); server.hasStaticData.should.equal(true); @@ -116,8 +158,8 @@ describe('Info operations', () => { xmin: 0, ymin: 0, xmax: 0, - ymax: 0 - } + ymax: 0, + }, }; const server = FeatureServer.serverInfo(input); server.hasStaticData.should.equal(true); @@ -129,16 +171,19 @@ describe('Info operations', () => { it('should support a passed in geometry type', () => { const input = { description: 'test', - extent: [[11, 12], [13, 14]], + extent: [ + [11, 12], + [13, 14], + ], layers: [ { type: 'FeatureCollection', metadata: { name: 'test', - geometryType: 'Point' - } - } - ] + geometryType: 'Point', + }, + }, + ], }; const server = FeatureServer.serverInfo(input); server.serviceDescription.should.equal('test'); @@ -157,21 +202,24 @@ describe('Info operations', () => { id: 1, defaultVisibility: false, minScale: 100, - maxScale: 30000 + maxScale: 30000, }; layer1.metadata = { ...layer1.metadata, id: 3, defaultVisibility: true, minScale: 200, - maxScale: 20000 + maxScale: 20000, }; const input = { hasStaticData: true, maxRecordCount: 100, description: 'test', - extent: [[11, 12], [13, 14]], - layers: [layer0, layer1] + extent: [ + [11, 12], + [13, 14], + ], + layers: [layer0, layer1], }; const server = FeatureServer.serverInfo(input); server.layers.length.should.equal(2); @@ -192,14 +240,17 @@ describe('Info operations', () => { metadata: { idField: 'test', geometryType: 'Polygon', - extent: [[11, 12], [13, 14]], + extent: [ + [11, 12], + [13, 14], + ], fields: [ { name: 'test', - type: 'integer' - } - ] - } + type: 'integer', + }, + ], + }, }; const layer = FeatureServer.layerInfo(input, {}); layer.fields[0].type.should.equal('esriFieldTypeOID'); @@ -209,18 +260,25 @@ describe('Info operations', () => { const input = { metadata: { geometryType: 'Polygon', - extent: [[11, 12], [13, 14]], + extent: [ + [11, 12], + [13, 14], + ], fields: [ { name: 'test', type: 'String', - length: 1000 - } - ] - } + length: 1000, + }, + ], + }, }; const layer = FeatureServer.layerInfo(input, {}); - layer.fields.find(f => { return f.name === 'test'; }).length.should.equal(1000); + layer.fields + .find((f) => { + return f.name === 'test'; + }) + .length.should.equal(1000); }); }); diff --git a/packages/featureserver/test/integration/layers.spec.js b/packages/featureserver/test/integration/layers.spec.js index 8d2902562..00b6b339c 100644 --- a/packages/featureserver/test/integration/layers.spec.js +++ b/packages/featureserver/test/integration/layers.spec.js @@ -3,36 +3,262 @@ const data = require('./fixtures/snow.json'); const should = require('should'); should.config.checkProtoEql = false; const _ = require('lodash'); -const Joi = require('joi'); -const { layerTemplateSchema } = require('./schemas'); describe('Layers operations', () => { describe('layers info', () => { it('should conform to the prescribed schema', () => { - const layers = FeatureServer.layersInfo(data); - const layerSchemaOverride = layerTemplateSchema.append({ - extent: Joi.object().keys({ - xmin: Joi.number().valid(-108.9395), - ymin: Joi.number().valid(37.084968), - xmax: Joi.number().valid(-102), - ymax: Joi.number().valid(40.8877), - spatialReference: Joi.object().keys({ - wkid: Joi.number().valid(4326), - latestWkid: Joi.number().valid(4326) - }) - }), - geometryType: Joi.string().allow('point'), - drawingInfo: Joi.object().keys({ - renderer: Joi.object().keys({ - type: Joi.string().allow('simple') - }).unknown() // TODO expand these? - }).unknown() // TODO expand these? - }); - const layersTemplateSchema = Joi.object({ - layers: Joi.array().items(layerSchemaOverride), - tables: Joi.array().empty() + const result = FeatureServer.layersInfo(data); + result.should.deepEqual({ + layers: [ + { + currentVersion: 11.1, + id: 0, + name: 'Snow', + type: 'Feature Layer', + displayField: '', + description: 'MyTestDesc', + copyrightText: + 'Copyright information varies by provider. For more information please contact the source of this data.', + defaultVisibility: true, + isDataVersioned: false, + hasContingentValuesDefinition: false, + supportsAppend: false, + supportsCalculate: false, + supportsASyncCalculate: false, + supportsTruncate: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsExceedsLimitStatistics: false, + supportsAdvancedQueries: true, + supportsValidateSql: false, + supportsLayerOverrides: false, + supportsTilesAndBasicQueriesMode: true, + supportsFieldDescriptionProperty: false, + supportsQuantizationEditMode: false, + supportsApplyEditsWithGlobalIds: false, + supportsReturningQueryGeometry: false, + advancedQueryCapabilities: { + supportsPagination: true, + supportsQueryAttachmentsCountOnly: false, + supportsPaginationOnAggregatedQueries: false, + supportsQueryRelatedPagination: false, + supportsQueryWithDistance: false, + supportsReturningQueryExtent: true, + supportsStatistics: true, + supportsOrderBy: true, + supportsDistinct: true, + supportsQueryWithResultType: false, + supportsSqlExpression: false, + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsPercentileStatistics: false, + supportedSpatialAggregationStatistics: [], + supportsLod: false, + supportsQueryWithLodSR: false, + supportedLodTypes: [], + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsQueryWithDatumTransformation: false, + supportsCurrentUserQueries: false, + supportsHavingClause: false, + supportsOutFieldSQLExpression: false, + supportsMaxRecordCountFactor: false, + supportsTopFeaturesQuery: false, + supportsDisjointSpatialRel: false, + supportsQueryWithCacheHint: false, + supportedOperationsWithCacheHint: [], + supportsQueryAnalytic: false, + supportsDefaultSR: false, + supportsFullTextSearch: false, + advancedQueryAnalyticCapabilities: {}, + advancedEditingCapabilities: {}, + }, + useStandardizedQueries: true, + allowGeometryUpdates: false, + hasAttachments: false, + htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasM: false, + hasZ: false, + objectIdField: 'OBJECTID', + uniqueIdField: { + name: 'OBJECTID', + isSystemMaintained: true, + }, + globalIdField: '', + typeIdField: '', + dateFieldsTimeReference: { + timeZone: 'UTC', + respectsDaylightSaving: false, + }, + preferredTimeReference: null, + templates: [], + supportedQueryFormats: 'JSON,geojson,PBF', + supportedAppendFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportedContingentValuesFormats: '', + hasStaticData: false, + maxRecordCount: 2000, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + tileMaxRecordCount: 2000, + maxRecordCountFactor: 1, + fields: [ + { + name: 'OBJECTID', + type: 'esriFieldTypeOID', + alias: 'OBJECTID', + sqlType: 'sqlTypeInteger', + domain: null, + defaultValue: null, + editable: false, + nullable: false, + }, + { + name: 'station name', + type: 'esriFieldTypeString', + alias: 'station name', + sqlType: 'sqlTypeOther', + domain: null, + defaultValue: null, + length: 128, + editable: false, + nullable: false, + }, + { + name: 'latitude', + type: 'esriFieldTypeDouble', + alias: 'latitude', + sqlType: 'sqlTypeOther', + domain: null, + defaultValue: null, + editable: false, + nullable: false, + }, + { + name: 'daily precip', + type: 'esriFieldTypeDouble', + alias: 'daily precip', + sqlType: 'sqlTypeOther', + domain: null, + defaultValue: null, + editable: false, + nullable: false, + }, + { + name: 'total precip', + type: 'esriFieldTypeDouble', + alias: 'total precip', + sqlType: 'sqlTypeOther', + domain: null, + defaultValue: null, + editable: false, + nullable: false, + }, + { + name: 'daily snow total', + type: 'esriFieldTypeDouble', + alias: 'daily snow total', + sqlType: 'sqlTypeOther', + domain: null, + defaultValue: null, + editable: false, + nullable: false, + }, + { + name: 'longitude', + type: 'esriFieldTypeDouble', + alias: 'longitude', + sqlType: 'sqlTypeOther', + domain: null, + defaultValue: null, + editable: false, + nullable: false, + }, + { + name: 'station', + type: 'esriFieldTypeString', + alias: 'station', + sqlType: 'sqlTypeOther', + domain: null, + defaultValue: null, + length: 128, + editable: false, + nullable: false, + }, + { + name: 'multi-day precip', + type: 'esriFieldTypeString', + alias: 'multi-day precip', + sqlType: 'sqlTypeOther', + domain: null, + defaultValue: null, + length: 128, + editable: false, + nullable: false, + }, + { + name: 'num of reports', + type: 'esriFieldTypeInteger', + alias: 'num of reports', + sqlType: 'sqlTypeOther', + domain: null, + defaultValue: null, + editable: false, + nullable: false, + }, + ], + relationships: [], + capabilities: 'Query', + ownershipBasedAccessControlForFeatures: { + allowOthersToQuery: true, + }, + types: [], + timeInfo: {}, + minScale: 0, + maxScale: 0, + drawingInfo: { + renderer: { + type: 'simple', + symbol: { + color: [45, 172, 128, 161], + outline: { + color: [190, 190, 190, 105], + width: 0.5, + type: 'esriSLS', + style: 'esriSLSSolid', + }, + size: 7.5, + type: 'esriSMS', + style: 'esriSMSCircle', + }, + }, + labelingInfo: null, + }, + extent: { + xmin: -108.9395, + xmax: -102, + ymin: 37.084968, + ymax: 40.8877, + spatialReference: { + wkid: 4326, + latestWkid: 4326, + }, + }, + supportsCoordinatesQuantization: false, + hasLabels: false, + geometryType: 'esriGeometryPoint', + }, + ], + tables: [], }); - layersTemplateSchema.validate(layers, { presence: 'required' }).should.not.have.property('error'); }); it('should work with geojson passed in', () => { @@ -47,7 +273,7 @@ describe('Layers operations', () => { foo: 'bar', displayField: 'myField', copyrightText: 'Custom copyright text', - capabilities: 'list,of,stuff' + capabilities: 'list,of,stuff', }; const layers = FeatureServer.layersInfo(input); layers.layers.length.should.equal(1); diff --git a/test/geoservice-client-requests-no-geom.spec.js b/test/geoservice-client-requests-no-geom.spec.js index 889629237..34f7226d9 100644 --- a/test/geoservice-client-requests-no-geom.spec.js +++ b/test/geoservice-client-requests-no-geom.spec.js @@ -3,7 +3,8 @@ const provider = require('@koopjs/provider-file-geojson'); const request = require('supertest'); const VERSION = 11.1; const FULL_VERSION = '11.1.0'; -const COPYRIGHT_TEXT = 'Copyright information varies by provider. For more information please contact the source of this data.'; +const COPYRIGHT_TEXT = + 'Copyright information varies by provider. For more information please contact the source of this data.'; const mockLogger = { debug: () => {}, @@ -66,6 +67,7 @@ describe('Typical Geoservice Client request sequence: Dataset with no geometry', parentLayerId: -1, defaultVisibility: true, subLayerIds: null, + type: 'Table', minScale: 0, maxScale: 0, }, @@ -98,19 +100,50 @@ describe('Typical Geoservice Client request sequence: Dataset with no geometry', tables: [ { currentVersion: VERSION, - fullVersion: FULL_VERSION, id: 0, name: 'no-geom-w-objectid.geojson', type: 'Table', description: 'GeoJSON from no-geom-w-objectid.geojson', copyrightText: COPYRIGHT_TEXT, - parentLayer: null, - subLayers: null, defaultVisibility: true, hasAttachments: false, htmlPopupType: 'esriServerHTMLPopupTypeNone', + hasContingentValuesDefinition: false, displayField: 'OBJECTID', - typeIdField: null, + typeIdField: '', + preferredTimeReference: null, + maxRecordCountFactor: 1, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + supportedAppendFormats: '', + supportedContingentValuesFormats: '', + supportedExportFormats: '', + supportedQueryFormats: 'JSON,geojson,PBF', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportsASyncCalculate: false, + supportsAdvancedQueries: true, + supportsAppend: false, + supportsApplyEditsWithGlobalIds: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsCalculate: false, + supportsExceedsLimitStatistics: false, + supportsFieldDescriptionProperty: false, + supportsLayerOverrides: false, + supportsQuantizationEditMode: false, + supportsReturningQueryGeometry: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsTilesAndBasicQueriesMode: true, + supportsTruncate: false, + supportsValidateSql: false, + templates: [], + tileMaxRecordCount: 2000, fields: [ { name: 'OBJECTID', @@ -159,34 +192,57 @@ describe('Typical Geoservice Client request sequence: Dataset with no geometry', relationships: [], capabilities: 'Query', maxRecordCount: 2000, - supportsStatistics: true, - supportsAdvancedQueries: true, - supportedQueryFormats: 'JSON', ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true, }, useStandardizedQueries: true, advancedQueryCapabilities: { - useStandardizedQueries: true, - supportsStatistics: true, - supportsOrderBy: true, + advancedEditingCapabilities: {}, + advancedQueryAnalyticCapabilities: {}, + supportedLodTypes: [], + supportedOperationsWithCacheHint: [], + supportedSpatialAggregationStatistics: [], + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsCurrentUserQueries: false, + supportsDefaultSR: false, + supportsDisjointSpatialRel: false, supportsDistinct: true, + supportsFullTextSearch: false, + supportsHavingClause: false, + supportsLod: false, + supportsMaxRecordCountFactor: false, + supportsOrderBy: true, + supportsOutFieldSQLExpression: false, supportsPagination: true, - supportsTrueCurve: false, + supportsPaginationOnAggregatedQueries: false, + supportsPercentileStatistics: false, + supportsQueryAnalytic: false, + supportsQueryAttachmentsCountOnly: false, + supportsQueryRelatedPagination: false, + supportsQueryWithCacheHint: false, + supportsQueryWithDatumTransformation: false, + supportsQueryWithDistance: false, + supportsQueryWithLodSR: false, + supportsQueryWithResultType: false, + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, supportsReturningQueryExtent: true, - supportsQueryWithDistance: true, + supportsSqlExpression: false, + supportsStatistics: true, + supportsTopFeaturesQuery: false, + }, + dateFieldsTimeReference: { + respectsDaylightSaving: false, + timeZone: 'UTC', }, - canModifyLayer: false, - dateFieldsTimeReference: null, isDataVersioned: false, - supportsRollbackOnFailureParameter: true, hasM: false, hasZ: false, - allowGeometryUpdates: true, + allowGeometryUpdates: false, objectIdField: 'OBJECTID', globalIdField: '', types: [], - templates: [], hasStaticData: false, timeInfo: {}, uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, @@ -210,19 +266,50 @@ describe('Typical Geoservice Client request sequence: Dataset with no geometry', const info = response.body; expect(info).toEqual({ currentVersion: VERSION, - fullVersion: FULL_VERSION, id: 0, name: 'no-geom-w-objectid.geojson', type: 'Table', description: 'GeoJSON from no-geom-w-objectid.geojson', copyrightText: COPYRIGHT_TEXT, - parentLayer: null, - subLayers: null, defaultVisibility: true, hasAttachments: false, + preferredTimeReference: null, + hasContingentValuesDefinition: false, + maxRecordCountFactor: 1, htmlPopupType: 'esriServerHTMLPopupTypeNone', displayField: 'OBJECTID', - typeIdField: null, + typeIdField: '', + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + supportedAppendFormats: '', + supportedContingentValuesFormats: '', + supportedExportFormats: '', + supportedQueryFormats: 'JSON,geojson,PBF', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportsASyncCalculate: false, + supportsAdvancedQueries: true, + supportsAppend: false, + supportsApplyEditsWithGlobalIds: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsCalculate: false, + supportsExceedsLimitStatistics: false, + supportsFieldDescriptionProperty: false, + supportsLayerOverrides: false, + supportsQuantizationEditMode: false, + supportsReturningQueryGeometry: false, + supportsRollbackOnFailureParameter: false, + supportsStatistics: true, + supportsTilesAndBasicQueriesMode: true, + supportsTruncate: false, + supportsValidateSql: false, + templates: [], + tileMaxRecordCount: 2000, fields: [ { name: 'OBJECTID', @@ -271,32 +358,55 @@ describe('Typical Geoservice Client request sequence: Dataset with no geometry', relationships: [], capabilities: 'Query', maxRecordCount: 2000, - supportsStatistics: true, - supportsAdvancedQueries: true, - supportedQueryFormats: 'JSON', ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true }, useStandardizedQueries: true, advancedQueryCapabilities: { - useStandardizedQueries: true, supportsStatistics: true, supportsOrderBy: true, supportsDistinct: true, supportsPagination: true, - supportsTrueCurve: false, supportsReturningQueryExtent: true, - supportsQueryWithDistance: true, + supportsQueryWithDistance: false, + advancedEditingCapabilities: {}, + advancedQueryAnalyticCapabilities: {}, + supportedLodTypes: [], + supportedOperationsWithCacheHint: [], + supportedSpatialAggregationStatistics: [], + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsCurrentUserQueries: false, + supportsDefaultSR: false, + supportsDisjointSpatialRel: false, + supportsFullTextSearch: false, + supportsHavingClause: false, + supportsLod: false, + supportsMaxRecordCountFactor: false, + supportsOutFieldSQLExpression: false, + supportsPaginationOnAggregatedQueries: false, + supportsPercentileStatistics: false, + supportsQueryAnalytic: false, + supportsQueryAttachmentsCountOnly: false, + supportsQueryRelatedPagination: false, + supportsQueryWithCacheHint: false, + supportsQueryWithDatumTransformation: false, + supportsQueryWithLodSR: false, + supportsQueryWithResultType: false, + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsSqlExpression: false, + supportsTopFeaturesQuery: false, + }, + dateFieldsTimeReference: { + respectsDaylightSaving: false, + timeZone: 'UTC', }, - canModifyLayer: false, - dateFieldsTimeReference: null, isDataVersioned: false, - supportsRollbackOnFailureParameter: true, hasM: false, hasZ: false, - allowGeometryUpdates: true, + allowGeometryUpdates: false, objectIdField: 'OBJECTID', globalIdField: '', types: [], - templates: [], hasStaticData: false, timeInfo: {}, uniqueIdField: { name: 'OBJECTID', isSystemMaintained: true }, diff --git a/test/helpers/client-response-fixtures.js b/test/helpers/client-response-fixtures.js index 9989ac0d2..3fd0efd71 100644 --- a/test/helpers/client-response-fixtures.js +++ b/test/helpers/client-response-fixtures.js @@ -1,11 +1,10 @@ -const VERSION = 11.1; -const FULL_VERSION = '11.1.0'; -const COPYRIGHT_TEXT = 'Copyright information varies by provider. For more information please contact the source of this data.'; +const VERSION = 11.1; +const COPYRIGHT_TEXT = + 'Copyright information varies by provider. For more information please contact the source of this data.'; function getServerInfo(id) { return { currentVersion: VERSION, - fullVersion: FULL_VERSION, maxRecordCount: 2000, serviceDescription: `GeoJSON from ${id}.geojson`, description: `GeoJSON from ${id}.geojson`, @@ -38,8 +37,10 @@ function getServerInfo(id) { minScale: 0, maxScale: 0, geometryType: 'esriGeometryPoint', + type: 'Feature Layer', }, ], + hasSharedDomains: false, supportedQueryFormats: 'JSON', capabilities: 'Query', syncEnabled: false, @@ -48,6 +49,20 @@ function getServerInfo(id) { supportsRelationshipsResource: false, allowGeometryUpdates: false, relationships: [], + supportedContingentValuesFormats: '', + supportedExportFormats: '', + supportsAppend: false, + supportsApplyEditsWithGlobalIds: false, + supportsContingentValuesJson: null, + supportsLayerOverrides: false, + supportsQueryContingentValues: false, + supportsReturnDeleteResults: false, + supportsSharedDomains: false, + supportsTemporalLayers: false, + supportsTilesAndBasicQueriesMode: true, + supportsVCSProjection: false, + supportsWebHooks: false, + layerOverridesEnabled: false, }; } @@ -56,19 +71,16 @@ function getLayersInfo(filename, idField) { layers: [ { currentVersion: VERSION, - fullVersion: FULL_VERSION, id: 0, name: `${filename}.geojson`, type: 'Feature Layer', description: `GeoJSON from ${filename}.geojson`, copyrightText: COPYRIGHT_TEXT, - parentLayer: null, - subLayers: null, defaultVisibility: true, hasAttachments: false, htmlPopupType: 'esriServerHTMLPopupTypeNone', displayField: idField, - typeIdField: null, + typeIdField: '', fields: [ { name: idField, @@ -119,28 +131,55 @@ function getLayersInfo(filename, idField) { maxRecordCount: 2000, supportsStatistics: true, supportsAdvancedQueries: true, - supportedQueryFormats: 'JSON', + supportedQueryFormats: 'JSON,geojson,PBF', ownershipBasedAccessControlForFeatures: { allowOthersToQuery: true, }, useStandardizedQueries: true, advancedQueryCapabilities: { - useStandardizedQueries: true, supportsStatistics: true, supportsOrderBy: true, supportsDistinct: true, supportsPagination: true, - supportsTrueCurve: false, supportsReturningQueryExtent: true, - supportsQueryWithDistance: true, + advancedEditingCapabilities: {}, + advancedQueryAnalyticCapabilities: {}, + supportedLodTypes: [], + supportedOperationsWithCacheHint: [], + supportedSpatialAggregationStatistics: [], + supportsAdvancedQueryRelated: false, + supportsCountDistinct: false, + supportsCurrentUserQueries: false, + supportsDefaultSR: false, + supportsDisjointSpatialRel: false, + supportsFullTextSearch: false, + supportsHavingClause: false, + supportsLod: false, + supportsMaxRecordCountFactor: false, + supportsOutFieldSQLExpression: false, + supportsPaginationOnAggregatedQueries: false, + supportsPercentileStatistics: false, + supportsQueryAnalytic: false, + supportsQueryAttachmentsCountOnly: false, + supportsQueryRelatedPagination: false, + supportsQueryWithCacheHint: false, + supportsQueryWithDatumTransformation: false, + supportsQueryWithDistance: false, + supportsQueryWithLodSR: false, + supportsQueryWithResultType: false, + supportsReturningGeometryCentroid: false, + supportsReturningGeometryEnvelope: false, + supportsSqlExpression: false, + supportsTopFeaturesQuery: false, + }, + allowGeometryUpdates: false, + dateFieldsTimeReference: { + respectsDaylightSaving: false, + timeZone: 'UTC', }, - canModifyLayer: false, - dateFieldsTimeReference: null, isDataVersioned: false, - supportsRollbackOnFailureParameter: true, hasM: false, hasZ: false, - allowGeometryUpdates: true, objectIdField: idField, globalIdField: '', types: [], @@ -150,7 +189,6 @@ function getLayersInfo(filename, idField) { uniqueIdField: { name: idField, isSystemMaintained: true }, minScale: 0, maxScale: 0, - canScaleSymbols: false, drawingInfo: { renderer: { type: 'simple', @@ -179,6 +217,36 @@ function getLayersInfo(filename, idField) { supportsCoordinatesQuantization: false, hasLabels: false, geometryType: 'esriGeometryPoint', + supportsAppend: false, + supportsApplyEditsWithGlobalIds: false, + supportsAttachmentsByUploadId: false, + supportsAttachmentsResizing: false, + supportsCalculate: false, + supportsExceedsLimitStatistics: false, + supportsFieldDescriptionProperty: false, + supportsLayerOverrides: false, + supportsQuantizationEditMode: false, + supportsReturningQueryGeometry: false, + supportsRollbackOnFailureParameter: false, + supportsTilesAndBasicQueriesMode: true, + supportsTruncate: false, + supportsValidateSql: false, + tileMaxRecordCount: 2000, + preferredTimeReference: null, + standardMaxRecordCount: 2000, + standardMaxRecordCountNoGeometry: 2000, + supportedAppendFormats: '', + supportedContingentValuesFormats: '', + supportedExportFormats: '', + supportedSpatialRelationships: [ + 'esriSpatialRelIntersects', + 'esriSpatialRelContains', + 'esriSpatialRelEnvelopeIntersects', + 'esriSpatialRelWithin', + ], + supportsASyncCalculate: false, + maxRecordCountFactor: 1, + hasContingentValuesDefinition: false, }, ], tables: [], From 5d7cc534b7d8639e7b0422c735f1b3ad551ab6aa Mon Sep 17 00:00:00 2001 From: Rich Gwozdz Date: Wed, 24 Jan 2024 11:55:28 -0800 Subject: [PATCH 2/9] chore: make test adjustments --- .changeset/rare-singers-matter.md | 5 ++ .changeset/weak-spies-rescue.md | 5 ++ .../helpers/feature-layer-metadata.spec.js | 29 +++++---- .../src/helpers/server-metadata.spec.js | 7 +- .../src/helpers/table-layer-metadata.spec.js | 65 ++++++++++--------- .../featureserver/src/metadata-defaults.js | 4 +- .../src/rest-info-route-handler.spec.js | 4 +- .../test/integration/info.spec.js | 7 +- .../test/integration/layers.spec.js | 17 +++-- .../test/integration/schemas/index.js | 8 +-- ...geoservice-client-requests-no-geom.spec.js | 19 +++++- test/geoservice-defaults.spec.js | 2 - test/helpers/client-response-fixtures.js | 2 +- 13 files changed, 100 insertions(+), 74 deletions(-) create mode 100644 .changeset/rare-singers-matter.md create mode 100644 .changeset/weak-spies-rescue.md diff --git a/.changeset/rare-singers-matter.md b/.changeset/rare-singers-matter.md new file mode 100644 index 000000000..954a5f487 --- /dev/null +++ b/.changeset/rare-singers-matter.md @@ -0,0 +1,5 @@ +--- +'@koopjs/featureserver': minor +--- + +- add support for PBF output on query endpoint diff --git a/.changeset/weak-spies-rescue.md b/.changeset/weak-spies-rescue.md new file mode 100644 index 000000000..e70d87767 --- /dev/null +++ b/.changeset/weak-spies-rescue.md @@ -0,0 +1,5 @@ +--- +'@koopjs/koop-core': minor +--- + +- add support for PBF format on GeoService output plugin query enpoint diff --git a/packages/featureserver/src/helpers/feature-layer-metadata.spec.js b/packages/featureserver/src/helpers/feature-layer-metadata.spec.js index 430c45c20..38a8ecc76 100644 --- a/packages/featureserver/src/helpers/feature-layer-metadata.spec.js +++ b/packages/featureserver/src/helpers/feature-layer-metadata.spec.js @@ -2,6 +2,7 @@ const should = require('should'); should.config.checkProtoEql = false; const sinon = require('sinon'); const proxyquire = require('proxyquire'); +const CURRENT_VERSION = 11.2; const calculateBoundsSpy = sinon.spy(function () { return { @@ -49,7 +50,7 @@ describe('FeatureLayerMetadata', () => { const featureLayerMetadata = new FeatureLayerMetadata(); featureLayerMetadata.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, id: 0, name: 'Not Set', type: 'Feature Layer', @@ -178,11 +179,11 @@ describe('FeatureLayerMetadata', () => { ); featureLayerMetadata.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, id: 0, name: 'Not Set', type: 'Feature Layer', - displayField: '', + displayField: 'OBJECTID', description: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', copyrightText: @@ -346,11 +347,11 @@ describe('FeatureLayerMetadata', () => { }, ); featureLayerMetadata.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, id: 0, name: 'Not Set', type: 'Feature Layer', - displayField: '', + displayField: 'OBJECTID', description: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', copyrightText: @@ -506,11 +507,11 @@ describe('FeatureLayerMetadata', () => { ); featureLayerMetadata.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, id: 0, name: 'Not Set', type: 'Feature Layer', - displayField: '', + displayField: 'OBJECTID', description: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', copyrightText: @@ -662,11 +663,11 @@ describe('FeatureLayerMetadata', () => { ); featureLayerMetadata.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, id: 0, name: 'Not Set', type: 'Feature Layer', - displayField: '', + displayField: 'OBJECTID', description: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', copyrightText: @@ -824,11 +825,11 @@ describe('FeatureLayerMetadata', () => { ); featureLayerMetadata.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, id: 0, name: 'Not Set', type: 'Feature Layer', - displayField: '', + displayField: 'OBJECTID', description: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', copyrightText: @@ -965,11 +966,11 @@ describe('FeatureLayerMetadata', () => { ); featureLayerMetadata.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, id: 0, name: 'Not Set', type: 'Feature Layer', - displayField: '', + displayField: 'OBJECTID', description: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', copyrightText: @@ -1132,7 +1133,7 @@ describe('FeatureLayerMetadata', () => { ); featureLayerMetadata.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, id: 99, name: 'Not Set', type: 'Feature Layer', diff --git a/packages/featureserver/src/helpers/server-metadata.spec.js b/packages/featureserver/src/helpers/server-metadata.spec.js index 102f1a0cd..7e7b9187c 100644 --- a/packages/featureserver/src/helpers/server-metadata.spec.js +++ b/packages/featureserver/src/helpers/server-metadata.spec.js @@ -2,6 +2,7 @@ const should = require('should'); should.config.checkProtoEql = false; const proxyquire = require('proxyquire'); const ServerMetadata = require('./server-metadata'); +const CURRENT_VERSION = 11.2; describe('ServerMetadata', () => { it('should use defaults when no overrides', () => { @@ -10,7 +11,7 @@ describe('ServerMetadata', () => { console.log(JSON.stringify(result)); result.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, serviceDescription: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', hasVersionedData: false, @@ -77,7 +78,7 @@ describe('ServerMetadata', () => { }); result.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, serviceDescription: 'goodbye', hasVersionedData: false, supportsDisconnectedEditing: false, @@ -132,7 +133,7 @@ describe('ServerMetadata', () => { }); result.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, serviceDescription: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', hasVersionedData: false, diff --git a/packages/featureserver/src/helpers/table-layer-metadata.spec.js b/packages/featureserver/src/helpers/table-layer-metadata.spec.js index 4bcafeda2..b806dac27 100644 --- a/packages/featureserver/src/helpers/table-layer-metadata.spec.js +++ b/packages/featureserver/src/helpers/table-layer-metadata.spec.js @@ -2,6 +2,7 @@ const should = require('should'); should.config.checkProtoEql = false; const sinon = require('sinon'); const proxyquire = require('proxyquire'); +const CURRENT_VERSION = 11.2; const createLayerMetadataFieldsSpy = sinon.spy(function () { return ['fields']; @@ -24,7 +25,7 @@ describe('TableLayerMetadata', () => { it('calling with new should return default metadata', () => { const tableLayerMetadata = new TableLayerMetadata(); tableLayerMetadata.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, id: 0, name: 'Not Set', type: 'Table', @@ -144,11 +145,11 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides(geojson); tableLayerMetadata.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, id: 0, name: 'Not Set', type: 'Table', - displayField: '', + displayField: 'OBJECTID', description: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', copyrightText: @@ -257,11 +258,11 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides({}, { layerId: '2' }); tableLayerMetadata.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, id: 2, name: 'Not Set', type: 'Table', - displayField: '', + displayField: 'OBJECTID', description: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', copyrightText: @@ -368,11 +369,11 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides({}, { id: 3 }); tableLayerMetadata.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, id: 3, name: 'Not Set', type: 'Table', - displayField: '', + displayField: 'OBJECTID', description: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', copyrightText: @@ -479,7 +480,7 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides({}, { displayField: 'hellow' }); tableLayerMetadata.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, id: 0, name: 'Not Set', type: 'Table', @@ -590,7 +591,7 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides({}, { idField: 'fluffy' }); tableLayerMetadata.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, id: 0, name: 'Not Set', type: 'Table', @@ -701,11 +702,11 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides({}, { hasZ: true }); tableLayerMetadata.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, id: 0, name: 'Not Set', type: 'Table', - displayField: '', + displayField: 'OBJECTID', description: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', copyrightText: @@ -812,11 +813,11 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides({}, { hasStaticData: true }); tableLayerMetadata.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, id: 0, name: 'Not Set', type: 'Table', - displayField: '', + displayField: 'OBJECTID', description: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', copyrightText: @@ -923,11 +924,11 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides({}, { hasStaticData: 'true' }); tableLayerMetadata.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, id: 0, name: 'Not Set', type: 'Table', - displayField: '', + displayField: 'OBJECTID', description: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', copyrightText: @@ -1034,11 +1035,11 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides({}, { capabilities: {} }); tableLayerMetadata.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, id: 0, name: 'Not Set', type: 'Table', - displayField: '', + displayField: 'OBJECTID', description: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', copyrightText: @@ -1148,11 +1149,11 @@ describe('TableLayerMetadata', () => { ); tableLayerMetadata.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, id: 0, name: 'Not Set', type: 'Table', - displayField: '', + displayField: 'OBJECTID', description: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', copyrightText: @@ -1262,11 +1263,11 @@ describe('TableLayerMetadata', () => { ); tableLayerMetadata.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, id: 0, name: 'Not Set', type: 'Table', - displayField: '', + displayField: 'OBJECTID', description: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', copyrightText: @@ -1373,11 +1374,11 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides({}, { hasStaticData: true }); tableLayerMetadata.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, id: 0, name: 'Not Set', type: 'Table', - displayField: '', + displayField: 'OBJECTID', description: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', copyrightText: @@ -1484,11 +1485,11 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides({}, { supportsPagination: 'false' }); tableLayerMetadata.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, id: 0, name: 'Not Set', type: 'Table', - displayField: '', + displayField: 'OBJECTID', description: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', copyrightText: @@ -1595,11 +1596,11 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides({}, { supportsPagination: false }); tableLayerMetadata.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, id: 0, name: 'Not Set', type: 'Table', - displayField: '', + displayField: 'OBJECTID', description: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', copyrightText: @@ -1706,11 +1707,11 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides({}, { hasAttachments: true }); tableLayerMetadata.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, id: 0, name: 'Not Set', type: 'Table', - displayField: '', + displayField: 'OBJECTID', description: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', copyrightText: @@ -1817,11 +1818,11 @@ describe('TableLayerMetadata', () => { tableLayerMetadata.mixinOverrides({}, { hasAttachments: 'true' }); tableLayerMetadata.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, id: 0, name: 'Not Set', type: 'Table', - displayField: '', + displayField: 'OBJECTID', description: 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.', copyrightText: @@ -1945,7 +1946,7 @@ describe('TableLayerMetadata', () => { id: 0, name: 'Hank Williams', type: 'Table', - displayField: '', + displayField: 'OBJECTID', description: 'There is a tear in my beer.', copyrightText: 'copyright-text', defaultVisibility: false, diff --git a/packages/featureserver/src/metadata-defaults.js b/packages/featureserver/src/metadata-defaults.js index 399447f50..6b6fe7cbf 100644 --- a/packages/featureserver/src/metadata-defaults.js +++ b/packages/featureserver/src/metadata-defaults.js @@ -1,7 +1,7 @@ const joi = require('joi'); const _ = require('lodash'); -const CURRENT_VERSION = 11.1; -const FULL_VERSION = '11.1.0'; +const CURRENT_VERSION = 11.2; +const FULL_VERSION = '11.2.0'; const MAX_RECORD_COUNT = 2000; const description = 'This is a feature service exposed with Koop. For more information go to https://github.com/koopjs/koop.'; diff --git a/packages/featureserver/src/rest-info-route-handler.spec.js b/packages/featureserver/src/rest-info-route-handler.spec.js index ddddfc93f..6ad71e719 100644 --- a/packages/featureserver/src/rest-info-route-handler.spec.js +++ b/packages/featureserver/src/rest-info-route-handler.spec.js @@ -1,7 +1,7 @@ const should = require('should'); // eslint-disable-line const restInfo = require('./rest-info-route-handler'); -const CURRENT_VERSION = 11.1; -const FULL_VERSION = '11.1.0'; +const CURRENT_VERSION = 11.2; +const FULL_VERSION = '11.2.0'; describe('rest/info handler', () => { it('should return default info', () => { diff --git a/packages/featureserver/test/integration/info.spec.js b/packages/featureserver/test/integration/info.spec.js index 2bba0b8be..ca748c659 100644 --- a/packages/featureserver/test/integration/info.spec.js +++ b/packages/featureserver/test/integration/info.spec.js @@ -3,8 +3,7 @@ const data = require('./fixtures/snow.json'); const should = require('should'); should.config.checkProtoEql = false; const _ = require('lodash'); -const Joi = require('joi'); -const { serverTemplateSchema } = require('./schemas'); +const CURRENT_VERSION = 11.2; describe('Info operations', () => { describe('rest info', () => { @@ -22,7 +21,7 @@ describe('Info operations', () => { }, }; const restInfo = FeatureServer.restInfo(supplementalRestInfo, req); - restInfo.should.have.property('currentVersion', 11.1); + restInfo.should.have.property('currentVersion', CURRENT_VERSION); restInfo.should.have.property('authInfo'); restInfo.authInfo.should.have.property('isTokenBasedSecurity', true); restInfo.authInfo.should.have @@ -35,7 +34,7 @@ describe('Info operations', () => { it('should conform to the prescribed schema', () => { const result = FeatureServer.serverInfo(data); result.should.deepEqual({ - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, serviceDescription: 'MyTestDesc', hasVersionedData: false, supportsDisconnectedEditing: false, diff --git a/packages/featureserver/test/integration/layers.spec.js b/packages/featureserver/test/integration/layers.spec.js index 00b6b339c..139f8a302 100644 --- a/packages/featureserver/test/integration/layers.spec.js +++ b/packages/featureserver/test/integration/layers.spec.js @@ -3,19 +3,22 @@ const data = require('./fixtures/snow.json'); const should = require('should'); should.config.checkProtoEql = false; const _ = require('lodash'); +const CURRENT_VERSION = 11.2; describe('Layers operations', () => { describe('layers info', () => { it('should conform to the prescribed schema', () => { - const result = FeatureServer.layersInfo(data); + const json = _.cloneDeep(data); + json.metadata.idField = 'id'; + const result = FeatureServer.layersInfo(json); result.should.deepEqual({ layers: [ { - currentVersion: 11.1, + currentVersion: CURRENT_VERSION, id: 0, name: 'Snow', type: 'Feature Layer', - displayField: '', + displayField: 'id', description: 'MyTestDesc', copyrightText: 'Copyright information varies by provider. For more information please contact the source of this data.', @@ -81,9 +84,9 @@ describe('Layers operations', () => { htmlPopupType: 'esriServerHTMLPopupTypeNone', hasM: false, hasZ: false, - objectIdField: 'OBJECTID', + objectIdField: 'id', uniqueIdField: { - name: 'OBJECTID', + name: 'id', isSystemMaintained: true, }, globalIdField: '', @@ -112,9 +115,9 @@ describe('Layers operations', () => { maxRecordCountFactor: 1, fields: [ { - name: 'OBJECTID', + name: 'id', type: 'esriFieldTypeOID', - alias: 'OBJECTID', + alias: 'id', sqlType: 'sqlTypeInteger', domain: null, defaultValue: null, diff --git a/packages/featureserver/test/integration/schemas/index.js b/packages/featureserver/test/integration/schemas/index.js index 717737334..26022f9bf 100644 --- a/packages/featureserver/test/integration/schemas/index.js +++ b/packages/featureserver/test/integration/schemas/index.js @@ -28,8 +28,8 @@ const fieldsTemplateSchema = Joi.object().keys({ }); const layerTemplateSchema = Joi.object().keys({ - currentVersion: Joi.number().valid(11.1), - fullVersion: Joi.string().valid('11.1.0'), + currentVersion: Joi.number().valid(11.2), + fullVersion: Joi.string().valid('11.2.0'), id: Joi.number().integer().valid(0), name: Joi.string().allow(''), type: Joi.string().allow('Feature Layer'), @@ -124,8 +124,8 @@ const oidTemplateSchema = Joi.object().keys({ }); const serverTemplateSchema = Joi.object().keys({ - currentVersion: Joi.number().valid(11.1), - fullVersion: Joi.string().valid('11.1.0'), + currentVersion: Joi.number().valid(11.2), + fullVersion: Joi.string().valid('11.2.0'), serviceDescription: Joi.string().allow(''), hasVersionedData: Joi.boolean().valid(false), supportsDisconnectedEditing: Joi.boolean().valid(false), diff --git a/test/geoservice-client-requests-no-geom.spec.js b/test/geoservice-client-requests-no-geom.spec.js index 34f7226d9..d6e459271 100644 --- a/test/geoservice-client-requests-no-geom.spec.js +++ b/test/geoservice-client-requests-no-geom.spec.js @@ -1,8 +1,7 @@ const Koop = require('@koopjs/koop-core'); const provider = require('@koopjs/provider-file-geojson'); const request = require('supertest'); -const VERSION = 11.1; -const FULL_VERSION = '11.1.0'; +const VERSION = 11.2; const COPYRIGHT_TEXT = 'Copyright information varies by provider. For more information please contact the source of this data.'; @@ -38,12 +37,13 @@ describe('Typical Geoservice Client request sequence: Dataset with no geometry', const serverInfo = response.body; expect(serverInfo).toEqual({ currentVersion: VERSION, - fullVersion: FULL_VERSION, maxRecordCount: 2000, serviceDescription: 'GeoJSON from no-geom-w-objectid.geojson', description: 'GeoJSON from no-geom-w-objectid.geojson', copyrightText: COPYRIGHT_TEXT, spatialReference: { wkid: 4326, latestWkid: 4326 }, + layerOverridesEnabled: false, + hasSharedDomains: false, fullExtent: { xmin: -180, ymin: -90, @@ -81,6 +81,19 @@ describe('Typical Geoservice Client request sequence: Dataset with no geometry', supportsRelationshipsResource: false, allowGeometryUpdates: false, relationships: [], + supportedContingentValuesFormats: '', + supportedExportFormats: '', + supportsAppend: false, + supportsApplyEditsWithGlobalIds: false, + supportsContingentValuesJson: null, + supportsLayerOverrides: false, + supportsQueryContingentValues: false, + supportsReturnDeleteResults: false, + supportsSharedDomains: false, + supportsTemporalLayers: false, + supportsTilesAndBasicQueriesMode: true, + supportsVCSProjection: false, + supportsWebHooks: false, }); } catch (error) { console.error(error); diff --git a/test/geoservice-defaults.spec.js b/test/geoservice-defaults.spec.js index b7062fe1b..c1df070e0 100644 --- a/test/geoservice-defaults.spec.js +++ b/test/geoservice-defaults.spec.js @@ -12,7 +12,6 @@ describe('Geoservices defaults settings', () => { const response = await request(koop.server).get('/file-geojson/rest/services/polygon/FeatureServer'); expect(response.status).toBe(200); expect(response.body.currentVersion).toBe(11.2); - expect(response.body.fullVersion).toBe('11.2.0'); } catch (error) { console.error(error); throw error; @@ -24,7 +23,6 @@ describe('Geoservices defaults settings', () => { const response = await request(koop.server).get('/file-geojson/rest/services/polygon/FeatureServer/0'); expect(response.status).toBe(200); expect(response.body.currentVersion).toBe(11.2); - expect(response.body.fullVersion).toBe('11.2.0'); } catch (error) { console.error(error); throw error; diff --git a/test/helpers/client-response-fixtures.js b/test/helpers/client-response-fixtures.js index 3fd0efd71..eef351e75 100644 --- a/test/helpers/client-response-fixtures.js +++ b/test/helpers/client-response-fixtures.js @@ -1,4 +1,4 @@ -const VERSION = 11.1; +const VERSION = 11.2; const COPYRIGHT_TEXT = 'Copyright information varies by provider. For more information please contact the source of this data.'; From 89e3c7b2203cde5b43899bd8111552a243e98638 Mon Sep 17 00:00:00 2001 From: Rich Gwozdz Date: Wed, 24 Jan 2024 12:14:04 -0800 Subject: [PATCH 3/9] chore: remove unused file --- .../src/helpers/calculate-extent.js | 18 ----- .../src/helpers/calculate-extent.spec.js | 77 ------------------- 2 files changed, 95 deletions(-) delete mode 100644 packages/featureserver/src/helpers/calculate-extent.js delete mode 100644 packages/featureserver/src/helpers/calculate-extent.spec.js diff --git a/packages/featureserver/src/helpers/calculate-extent.js b/packages/featureserver/src/helpers/calculate-extent.js deleted file mode 100644 index 98610b0c1..000000000 --- a/packages/featureserver/src/helpers/calculate-extent.js +++ /dev/null @@ -1,18 +0,0 @@ -const { calculateBounds } = require('@terraformer/spatial'); -const logManager = require('../log-manager'); -const normalizeExtent = require('./normalize-extent'); - -function calculateExtent ({ isLayer, geojson, spatialReference }) { - if (!isLayer) { - return; - } - - try { - const bounds = calculateBounds(geojson); - return normalizeExtent(bounds, spatialReference); - } catch (error) { - logManager.logger.debug(`Could not calculate extent from data: ${error.message}`); - } -} - -module.exports = calculateExtent; diff --git a/packages/featureserver/src/helpers/calculate-extent.spec.js b/packages/featureserver/src/helpers/calculate-extent.spec.js deleted file mode 100644 index 1caab0095..000000000 --- a/packages/featureserver/src/helpers/calculate-extent.spec.js +++ /dev/null @@ -1,77 +0,0 @@ -const should = require('should') // eslint-disable-line -const calculateExtent = require('./calculate-extent'); - -describe('calculate-extent', () => { - it('calculateExtent: no data passed', () => { - const extent = calculateExtent({}); - should(extent).equal(undefined); - }); - - it('calculateExtent: Point', () => { - const extent = calculateExtent({ - isLayer: true, - geojson: { - type: 'Feature', - geometry: { - type: 'Point', - coordinates: [102.0, 0.5] - }, - properties: { - prop0: 'value0' - } - }, - spatialReference: 4326 - }); - should(extent).deepEqual({ xmin: 102, ymin: 0.5, xmax: 102, ymax: 0.5, spatialReference: 4326 }); - }); - - it('calculateExtent: LineString', () => { - const extent = calculateExtent({ - isLayer: true, - geojson: { - type: 'Feature', - geometry: { - type: 'LineString', - coordinates: [ - [102.0, 0.5], - [102.3, 0.8], - [103.1, 1.2], - [103.0, 1.0], - [102.6, 0.9] - ] - }, - properties: { - prop0: 'value0' - } - }, - spatialReference: 4326 - }); - should(extent).deepEqual({ xmin: 102, ymin: 0.5, xmax: 103.1, ymax: 1.2, spatialReference: 4326 }); - }); - - it('calculateExtent: Polygon', () => { - const extent = calculateExtent({ - isLayer: true, - geojson: { - type: 'Feature', - geometry: { - type: 'Polygon', - coordinates: [ - [ - [100.0, 0.0], - [101.0, 0.0], - [101.0, 1.0], - [100.0, 1.0], - [100.0, 0.0] - ] - ] - }, - properties: { - prop0: 'value0' - } - }, - spatialReference: 102100 - }); - should(extent).deepEqual({ xmin: 100, ymin: 0.0, xmax: 101.0, ymax: 1.0, spatialReference: 102100 }); - }); -}); From f3405ae2b599dabe38c9a32c32c191e185318258 Mon Sep 17 00:00:00 2001 From: Rich Gwozdz Date: Wed, 24 Jan 2024 13:04:32 -0800 Subject: [PATCH 4/9] chore: refactor fields warnings --- packages/featureserver/src/query/index.js | 2 -- packages/featureserver/src/query/log-warnings.js | 16 ++++++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/featureserver/src/query/index.js b/packages/featureserver/src/query/index.js index caed9fb70..29fe113de 100644 --- a/packages/featureserver/src/query/index.js +++ b/packages/featureserver/src/query/index.js @@ -20,7 +20,6 @@ function query (json, requestParams = {}) { const { f: requestedFormat } = requestParams; - // TODO: if format PBF, need to send pbf if only count requested if (shouldRenderPrecalculatedData(json, requestParams)) { return renderPrecalculatedData(json, requestParams); } @@ -75,7 +74,6 @@ function renderPrecalculatedData (data, { const retVal = {}; - // TODO: if only count, and f=pbf need to encode response if (returnCountOnly) { retVal.count = count; } diff --git a/packages/featureserver/src/query/log-warnings.js b/packages/featureserver/src/query/log-warnings.js index 54f13d7b8..993f91b1a 100644 --- a/packages/featureserver/src/query/log-warnings.js +++ b/packages/featureserver/src/query/log-warnings.js @@ -5,7 +5,6 @@ const logManager = require('../log-manager'); function logWarnings(geojson, format, outFields = '*') { const { metadata = {}, features } = geojson; const esriFormat = format !== geojson; - const outFieldsSet = outFields === '*' || outFields === '' ? null : outFields.split(','); const properties = _.get(features, '[0].properties') || _.get(features, '[0].attributes'); if (esriFormat && !metadata.idField && properties?.OBJECTID === undefined) { @@ -21,9 +20,7 @@ function logWarnings(geojson, format, outFields = '*') { } if (metadata.fields && properties) { - const fields = outFieldsSet ? metadata.fields.filter(field => { - return outFieldsSet.includes(field.name || field.alias); - }) : metadata.fields; + const fields = getFieldsDefinitionsForResponse(metadata.fields, outFields); compareFieldDefintionsToFeature(fields, properties); @@ -35,6 +32,17 @@ function hasMixedCaseObjectIdKey(idField = '') { return idField.toLowerCase() === 'objectid' && idField !== 'OBJECTID'; } +function getFieldsDefinitionsForResponse(fields, outFields) { + const outFieldsSet = (outFields === '*' || outFields === '') ? null : outFields.split(','); + if (!outFieldsSet) { + return fields; + } + + return fields.filter(field => { + return outFieldsSet.includes(field.name || field.alias); + }); +} + function compareFieldDefintionsToFeature(fieldDefinitions, featureProperties) { fieldDefinitions.forEach(({ name, alias, type }) => { // look for a defined field in the features properties From 553310857fe1f1d254d1fc4c51c2afcbaa2dd1a3 Mon Sep 17 00:00:00 2001 From: Rich Gwozdz Date: Wed, 24 Jan 2024 13:41:16 -0800 Subject: [PATCH 5/9] chore: optimize warnings --- packages/featureserver/src/query/index.js | 14 +++++--------- .../featureserver/src/query/log-warnings.js | 18 ++++++++++++------ .../src/query/log-warnings.spec.js | 15 ++++++++------- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/packages/featureserver/src/query/index.js b/packages/featureserver/src/query/index.js index 29fe113de..c6c35770c 100644 --- a/packages/featureserver/src/query/index.js +++ b/packages/featureserver/src/query/index.js @@ -24,11 +24,13 @@ function query (json, requestParams = {}) { return renderPrecalculatedData(json, requestParams); } + logWarnings(json, requestParams); + const data = (skipFiltering || !features) ? json : filterAndTransform(json, requestParams); - if(shouldLogWarnings(requestParams)) { - logWarnings(data, requestedFormat, requestParams.outFields); - } + + + // TODO: Bug when count or extent requested. @@ -47,12 +49,6 @@ function query (json, requestParams = {}) { }); } -function shouldLogWarnings(requestParams) { - const { returnCountOnly, returnExtentOnly, returnIdsOnly } = requestParams; - - return !(returnCountOnly || returnExtentOnly || returnIdsOnly); -} - function shouldRenderPrecalculatedData (json, requestParameters) { const { statistics, count, extent } = json; const { returnCountOnly, returnExtentOnly } = requestParameters; diff --git a/packages/featureserver/src/query/log-warnings.js b/packages/featureserver/src/query/log-warnings.js index 993f91b1a..811526e3f 100644 --- a/packages/featureserver/src/query/log-warnings.js +++ b/packages/featureserver/src/query/log-warnings.js @@ -2,18 +2,24 @@ const _ = require('lodash'); const { getDataTypeFromValue } = require('../helpers'); const logManager = require('../log-manager'); -function logWarnings(geojson, format, outFields = '*') { +function logFeatureCollectionWarnings(geojson, requestParams) { + const { f, outFields = '*', returnCountOnly, returnExtentOnly, returnIdsOnly } = requestParams; + + if (f === 'geojson' || returnCountOnly || returnExtentOnly || returnIdsOnly) { + return; + } + const { metadata = {}, features } = geojson; - const esriFormat = format !== geojson; - const properties = _.get(features, '[0].properties') || _.get(features, '[0].attributes'); - if (esriFormat && !metadata.idField && properties?.OBJECTID === undefined) { + const properties = _.get(features, '[0].properties'); + + if (!metadata.idField && properties?.OBJECTID === undefined) { logManager.logger.debug( `provider data has no OBJECTID and has no "idField" assignment. You will get the most reliable behavior from ArcGIS clients if the provider assigns the "idField" to a property that is an integer in range 0 - ${Number.MAX_SAFE_INTEGER}. An OBJECTID field will be auto-generated in the absence of an "idField" assignment.`, ); } - if (esriFormat && hasMixedCaseObjectIdKey(metadata.idField)) { + if (hasMixedCaseObjectIdKey(metadata.idField)) { logManager.logger.debug( 'requested provider has "idField" that is a mixed-case version of "OBJECTID". This can cause errors in ArcGIS clients.', ); @@ -85,4 +91,4 @@ function isEsriTypeMatchException (definitionType, propertyType) { } } -module.exports = { logWarnings }; +module.exports = { logWarnings: logFeatureCollectionWarnings }; diff --git a/packages/featureserver/src/query/log-warnings.spec.js b/packages/featureserver/src/query/log-warnings.spec.js index a71d5594d..eedbfaa78 100644 --- a/packages/featureserver/src/query/log-warnings.spec.js +++ b/packages/featureserver/src/query/log-warnings.spec.js @@ -18,7 +18,7 @@ describe('logWarnings', () => { }); it('should log missing idField', () => { - logWarnings({}); + logWarnings({}, {}); loggerSpy.callCount.should.equal(1); loggerSpy.firstCall.args.should.deepEqual([ `provider data has no OBJECTID and has no "idField" assignment. You will get the most reliable behavior from ArcGIS clients if the provider assigns the "idField" to a property that is an integer in range 0 - ${Number.MAX_SAFE_INTEGER}. An OBJECTID field will be auto-generated in the absence of an "idField" assignment.`, @@ -26,7 +26,7 @@ describe('logWarnings', () => { }); it('should log mixed-case OBJECTID', () => { - logWarnings({ metadata: { idField: 'objEctId' } }); + logWarnings({ metadata: { idField: 'objEctId' } }, {}); loggerSpy.callCount.should.equal(1); loggerSpy.firstCall.args.should.deepEqual([ 'requested provider has "idField" that is a mixed-case version of "OBJECTID". This can cause errors in ArcGIS clients.', @@ -37,7 +37,7 @@ describe('logWarnings', () => { logWarnings({ metadata: { fields: [{ name: 'foo', type: 'String' }] }, features: [{ properties: {} }], - }); + }, {}); loggerSpy.callCount.should.equal(2); loggerSpy.secondCall.args.should.deepEqual([ 'field definition "foo (String)" not found in first feature of provider\'s GeoJSON', @@ -48,7 +48,8 @@ describe('logWarnings', () => { logWarnings({ metadata: { fields: [{ name: 'foo', type: 'String' }] }, features: [{ properties: { foo: 1000 } }], - }); + }, + {}); loggerSpy.callCount.should.equal(2); loggerSpy.secondCall.args.should.deepEqual([ 'field definition "foo (String)" not found in first feature of provider\'s GeoJSON', @@ -59,7 +60,7 @@ describe('logWarnings', () => { logWarnings({ metadata: { fields: [{ name: 'foo', type: 'String' }] }, features: [{ properties: { foo: 'bar' } }], - }); + }, {}); loggerSpy.callCount.should.equal(1); }); @@ -67,7 +68,7 @@ describe('logWarnings', () => { logWarnings({ metadata: { fields: [{ name: 'foo', type: 'Date' }] }, features: [{ properties: { foo: 12345 } }], - }); + }, {}); loggerSpy.callCount.should.equal(1); }); @@ -75,7 +76,7 @@ describe('logWarnings', () => { logWarnings({ metadata: { fields: [] }, features: [{ properties: { foo: 'bar' } }], - }); + }, {}); loggerSpy.callCount.should.equal(2); loggerSpy.secondCall.args.should.deepEqual([ 'requested provider has feature with property "foo" that was not defined in metadata fields array', From 8f071f43afa1a5d6d4838c8e614e1ddd7f66cb0c Mon Sep 17 00:00:00 2001 From: Rich Gwozdz Date: Wed, 24 Jan 2024 13:55:59 -0800 Subject: [PATCH 6/9] chore: file rename --- packages/featureserver/src/query/index.js | 10 ++-------- .../{log-warnings.js => log-provider-data-warnings.js} | 4 ++-- ...ings.spec.js => log-provider-data-warnings.spec.js} | 0 3 files changed, 4 insertions(+), 10 deletions(-) rename packages/featureserver/src/query/{log-warnings.js => log-provider-data-warnings.js} (96%) rename packages/featureserver/src/query/{log-warnings.spec.js => log-provider-data-warnings.spec.js} (100%) diff --git a/packages/featureserver/src/query/index.js b/packages/featureserver/src/query/index.js index c6c35770c..f158bd1e0 100644 --- a/packages/featureserver/src/query/index.js +++ b/packages/featureserver/src/query/index.js @@ -1,6 +1,6 @@ const _ = require('lodash'); const { filterAndTransform } = require('./filter-and-transform'); -const { logWarnings } = require('./log-warnings'); +const { logProviderDataWarnings } = require('./log-provider-data-warnings'); const { renderFeaturesResponse } = require('./render-features'); const { renderStatisticsResponse } = require('./render-statistics'); const { renderPrecalculatedStatisticsResponse } = require('./render-precalculated-statistics'); @@ -15,11 +15,10 @@ function query (json, requestParams = {}) { all: skipFiltering } = {} } = json; + const { f: requestedFormat } = requestParams; validate(requestParams); - const { f: requestedFormat } = requestParams; - if (shouldRenderPrecalculatedData(json, requestParams)) { return renderPrecalculatedData(json, requestParams); } @@ -28,11 +27,6 @@ function query (json, requestParams = {}) { const data = (skipFiltering || !features) ? json : filterAndTransform(json, requestParams); - - - - - // TODO: Bug when count or extent requested. // QUESTION: Is this problematic if its an aggregation with stats? if (requestedFormat === 'geojson') { diff --git a/packages/featureserver/src/query/log-warnings.js b/packages/featureserver/src/query/log-provider-data-warnings.js similarity index 96% rename from packages/featureserver/src/query/log-warnings.js rename to packages/featureserver/src/query/log-provider-data-warnings.js index 811526e3f..51d949a82 100644 --- a/packages/featureserver/src/query/log-warnings.js +++ b/packages/featureserver/src/query/log-provider-data-warnings.js @@ -2,7 +2,7 @@ const _ = require('lodash'); const { getDataTypeFromValue } = require('../helpers'); const logManager = require('../log-manager'); -function logFeatureCollectionWarnings(geojson, requestParams) { +function logProviderDataWarnings(geojson, requestParams) { const { f, outFields = '*', returnCountOnly, returnExtentOnly, returnIdsOnly } = requestParams; if (f === 'geojson' || returnCountOnly || returnExtentOnly || returnIdsOnly) { @@ -91,4 +91,4 @@ function isEsriTypeMatchException (definitionType, propertyType) { } } -module.exports = { logWarnings: logFeatureCollectionWarnings }; +module.exports = { logProviderDataWarnings }; diff --git a/packages/featureserver/src/query/log-warnings.spec.js b/packages/featureserver/src/query/log-provider-data-warnings.spec.js similarity index 100% rename from packages/featureserver/src/query/log-warnings.spec.js rename to packages/featureserver/src/query/log-provider-data-warnings.spec.js From 85c235fb0872430ff9cb3b6b91f7445a97966622 Mon Sep 17 00:00:00 2001 From: Rich Gwozdz Date: Wed, 24 Jan 2024 14:00:40 -0800 Subject: [PATCH 7/9] chore: log warnings test file --- packages/featureserver/src/query/index.js | 2 +- .../query/log-provider-data-warnings.spec.js | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/featureserver/src/query/index.js b/packages/featureserver/src/query/index.js index f158bd1e0..6541f351b 100644 --- a/packages/featureserver/src/query/index.js +++ b/packages/featureserver/src/query/index.js @@ -23,7 +23,7 @@ function query (json, requestParams = {}) { return renderPrecalculatedData(json, requestParams); } - logWarnings(json, requestParams); + logProviderDataWarnings(json, requestParams); const data = (skipFiltering || !features) ? json : filterAndTransform(json, requestParams); diff --git a/packages/featureserver/src/query/log-provider-data-warnings.spec.js b/packages/featureserver/src/query/log-provider-data-warnings.spec.js index eedbfaa78..fbfb238b9 100644 --- a/packages/featureserver/src/query/log-provider-data-warnings.spec.js +++ b/packages/featureserver/src/query/log-provider-data-warnings.spec.js @@ -2,10 +2,10 @@ const should = require('should'); // eslint-disable-line const sinon = require('sinon'); const proxyquire = require('proxyquire'); -describe('logWarnings', () => { +describe('logProviderDataWarnings', () => { const loggerSpy = sinon.spy(() => {}); - const { logWarnings } = proxyquire('./log-warnings', { + const { logProviderDataWarnings } = proxyquire('./log-provider-data-warnings', { '../log-manager': { logger: { debug: loggerSpy, @@ -18,7 +18,7 @@ describe('logWarnings', () => { }); it('should log missing idField', () => { - logWarnings({}, {}); + logProviderDataWarnings({}, {}); loggerSpy.callCount.should.equal(1); loggerSpy.firstCall.args.should.deepEqual([ `provider data has no OBJECTID and has no "idField" assignment. You will get the most reliable behavior from ArcGIS clients if the provider assigns the "idField" to a property that is an integer in range 0 - ${Number.MAX_SAFE_INTEGER}. An OBJECTID field will be auto-generated in the absence of an "idField" assignment.`, @@ -26,7 +26,7 @@ describe('logWarnings', () => { }); it('should log mixed-case OBJECTID', () => { - logWarnings({ metadata: { idField: 'objEctId' } }, {}); + logProviderDataWarnings({ metadata: { idField: 'objEctId' } }, {}); loggerSpy.callCount.should.equal(1); loggerSpy.firstCall.args.should.deepEqual([ 'requested provider has "idField" that is a mixed-case version of "OBJECTID". This can cause errors in ArcGIS clients.', @@ -34,7 +34,7 @@ describe('logWarnings', () => { }); it('should log field definition not found in feature', () => { - logWarnings({ + logProviderDataWarnings({ metadata: { fields: [{ name: 'foo', type: 'String' }] }, features: [{ properties: {} }], }, {}); @@ -45,7 +45,7 @@ describe('logWarnings', () => { }); it('should log field definition - feature property type mismatch', () => { - logWarnings({ + logProviderDataWarnings({ metadata: { fields: [{ name: 'foo', type: 'String' }] }, features: [{ properties: { foo: 1000 } }], }, @@ -57,7 +57,7 @@ describe('logWarnings', () => { }); it('should not log warning if field definition matches feature', () => { - logWarnings({ + logProviderDataWarnings({ metadata: { fields: [{ name: 'foo', type: 'String' }] }, features: [{ properties: { foo: 'bar' } }], }, {}); @@ -65,7 +65,7 @@ describe('logWarnings', () => { }); it('should not log warning if field type mismatch is Esri exception', () => { - logWarnings({ + logProviderDataWarnings({ metadata: { fields: [{ name: 'foo', type: 'Date' }] }, features: [{ properties: { foo: 12345 } }], }, {}); @@ -73,7 +73,7 @@ describe('logWarnings', () => { }); it('should log feature property not found in field definitions', () => { - logWarnings({ + logProviderDataWarnings({ metadata: { fields: [] }, features: [{ properties: { foo: 'bar' } }], }, {}); From a54a248f1e054ab0ee8dbf979e24bec5ca40041a Mon Sep 17 00:00:00 2001 From: Rich Gwozdz Date: Wed, 24 Jan 2024 14:02:52 -0800 Subject: [PATCH 8/9] chore: spy for log-warnings --- packages/featureserver/src/query/index.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/featureserver/src/query/index.spec.js b/packages/featureserver/src/query/index.spec.js index e8e3c76e2..e66c69146 100644 --- a/packages/featureserver/src/query/index.spec.js +++ b/packages/featureserver/src/query/index.spec.js @@ -31,8 +31,8 @@ const stub = { './filter-and-transform': { filterAndTransform: filterAndTransformSpy }, - './log-warnings': { - logWarnings: logWarningsSpy + './log-provider-data-warnings': { + logProviderDataWarnings: logWarningsSpy }, './render-features': { renderFeaturesResponse: renderFeaturesResponseSpy From 0c32a354a8185faebaed2d95fec8210dcfcb4a44 Mon Sep 17 00:00:00 2001 From: Rich Gwozdz Date: Wed, 24 Jan 2024 16:01:05 -0800 Subject: [PATCH 9/9] chore: mop up stray comments, etc --- packages/featureserver/coverage-unit.svg | 8 ++-- packages/featureserver/coverage.svg | 8 ++-- .../src/query/log-provider-data-warnings.js | 2 +- .../query/log-provider-data-warnings.spec.js | 40 ++++++++++++++++++- .../general-response-handler.js | 1 - .../helpers/send-pbf/index.js | 2 +- .../send-pbf/transform-features-for-pbf.js | 10 ++--- .../send-pbf/transform-to-pbf-attributes.js | 2 - .../query-response-handler.js | 9 ++--- 9 files changed, 57 insertions(+), 25 deletions(-) diff --git a/packages/featureserver/coverage-unit.svg b/packages/featureserver/coverage-unit.svg index fbc83b818..142927d86 100644 --- a/packages/featureserver/coverage-unit.svg +++ b/packages/featureserver/coverage-unit.svg @@ -1,5 +1,5 @@ - - coverage: 94.88% + + coverage: 94.93% @@ -13,8 +13,8 @@ \ No newline at end of file diff --git a/packages/featureserver/coverage.svg b/packages/featureserver/coverage.svg index 2e4ee4aba..c08c6584c 100644 --- a/packages/featureserver/coverage.svg +++ b/packages/featureserver/coverage.svg @@ -1,5 +1,5 @@ - - coverage: 98.03% + + coverage: 98.11% @@ -13,8 +13,8 @@ \ No newline at end of file diff --git a/packages/featureserver/src/query/log-provider-data-warnings.js b/packages/featureserver/src/query/log-provider-data-warnings.js index 51d949a82..a8c3c3c6e 100644 --- a/packages/featureserver/src/query/log-provider-data-warnings.js +++ b/packages/featureserver/src/query/log-provider-data-warnings.js @@ -45,7 +45,7 @@ function getFieldsDefinitionsForResponse(fields, outFields) { } return fields.filter(field => { - return outFieldsSet.includes(field.name || field.alias); + return outFieldsSet.includes(field.alias) || outFieldsSet.includes(field.name); }); } diff --git a/packages/featureserver/src/query/log-provider-data-warnings.spec.js b/packages/featureserver/src/query/log-provider-data-warnings.spec.js index fbfb238b9..bd2a921df 100644 --- a/packages/featureserver/src/query/log-provider-data-warnings.spec.js +++ b/packages/featureserver/src/query/log-provider-data-warnings.spec.js @@ -45,6 +45,18 @@ describe('logProviderDataWarnings', () => { }); it('should log field definition - feature property type mismatch', () => { + logProviderDataWarnings({ + metadata: { fields: [{ name: 'foo', type: 'String' }] }, + features: [{ properties: { foo: 1000 } }], + }, + { outFields: '' }); + loggerSpy.callCount.should.equal(2); + loggerSpy.secondCall.args.should.deepEqual([ + 'field definition "foo (String)" not found in first feature of provider\'s GeoJSON', + ]); + }); + + it('should log field definition - feature property type mismatch (outFields as empty string)', () => { logProviderDataWarnings({ metadata: { fields: [{ name: 'foo', type: 'String' }] }, features: [{ properties: { foo: 1000 } }], @@ -56,6 +68,32 @@ describe('logProviderDataWarnings', () => { ]); }); + it('should log field definition - feature property type mismatch (outFields defined)', () => { + logProviderDataWarnings({ + metadata: { fields: [{ name: 'foo', type: 'String' }] }, + features: [{ properties: { foo: 1000 } }], + }, + { outFields: 'foo'}); + + loggerSpy.callCount.should.equal(2); + loggerSpy.secondCall.args.should.deepEqual([ + 'field definition "foo (String)" not found in first feature of provider\'s GeoJSON', + ]); + }); + + it('should log field definition - feature property type mismatch (outFields defined with alias)', () => { + logProviderDataWarnings({ + metadata: { fields: [{ name: 'foo', alias: 'food', type: 'String' }] }, + features: [{ properties: { foo: 1000 } }], + }, + { outFields: 'food'}); + + loggerSpy.callCount.should.equal(2); + loggerSpy.secondCall.args.should.deepEqual([ + 'field definition "foo (String)" not found in first feature of provider\'s GeoJSON', + ]); + }); + it('should not log warning if field definition matches feature', () => { logProviderDataWarnings({ metadata: { fields: [{ name: 'foo', type: 'String' }] }, @@ -64,7 +102,7 @@ describe('logProviderDataWarnings', () => { loggerSpy.callCount.should.equal(1); }); - it('should not log warning if field type mismatch is Esri exception', () => { + it('should not log warning if field type mismatch is Esri date exception', () => { logProviderDataWarnings({ metadata: { fields: [{ name: 'foo', type: 'Date' }] }, features: [{ properties: { foo: 12345 } }], diff --git a/packages/featureserver/src/response-handlers/general-response-handler.js b/packages/featureserver/src/response-handlers/general-response-handler.js index 9117c505d..b9d59648f 100644 --- a/packages/featureserver/src/response-handlers/general-response-handler.js +++ b/packages/featureserver/src/response-handlers/general-response-handler.js @@ -11,6 +11,5 @@ module.exports = function generalResponseHandler(res, payload, requestParameters return sendPrettyJson(res, payload); } - // payload here might be Esri JSON or GeoJSON return res.status(200).json(payload); }; diff --git a/packages/featureserver/src/response-handlers/helpers/send-pbf/index.js b/packages/featureserver/src/response-handlers/helpers/send-pbf/index.js index 0918ef621..9382736a7 100644 --- a/packages/featureserver/src/response-handlers/helpers/send-pbf/index.js +++ b/packages/featureserver/src/response-handlers/helpers/send-pbf/index.js @@ -14,9 +14,9 @@ function sendPbf(res, payload, requestParameters) { throw error; } - const pbfPayload = setPbfPayload(payload, requestParameters); const buffer = FeatureCollectionProto.encode(pbfPayload).finish(); + res.writeHead(200, [ ['content-type', 'application/x-protobuf'], ['content-length', buffer.length], diff --git a/packages/featureserver/src/response-handlers/helpers/send-pbf/transform-features-for-pbf.js b/packages/featureserver/src/response-handlers/helpers/send-pbf/transform-features-for-pbf.js index 3686f4a19..67ca4e006 100644 --- a/packages/featureserver/src/response-handlers/helpers/send-pbf/transform-features-for-pbf.js +++ b/packages/featureserver/src/response-handlers/helpers/send-pbf/transform-features-for-pbf.js @@ -3,16 +3,14 @@ const { getGeometryTransform } = require('./get-geometry-transform'); const { transformToPbfAttributes } = require('./transform-to-pbf-attributes'); const { transformToPbfGeometry } = require('./transform-to-pbf-geometry'); - - function transformFeaturesForPbf(json, quantizationParameters) { - const fields = _.orderBy(json.fields, ['name'], ['asc']); const { objectIdFieldName, uniqueIdField, geometryType, spatialReference } = json; - + const fields = _.orderBy(json.fields, ['name'], ['asc']); + const geometryTransform = getGeometryTransform(spatialReference, quantizationParameters); const features = json.features.map( - transformFunction(fields, geometryTransform), + transformFeatureFunction(fields, geometryTransform), ); return { @@ -26,7 +24,7 @@ function transformFeaturesForPbf(json, quantizationParameters) { }; } -function transformFunction(fields, geometryTransform) { +function transformFeatureFunction(fields, geometryTransform) { const fieldMap = fields.reduce((acc, cur) => { acc[cur.name] = cur.type; return acc; diff --git a/packages/featureserver/src/response-handlers/helpers/send-pbf/transform-to-pbf-attributes.js b/packages/featureserver/src/response-handlers/helpers/send-pbf/transform-to-pbf-attributes.js index 62109a2b0..01a6fc36e 100644 --- a/packages/featureserver/src/response-handlers/helpers/send-pbf/transform-to-pbf-attributes.js +++ b/packages/featureserver/src/response-handlers/helpers/send-pbf/transform-to-pbf-attributes.js @@ -26,8 +26,6 @@ function transformToPbfAttributes(attributes, fieldMap) { .value(); } - - module.exports = { transformToPbfAttributes }; diff --git a/packages/featureserver/src/response-handlers/query-response-handler.js b/packages/featureserver/src/response-handlers/query-response-handler.js index 59eadcee5..344a67480 100644 --- a/packages/featureserver/src/response-handlers/query-response-handler.js +++ b/packages/featureserver/src/response-handlers/query-response-handler.js @@ -11,14 +11,13 @@ module.exports = function queryResponseHandler (res, payload, requestParameters) return sendCallbackResponse(res, payload, callback); } - if (f === 'pjson') { - return sendPrettyJson(res, payload); - } - if (f === 'pbf') { return sendPbf(res, payload, requestParameters); } - // payload here might be Esri JSON or GeoJSON + if (f === 'pjson') { + return sendPrettyJson(res, payload); + } + return res.status(200).json(payload); };