Skip to content
Merged
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
5 changes: 4 additions & 1 deletion Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,14 @@
(<https://github.com/aws/graph-explorer/pull/500>)
- Fix running in production mode locally with Node
(<https://github.com/aws/graph-explorer/pull/500>)
- Add type checking and linting to server code
(<https://github.com/aws/graph-explorer/pull/522>)
- Update dependencies (<https://github.com/aws/graph-explorer/pull/475>,
<https://github.com/aws/graph-explorer/pull/486>,
<https://github.com/aws/graph-explorer/pull/490>,
<https://github.com/aws/graph-explorer/pull/492>,
<https://github.com/aws/graph-explorer/pull/491>)
<https://github.com/aws/graph-explorer/pull/491>,
<https://github.com/aws/graph-explorer/pull/522>)

## Release 1.8.0

Expand Down
13 changes: 3 additions & 10 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import globals from "globals";
import pluginJs from "@eslint/js";
import tseslint from "typescript-eslint";
import pluginReactConfig from "eslint-plugin-react/configs/recommended.js";
import reactLint from "eslint-plugin-react";
import reactHooksLint from "eslint-plugin-react-hooks";
import tanstackQueryLint from "@tanstack/eslint-plugin-query";
import eslintConfigPrettier from "eslint-config-prettier";
import {
fixupConfigRules,
fixupPluginRules,
includeIgnoreFile,
} from "@eslint/compat";
import { fixupPluginRules, includeIgnoreFile } from "@eslint/compat";
import path from "node:path";
import { fileURLToPath } from "node:url";

Expand All @@ -19,14 +15,11 @@ const gitignorePath = path.resolve(__dirname, ".gitignore");

export default [
includeIgnoreFile(gitignorePath),
{
ignores: ["packages/graph-explorer-proxy-server/*"],
},
{ languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } } },
{ languageOptions: { globals: { ...globals.browser, ...globals.node } } },
pluginJs.configs.recommended,
...tseslint.configs.recommendedTypeChecked,
...fixupConfigRules(pluginReactConfig),
reactLint.configs.flat.recommended,
{
plugins: { "react-hooks": fixupPluginRules(reactHooksLint) },
rules: { ...reactHooksLint.configs.recommended.rules },
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"test": "pnpm --stream -r run test",
"test:watch": "pnpm --stream -r run test:watch",
"coverage": "pnpm --stream -r run coverage",
"check:types": "tsc --project packages/graph-explorer/",
"check:types": "tsc --project packages/graph-explorer/ && tsc --project packages/graph-explorer-proxy-server",
"checks": "pnpm run '/^check:.*/'",
"start": "pnpm --filter \"graph-explorer-proxy-server\" run start",
"start:client": "pnpm --filter \"graph-explorer\" run serve",
Expand All @@ -31,7 +31,7 @@
"@tanstack/eslint-plugin-query": "^5.51.1",
"eslint": "^9.7.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-react": "^7.34.1",
"eslint-plugin-react": "^7.35.0",
"eslint-plugin-react-hooks": "^4.6.2",
"globals": "^15.8.0",
"husky": "^9.0.11",
Expand Down
63 changes: 34 additions & 29 deletions packages/graph-explorer-proxy-server/node-server.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import express from "express";
import express, { NextFunction, Request, Response } from "express";
import cors from "cors";
import compression from "compression";
import dotenv from "dotenv";
import fetch from "node-fetch";
import fetch, { RequestInit } from "node-fetch";
import https from "https";
import bodyParser from "body-parser";
import fs from "fs";
Expand All @@ -24,7 +24,6 @@ dotenv.config({
});

const DEFAULT_SERVICE_TYPE = "neptune-db";
const NEPTUNE_ANALYTICS_SERVICE_TYPE = "neptune-graph";

interface DbQueryIncomingHttpHeaders extends IncomingHttpHeaders {
queryid?: string;
Expand All @@ -44,7 +43,7 @@ app.use(requestLoggingMiddleware(proxyLogger));
// Function to get IAM headers for AWS4 signing process.
async function getIAMHeaders(options) {
const credentialProvider = fromNodeProviderChain();
let creds = await credentialProvider();
const creds = await credentialProvider();
if (creds === undefined) {
throw new Error(
"IAM is enabled but credentials cannot be found on the credential provider chain."
Expand All @@ -60,7 +59,12 @@ async function getIAMHeaders(options) {
}

// Error handler middleware to log errors and send appropriate response.
const errorHandler = (error, request, response, next) => {
const errorHandler = (
error: any,
_request: Request,
response: Response,
_next: NextFunction
) => {
if (error.extraInfo) {
proxyLogger.error(error.extraInfo + error.message);
proxyLogger.debug(error.stack);
Expand All @@ -80,11 +84,11 @@ const errorHandler = (error, request, response, next) => {

// Function to retry fetch requests with exponential backoff.
const retryFetch = async (
url,
options,
isIamEnabled,
region,
serviceType,
url: URL,
options: any,
isIamEnabled: boolean,
region: string | undefined,
serviceType: string,
retryDelay = 10000,
refetchMaxRetries = 1
) => {
Expand Down Expand Up @@ -149,13 +153,13 @@ const retryFetch = async (

// Function to fetch data from the given URL and send it as a response.
async function fetchData(
res,
next,
url,
options,
isIamEnabled,
region,
serviceType
res: Response,
next: NextFunction,
url: string,
options: RequestInit,
isIamEnabled: boolean,
region: string | undefined,
serviceType: string
) {
try {
const response = await retryFetch(
Expand Down Expand Up @@ -198,7 +202,7 @@ if (staticFilesVirtualPath) {
}

// POST endpoint for SPARQL queries.
app.post("/sparql", async (req, res, next) => {
app.post("/sparql", (req, res, next) => {
// Gather info from the headers
const headers = req.headers as DbQueryIncomingHttpHeaders;
const queryId = headers["queryid"];
Expand Down Expand Up @@ -232,7 +236,7 @@ app.post("/sparql", async (req, res, next) => {
);
} catch (err) {
// Not really an error
proxyLogger.warn("Failed to cancel the query: " + err);
proxyLogger.warn("Failed to cancel the query: %o", err);
}
}

Expand Down Expand Up @@ -281,7 +285,7 @@ app.post("/sparql", async (req, res, next) => {
});

// POST endpoint for Gremlin queries.
app.post("/gremlin", async (req, res, next) => {
app.post("/gremlin", (req, res, next) => {
// Gather info from the headers
const headers = req.headers as DbQueryIncomingHttpHeaders;
const queryId = headers["queryid"];
Expand Down Expand Up @@ -317,7 +321,7 @@ app.post("/gremlin", async (req, res, next) => {
);
} catch (err) {
// Not really an error
proxyLogger.warn("Failed to cancel the query: " + err);
proxyLogger.warn("Failed to cancel the query: %o", err);
}
}

Expand Down Expand Up @@ -358,15 +362,16 @@ app.post("/gremlin", async (req, res, next) => {
});

// POST endpoint for openCypher queries.
app.post("/openCypher", async (req, res, next) => {
app.post("/openCypher", (req, res, next) => {
// Validate the input before making any external calls.
if (!req.body.query) {
return res
.status(400)
.send({ error: "[Proxy]OpenCypher: query not provided" });
}

const rawUrl = `${req.headers["graph-db-connection-url"]}/openCypher`;
const headers = req.headers as DbQueryIncomingHttpHeaders;
const rawUrl = `${headers["graph-db-connection-url"]}/openCypher`;
const requestOptions = {
method: "POST",
headers: {
Expand All @@ -376,10 +381,10 @@ app.post("/openCypher", async (req, res, next) => {
body: `query=${encodeURIComponent(req.body.query)}`,
};

const isIamEnabled = !!req.headers["aws-neptune-region"];
const region = isIamEnabled ? req.headers["aws-neptune-region"] : "";
const isIamEnabled = !!headers["aws-neptune-region"];
const region = isIamEnabled ? headers["aws-neptune-region"] : "";
const serviceType = isIamEnabled
? req.headers["service-type"] ?? DEFAULT_SERVICE_TYPE
? headers["service-type"] ?? DEFAULT_SERVICE_TYPE
: "";

fetchData(
Expand All @@ -394,7 +399,7 @@ app.post("/openCypher", async (req, res, next) => {
});

// GET endpoint to retrieve PropertyGraph statistics summary for Neptune Analytics.
app.get("/summary", async (req, res, next) => {
app.get("/summary", (req, res, next) => {
const headers = req.headers as DbQueryIncomingHttpHeaders;
const isIamEnabled = !!headers["aws-neptune-region"];
const serviceType = isIamEnabled
Expand All @@ -420,7 +425,7 @@ app.get("/summary", async (req, res, next) => {
});

// GET endpoint to retrieve PropertyGraph statistics summary for Neptune DB.
app.get("/pg/statistics/summary", async (req, res, next) => {
app.get("/pg/statistics/summary", (req, res, next) => {
const headers = req.headers as DbQueryIncomingHttpHeaders;
const isIamEnabled = !!headers["aws-neptune-region"];
const serviceType = isIamEnabled
Expand All @@ -446,7 +451,7 @@ app.get("/pg/statistics/summary", async (req, res, next) => {
});

// GET endpoint to retrieve RDF statistics summary.
app.get("/rdf/statistics/summary", async (req, res, next) => {
app.get("/rdf/statistics/summary", (req, res, next) => {
const headers = req.headers as DbQueryIncomingHttpHeaders;
const isIamEnabled = !!headers["aws-neptune-region"];
const serviceType = isIamEnabled
Expand Down
Loading