Skip to content

Commit

Permalink
Merge branch 'master' into exp-proprietary-brands
Browse files Browse the repository at this point in the history
  • Loading branch information
RobinTail committed May 9, 2024
2 parents 1a4f81e + ceab390 commit 037147d
Show file tree
Hide file tree
Showing 21 changed files with 242 additions and 145 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

## Version 18

### v18.5.2

- Muted uploader logs related to non-eligible requests;
- Another performance improvement.

### v18.5.1

- A small performance improvement for `Integration` and `Documentation`.

### v18.5.0

- Major update on metadata: ~~`withMeta()`~~ is no longer required, deprecated and will be removed in v19:
Expand Down
2 changes: 1 addition & 1 deletion example/example.documentation.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
openapi: 3.1.0
info:
title: Example API
version: 18.5.0
version: 18.5.2
paths:
/v1/user/retrieve:
get:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "express-zod-api",
"version": "18.5.0",
"version": "18.5.2",
"description": "A Typescript library to help you get an API server up and running with I/O schema validation and custom middlewares in minutes.",
"license": "MIT",
"repository": {
Expand Down
6 changes: 3 additions & 3 deletions src/common-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import { InputValidationError, OutputValidationError } from "./errors";
import { AbstractLogger } from "./logger";
import { getMeta } from "./metadata";
import { AuxMethod, Method } from "./method";
import { mimeMultipart } from "./mime";
import { contentTypes } from "./content-type";

export type FlatObject = Record<string, unknown>;

const areFilesAvailable = (request: Request): boolean => {
const contentType = request.header("content-type") || "";
const isMultipart = contentType.toLowerCase().startsWith(mimeMultipart);
return "files" in request && isMultipart;
const isUpload = contentType.toLowerCase().startsWith(contentTypes.upload);
return "files" in request && isUpload;
};

export const defaultInputSources: InputSources = {
Expand Down
7 changes: 7 additions & 0 deletions src/content-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const contentTypes = {
json: "application/json",
upload: "multipart/form-data",
raw: "application/octet-stream",
};

export type ContentType = keyof typeof contentTypes;
6 changes: 3 additions & 3 deletions src/documentation-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ export const depictDiscriminatedUnion: Depicter<
> = ({ schema: { options, discriminator }, next }) => {
return {
discriminator: { propertyName: discriminator },
oneOf: Array.from(options.values()).map(next),
oneOf: options.map(next),
};
};

Expand Down Expand Up @@ -683,7 +683,7 @@ export const extractObjectSchema = (
subject instanceof z.ZodUnion ||
subject instanceof z.ZodDiscriminatedUnion
) {
return Array.from(subject.options.values())
return subject.options
.map((option) => extractObjectSchema(option, tfError))
.reduce((acc, option) => acc.merge(option.partial()), z.object({}));
} else if (subject instanceof z.ZodEffects) {
Expand Down Expand Up @@ -828,7 +828,7 @@ export const onEach: Depicter<z.ZodTypeAny, "each"> = ({
result.type = makeNullableType(prev);
}
if (examples.length) {
result.examples = Array.from(examples);
result.examples = examples.slice();
}
return result;
};
Expand Down
40 changes: 18 additions & 22 deletions src/documentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,8 @@ interface DocumentationParams {
}

export class Documentation extends OpenApiBuilder {
protected lastSecuritySchemaIds: Partial<Record<SecuritySchemeType, number>> =
{};
protected lastOperationIdSuffixes: Record<string, number> = {};
protected lastSecuritySchemaIds = new Map<SecuritySchemeType, number>();
protected lastOperationIdSuffixes = new Map<string, number>();

protected makeRef(
name: string,
Expand All @@ -88,28 +87,27 @@ export class Documentation extends OpenApiBuilder {
protected ensureUniqOperationId(
path: string,
method: Method,
userDefinedOperationId?: string,
userDefined?: string,
) {
if (userDefinedOperationId) {
assert(
!(userDefinedOperationId in this.lastOperationIdSuffixes),
const operationId = userDefined || makeCleanId(method, path);
let lastSuffix = this.lastOperationIdSuffixes.get(operationId);
if (lastSuffix === undefined) {
this.lastOperationIdSuffixes.set(operationId, 1);
return operationId;
}
if (userDefined) {
assert.fail(
new DocumentationError({
message: `Duplicated operationId: "${userDefinedOperationId}"`,
message: `Duplicated operationId: "${userDefined}"`,
method,
isResponse: false,
path,
}),
);
this.lastOperationIdSuffixes[userDefinedOperationId] = 1;
return userDefinedOperationId;
}
const operationId = makeCleanId(method, path);
if (operationId in this.lastOperationIdSuffixes) {
this.lastOperationIdSuffixes[operationId]++;
return `${operationId}${this.lastOperationIdSuffixes[operationId]}`;
}
this.lastOperationIdSuffixes[operationId] = 1;
return operationId;
lastSuffix++;
this.lastOperationIdSuffixes.set(operationId, lastSuffix);
return `${operationId}${lastSuffix}`;
}

protected ensureUniqSecuritySchemaName(subject: SecuritySchemeObject) {
Expand All @@ -122,11 +120,9 @@ export class Documentation extends OpenApiBuilder {
return name;
}
}
this.lastSecuritySchemaIds[subject.type] =
(this.lastSecuritySchemaIds?.[subject.type] || 0) + 1;
return `${subject.type.toUpperCase()}_${
this.lastSecuritySchemaIds[subject.type]
}`;
const nextId = (this.lastSecuritySchemaIds.get(subject.type) || 0) + 1;
this.lastSecuritySchemaIds.set(subject.type, nextId);
return `${subject.type.toUpperCase()}_${nextId}`;
}

public constructor({
Expand Down
26 changes: 18 additions & 8 deletions src/endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { AbstractLogger } from "./logger";
import { LogicalContainer, combineContainers } from "./logical-container";
import { AuxMethod, Method } from "./method";
import { AnyMiddlewareDef } from "./middleware";
import { mimeJson, mimeMultipart, mimeRaw } from "./mime";
import { ContentType, contentTypes } from "./content-type";
import { AnyResultHandlerDefinition } from "./result-handler";
import { Security } from "./security";

Expand Down Expand Up @@ -61,6 +61,7 @@ export abstract class AbstractEndpoint {
public abstract getScopes(): string[];
public abstract getTags(): string[];
public abstract getOperationId(method: Method): string | undefined;
public abstract getRequestType(): ContentType;
}

export class Endpoint<
Expand All @@ -81,6 +82,7 @@ export class Endpoint<
readonly #scopes: SCO[];
readonly #tags: TAG[];
readonly #getOperationId: (method: Method) => string | undefined;
readonly #requestType: ContentType;

constructor({
methods,
Expand Down Expand Up @@ -128,10 +130,13 @@ export class Endpoint<
this.#responses = {
positive: normalizeApiResponse(
resultHandler.getPositiveResponse(outputSchema),
{ mimeTypes: [mimeJson], statusCodes: [defaultStatusCodes.positive] },
{
mimeTypes: [contentTypes.json],
statusCodes: [defaultStatusCodes.positive],
},
),
negative: normalizeApiResponse(resultHandler.getNegativeResponse(), {
mimeTypes: [mimeJson],
mimeTypes: [contentTypes.json],
statusCodes: [defaultStatusCodes.negative],
}),
};
Expand All @@ -143,12 +148,13 @@ export class Endpoint<
),
);
}
this.#requestType = hasUpload(inputSchema)
? "upload"
: hasRaw(inputSchema)
? "raw"
: "json";
this.#mimeTypes = {
input: hasUpload(inputSchema)
? [mimeMultipart]
: hasRaw(inputSchema)
? [mimeRaw]
: [mimeJson],
input: [contentTypes[this.#requestType]],
positive: this.#responses.positive.flatMap(({ mimeTypes }) => mimeTypes),
negative: this.#responses.negative.flatMap(({ mimeTypes }) => mimeTypes),
};
Expand Down Expand Up @@ -178,6 +184,10 @@ export class Endpoint<
return this.#mimeTypes[variant];
}

public override getRequestType() {
return this.#requestType;
}

public override getResponses(variant: ResponseVariant) {
return this.#responses[variant];
}
Expand Down
7 changes: 5 additions & 2 deletions src/integration-helpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import ts from "typescript";
import { chain, toPairs } from "ramda";
import { Method } from "./method";

export const f = ts.factory;

Expand Down Expand Up @@ -74,10 +75,10 @@ export const makeEmptyInitializingConstructor = (
params: ts.ParameterDeclaration[],
) => f.createConstructorDeclaration(undefined, params, f.createBlock([]));

export const makeQuotedProp = (name: string, ref: string) =>
export const makeInterfaceProp = (name: string, ref: string) =>
f.createPropertySignature(
undefined,
`"${name}"`,
name,
undefined,
f.createTypeReferenceNode(ref),
);
Expand Down Expand Up @@ -205,3 +206,5 @@ export const makeObjectKeysReducer = (
initial,
],
);

export const quoteProp = (...parts: [Method, string]) => `"${parts.join(" ")}"`;
Loading

0 comments on commit 037147d

Please sign in to comment.