Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend graphql queries with a geo query #3084

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
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
13 changes: 12 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,18 @@ task dbRollback(dependsOn: 'compileJava', type: JavaExec) {
}
}

task dbDrop_postgis(dependsOn: "compileJava", type: com.bmuschko.gradle.docker.tasks.container.DockerExecContainer) {
group = "database"
description = "Drops PostGIS extension and related objects on PostgreSQL."
targetContainerId { dbContainerName }
commands = [ ["psql", "-U", run.environment["ANET_DB_USERNAME"], "-d", run.environment["ANET_DB_NAME"], "-c", "DROP EXTENSION IF EXISTS postgis CASCADE"] as String[] ]
}

task dbDrop(dependsOn: 'compileJava', type: JavaExec) {
if(!isMssql) {
dependsOn 'dbDrop_postgis'
tasks.findByName('dbDrop_postgis').mustRunAfter('compileJava')
}
group = "database"
description = "Runs the ANET database drop-all (Liquibase) command."
classpath = sourceSets.main.runtimeClasspath
Expand Down Expand Up @@ -339,7 +350,7 @@ task dockerPushImage(type: com.bmuschko.gradle.docker.tasks.image.DockerPushImag

task dockerPullDB(type: com.bmuschko.gradle.docker.tasks.image.DockerPullImage) {
group = "database container"
image = isMssql ? "ncia/anet-mssql-linux:latest" : "postgres:latest"
image = isMssql ? "ncia/anet-mssql-linux:latest" : "postgis/postgis:latest"
description = "Pulls a docker image for the ANET DB from ${image}."
}

Expand Down
1 change: 1 addition & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"@wdio/sync": "6.10.0",
"aigle": "1.14.1",
"autoprefixer": "9.8.6",
"axios": "0.21.0",
"ava": "3.13.0",
"babel-jest": "26.6.3",
"babel-loader": "8.2.1",
Expand Down
187 changes: 187 additions & 0 deletions client/tests/webdriver/specs/geoSearchApi.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import { expect } from "chai"
import lodash from "lodash"

const axios = require("axios")

const apiUrl = `${process.env.SERVER_URL}/graphql?user=erin&pass=erin`

// centered on General Hospital, zoom level 15, not padded
const polygon01 =
"POLYGON ((-52.77621746063233 47.56453866910163, -52.741935 47.56453866910163, -52.70763874053956 47.56453866910163, -52.70763874053956 47.571772, -52.70763874053956 47.57901543200339, -52.741935 47.57901543200339, -52.77621746063233 47.57901543200339, -52.77621746063233 47.571772, -52.77621746063233 47.56453866910163))"

// centered on General Hospital, zoom level 15, padded by 0.5
const polygon02 =
"POLYGON ((-52.81050682067872 47.55730028765075, -52.741935 47.55730028765075, -52.67334938049317 47.55730028765075, -52.67334938049317 47.571772, -52.67334938049317 47.58625381345426, -52.741935 47.58625381345426, -52.81050682067872 47.58625381345426, -52.81050682067872 47.571772, -52.81050682067872 47.55730028765075))"

// centered on General Hospital, zoom level 11, padded by 0.5
const polygon03 =
"POLYGON ((-57.13165283203125 46.642388783472754, -52.741935 46.642388783472754, -48.35357666015625 46.642388783472754, -48.35357666015625 47.571772, -48.35357666015625 48.49544709355706, -52.741935 48.49544709355706, -57.13165283203125 48.49544709355706, -57.13165283203125 47.571772, -57.13165283203125 46.642388783472754))"

const locationQueryTemplate = {
operationName: null,
variables: {
locationQuery: {
pageSize: 0,
withinPolygon: "",
sortBy: "NAME"
}
},
query: `
query($locationQuery: LocationSearchQueryInput) {
locationList(query: $locationQuery) {
totalCount
list {
name
}
}
}
`
}

const reportQueryTemplate = {
operationName: null,
variables: {
reportQuery: {
pageSize: 0,
state: "PUBLISHED",
sortBy: "ENGAGEMENT_DATE",
withinPolygon: ""
}
},
query: `
query ($reportQuery: ReportSearchQueryInput) {
reportList(query: $reportQuery) {
totalCount
list {
location {
name
}
}
}
}
`
}

const positionQueryTemplate = {
operationName: null,
variables: {
positionQuery: {
pageSize: 0,
withinPolygon: ""
}
},
query: `
query ($positionQuery: PositionSearchQueryInput) {
positionList(query: $positionQuery) {
totalCount
list {
name
location {
name
}
}
}
}
`
}

describe("Location geo search, when map center is (-52.741935 47.571772)", () => {
it("should return one location for visible map bounds", async() => {
const query = lodash.cloneDeep(locationQueryTemplate)
query.variables.locationQuery.withinPolygon = polygon01
const result = await axios.post(apiUrl, query)
expect(result?.status).to.equal(200)

const resultList = result?.data?.data?.locationList?.list || []
expect(resultList.length).to.equal(1)
expect(resultList[0].name).to.equal("General Hospital")
})

const locations01 = [
"Cabot Tower",
"Fort Amherst",
"General Hospital",
"Murray's Hotel",
"Wishingwells Park"
]

it("should return 5 locations for padded (0.5) map bounds", async() => {
const query = lodash.cloneDeep(locationQueryTemplate)
query.variables.locationQuery.withinPolygon = polygon02
const result = await axios.post(apiUrl, query)
expect(result?.status).to.equal(200)

const resultList = result?.data?.data?.locationList?.list || []
expect(resultList.length).to.equal(5)
expect(resultList[0].name).to.equal(locations01[0])
expect(resultList[1].name).to.equal(locations01[1])
expect(resultList[2].name).to.equal(locations01[2])
expect(resultList[3].name).to.equal(locations01[3])
expect(resultList[4].name).to.equal(locations01[4])
})

it("should return 9 locations in zoom level 11 for padded (0.5) map bounds", async() => {
const query = lodash.cloneDeep(locationQueryTemplate)
query.variables.locationQuery.withinPolygon = polygon03
const result = await axios.post(apiUrl, query)
expect(result?.status).to.equal(200)

const resultList = result?.data?.data?.locationList?.list || []
expect(resultList.length).to.equal(9)
expect(resultList[0].name).to.equal("Cabot Tower")
expect(resultList[1].name).to.equal("Conception Bay South Police Station")
expect(resultList[2].name).to.equal("Fort Amherst")
expect(resultList[3].name).to.equal("General Hospital")
expect(resultList[4].name).to.equal("Harbour Grace Police Station")
expect(resultList[5].name).to.equal("Murray's Hotel")
expect(resultList[6].name).to.equal("Portugal Cove Ferry Terminal")
expect(resultList[7].name).to.equal("St Johns Airport")
expect(resultList[8].name).to.equal("Wishingwells Park")
})

it("should return reports in only General Hospital for visible map bounds", async() => {
const query = lodash.cloneDeep(reportQueryTemplate)
query.variables.reportQuery.withinPolygon = polygon01
const result = await axios.post(apiUrl, query)
expect(result?.status).to.equal(200)

const resultList = result?.data?.data?.reportList?.list || []
expect(resultList.length).to.be.gt(1)
expect(
resultList.every(r => r.location.name === "General Hospital")
).to.equal(true)
})

it(`should return reports from any of "${locations01.join()}" for padded (0.5) map bounds`, async() => {
const query = lodash.cloneDeep(reportQueryTemplate)
query.variables.reportQuery.withinPolygon = polygon02
const result = await axios.post(apiUrl, query)
expect(result?.status).to.equal(200)

const resultList = result?.data?.data?.reportList?.list || []
expect(resultList.length).to.be.gt(1)
resultList.forEach(r => expect(r.location.name).to.be.oneOf(locations01))
})

it("shouldn't return any positions for visible map bounds", async() => {
const query = lodash.cloneDeep(positionQueryTemplate)
query.variables.positionQuery.withinPolygon = polygon01
const result = await axios.post(apiUrl, query)
expect(result?.status).to.equal(200)

const resultList = result?.data?.data?.positionList?.list || []
expect(resultList.length).to.equal(0)
})

it("should return one position for padded map bounds", async() => {
const query = lodash.cloneDeep(positionQueryTemplate)
query.variables.positionQuery.withinPolygon = polygon02
const result = await axios.post(apiUrl, query)
expect(result?.status).to.equal(200)

const resultList = result?.data?.data?.positionList?.list || []
expect(resultList.length).to.equal(1)
expect(resultList[0].name).to.equal("Planning Captain")
expect(resultList[0].location.name).to.equal("Wishingwells Park")
})
})
9 changes: 8 additions & 1 deletion client/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4286,6 +4286,13 @@ axe-core@^4.0.2:
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.0.2.tgz#c7cf7378378a51fcd272d3c09668002a4990b1cb"
integrity sha512-arU1h31OGFu+LPrOLGZ7nB45v940NMDMEJeNmbutu57P+UFDVnkZg3e+J1I2HJRZ9hT7gO8J91dn/PMrAiKakA==

axios@0.21.0:
version "0.21.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.0.tgz#26df088803a2350dff2c27f96fef99fe49442aca"
integrity sha512-fmkJBknJKoZwem3/IKSSLpkdNXZeBu5Q7GA/aRsr2btgrptmSCxi2oFjZHqGdK9DoTil9PIHlPIZw2EcRJXRvw==
dependencies:
follow-redirects "^1.10.0"

axios@^0.19.2:
version "0.19.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27"
Expand Down Expand Up @@ -8555,7 +8562,7 @@ follow-redirects@1.5.10:
dependencies:
debug "=3.1.0"

follow-redirects@^1.0.0:
follow-redirects@^1.0.0, follow-redirects@^1.10.0:
version "1.13.0"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db"
integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==
Expand Down
37 changes: 19 additions & 18 deletions insertBaseData-mssql.sql
Original file line number Diff line number Diff line change
Expand Up @@ -437,24 +437,24 @@ INSERT INTO approvers (approvalStepUuid, positionUuid)
AND approvalSteps.type = 1;

-- Create locations
INSERT INTO locations (uuid, name, lat, lng, createdAt, updatedAt)
VALUES (N'cc49bb27-4d8f-47a8-a9ee-af2b68b992ac', 'St Johns Airport', 47.613442, -52.740936, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO locations (uuid, name, lat, lng, createdAt, updatedAt)
VALUES (N'8c138750-91ce-41bf-9b4c-9f0ddc73608b', 'Murray''s Hotel', 47.561517, -52.708760, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO locations (uuid, name, lat, lng, createdAt, updatedAt)
VALUES (N'9c982685-5946-4dad-a7ee-0f5a12f5e170', 'Wishingwells Park', 47.560040, -52.736962, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO locations (uuid, name, lat, lng, createdAt, updatedAt)
VALUES (N'0855fb0a-995e-4a79-a132-4024ee2983ff', 'General Hospital', 47.571772, -52.741935, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO locations (uuid, name, lat, lng, createdAt, updatedAt)
VALUES (N'95446f93-249b-4aa9-b98a-7bd2c4680718', 'Portugal Cove Ferry Terminal', 47.626718, -52.857241, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO locations (uuid, name, lat, lng, createdAt, updatedAt)
VALUES (N'c8fdb53f-6f93-46fc-b0fa-f005c7b49667', 'Cabot Tower', 47.570010, -52.681770, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO locations (uuid, name, lat, lng, createdAt, updatedAt)
VALUES (N'c7a9f420-457a-490c-a810-b504c022cf1e', 'Fort Amherst', 47.563763, -52.680590, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO locations (uuid, name, lat, lng, createdAt, updatedAt)
VALUES (N'7339f9e3-99d1-497a-9e3b-1269c4c287fe', 'Harbour Grace Police Station', 47.705133, -53.214422, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO locations (uuid, name, lat, lng, createdAt, updatedAt)
VALUES (N'f2207d9b-204b-4cb5-874d-3fe6bc6f8acd', 'Conception Bay South Police Station', 47.526784, -52.954739, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO locations (uuid, name, lat, lng, geometry, createdAt, updatedAt)
VALUES (N'cc49bb27-4d8f-47a8-a9ee-af2b68b992ac', 'St Johns Airport', 47.613442, -52.740936, geometry::Point(-52.740936, 47.613442, 3857), CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO locations (uuid, name, lat, lng, geometry, createdAt, updatedAt)
VALUES (N'8c138750-91ce-41bf-9b4c-9f0ddc73608b', 'Murray''s Hotel', 47.561517, -52.708760, geometry::Point(-52.708760, 47.561517, 3857), CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO locations (uuid, name, lat, lng, geometry, createdAt, updatedAt)
VALUES (N'9c982685-5946-4dad-a7ee-0f5a12f5e170', 'Wishingwells Park', 47.560040, -52.736962, geometry::Point(-52.736962, 47.560040, 3857), CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO locations (uuid, name, lat, lng, geometry, createdAt, updatedAt)
VALUES (N'0855fb0a-995e-4a79-a132-4024ee2983ff', 'General Hospital', 47.571772, -52.741935, geometry::Point(-52.741935, 47.571772, 3857), CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO locations (uuid, name, lat, lng, geometry, createdAt, updatedAt)
VALUES (N'95446f93-249b-4aa9-b98a-7bd2c4680718', 'Portugal Cove Ferry Terminal', 47.626718, -52.857241, geometry::Point(-52.857241, 47.626718, 3857), CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO locations (uuid, name, lat, lng, geometry, createdAt, updatedAt)
VALUES (N'c8fdb53f-6f93-46fc-b0fa-f005c7b49667', 'Cabot Tower', 47.570010, -52.681770, geometry::Point(-52.681770, 47.570010, 3857), CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO locations (uuid, name, lat, lng, geometry, createdAt, updatedAt)
VALUES (N'c7a9f420-457a-490c-a810-b504c022cf1e', 'Fort Amherst', 47.563763, -52.680590, geometry::Point(-52.680590, 47.563763, 3857), CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO locations (uuid, name, lat, lng, geometry, createdAt, updatedAt)
VALUES (N'7339f9e3-99d1-497a-9e3b-1269c4c287fe', 'Harbour Grace Police Station', 47.705133, -53.214422, geometry::Point(-53.214422, 47.705133, 3857), CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO locations (uuid, name, lat, lng, geometry, createdAt, updatedAt)
VALUES (N'f2207d9b-204b-4cb5-874d-3fe6bc6f8acd', 'Conception Bay South Police Station', 47.526784, -52.954739, geometry::Point(-52.954739, 47.526784, 3857), CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO locations (uuid, name, createdAt, updatedAt)
VALUES (N'e0ff0d6c-e663-4639-a44d-b075bf1e690d', 'MoD Headquarters Kabul', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
INSERT INTO locations (uuid, name, createdAt, updatedAt)
Expand Down Expand Up @@ -554,6 +554,7 @@ INSERT INTO positionRelationships (positionUuid_a, positionUuid_b, createdAt, up

UPDATE positions SET locationUuid = (SELECT uuid from LOCATIONS where name = 'Kabul Police Academy') WHERE name = 'Chief of Police';
UPDATE positions SET locationUuid = (SELECT uuid from LOCATIONS where name = 'MoD Headquarters Kabul') WHERE name = 'Cost Adder - MoD';
UPDATE positions SET locationUuid = (SELECT uuid from LOCATIONS where name = 'Wishingwells Park') WHERE name = 'Planning Captain';

--Write a couple reports!
DECLARE @reportUuid varchar(36);
Expand Down
2 changes: 2 additions & 0 deletions mssql2pg.pl
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,5 @@
s/cast\((\S+) as datetime2\((\d+)\)\)/"date_trunc(" . ($2 eq '3' ? "'milliseconds'" : "'second'") . ", $1)"/ie;
# Function to generate uuid's
s/lower\(newid\(\)\)/uuid_generate_v4()/g;
# convert point geometry functions
s/geometry::Point\((-\d{2}\.\d{6}, \d{2}\.\d{6})/ST_SetSRID(ST_MakePoint($1)/g;
2 changes: 2 additions & 0 deletions prepare-psql.sql
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ CREATE OR REPLACE AGGREGATE tsvector_agg (tsvector) (

-- make sure the preconditions for UUID prefix search are met
CREATE EXTENSION IF NOT EXISTS pg_trgm;

CREATE EXTENSION IF NOT EXISTS postgis;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please add a comment why this extension is added.

15 changes: 15 additions & 0 deletions src/main/java/mil/dds/anet/beans/search/LocationSearchQuery.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
package mil.dds.anet.beans.search;

import io.leangen.graphql.annotations.GraphQLInputField;
import io.leangen.graphql.annotations.GraphQLQuery;

public class LocationSearchQuery extends AbstractSearchQuery<LocationSearchSortBy> {

@GraphQLQuery
@GraphQLInputField
private String withinPolygon;

public LocationSearchQuery() {
super(LocationSearchSortBy.NAME);
}

public String getWithinPolygon() {
return withinPolygon;
}

public void setWithinPolygon(String withinPolygon) {
this.withinPolygon = withinPolygon;
}

@Override
public LocationSearchQuery clone() throws CloneNotSupportedException {
return (LocationSearchQuery) super.clone();
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/mil/dds/anet/beans/search/PositionSearchQuery.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ public class PositionSearchQuery extends AbstractSearchQuery<PositionSearchSortB
@GraphQLQuery
@GraphQLInputField
private Boolean hasCounterparts;
@GraphQLQuery
@GraphQLInputField
private String withinPolygon;

public PositionSearchQuery() {
super(PositionSearchSortBy.NAME);
Expand Down Expand Up @@ -103,6 +106,14 @@ public void setHasCounterparts(Boolean hasCounterparts) {
this.hasCounterparts = hasCounterparts;
}

public String getWithinPolygon() {
return withinPolygon;
}

public void setWithinPolygon(String withinPolygon) {
this.withinPolygon = withinPolygon;
}

@Override
public int hashCode() {
return Objects.hash(super.hashCode(), matchPersonName, organizationUuid, orgRecurseStrategy,
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/mil/dds/anet/beans/search/ReportSearchQuery.java
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ public class ReportSearchQuery extends AbstractSearchQuery<ReportSearchSortBy> {
@GraphQLQuery
@GraphQLInputField
private Boolean sensitiveInfo;
@GraphQLQuery
@GraphQLInputField
private String withinPolygon;

// internal search parameter:
private boolean systemSearch;

Expand Down Expand Up @@ -358,6 +362,14 @@ public void setSensitiveInfo(Boolean sensitiveInfo) {
this.sensitiveInfo = sensitiveInfo;
}

public String getWithinPolygon() {
return withinPolygon;
}

public void setWithinPolygon(String withinPolygon) {
this.withinPolygon = withinPolygon;
}

@JsonIgnore
public boolean isSystemSearch() {
return systemSearch;
Expand Down
Loading