Skip to content

Commit

Permalink
Fix misplacement of http typekits.
Browse files Browse the repository at this point in the history
  • Loading branch information
tjprescott committed Jan 29, 2025
1 parent 4c05421 commit 3d55dfb
Show file tree
Hide file tree
Showing 14 changed files with 65 additions and 501 deletions.
68 changes: 33 additions & 35 deletions packages/http/src/experimental/typekit/kits/http-operation.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
import { ignoreDiagnostics, Operation, StringLiteral, Type, VoidType } from "@typespec/compiler";
import { defineKit, Typekit } from "@typespec/compiler/experimental/typekit";
import { defineKit, Typekit } from "@typespec/compiler/typekit";
import { HttpOperation, HttpOperationResponseContent, HttpStatusCodesEntry } from "../../../types.js";
import { getHttpOperation } from "../../../operations.js";
import {
HttpOperation,
HttpOperationResponseContent,
HttpStatusCodesEntry,
} from "../../../types.js";

/**
* Utilities for working with HTTP operations.
* Structure of a flat HTTP response, which is grouped by status code and content type.
* @experimental
*/
export interface FlatHttpResponse {
/**
* Response status code.
*/
statusCode: HttpStatusCodesEntry;
/**
* Content type. Might be undefined if the response does not have a body.
*/
contentType?: string;
/**
* Response content.
*/
responseContent: HttpOperationResponseContent;
/**
* Response type.
*
*/
type: Type;
}

export interface HttpOperationKit {
/**
* Get the corresponding HTTP operation for the given TypeSpec operation. The same
Expand All @@ -31,33 +47,11 @@ export interface HttpOperationKit {
getReturnType(op: Operation, options?: { includeErrors?: boolean }): Type;
}

/**
* Structure of a flat HTTP response, which is grouped by status code and content type.
*/
export interface FlatHttpResponse {
/**
* Response status code.
*/
statusCode: HttpStatusCodesEntry;
/**
* Content type. Might be undefined if the response does not have a body.
*/
contentType?: string;
/**
* Response content.
*/
responseContent: HttpOperationResponseContent;
}

interface TypekitExtension {
/**
* Utilities for working with HTTP operations.
* @experimental
*/
httpOperation: HttpOperationKit;
}

declare module "@typespec/compiler/experimental/typekit" {
declare module "@typespec/compiler/typekit" {
interface Typekit extends TypekitExtension {}
}

Expand All @@ -66,11 +60,11 @@ defineKit<TypekitExtension>({
get(op) {
return ignoreDiagnostics(getHttpOperation(this.program, op));
},
getReturnType(operation, options) {
let responses = this.httpOperation.getResponses(operation);
getReturnType(httpOperation, options) {
let responses = this.httpOperation.getResponses(httpOperation);

if (!options?.includeErrors) {
responses = responses.filter((r) => !this.httpResponse.isErrorResponse(r.responseContent));
responses = responses.filter((r) => !this.httpResponse.isErrorResponse(r));
}

const voidType = { kind: "Intrinsic", name: "void" } as VoidType;
Expand Down Expand Up @@ -112,15 +106,19 @@ defineKit<TypekitExtension>({
contentType = "application/json";
}

responsesMap.push({ statusCode: response.statusCodes, contentType, responseContent });
responsesMap.push({
statusCode: response.statusCodes,
contentType,
responseContent,
type: response.type,
});
}
}

return responsesMap;
},
},
});

function getEffectiveType(typekit: Typekit, type?: Type): Type {
if (type === undefined) {
return { kind: "Intrinsic", name: "void" } as VoidType;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Type } from "@typespec/compiler";
import { defineKit } from "@typespec/compiler/typekit";
import { getHttpPart, HttpPart } from "../../private.decorators.js";
import { getHttpPart, HttpPart } from "../../../private.decorators.js";

export interface HttpPartKit {
/**
Expand All @@ -20,7 +20,7 @@ export interface HttpPartKit {
unpack(type: Type): Type;
}

export interface TypekitExtension {
interface TypekitExtension {
httpPart: HttpPartKit;
}

Expand Down
40 changes: 15 additions & 25 deletions packages/http/src/experimental/typekit/kits/http-request.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Model, ModelProperty } from "@typespec/compiler";
import { defineKit } from "@typespec/compiler/experimental/typekit";
import { defineKit } from "@typespec/compiler/typekit";
import { HttpOperation } from "../../../types.js";

export type HttpRequestParameterKind = "query" | "header" | "path" | "contentType" | "body";

interface HttpRequestKit {
body: {
/**
* Checks the body is a property explicitly tagged with @body or @bodyRoot
* Checks the body is a property explicitly tagged with @body @bodyRoot or @multipartBody
* @param httpOperation the http operation to check
*/
isExplicit(httpOperation: HttpOperation): boolean;
Expand All @@ -32,7 +32,7 @@ interface TypekitExtension {
httpRequest: HttpRequestKit;
}

declare module "@typespec/compiler/experimental/typekit" {
declare module "@typespec/compiler/typekit" {
interface Typekit extends TypekitExtension {}
}

Expand All @@ -42,7 +42,7 @@ defineKit<TypekitExtension>({
isExplicit(httpOperation: HttpOperation) {
return (
httpOperation.parameters.properties.find(
(p) => p.kind === "body" || p.kind === "bodyRoot",
(p) => p.kind === "body" || p.kind === "bodyRoot" || p.kind === "multipartBody",
) !== undefined
);
},
Expand Down Expand Up @@ -74,35 +74,25 @@ defineKit<TypekitExtension>({
kind: HttpRequestParameterKind | HttpRequestParameterKind[],
): Model | undefined {
const kinds = new Set(Array.isArray(kind) ? kind : [kind]);
const parameterProperties: ModelProperty[] = [];
const parameterProperties = new Map<string, ModelProperty>();

for (const kind of kinds) {
kinds.forEach((kind) => {
if (kind === "body") {
const bodyParams = Array.from(
this.httpRequest.getBodyParameters(httpOperation)?.properties.values() ?? [],
);
if (bodyParams) {
parameterProperties.push(...bodyParams);
}
this.httpRequest
.getBodyParameters(httpOperation)
?.properties.forEach((value, key) => parameterProperties.set(key, value));
} else {
const params = httpOperation.parameters.properties
.filter((p) => p.kind === kind)
.map((p) => p.property);
parameterProperties.push(...params);
httpOperation.parameters.properties
.filter((p) => p.kind === kind && p.property)
.forEach((p) => parameterProperties.set(p.property!.name, p.property!));
}
}
});

if (parameterProperties.length === 0) {
if (parameterProperties.size === 0) {
return undefined;
}

const properties = parameterProperties.reduce(
(acc, prop) => {
acc[prop.name] = prop;
return acc;
},
{} as Record<string, ModelProperty>,
);
const properties = Object.fromEntries(parameterProperties);

return this.model.create({ properties });
},
Expand Down
40 changes: 7 additions & 33 deletions packages/http/src/experimental/typekit/kits/http-response.ts
Original file line number Diff line number Diff line change
@@ -1,58 +1,32 @@
import { isErrorModel } from "@typespec/compiler";
import { defineKit } from "@typespec/compiler/experimental/typekit";
import {
HttpOperationResponseContent,
HttpStatusCodeRange,
HttpStatusCodesEntry,
} from "../../../types.js";
import { defineKit } from "@typespec/compiler/typekit";
import { FlatHttpResponse } from "./http-operation.js";
import { HttpStatusCodeRange, HttpStatusCodesEntry } from "../../../types.js";

/**
* Utilities for working with HTTP responses.
* @experimental
*/
export interface HttpResponseKit {
interface HttpResponseKit {
/**
* Check if the response is an error response.
*/
isErrorResponse(response: HttpOperationResponseContent): boolean;
/**
* utilities to perform checks on status codes
*/
isErrorResponse(response: FlatHttpResponse): boolean;
statusCode: {
/**
* Check if the status code is a single status code
* @param statusCode status code to check
*/
isSingle(statusCode: HttpStatusCodesEntry): statusCode is number;
/**
* Check if the status code is a range of status codes
* @param statusCode status code to check
*/
isRange(statusCode: HttpStatusCodesEntry): statusCode is HttpStatusCodeRange;
/**
* Check if the status code is a default status code
* @param statusCode status code to check
*/
isDefault(statusCode: HttpStatusCodesEntry): statusCode is "*";
};
}

interface TypekitExtension {
/**
* Utilities for working with HTTP responses.
* @experimental
*/
httpResponse: HttpResponseKit;
}

declare module "@typespec/compiler/experimental/typekit" {
declare module "@typespec/compiler/typekit" {
interface Typekit extends TypekitExtension {}
}

defineKit<TypekitExtension>({
httpResponse: {
isErrorResponse(response) {
return response.body ? isErrorModel(this.program, response.body.type) : false;
return this.model.is(response.type) ? isErrorModel(this.program, response.type) : false;
},
statusCode: {
isSingle(statusCode) {
Expand Down
2 changes: 2 additions & 0 deletions packages/http/src/experimental/typekit/kits/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export * from "./http-operation.js";
export * from "./http-part.js";
export * from "./http-request.js";
export * from "./http-response.js";
export * from "./model-property.js";
export * from "./model.js";
50 changes: 3 additions & 47 deletions packages/http/src/experimental/typekit/kits/model-property.ts
Original file line number Diff line number Diff line change
@@ -1,70 +1,26 @@
import { ModelProperty } from "@typespec/compiler";
import { defineKit } from "@typespec/compiler/experimental/typekit";
import {
getHeaderFieldOptions,
getPathParamOptions,
getQueryParamOptions,
isHeader,
isMultipartBodyProperty,
isPathParam,
isQueryParam,
} from "../../../decorators.js";
import { defineKit } from "@typespec/compiler/typekit";
import { HeaderFieldOptions, PathParameterOptions, QueryParameterOptions } from "../../../types.js";
import { isHeader, getHeaderFieldOptions, isPathParam, getPathParamOptions, isQueryParam, getQueryParamOptions, isMultipartBodyProperty } from "../../../decorators.js";

/**
* Utilities for working with model properties in the context of Http.
* @experimental
*/
export interface HttpModelProperty {
/**
* Get the Http parameter options for a model property.
* @param prop a TypeSpec ModelProperty
*/
getHttpParamOptions(
prop: ModelProperty,
): HeaderFieldOptions | PathParameterOptions | QueryParameterOptions | undefined;
/**
* Get the Http header options for a model property.
* @param prop a TypeSpec ModelProperty
*/
getHttpHeaderOptions(prop: ModelProperty): HeaderFieldOptions | undefined;
/**
* Get the Http path options for a model property.
* @param prop a TypeSpec ModelProperty
*/
getHttpPathOptions(prop: ModelProperty): PathParameterOptions | undefined;
/**
* Get the Http query options for a model property.
* @param prop a TypeSpec ModelProperty
*/
getHttpQueryOptions(prop: ModelProperty): QueryParameterOptions | undefined;
/**
* Check if a model property is an Http header.
* @param prop a TypeSpec ModelProperty
*/
isHttpHeader(prop: ModelProperty): boolean;
/**
* Check if a model property is an Http path parameter.
* @param prop a TypeSpec ModelProperty
*/
isHttpPathParam(prop: ModelProperty): boolean;
/**
* Check if a model property is an Http query parameter.
* @param prop a TypeSpec ModelProperty
*/
isHttpQueryParam(prop: ModelProperty): boolean;
/**
* Check if a model property is an Http multipart body.
* @param prop a TypeSpec ModelProperty
*/
isHttpMultipartBody(prop: ModelProperty): boolean;
}

interface TypekitExtension {
modelProperty: HttpModelProperty;
}

declare module "@typespec/compiler/experimental/typekit" {
declare module "@typespec/compiler/typekit" {
interface ModelPropertyKit extends HttpModelProperty {}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Model } from "@typespec/compiler";
import { defineKit } from "@typespec/compiler/typekit";
import { isHttpFile } from "../../private.decorators.js";
import { isHttpFile } from "../../../private.decorators.js";

export interface HttpModel {
isHttpFile(model: Model): boolean;
Expand Down
1 change: 0 additions & 1 deletion packages/http/src/typekit/index.ts

This file was deleted.

Loading

0 comments on commit 3d55dfb

Please sign in to comment.