Skip to content

Commit

Permalink
fix(middleware): issue with singleton middleware having its original …
Browse files Browse the repository at this point in the history
…called

TODO: Create a middleware index just like a resource index. Associate resources with middleware, middleware with middleware, etc.
  • Loading branch information
crookse committed Jan 8, 2024
1 parent 98dc16c commit 2bbf968
Show file tree
Hide file tree
Showing 13 changed files with 123 additions and 74 deletions.
6 changes: 5 additions & 1 deletion src/modules/middleware/AcceptHeader/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,11 @@ class AcceptHeaderMiddleware extends Middleware {
* provided, it uses its default options.
*/
function AcceptHeader(options: Options = defaultOptions) {

Check failure on line 144 in src/modules/middleware/AcceptHeader/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Bun (ubuntu-latest, 18.x)

Property 'handleHeaders' of exported class expression may not be private or protected.

Check failure on line 144 in src/modules/middleware/AcceptHeader/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Bun (ubuntu-latest, 18.x)

Property 'handleIfAcceptHeaderMissing' of exported class expression may not be private or protected.

Check failure on line 144 in src/modules/middleware/AcceptHeader/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Bun (ubuntu-latest, 18.x)

Property 'sendResponse' of exported class expression may not be private or protected.

Check failure on line 144 in src/modules/middleware/AcceptHeader/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Node (ubuntu-latest, 20.x)

Property 'handleHeaders' of exported class expression may not be private or protected.

Check failure on line 144 in src/modules/middleware/AcceptHeader/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Node (ubuntu-latest, 20.x)

Property 'handleIfAcceptHeaderMissing' of exported class expression may not be private or protected.

Check failure on line 144 in src/modules/middleware/AcceptHeader/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Node (ubuntu-latest, 20.x)

Property 'sendResponse' of exported class expression may not be private or protected.

Check failure on line 144 in src/modules/middleware/AcceptHeader/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Bun (ubuntu-latest, 20.x)

Property 'handleHeaders' of exported class expression may not be private or protected.

Check failure on line 144 in src/modules/middleware/AcceptHeader/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Bun (ubuntu-latest, 20.x)

Property 'handleIfAcceptHeaderMissing' of exported class expression may not be private or protected.

Check failure on line 144 in src/modules/middleware/AcceptHeader/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Bun (ubuntu-latest, 20.x)

Property 'sendResponse' of exported class expression may not be private or protected.
return new AcceptHeaderMiddleware(options);
return class DefaultAcceptHeaderMiddleware extends AcceptHeaderMiddleware {
constructor() {
super(options);
}
};
}

// FILE MARKER - PUBLIC API ////////////////////////////////////////////////////
Expand Down
6 changes: 5 additions & 1 deletion src/modules/middleware/CORS/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,11 @@ class CORSMiddleware extends Middleware {
* provided, it uses its default options.
*/
function CORS(options: Options = defaultOptions) {

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Bun (ubuntu-latest, 18.x)

Property 'setPreflightHeaders' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Bun (ubuntu-latest, 18.x)

Property 'appendHeaderValue' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Bun (ubuntu-latest, 18.x)

Property 'getAllowOriginHeaderValue' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Bun (ubuntu-latest, 18.x)

Property 'getCorsResponseHeaders' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Bun (ubuntu-latest, 18.x)

Property 'setHeaderAllowCredentials' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Bun (ubuntu-latest, 18.x)

Property 'setHeaderAllowHeaders' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Bun (ubuntu-latest, 18.x)

Property 'setHeaderAllowMethods' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Node (ubuntu-latest, 20.x)

Property 'setPreflightHeaders' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Node (ubuntu-latest, 20.x)

Property 'appendHeaderValue' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Node (ubuntu-latest, 20.x)

Property 'getAllowOriginHeaderValue' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Node (ubuntu-latest, 20.x)

Property 'getCorsResponseHeaders' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Node (ubuntu-latest, 20.x)

Property 'setHeaderAllowCredentials' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Node (ubuntu-latest, 20.x)

Property 'setHeaderAllowHeaders' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Node (ubuntu-latest, 20.x)

Property 'setHeaderAllowMethods' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Node (ubuntu-latest, 18.x)

Property 'setPreflightHeaders' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Node (ubuntu-latest, 18.x)

Property 'appendHeaderValue' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Node (ubuntu-latest, 18.x)

Property 'getAllowOriginHeaderValue' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Node (ubuntu-latest, 18.x)

Property 'getCorsResponseHeaders' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Node (ubuntu-latest, 18.x)

Property 'setHeaderAllowCredentials' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Node (ubuntu-latest, 18.x)

Property 'setHeaderAllowHeaders' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Node (ubuntu-latest, 18.x)

Property 'setHeaderAllowMethods' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Node (ubuntu-latest, 18.x)

Property 'setHeaderAllowOrigin' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Node (ubuntu-latest, 18.x)

Property 'setHeaderExposeHeaders' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Node (ubuntu-latest, 18.x)

Property 'setHeaderMaxAge' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Bun (ubuntu-latest, 20.x)

Property 'setPreflightHeaders' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Bun (ubuntu-latest, 20.x)

Property 'appendHeaderValue' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Bun (ubuntu-latest, 20.x)

Property 'getAllowOriginHeaderValue' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Bun (ubuntu-latest, 20.x)

Property 'getCorsResponseHeaders' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Bun (ubuntu-latest, 20.x)

Property 'setHeaderAllowCredentials' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Bun (ubuntu-latest, 20.x)

Property 'setHeaderAllowHeaders' of exported class expression may not be private or protected.

Check failure on line 393 in src/modules/middleware/CORS/mod.ts

View workflow job for this annotation

GitHub Actions / Compat - Bun (ubuntu-latest, 20.x)

Property 'setHeaderAllowMethods' of exported class expression may not be private or protected.
return new CORSMiddleware(options);
return class DefaultCORSMiddleware extends CORSMiddleware {
constructor() {
super(options);
}
};
}

// FILE MARKER - PUBLIC API ////////////////////////////////////////////////////
Expand Down
6 changes: 5 additions & 1 deletion src/modules/middleware/ETag/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,11 @@ class ETagMiddleware extends Middleware {
* provided, it uses its default options.
*/
function ETag(options: Options = defaultOptions) {
return new ETagMiddleware(options);
return class DefaultETagMiddleware extends ETagMiddleware {
constructor() {
super(options);
}
};
}

// FILE MARKER - PUBLIC API ////////////////////////////////////////////////////
Expand Down
6 changes: 5 additions & 1 deletion src/modules/middleware/RateLimiter/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,11 @@ class RateLimiterMiddleware extends Middleware {
* provided, it uses its default options.
*/
function RateLimiter(options: Options) {
return new RateLimiterMiddleware(options);
return class DefaultRateLimiterMiddleware extends RateLimiterMiddleware {
constructor() {
super(options);
}
};
}

// FILE MARKER - PUBLIC API ////////////////////////////////////////////////////
Expand Down
8 changes: 4 additions & 4 deletions src/standard/handlers/ResourcesIndex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,12 @@ class ResourcesIndex extends AbstractSearchIndex<SearchResult | null> {
.resolve()
.then(() => this.#validateRequest(request))
.then(() => this.search(request))
.then((result) =>
super.sendToNextHandler<Output>({
.then((result) => {
return super.sendToNextHandler<Output>({
request,
result,
})
);
});
});
}

protected override buildIndex(resources: ResourceClasses[]): void {
Expand Down
31 changes: 21 additions & 10 deletions src/standard/http/Middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class Middleware extends Resource {
}

/**
* @TODO (crookse) Is this needed?
* Use this method to intercept the request before it is passed to the
* resource's HTTP method. With this method, you can short-circuit the
* request, modify the request, send the request to the resource based on
Expand Down Expand Up @@ -101,42 +102,52 @@ class Middleware extends Resource {
}

public CONNECT(input: unknown): unknown {
return this.#delegate(input, "CONNECT");
return this.#callOriginalHttpMethod(input, "CONNECT");
}

public DELETE(input: unknown): unknown {
return this.#delegate(input, "DELETE");
return this.#callOriginalHttpMethod(input, "DELETE");
}

public GET(input: unknown): unknown {
return this.#delegate(input, "GET");
return this.#callOriginalHttpMethod(input, "GET");
}

public HEAD(input: unknown): unknown {
return this.#delegate(input, "HEAD");
return this.#callOriginalHttpMethod(input, "HEAD");
}

public OPTIONS(input: unknown): unknown {
return this.#delegate(input, "OPTIONS");
return this.#callOriginalHttpMethod(input, "OPTIONS");
}

public PATCH(input: unknown): unknown {
return this.#delegate(input, "PATCH");
return this.#callOriginalHttpMethod(input, "PATCH");
}

public POST(input: unknown): unknown {
return this.#delegate(input, "POST");
return this.#callOriginalHttpMethod(input, "POST");
}

public PUT(input: unknown): unknown {
return this.#delegate(input, "PUT");
return this.#callOriginalHttpMethod(input, "PUT");
}

public TRACE(input: unknown): unknown {
return this.#delegate(input, "TRACE");
return this.#callOriginalHttpMethod(input, "TRACE");
}

#delegate(input: unknown, method: MethodOf<Middleware>): unknown {
/**
* Call this middleware's original class' HTTP method.
* @param input The input passed to this middleware and to forward to the
* original class this middleware wraps.
* @param method The HTTP method to call on the original.
* @returns The result of the original's HTTP method call.
*/
#callOriginalHttpMethod(
input: unknown,
method: MethodOf<Middleware>,
): unknown {
if (!this.original) {
throw new Error("Failed to create middleware. No original.");
}
Expand Down
98 changes: 60 additions & 38 deletions src/standard/http/ResourceGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type ResourceClasses = (typeof Resource | typeof Resource[])[];
*/
class Builder implements IBuilder {
#path_prefixes: string[] = [];
#middleware: Middleware[] = [];
#middleware: typeof Middleware[] = [];
#resources: typeof Resource[] = [];

/**
Expand Down Expand Up @@ -138,7 +138,7 @@ class Builder implements IBuilder {
* ```
*/
middleware(
...middleware: Middleware[]
...middleware: typeof Middleware[]
): this {
this.#middleware = middleware;
return this;
Expand Down Expand Up @@ -174,37 +174,41 @@ function createGroupWithPrefixes(
return resourceClasses;
}

return resourceClasses.map((ResourceClass: typeof Resource) => {
// Here we are creating the proxy that will be used by the client. The
// only purpose of this proxy is to be instantiable so it can be
// instantiated by the client and have the paths set using the prefixes.
return class PrefixedResourceProxy extends ResourceClass {
constructor() {
super();
this.paths = this.#getPathsWithPrefixes();
}

#getPathsWithPrefixes(): string[] {
if (!prefixes || prefixes.length <= 0) {
return this.paths;
const prefixedResources = resourceClasses.map(
(ResourceClass: typeof Resource) => {
// Here we are creating the proxy that will be used by the client. The
// only purpose of this proxy is to be instantiable so it can be
// instantiated by the client and have the paths set using the prefixes.
return class PrefixedResourceProxy extends ResourceClass {
constructor() {
super();
this.paths = this.#getPathsWithPrefixes();
}

const paths: string[] = [];
#getPathsWithPrefixes(): string[] {
if (!prefixes || prefixes.length <= 0) {
return this.paths;
}

for (const prefix of prefixes) {
for (const path of this.paths) {
paths.push(prefix + path);
const paths: string[] = [];

for (const prefix of prefixes) {
for (const path of this.paths) {
paths.push(prefix + path);
}
}

return paths;
}
};
},
);

return paths;
}
};
});
return prefixedResources;
}

function createGroupWithMiddleware(
middlewareClasses: Middleware[],
middlewareClasses: typeof Middleware[],
resourceClasses: typeof Resource[],
): typeof Resource[] {
if (arrayEmpty(middlewareClasses)) {
Expand All @@ -222,7 +226,7 @@ function createGroupWithMiddleware(
const resourceInstance = new ResourceClass();

if (middlewareClasses.length <= 1) {
first = middlewareCopies[0];
first = new middlewareCopies[0]();
first.setOriginal(resourceInstance);
} else {
// If more than one middleware instance exists, then we link them together
Expand All @@ -248,12 +252,20 @@ function createGroupWithMiddleware(
// }
//

const firstMiddlewareInstance = middlewareCopies.shift();
const firstClass = middlewareCopies.shift();

if (!firstClass) {
throw new Error(`Failed to find middleware for ResourceGroup creation`);
}

const firstMiddlewareInstance: Middleware = new firstClass();

if (firstMiddlewareInstance) {
first = firstMiddlewareInstance;

middlewareCopies.reduce(
middlewareCopies.map((middlewareClass) => {
return new middlewareClass();
}).reduce(
(previousMiddlewareInstance, currentMiddlewareInstance, index) => {
// Last middleware instance wraps the resource
if (index + 1 === middlewareCopies.length) {
Expand All @@ -264,7 +276,7 @@ function createGroupWithMiddleware(

return currentMiddlewareInstance;
},
first,
firstMiddlewareInstance,
);
}
}
Expand All @@ -275,45 +287,55 @@ function createGroupWithMiddleware(

// We extend the original resource class so the paths are kept intact
const p = class MiddlewareEntryPoint extends ResourceClass {
protected first = first;

constructor() {
super();
}

public CONNECT(input: unknown): unknown {
return first.CONNECT(input);
return this.first.CONNECT(input);
}

public DELETE(input: unknown): unknown {
return first.DELETE(input);
return this.first.DELETE(input);
}

public GET(input: unknown): unknown {
return first.GET(input);
return this.first.GET(input);
}

public HEAD(input: unknown): unknown {
return first.HEAD(input);
return this.first.HEAD(input);
}

public OPTIONS(input: unknown): unknown {
return first.OPTIONS(input);
return this.first.OPTIONS(input);
}

public PATCH(input: unknown): unknown {
return first.PATCH(input);
return this.first.PATCH(input);
}

public POST(input: unknown): unknown {
return first.POST(input);
return this.first.POST(input);
}

public PUT(input: unknown): unknown {
return first.PUT(input);
return this.first.PUT(input);
}

public TRACE(input: unknown): unknown {
return first.TRACE(input);
return this.first.TRACE(input);
}
};

Object.defineProperty(p, "name", {
value: resourceInstance.constructor.name + "MiddlewareProxy",
value: resourceInstance.constructor.name + "_MiddlewareProxy",
});

Object.defineProperty(p, "paths", {
value: resourceInstance.paths,
});

return p;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ const groupWithBlockedMethods = ResourceGroup
.resources(Home)
.pathPrefixes("/api/v1")
.middleware(
new MiddlewareBlockedMethods(),
MiddlewareBlockedMethods,
)
.build();

Expand All @@ -206,7 +206,7 @@ const groupWithAll = ResourceGroup
.resources(UsersAll)
.pathPrefixes("/api/v2")
.middleware(
new MiddlewareALL(),
MiddlewareALL,
)
.build();

Expand All @@ -215,8 +215,8 @@ const groupWithAllGet = ResourceGroup
.resources(UsersAllGet)
.pathPrefixes("/api/v2")
.middleware(
new MiddlewareALL(),
new MiddlewareGET(),
MiddlewareALL,
MiddlewareGET,
)
.build();

Expand All @@ -225,11 +225,11 @@ const groupWithAllGetGetAgain = ResourceGroup
.resources(UsersAllGetGetAgain)
.pathPrefixes("/api/v2")
.middleware(
new MiddlewareALL(),
new MiddlewareGET(),
new MiddlewareGETAgain(),
new MiddlewareGETAgain2(),
new MiddlewareGETAgain3(),
MiddlewareALL,
MiddlewareGET,
MiddlewareGETAgain,
MiddlewareGETAgain2,
MiddlewareGETAgain3,
)
.build();

Expand Down
2 changes: 1 addition & 1 deletion tests/middleware/deno/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export function query(kvp?: Record<string, string>) {
* ```
*/
export function chain(options: {
middleware?: Chain.Middleware[];
middleware?: typeof Chain.Middleware[];
resources?: typeof Chain.Resource[];
}) {
const {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ function getAcceptHeaderMiddleware(
return AcceptHeader(options);
}

return new class AcceptHeaderLogged extends AcceptHeaderMiddleware {
return class AcceptHeaderLogged extends AcceptHeaderMiddleware {
constructor() {
super(options);
}
Expand All @@ -387,5 +387,5 @@ function getAcceptHeaderMiddleware(
return r;
});
}
}();
};
}
Loading

0 comments on commit 2bbf968

Please sign in to comment.