Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
# [1.75.0](https://github.com/Greenstand/treetracker-query-api/compare/v1.74.1...v1.75.0) (2025-02-24)

### Bug Fixes

- add limit to tree query by geometry ([70926eb](https://github.com/Greenstand/treetracker-query-api/commit/70926eb7644d1e9ff5687afdbebe16031557eb55))
- change geometry input format to geojson array ([fd50334](https://github.com/Greenstand/treetracker-query-api/commit/fd5033457a09a6ce43e3a6c5f9078352b31b29a2))

### Features

- add filter by geom for trees route ([97f583e](https://github.com/Greenstand/treetracker-query-api/commit/97f583ef0143e8284f748848fecb68b56f197125))

## [1.74.1](https://github.com/Greenstand/treetracker-query-api/compare/v1.74.0...v1.74.1) (2025-02-03)

### Bug Fixes
Expand Down
34 changes: 34 additions & 0 deletions __tests__/e2e/trees.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import exp from 'constants';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is irrelevant to your work right? Why you are uploading this? How do you folk the repo?

import supertest from 'supertest';
import app from '../../server/app';

Expand Down Expand Up @@ -135,4 +136,37 @@ describe('trees', () => {
},
1000 * 30,
);

it(
'trees/?geoJsonStr=encodedgeoJson',
async () => {
const geoJsonArr = [
{
type: 'Feature',
geometry: { type: 'Point', coordinates: [-122.6064, 39.0619] },
properties: {},
},
{
type: 'Feature',
geometry: { type: 'Point', coordinates: [-123.3139, 38.1519] },
properties: {},
},
{
type: 'Feature',
geometry: { type: 'Point', coordinates: [-121.1001, 37.8879] },
properties: {},
},
];

const encodedgeoJson = encodeURIComponent(JSON.stringify(geoJsonArr));

const response = await supertest(app).get(
`/trees?geoJsonStr=${encodedgeoJson}`,
);
expect(response.status).toBe(200);
expect(response.body.trees.length).toBe(145);
expect(response.body.total).toBe(145);
},
1000 * 30,
);
});
14 changes: 3 additions & 11 deletions deployment/base/deployment.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
apiVersion: apps/v1
apiVersion: apps/v1
kind: Deployment
metadata:
name: treetracker-query-api
Expand All @@ -15,18 +15,10 @@ spec:
labels:
app: treetracker-query-api
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: doks.digitalocean.com/node-pool
operator: In
values:
- microservices-node-pool
containers:
- name: treetracker-query-api
image: greenstand/treetracker-query-api:TAG
image: greenstand/treetracker-query-api:v1.2.3
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why you need to change this?
I thought you shouldn’t touch this existing deployment, you can create a new deployment for just postgrest service, right?

imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
env:
Expand Down
9 changes: 8 additions & 1 deletion deployment/base/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- deployment.yaml
- mapping.yaml
- service.yaml
- database-connection-sealed-secret.yaml
- postgrest-config.yaml
- postgrest-deployment.yaml
- postgrest-service.yaml
- postgrest-ingress.yaml

Empty file added deployment/base/master-key.yaml
Empty file.
11 changes: 11 additions & 0 deletions deployment/base/postgrest-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: postgrest-config
namespace: webmap
data:
postgrest.conf: |
db-uri = "$(PGRST_DB_URI)"
db-schema = "public"
db-anon-role = "readonly_user"
server-port = 3000
32 changes: 32 additions & 0 deletions deployment/base/postgrest-deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgrest
namespace: webmap
spec:
replicas: 1
selector:
matchLabels:
app: postgrest
template:
metadata:
labels:
app: postgrest
spec:
containers:
- name: postgrest
image: postgrest/postgrest:v12.0.2
ports:
- containerPort: 3000
env:
- name: PGRST_DB_URI
valueFrom:
secretKeyRef:
name: query-api-database-connection
key: db
- name: PGRST_DB_SCHEMA
value: "public"
- name: PGRST_DB_ANON_ROLE
value: "readonly_user"
- name: PGRST_SERVER_PORT
value: "3000"
18 changes: 18 additions & 0 deletions deployment/base/postgrest-ingress.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: postgrest-ingress
namespace: webmap
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- http:
paths:
- path: /query-postgrest(/|$)(.*)
pathType: Prefix
backend:
service:
name: postgrest-service
port:
number: 3000
12 changes: 12 additions & 0 deletions deployment/base/postgrest-service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: v1
kind: Service
metadata:
name: postgrest-service
namespace: webmap
spec:
selector:
app: postgrest
ports:
- port: 3000
targetPort: 3000
type: ClusterIP
3 changes: 2 additions & 1 deletion deployment/base/service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ apiVersion: v1
kind: Service
metadata:
name: treetracker-query-api
namespace: webmap
spec:
selector:
app: treetracker-query-api
ports:
- name: http
protocol: TCP
port: 80
targetPort: 3006
targetPort: 3006 # change if container doesn't use 3006
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "treetracker-query-api",
"version": "1.74.1",
"version": "1.75.0",
"private": false,
"keywords": [
"ecology"
Expand Down
50 changes: 50 additions & 0 deletions server/infra/database/TreeRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ import HttpError from 'utils/HttpError';
import BaseRepository from './BaseRepository';
import Session from './Session';

type GeoJson = Partial<{
geometry: {
coordinates: number[];
};
}>;

export default class TreeRepository extends BaseRepository<Tree> {
constructor(session: Session) {
super('trees', session);
Expand Down Expand Up @@ -304,4 +310,48 @@ export default class TreeRepository extends BaseRepository<Tree> {
const object = await this.session.getDB().raw(sql);
return object.rows;
}

async getByGeometry(
geoJsonArr: GeoJson[],
options: FilterOptions,
totalCount = false,
) {
const { limit } = options;
const pointArray = geoJsonArr.map(
(item) =>
`ST_MakePoint(${item.geometry?.coordinates[0]}, ${item.geometry?.coordinates[1]})`,
);
pointArray.push(
`ST_MakePoint(${geoJsonArr[0].geometry?.coordinates[0]}, ${geoJsonArr[0].geometry?.coordinates[1]})`,
);
if (totalCount) {
const totalSql = `
SELECT
COUNT(*)
FROM trees t
WHERE
ST_CONTAINS(
ST_SETSRID(ST_CONVEXHULL(ST_MAKELINE(ARRAY[${pointArray.toString()}])), 4326),
ST_SETSRID(ST_POINT(t.lon, t.lat), 4326)
)
`;
const total = await this.session.getDB().raw(totalSql);
return parseInt(total.rows[0].count.toString());
}

const sql = `
SELECT
*
FROM trees t
WHERE
ST_CONTAINS(
ST_SETSRID(ST_CONVEXHULL(ST_MAKELINE(ARRAY[${pointArray.toString()}])), 4326),
ST_SETSRID(ST_POINT(t.lon, t.lat), 4326)
)
LIMIT ${limit}
`;

const object = await this.session.getDB().raw(sql);
return object.rows;
}
}
23 changes: 23 additions & 0 deletions server/models/Tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@ import Tree from 'interfaces/Tree';
import { delegateRepository } from '../infra/database/delegateRepository';
import TreeRepository from '../infra/database/TreeRepository';

type GeoJson = Partial<{
geometry: {
coordinates: number[];
};
}>;
type Filter = Partial<{
organization_id: number;
date_range: { startDate: string; endDate: string };
tag: string;
wallet_id: string;
geoJsonArr: GeoJson[];
}>;

function getByFilter(
Expand Down Expand Up @@ -41,6 +47,14 @@ function getByFilter(
const trees = await treeRepository.getByWallet(filter.wallet_id, options);
return trees;
}
if (filter.geoJsonArr) {
log.warn('using geometry filter...');
const trees = await treeRepository.getByGeometry(filter.geoJsonArr, {
...options,
limit: 500,
});
return trees;
}

const trees = await treeRepository.getByFilter(filter, options);
return trees;
Expand Down Expand Up @@ -84,6 +98,15 @@ function countByFilter(
return total;
}

if (filter.geoJsonArr) {
log.warn('using geometry filter...');
const total = await treeRepository.getByGeometry(
filter.geoJsonArr,
options,
true,
);
return total;
}
const total = await treeRepository.countByFilter(filter);
return total;
};
Expand Down
12 changes: 12 additions & 0 deletions server/routers/treesRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,21 @@ import TreeModel from '../models/Tree';
import HttpError from '../utils/HttpError';

const router = express.Router();

type GeoJson = Partial<{
geometry: {
coordinates: number[];
};
}>;

type Filter = Partial<{
planter_id: number;
organization_id: number;
date_range: { startDate: string; endDate: string };
tag: string;
wallet_id: string;
active: true;
geoJsonArr: GeoJson[];
}>;

router.get(
Expand Down Expand Up @@ -70,6 +78,7 @@ router.get(
offset: Joi.number().integer().min(0),
startDate: Joi.string().regex(/^\d{4}-\d{2}-\d{2}$/),
endDate: Joi.string().regex(/^\d{4}-\d{2}-\d{2}$/),
geoJsonStr: Joi.string(),
}),
);
const {
Expand All @@ -83,6 +92,7 @@ router.get(
endDate,
tag,
wallet_id,
geoJsonStr,
} = req.query;
const repo = new TreeRepository(new Session());
const filter: Filter = { active: true };
Expand All @@ -105,6 +115,8 @@ router.get(
filter.tag = tag;
} else if (wallet_id) {
filter.wallet_id = wallet_id;
} else if (geoJsonStr) {
filter.geoJsonArr = JSON.parse(decodeURIComponent(geoJsonStr));
}

const result = await TreeModel.getByFilter(repo)(filter, options);
Expand Down