Skip to content

Commit

Permalink
Remove method from config endpoints (#35)
Browse files Browse the repository at this point in the history
* update request/response logging

* remove method from endpoint config

* update logging in request

* add changeset
  • Loading branch information
gladwindos committed Mar 19, 2024
1 parent 37f1e42 commit b59317c
Show file tree
Hide file tree
Showing 9 changed files with 40 additions and 55 deletions.
5 changes: 5 additions & 0 deletions .changeset/rich-frogs-grab.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@gateweaver/server": patch
---

Update config
13 changes: 0 additions & 13 deletions gateweaver.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,14 @@ policyDefinitions:

endpoints:
- path: "/todos"
method: "GET"
target:
url: "https://jsonplaceholder.typicode.com/todos"
transformedResponse:
headers:
test-header: "test"
policies:
- cors
- rateLimit
- path: "/todos"
method: "POST"
target:
url: "https://jsonplaceholder.typicode.com/todos"
transformedRequest:
headers:
Content-Type: "application/json"
policies:
- cors
- path: "/giphy/trending"
method: "GET"
target:
url: "https://api.giphy.com/v1/gifs/trending"
transformedRequest:
Expand All @@ -45,7 +33,6 @@ endpoints:
policies:
- cors
- path: "/spotify/artist"
method: "GET"
target:
url: "https://api.spotify.com/v1/artists/4Z8W4fKeB5YxbusRsdQVPb"
transformedRequest:
Expand Down
4 changes: 1 addition & 3 deletions packages/server/src/config/config.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
Target,
Endpoint,
Config,
HttpMethod,
TransformedRequest,
TransformedResponse,
} from "./config.types";
Expand Down Expand Up @@ -45,7 +44,6 @@ const endpointSchema: JSONSchemaType<Endpoint> = {
type: "object",
properties: {
path: { type: "string" },
method: { type: "string", enum: Object.values(HttpMethod) },
target: targetSchema,
transformedRequest: { ...transformedRequestSchema, nullable: true },
transformedResponse: { ...transformedResponseSchema, nullable: true },
Expand All @@ -55,7 +53,7 @@ const endpointSchema: JSONSchemaType<Endpoint> = {
nullable: true,
},
},
required: ["path", "method", "target"],
required: ["path", "target"],
};

export const configSchema: JSONSchemaType<Config> = {
Expand Down
9 changes: 0 additions & 9 deletions packages/server/src/config/config.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,6 @@ export interface Target {
url: string;
}

export enum HttpMethod {
GET = "GET",
POST = "POST",
PUT = "PUT",
PATCH = "PATCH",
DELETE = "DELETE",
}

export interface TransformedRequest {
headers?: Record<string, string>;
query?: Record<string, string>;
Expand All @@ -23,7 +15,6 @@ export interface TransformedResponse {

export interface Endpoint {
path: string;
method: HttpMethod;
target: Target;
transformedRequest?: TransformedRequest;
transformedResponse?: TransformedResponse;
Expand Down
14 changes: 3 additions & 11 deletions packages/server/src/config/validate-config.test.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
import { PolicyOption, RateLimitPolicy } from "@gateweaver/policies";
import { Config, HttpMethod } from "./config.types";
import { Config } from "./config.types";
import { validateConfig } from "./validate-config";

describe("validateConfig", () => {
it("should not throw an error if config is valid", () => {
const validConfig: Config = {
endpoints: [
{
method: HttpMethod.GET,
path: "/path1",
target: {
url: "http:https://example.com",
},
},
{
method: HttpMethod.POST,
path: "/path2",
target: {
url: "http:https://example.com",
Expand All @@ -26,18 +24,16 @@ describe("validateConfig", () => {
expect(() => validateConfig(validConfig)).not.toThrow();
});

it("should throw an error if endpoint path/method combination is duplicated", () => {
it("should throw an error if endpoint path is duplicated", () => {
const invalidConfig: Config = {
endpoints: [
{
method: HttpMethod.GET,
path: "/duplicate",
target: {
url: "http:https://example.com",
},
},
{
method: HttpMethod.GET,
path: "/duplicate",
target: {
url: "http:https://example.com",
Expand All @@ -47,15 +43,14 @@ describe("validateConfig", () => {
};

expect(() => validateConfig(invalidConfig)).toThrow(
"Duplicate endpoint path/method combination: GET /duplicate",
"Duplicate endpoint path: /duplicate",
);
});

it("should throw an error if endpoint path is invalid", () => {
const invalidConfig: Config = {
endpoints: [
{
method: HttpMethod.GET,
path: "invalid path",
target: {
url: "http:https://example.com",
Expand All @@ -73,7 +68,6 @@ describe("validateConfig", () => {
const invalidPolicyConfig: Config = {
endpoints: [
{
method: HttpMethod.GET,
path: "/path1",
target: {
url: "http:https://example.com",
Expand All @@ -97,7 +91,6 @@ describe("validateConfig", () => {
const invalidJwtPolicyConfig: Config = {
endpoints: [
{
method: HttpMethod.GET,
path: "/path1",
target: {
url: "http:https://example.com",
Expand All @@ -121,7 +114,6 @@ describe("validateConfig", () => {
const invalidPolicyConfig: Config = {
endpoints: [
{
method: HttpMethod.GET,
path: "/path1",
target: {
url: "http:https://example.com",
Expand Down
12 changes: 6 additions & 6 deletions packages/server/src/config/validate-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@ export class InvalidConfigError extends Error {
}
}

const checkEndpointUniqueness = (
const checkPathUniqueness = (
endpoint: Endpoint,
endpointPaths: Set<string>,
): string | null => {
const pathKey = `${endpoint.method} ${endpoint.path}`;
const pathKey = endpoint.path;

if (endpointPaths.has(pathKey)) {
return `Duplicate endpoint path/method combination: ${pathKey}`;
return `Duplicate endpoint path: ${pathKey}`;
} else {
endpointPaths.add(pathKey);
return null;
}
};

const validateEndpointPath = (endpoint: Endpoint): string | null => {
const validatePathFormat = (endpoint: Endpoint): string | null => {
const pathPattern = /^\/[a-zA-Z0-9\-_/]*\/?$/;
if (!pathPattern.test(endpoint.path)) {
return `Invalid path: '${endpoint.path}'. Must start with / and only contain alphanumeric characters, hyphens, and underscores`;
Expand All @@ -39,12 +39,12 @@ const validateEndpoints = (endpoints: Endpoint[]): string[] => {
const endpointPaths = new Set<string>();

endpoints.forEach((endpoint) => {
const uniquenessError = checkEndpointUniqueness(endpoint, endpointPaths);
const uniquenessError = checkPathUniqueness(endpoint, endpointPaths);
if (uniquenessError) {
errors.push(uniquenessError);
}

const pathError = validateEndpointPath(endpoint);
const pathError = validatePathFormat(endpoint);
if (pathError) {
errors.push(pathError);
}
Expand Down
11 changes: 9 additions & 2 deletions packages/server/src/middleware/http-logger.middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@ import { Request, Response, NextFunction } from "express";
import { logger } from "../utils";

export const httpLogger = (req: Request, res: Response, next: NextFunction) => {
res.on("finish", () => {
logger.info(`[${req.method}] ${req.originalUrl} - ${res.statusCode}`);
const query = Object.keys(req.query).length ? req.query : undefined;
const body = Object.keys(req.body).length ? req.body : undefined;

logger.info({
message: "Incoming request",
method: req.method,
path: req.path,
query,
body,
});
next();
};
4 changes: 2 additions & 2 deletions packages/server/src/router/setup/setup-policies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const setupPolicies = (
!policies.includes(PolicyOption.Jwt)
) {
logger.warn(
`Rate limit by jwt is enabled but jwt policy is not enabled for endpoint ${endpoint.path}`,
`rateLimitBy is set to jwt, but jwt policy is not enabled for endpoint ${endpoint.path}`,
);
}

Expand All @@ -44,7 +44,7 @@ export const setupPolicies = (
!policies.includes(PolicyOption.ApiKey)
) {
logger.warn(
`Rate limit by apiKey is enabled but apiKey policy is not enabled for endpoint ${endpoint.path}`,
`rateLimitBy is set to apiKey, but apiKey policy is not enabled for endpoint ${endpoint.path}`,
);
}

Expand Down
23 changes: 14 additions & 9 deletions packages/server/src/router/setup/setup-proxy.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IncomingMessage } from "http";
import { Router } from "express";
import { Request, Response, Router } from "express";
import { createProxyMiddleware, type Options } from "http-proxy-middleware";
import { PolicyOption } from "@gateweaver/policies";
import { Endpoint } from "../../config/config.types";
Expand All @@ -24,7 +24,11 @@ export const setupProxy = (router: Router, endpoint: Endpoint) => {
return url;
};

const onProxyRes = (proxyRes: IncomingMessage) => {
const onProxyRes = (
proxyRes: IncomingMessage,
req: Request,
res: Response,
) => {
proxyRes.headers = {
...proxyRes.headers,
...endpoint.transformedResponse?.headers,
Expand Down Expand Up @@ -54,6 +58,13 @@ export const setupProxy = (router: Router, endpoint: Endpoint) => {
"Access-Control-Max-Age",
]);
}

logger.info({
message: "Proxy response",
path: endpoint.path,
target: endpoint.target.url,
status: res.statusCode,
});
};

const logProvider = () => {
Expand All @@ -77,11 +88,5 @@ export const setupProxy = (router: Router, endpoint: Endpoint) => {
logLevel: "error",
};

router.use(
endpoint.path,
createProxyMiddleware(
(_, req) => req.method === endpoint.method,
proxyOptions,
),
);
router.use(endpoint.path, createProxyMiddleware(proxyOptions));
};

0 comments on commit b59317c

Please sign in to comment.