Skip to content

Commit 0612031

Browse files
authored
Refactor FeatureServer/Output-GeoServices Integration (#999)
* Refactor: use restInfo handler directly in output-geoservice (#967) * Refactor server info-handler (#974) * refactor layers-info handler (#975) * refactor feature server layers-info handler (#976) * refactor: query handler (#977) * refactor: generate-renderer and query-related-requests (#997) * refactor: remove route module (#998)
1 parent 611a0f7 commit 0612031

File tree

88 files changed

+4129
-23645
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+4129
-23645
lines changed

.changeset/rare-lizards-exercise.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@koopjs/featureserver": major
3+
---
4+
5+
- Remove route module and refactor signatures of handlers; consumers can no longer use FeatureServer.route. If using this directly with Express, you need to define each route and bind to the correct FeatureServer handler.

.coverage_json/coverage-summary.json

+872-1
Large diffs are not rendered by default.

.github/workflows/pr-tests.yml

+2
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ on:
55
branches:
66
- master
77
- beta
8+
- next
89
paths:
910
- "./.github/**.yml"
1011
- "**/packages/**.js"
1112
- "**/packages/**/package.json"
1213
- "test/**/*.js"
14+
- "ci/**/*.js"
1315

1416
jobs:
1517
pr-tests:

.husky/pre-push

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env sh
2+
echo "\nRunning code linting...\n"
3+
npm run lint

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,12 @@ The Koop dependency graph is shown below.
5757
![Screen Shot 2022-11-30 at 1 03 46 PM](https://user-images.githubusercontent.com/4369192/204908289-82659cfe-fcf3-404a-aa70-79baf540f1b8.png)
5858

5959
### Test Coverage
60-
Test coverages for each package are shown below. Coverage for some packages includes integration tests as opposed to true unit tests. Difference in coverage is shown below. Our goal is to have complete unit test coverage, and breakout integration/e2e tests separately.
60+
Test coverages for each package are shown below. Coverage for winnow package includes integration tests as opposed to true unit tests. Our goal is to have complete unit test coverage, and breakout integration/e2e tests separately.
6161

6262
| package | integration + unit | unit |
6363
|---|---|---|
6464
|cache-memory|N/A|![coverage](./packages/cache-memory/coverage.svg)|
65-
|featureserver|![coverage](./packages/featureserver/coverage.svg)|![coverage](./packages/featureserver/coverage-unit.svg)|
65+
|featureserver|![coverage](./packages/featureserver/coverage.svg)|
6666
|koop-core|N/A|![coverage](./packages/core/coverage.svg)|
6767
|logger|N/A|![coverage](./packages/logger/coverage.svg)|
6868
|output-geoservices|N/A|![coverage](./packages/output-geoservices/coverage.svg)|

ci/format-branch-coverage-changes.js

+31-14
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
const { writeFileSync, existsSync } = require('fs');
22
const json2md = require('json2md');
33
const coverageSummary = require('../.coverage_json/coverage-summary.json');
4-
54
const markdownFilePath = '.branch-coverage-changes.md';
65

76
if (!existsSync('.coverage_changes_json/coverage-summary.json')) {
@@ -16,29 +15,43 @@ if (!existsSync('.coverage_changes_json/coverage-summary.json')) {
1615
}
1716

1817
const coverageChangesSummary = require('../.coverage_changes_json/coverage-summary.json');
18+
1919
const rows = Object.entries(coverageChangesSummary)
2020
.filter(([filePath]) => {
2121
return filePath !== 'total';
2222
})
2323
.map(([filePath, changesCoverage]) => {
2424
const packageFilePath = `packages${filePath.split('packages')[1]}`;
2525
const masterCoverage = coverageSummary[packageFilePath];
26+
2627
return [
2728
packageFilePath,
28-
formatCovComparison(changesCoverage.statements.pct, masterCoverage?.statements?.pct || 0),
29-
formatCovComparison(changesCoverage.branches.pct, masterCoverage?.branches?.pct || 0),
30-
formatCovComparison(changesCoverage.functions.pct, masterCoverage?.functions?.pct || 0),
31-
formatCovComparison(changesCoverage.lines.pct, masterCoverage?.lines?.pct || 0),
29+
formatCovComparison(
30+
changesCoverage.statements.pct,
31+
masterCoverage?.statements?.pct || null,
32+
),
33+
formatCovComparison(
34+
changesCoverage.branches.pct,
35+
masterCoverage?.branches?.pct || null,
36+
),
37+
formatCovComparison(
38+
changesCoverage.functions.pct,
39+
masterCoverage?.functions?.pct || null,
40+
),
41+
formatCovComparison(
42+
changesCoverage.lines.pct,
43+
masterCoverage?.lines?.pct || null,
44+
),
3245
];
3346
});
3447

35-
const headers = ['File Path', 'Statements', 'Branches', 'Functions', 'Lines'];
48+
const headers = ['File Path', 'Statements', 'Branches', 'Functions', ' Lines '];
3649

3750
const table = json2md([{ h2: 'Coverage Report (change vs master)' }, { table: { headers, rows } }]);
3851

3952
const alignedTable = table.replace(
40-
'| --------- | ---------- | -------- | --------- | ----- |',
41-
'| :--------- | ----------: | --------: | ---------: | -----: |',
53+
'| --------- | --------- | -------- | --------- | --------- |',
54+
'| :--------- | ---------: | --------: | ---------: | ---------: |',
4255
);
4356

4457
const markdown = `[g-img]: https://github.com/koopjs/koop/assets/4369192/fd82d4b7-7f6e-448c-a56c-82ac6781a629
@@ -52,25 +65,29 @@ ${alignedTable}`;
5265
writeFileSync(markdownFilePath, markdown, 'utf8');
5366

5467
function formatCovComparison(changePct, mainPct) {
55-
return `${formatCovPct(changePct)} vs ${formatCovPct(mainPct)}`;
68+
return `${formatCovPct(changePct)}<br>vs<br>${formatCovPct(mainPct)}`;
5669
}
5770

5871
function formatCovPct(pct) {
72+
if (!pct) {
73+
return '(NA)';
74+
}
75+
5976
if (pct === 100) {
60-
return `${pct} ![green][g-img]`;
77+
return `${pct.toFixed(1)} ![green][g-img]`;
6178
}
6279

6380
if (pct > 90) {
64-
return `${pct} ![yellowGreen][yg-img]`;
81+
return `${pct.toFixed(1)} ![yellowGreen][yg-img]`;
6582
}
6683

6784
if (pct > 80) {
68-
return `${pct} ![yellow][y-img]`;
85+
return `${pct.toFixed(1)} ![yellow][y-img]`;
6986
}
7087

7188
if (pct > 70) {
72-
return `${pct} ![orange][o-img]`;
89+
return `${pct.toFixed(1)} ![orange][o-img]`;
7390
}
7491

75-
return `${pct} ![red][r-img]`;
92+
return `${pct.toFixed(1)} ![red][r-img]`;
7693
}

ci/run-coverage-on-branch-changes.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ function getCovCmd(package, srcFiles) {
6262
}
6363

6464
function getTestCmd(package) {
65-
if (package === 'featureserver' || package === 'winnow') {
65+
if (package === 'winnow') {
6666
return 'npm run test:unit > /dev/null';
6767
}
6868

ci/run-test-coverage-analysis.js

+11-11
Original file line numberDiff line numberDiff line change
@@ -8,35 +8,35 @@ const context = argv.context || 'all';
88

99
workspaces.forEach((workspace) => {
1010
process.chdir(workspace);
11-
const package = workspace.split(path.sep).pop();
12-
console.log(`Package "${package}":`);
11+
const pkg = workspace.split(path.sep).pop();
12+
console.log(`Package "${pkg}":`);
1313
process.stdout.write(` - running ${context} test coverage...`);
14-
shell.exec(getCovCmd(package, context));
14+
shell.exec(getCovCmd(pkg, context));
1515
process.stdout.write('completed.\n');
1616
process.stdout.write(` - generating ${context} test coverage badge...`);
17-
shell.exec(getBadgeCmd(package, context));
17+
shell.exec(getBadgeCmd(pkg, context));
1818
process.stdout.write('completed.\n\n');
1919
process.chdir('../..');
2020
});
2121

22-
function getCovCmd(package, context) {
23-
if (package === 'output-geoservices') {
22+
function getCovCmd(pkg, context) {
23+
if (pkg === 'output-geoservices') {
2424
return 'npm test -- --coverage --coverageDirectory=.coverage/all --reporters --silent > /dev/null';
2525
}
2626

27-
return `npx cross-env SUPPRESS_NO_CONFIG_WARNING=true nyc -r=json-summary -r=json --report-dir=.coverage/all --temp-dir=.coverage/all/analysis ${getTestCmd(package, context)}`;
27+
return `npx cross-env SUPPRESS_NO_CONFIG_WARNING=true nyc -r=json-summary -r=json --report-dir=.coverage/all --temp-dir=.coverage/all/analysis ${getTestCmd(pkg, context)}`;
2828
}
2929

30-
function getBadgeCmd(package, context) {
31-
if (context === 'unit' && (package === 'featureserver' || package === 'winnow')) {
30+
function getBadgeCmd(pkg, context) {
31+
if (context === 'unit' && pkg === 'winnow') {
3232
return 'npx coverage-badges-cli --source .coverage/all/coverage-summary.json --output ./coverage-unit.svg > /dev/null';
3333
}
3434

3535
return 'npx coverage-badges-cli --source .coverage/all/coverage-summary.json --output ./coverage.svg > /dev/null';
3636
}
3737

38-
function getTestCmd(package, context) {
39-
if (context === 'unit' && (package === 'featureserver' || package === 'winnow')) {
38+
function getTestCmd(pkg, context) {
39+
if (context === 'unit' && pkg === 'winnow') {
4040
return 'npm run test:unit > /dev/null';
4141
}
4242

packages/featureserver/README.md

+3-86
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,20 @@ const app = express() // set up a basic express server
1414
const FeatureServer = require('@koopjs/featureserver')
1515
const cache = require('cache')
1616

17-
// We only need one handler because FeatureServer.route is going to do all the work
17+
1818
const handler = (req, res) => {
1919
cache.get(/* some geojson */, (err, data) => {
2020
if (err) res.status(500).json({error: err.message})
21-
else FeatureServer.route(req, res, data)
21+
else FeatureServer.query(req, res, data)
2222
})
2323
}
2424

25-
// Sets up all of the handled routes to support `GET` and `POST`
26-
const routes = ['/FeatureServer', '/FeatureServer/layers', '/FeatureServer/:layer', '/FeatureServer/:layer/:method']
27-
28-
routes.forEach(route => {
29-
app.route(route)
25+
app.route('/FeatureServer/:layer/query')
3026
.get(handler)
3127
.post(handler)
32-
})
3328
```
3429

3530
## API
36-
* [FeatureServer.route](#featureserver.route)
3731
* [FeatureServer.query](#featureserver.query)
3832
* [FeatureServer.restInfo](#featureserver.serverInfo)
3933
* [FeatureServer.serverInfo](#featureserver.serverInfo)
@@ -43,83 +37,6 @@ routes.forEach(route => {
4337
* [FeatureServer.queryRelatedRecords](#featureserver.queryRelatedRecords)
4438
* [FeatureServer.setDefaults](#featureserver.setDefaults)
4539

46-
### FeatureServer.route
47-
Pass in an `incoming request object`, an `outgoing response object`, a `geojson` object, and `options` and this function will route and return a geoservices compliant response
48-
49-
- Supports: '/FeatureServer', '/FeatureServer/layers', '/FeatureServer/:layer', '/FeatureServer/:layer/:method'
50-
- _Note_: only `query`, `info`, and `generateRenderer` are supported methods at this time.
51-
52-
```js
53-
FeatureServer.route(req, res, data, options)
54-
```
55-
56-
- **data** is either a geojson object extended with some additional properties or an object with a layers property which an array of extended geojson objects. These properties are optional and can be used to provide more specific metadata or to shortcut the built in filtering mechanism.
57-
58-
e.g.
59-
60-
```js
61-
{
62-
type: 'FeatureCollection' // Static
63-
features: Array, // GeoJSON features
64-
statistics: Object, // pass statistics to an outStatistics request to or else they will be calculated from geojson features passed in
65-
metadata: {
66-
id: number, // The unique layer id. If supplied for one layer, you should supply for all layers to avoid multiple layers having the same id.
67-
name: String, // The name of the layer
68-
description: String, // The description of the layer
69-
copyrightText: String, // The copyright text (layer attribution text)
70-
extent: Array, // valid extent array e.g. [[180,90],[-180,-90]]
71-
displayField: String, // The display field to be used by a client
72-
geometryType: String // REQUIRED if no features are returned with this object Point || MultiPoint || LineString || MultiLineString || Polygon || MultiPolygon
73-
idField: String, // unique identifier field,
74-
maxRecordCount: Number, // the maximum number of features a provider can return at once
75-
limitExceeded: Boolean, // whether or not the server has limited the features returned
76-
timeInfo: Object, // describes the time extent and capabilities of the layer,
77-
transform: Object, // describes a quantization transformation
78-
renderer: Object, // provider can over-ride default symbology of FeatureServer output with a renderer object. See https://developers.arcgis.com/web-map-specification/objects/simpleRenderer, for object specification.
79-
defaultVisibility: boolean, // The default visibility of this layer
80-
minScale: number, // The minScale value for this layer
81-
maxScale: number, // The maxScale value for this layer
82-
fields: [
83-
{ // Subkeys are optional
84-
name: String,
85-
type: String, // 'Date' || 'Double' || 'Integer' || 'String'
86-
alias: String, // how should clients display this field name,
87-
}
88-
],
89-
supportedQueryFormats: String | Array // 'JSON,geojson' || ['JSON', 'geojson']
90-
},
91-
capabilities: {
92-
quantization: Boolean // True if the provider supports quantization
93-
},
94-
filtersApplied: {
95-
all: Boolean // true if all post processing should be skipped
96-
geometry: Boolean, // true if a geometric filter has already been applied to the data
97-
where: Boolean, // true if a sql-like where filter has already been applied to the data
98-
offset: Boolean // true if the result offset has already been applied to the data,
99-
limit: Boolean // true if the result count has already been limited,
100-
projection: Boolean // true if the result data has already been projected
101-
}
102-
count: Number // pass count if the number of features in a query has been pre-calculated
103-
}
104-
```
105-
106-
or
107-
108-
```js
109-
{
110-
layers: [
111-
{
112-
type: 'FeatureCollection'
113-
...
114-
},
115-
{
116-
type: 'FeatureCollection'
117-
...
118-
}
119-
]
120-
```
121-
- **options** is an object that dictates method actions. See `FeatureServer.query` and `FeatureServer.generateRenderer` for more details.
122-
12340
### FeatureServer.query
12441
Pass in `geojson` and `options` (a valid [geoservices query object](https://geoservices.github.io/query.html)), and the function will perform the query and return a valid geoservices query object. The in addition to input `statistics: {}`, following is an example of _all_ query `options` that can be passed into the query route: '/FeatureServer/:layer/query'
12542

packages/featureserver/coverage-unit.svg

-20
This file was deleted.

packages/featureserver/coverage.svg

+7-7
Loading

packages/featureserver/package.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77
"test": "test"
88
},
99
"scripts": {
10-
"test": "mocha '**/*.spec.js' --reporter even-more-min --recursive -t 5000",
11-
"test:unit": "mocha 'src/**/*.spec.js' --reporter even-more-min --recursive -t 5000"
10+
"test": "mocha '**/*.spec.js' --reporter even-more-min --recursive -t 5000"
1211
},
1312
"contributors": [
1413
{

0 commit comments

Comments
 (0)