From 013fef0f0de20bc8676689423d313e2c8c19743c Mon Sep 17 00:00:00 2001 From: Peter Rauscher Date: Wed, 9 Nov 2022 23:45:48 +0000 Subject: [PATCH] OAP-38: Add /ngrams endpoint to API (#19) * Added data function to query ngrams within the api * removed cleaning and seeding tasks from API level, handled at engine level * Added /:handle/ngrams endpoint to API routes * Reflected change from uuid to handle in log messages within API * Just some API readme changes * Added regex to routes to mitigate url decoding, plus added validation function for handle Co-authored-by: j-sofia Co-authored-by: Peter Rauscher Co-authored-by: j-sofia --- api/README.md | 13 +- api/app.js | 3 +- api/db/clean.js | 17 --- api/db/data.js | 17 ++- api/db/seed.js | 14 --- api/package-lock.json | 283 +++++++++++++++++++++++++++++++++++++++++- api/package.json | 2 - api/routes.js | 37 ++++-- api/validate.js | 3 +- 9 files changed, 336 insertions(+), 53 deletions(-) delete mode 100644 api/db/clean.js delete mode 100644 api/db/seed.js diff --git a/api/README.md b/api/README.md index 62f6bdc..ddc624e 100644 --- a/api/README.md +++ b/api/README.md @@ -20,16 +20,21 @@ DATABASE_URL="postgres://postgres:password@localhost:5432/postgres" PORT=3001 ``` +To populate the database with seed data, run `make setup-env` from `oapen-engine/` + ## Running with npm ``` npm ci -npm run clean npm start ``` -Endpoint: /GET http://localhost:3001/api/{item_uuid} +## Endpoints -e.g. http://localhost:3001/api/a91a6b7d-874a-4144-b44d-0da647a82acc +- `Endpoint: /GET http://localhost:3001/api/{handle}` -To populate the database with seed data, run `make setup-env` from `oapen-engine/` + - e.g. http://localhost:3001/api/20.400.12657/47581 + +- `Endpoint: /GET http://localhost:3001/api/{handle}/ngrams` + + - e.g. http://localhost:3001/api/20.400.12657/47581/ngrams diff --git a/api/app.js b/api/app.js index 00f9d7a..216dbb4 100644 --- a/api/app.js +++ b/api/app.js @@ -17,5 +17,6 @@ const port = process.env.PORT || 3001; app.listen(port, () => { console.log("Suggestion Service API is up on port " + port); - console.log("Endpoint: /GET http://localhost:" + port + "/api/{item_uuid}"); + console.log("Endpoint: /GET http://localhost:" + port + "/api/{handle}"); + console.log("Endpoint: /GET http://localhost:" + port + "/api/{handle}/ngrams") }); diff --git a/api/db/clean.js b/api/db/clean.js deleted file mode 100644 index e635cf5..0000000 --- a/api/db/clean.js +++ /dev/null @@ -1,17 +0,0 @@ -require("dotenv").config({ path: "./db/database.env" }); -const db = require("./connection.js"); - -db.none(` -DROP SCHEMA IF EXISTS oapen_suggestions CASCADE; -DROP TABLE IF EXISTS suggestions CASCADE; -DROP TYPE IF EXISTS suggestion CASCADE; -`); - -db.none(` -CREATE TYPE suggestion AS (id uuid, rank int); -CREATE SCHEMA oapen_suggestions - CREATE TABLE IF NOT EXISTS suggestions ( - item_id uuid PRIMARY KEY, - name text, - suggestions suggestion[] - );`); diff --git a/api/db/data.js b/api/db/data.js index 841afbc..fe58384 100644 --- a/api/db/data.js +++ b/api/db/data.js @@ -3,10 +3,20 @@ const { ParameterizedQuery: PQ } = require("pg-promise"); const db = require("./connection.js"); -async function querySuggestions(id) { - await validate.checkHandle(id); +async function querySuggestions(handle) { + await validate.checkHandle(handle); - const query = new PQ({ text: "SELECT * FROM oapen_suggestions.suggestions WHERE item_id = $1", values: [id] }); + const query = new PQ({ text: "SELECT * FROM oapen_suggestions.suggestions WHERE handle = $1", values: [handle] }); + + return db.one(query).catch((error) => { + return { error: { name: error.name, message: error.message } }; + }); +} + +async function queryNgrams(handle) { + await validate.checkHandle(handle); + + const query = new PQ({ text: "SELECT * FROM oapen_suggestions.ngrams WHERE handle = $1", values: [handle] }); return db.one(query).catch((error) => { return { error: { name: error.name, message: error.message } }; @@ -15,4 +25,5 @@ async function querySuggestions(id) { module.exports = { querySuggestions, + queryNgrams, }; diff --git a/api/db/seed.js b/api/db/seed.js deleted file mode 100644 index c81e85b..0000000 --- a/api/db/seed.js +++ /dev/null @@ -1,14 +0,0 @@ -require("dotenv").config({ path: "./db/database.env" }); -const db = require("./connection.js"); - -//seed can be run from "make setup-env" from oapen-engine/ -//"7b5fdf5b-9ffa-4073-84fe-c21cce0025b5" "Energy Poverty, Practice, and Policy" "{""(7b5fdf5b-9ffa-4073-84fe-c21cce0025b5,0)""}" -//"a91a6b7d-874a-4144-b44d-0da647a82acc" "The Future European Energy System" "{""(a91a6b7d-874a-4144-b44d-0da647a82acc,1)""}" -//"858df59f-0014-4355-8435-dca9c187ce0c" "Literatura latinoamericana mundial" "{""(858df59f-0014-4355-8435-dca9c187ce0c,2)""}" -//"44c40c47-df67-476a-9603-3054f726b156" "Religion and Governance in England’s Emerging Colonial Empire, 1601–1698" "{""(44c40c47-df67-476a-9603-3054f726b156,3)""}" -//"2ce07264-58ac-426a-9c88-500f8b47e7f5" "Open Science: the Very Idea" "{""(2ce07264-58ac-426a-9c88-500f8b47e7f5,4)""}" -//"4ba6ae5d-1797-4def-b0d7-1cd8652e5cd9" "Embodying Contagion" "{""(4ba6ae5d-1797-4def-b0d7-1cd8652e5cd9,5)""}" -//"57753423-cb8a-4f08-815d-ac7aa19b049b" "Thou Shalt Forget" "{""(57753423-cb8a-4f08-815d-ac7aa19b049b,6)""}" -//"7df13adb-771b-4d66-b081-2345059622bc" "Engines of Order" "{""(7df13adb-771b-4d66-b081-2345059622bc,7)""}" -//"ae797d93-6c5e-46ee-a193-45cd7c114e65" "Atlas" "{""(ae797d93-6c5e-46ee-a193-45cd7c114e65,8)""}" -//"8e001ebf-5e63-44f5-9f31-5d79389e0f14" "Vulnerable" "{""(8e001ebf-5e63-44f5-9f31-5d79389e0f14,9)""}" diff --git a/api/package-lock.json b/api/package-lock.json index 8991f25..8ece092 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -7,11 +7,13 @@ "": { "name": "oapen-suggestion-service-api", "version": "0.0.1", - "license": "ISC", + "license": "../LICENSE.md", "dependencies": { + "dotenv": "^16.0.3", "express": "^4.18.1", "node-watch": "^0.7.3", - "nodemon": "^2.0.20" + "nodemon": "^2.0.20", + "pg-promise": "^10.12.0" } }, "node_modules/abbrev": { @@ -48,6 +50,14 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, + "node_modules/assert-options": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/assert-options/-/assert-options-0.7.0.tgz", + "integrity": "sha512-7q9uNH/Dh8gFgpIIb9ja8PJEWA5AQy3xnBC8jtKs8K/gNVCr1K6kIvlm59HUyYgvM7oEDoLzGgPcGd9FqhtXEQ==", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -104,6 +114,14 @@ "node": ">=8" } }, + "node_modules/buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", + "engines": { + "node": ">=4" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -667,6 +685,102 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, + "node_modules/pg": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.8.0.tgz", + "integrity": "sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw==", + "dependencies": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.5.0", + "pg-pool": "^3.5.2", + "pg-protocol": "^1.5.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-connection-string": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", + "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-minify": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/pg-minify/-/pg-minify-1.6.2.tgz", + "integrity": "sha512-1KdmFGGTP6jplJoI8MfvRlfvMiyBivMRP7/ffh4a11RUFJ7kC2J0ZHlipoKiH/1hz+DVgceon9U2qbaHpPeyPg==", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/pg-pool": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.2.tgz", + "integrity": "sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-promise": { + "version": "10.12.1", + "resolved": "https://registry.npmjs.org/pg-promise/-/pg-promise-10.12.1.tgz", + "integrity": "sha512-SiJkBUDGq7PNfJFJbWferodsSH+vLrhte0Q0kVgQbwlNYeKmp9Hhkr+357+5DWEuBGOHhSu1UQffSSf5HVqRtA==", + "dependencies": { + "assert-options": "0.7.0", + "pg": "8.8.0", + "pg-minify": "1.6.2", + "spex": "3.2.0" + }, + "engines": { + "node": ">=12.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz", + "integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dependencies": { + "split2": "^4.1.0" + } + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -678,6 +792,41 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -853,6 +1002,22 @@ "semver": "bin/semver.js" } }, + "node_modules/spex": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spex/-/spex-3.2.0.tgz", + "integrity": "sha512-9srjJM7NaymrpwMHvSmpDeIK5GoRMX/Tq0E8aOlDPS54dDnDUIp30DrP9SphMPEETDLzEM9+4qo+KipmbtPecg==", + "engines": { + "node": ">=4.5" + } + }, + "node_modules/split2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", + "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -981,6 +1146,11 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, + "assert-options": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/assert-options/-/assert-options-0.7.0.tgz", + "integrity": "sha512-7q9uNH/Dh8gFgpIIb9ja8PJEWA5AQy3xnBC8jtKs8K/gNVCr1K6kIvlm59HUyYgvM7oEDoLzGgPcGd9FqhtXEQ==" + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1027,6 +1197,11 @@ "fill-range": "^7.0.1" } }, + "buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" + }, "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -1434,11 +1609,105 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, + "pg": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.8.0.tgz", + "integrity": "sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw==", + "requires": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.5.0", + "pg-pool": "^3.5.2", + "pg-protocol": "^1.5.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + } + }, + "pg-connection-string": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", + "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" + }, + "pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" + }, + "pg-minify": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/pg-minify/-/pg-minify-1.6.2.tgz", + "integrity": "sha512-1KdmFGGTP6jplJoI8MfvRlfvMiyBivMRP7/ffh4a11RUFJ7kC2J0ZHlipoKiH/1hz+DVgceon9U2qbaHpPeyPg==" + }, + "pg-pool": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.2.tgz", + "integrity": "sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==", + "requires": {} + }, + "pg-promise": { + "version": "10.12.1", + "resolved": "https://registry.npmjs.org/pg-promise/-/pg-promise-10.12.1.tgz", + "integrity": "sha512-SiJkBUDGq7PNfJFJbWferodsSH+vLrhte0Q0kVgQbwlNYeKmp9Hhkr+357+5DWEuBGOHhSu1UQffSSf5HVqRtA==", + "requires": { + "assert-options": "0.7.0", + "pg": "8.8.0", + "pg-minify": "1.6.2", + "spex": "3.2.0" + } + }, + "pg-protocol": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz", + "integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==" + }, + "pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "requires": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + } + }, + "pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "requires": { + "split2": "^4.1.0" + } + }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, + "postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" + }, + "postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==" + }, + "postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==" + }, + "postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "requires": { + "xtend": "^4.0.0" + } + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -1568,6 +1837,16 @@ } } }, + "spex": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spex/-/spex-3.2.0.tgz", + "integrity": "sha512-9srjJM7NaymrpwMHvSmpDeIK5GoRMX/Tq0E8aOlDPS54dDnDUIp30DrP9SphMPEETDLzEM9+4qo+KipmbtPecg==" + }, + "split2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", + "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==" + }, "statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", diff --git a/api/package.json b/api/package.json index c58a083..50b53aa 100644 --- a/api/package.json +++ b/api/package.json @@ -4,8 +4,6 @@ "description": "An API which allows you to search for books with similar content from the oapen.org library.", "main": "app.js", "scripts": { - "clean": "node ./db/clean.js", - "seed": "node ./db/seed.js", "dev": "nodemon ./app.js", "start": "node app.js" }, diff --git a/api/routes.js b/api/routes.js index 32d37bd..fbf0d8c 100644 --- a/api/routes.js +++ b/api/routes.js @@ -1,11 +1,12 @@ const express = require("express"); const router = express.Router(); -const validate = require("./validate"); +const pgp = require("pg-promise"); +const validate = require("./validate"); const data = require("./db/data.js"); -//GET endpoint -router.get("/:handle", async (req, res) => { +//GET endpoint for suggestions +router.get("/:handle([0-9]+.[0-9]+.[0-9]+/[0-9]+)", async (req, res) => { try { var handle = req.params.handle; await validate.checkHandle(handle); @@ -13,16 +14,34 @@ router.get("/:handle", async (req, res) => { let responseData = await data.querySuggestions(handle); if (responseData.error && responseData.error.name === pgp.errors.QueryResultError.name) { - res.status(404).json({ error: responseData.error.message }); - return; + return res.status(404).json({ error: responseData.error.message }); } else if (responseData.error) { - res.status(500).json(responseData); - return; + return res.status(500).json(responseData); } - res.status(200).json(responseData); + return res.status(200).json(responseData); } catch (e) { - res.status(500).json({ error: "Internal server error" }); + return res.status(500).json({ error: "Internal server error" }); + } +}); + +//GET endpoint for ngrams +router.get('/:handle([0-9]+.[0-9]+.[0-9]+/[0-9]+)/ngrams', async (req, res) => { + try { + var handle = req.params.handle; + await validate.checkHandle(handle); + + let responseData = await data.queryNgrams(handle); + + if (responseData.error && responseData.error.name === pgp.errors.QueryResultError.name) { + return res.status(404).json({ error: responseData.error.message }); + } else if (responseData.error) { + return res.status(500).json(responseData); + } + + return res.status(200).json(responseData); + } catch (e) { + return res.status(500).json({ error: "Internal server error" }); } }); diff --git a/api/validate.js b/api/validate.js index 4b9c5ba..36c62ab 100644 --- a/api/validate.js +++ b/api/validate.js @@ -1,5 +1,6 @@ let checkHandle = async (handle) => { - // TODO: Validate the book's handle + if (!handle || typeof handle !== "string" || !handle.trim()) throw "Invalid handle, cannot be undefined."; + if (!new RegExp('([0-9]+.[0-9]+.[0-9]+/[0-9]+)', 'g').test(handle)) throw "Invalid handle, exmaple format: 20.500.12657/47586" return true; };