Skip to content

[http] WIP, merge-patch implementation #7121

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from 5 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: fix
packages:
- "@typespec/compiler"
---

Weakened rules around `@mediaTypeHint` decorator, allowing media type hints with suffixes like "application/merge-patch+json".
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: feature
packages:
- "@typespec/http"
---

Implemented JSON Merge-Patch wrappers. This allows converting a type to a JSON Merge-Patch compatible update record using the `MergePatchUpdate` and `MergePatchCreateOrUpdate` templates.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: feature
packages:
- "@typespec/compiler"
---

Exposed experimental function `isMutableType` as `unsafe_isMutableType`.
1 change: 1 addition & 0 deletions packages/compiler/src/experimental/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export {
MutatorRecord as unsafe_MutatorRecord,
MutatorReplaceFn as unsafe_MutatorReplaceFn,
MutatorWithNamespace as unsafe_MutatorWithNamespace,
isMutableType as unsafe_isMutableType,
mutateSubgraph as unsafe_mutateSubgraph,
mutateSubgraphWithNamespace as unsafe_mutateSubgraphWithNamespace,
} from "./mutators.js";
Expand Down
6 changes: 0 additions & 6 deletions packages/compiler/src/lib/decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -370,12 +370,6 @@ export const $mediaTypeHint: MediaTypeHintDecorator = (
format: { mimeType: mediaType },
target: context.getArgumentTarget(0)!,
});
} else if (mimeTypeObj.suffix) {
reportDiagnostic(context.program, {
code: "no-mime-type-suffix",
format: { mimeType: mediaType, suffix: mimeTypeObj.suffix },
target: context.getArgumentTarget(0)!,
});
}

setMediaTypeHint(context.program, target, mediaType);
Expand Down
17 changes: 17 additions & 0 deletions packages/http/generated-defs/TypeSpec.Http.Private.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ export interface HttpPartOptions {
readonly name?: string;
}

export interface ApplyMergePatchOptions {
readonly visibilityMode: unknown;
}

export type PlainDataDecorator = (context: DecoratorContext, target: Model) => void;

export type HttpFileDecorator = (context: DecoratorContext, target: Model) => void;
Expand All @@ -15,6 +19,18 @@ export type HttpPartDecorator = (
options: HttpPartOptions,
) => void;

/**
* Performs the canonical merge-patch transformation on the given model and injects its
* transformed properties into the target.
*/
export type ApplyMergePatchDecorator = (
context: DecoratorContext,
target: Model,
source: Model,
nameTemplate: string,
options: ApplyMergePatchOptions,
) => void;

/**
* Specify if inapplicable metadata should be included in the payload for the given entity.
*
Expand All @@ -30,5 +46,6 @@ export type TypeSpecHttpPrivateDecorators = {
plainData: PlainDataDecorator;
httpFile: HttpFileDecorator;
httpPart: HttpPartDecorator;
applyMergePatch: ApplyMergePatchDecorator;
includeInapplicableMetadataInPayload: IncludeInapplicableMetadataInPayloadDecorator;
};
20 changes: 20 additions & 0 deletions packages/http/lib/main.tsp
Original file line number Diff line number Diff line change
Expand Up @@ -271,3 +271,23 @@ model Link {
}

scalar LinkHeader<T extends Record<url> | Link[]> extends string;

@friendlyName(NameTemplate, T)
@mediaTypeHint("application/merge-patch+json")
@applyMergePatch(T, NameTemplate, #{ visibilityMode: Private.MergePatchVisibilityMode.Update })
model MergePatchUpdate<
T extends Reflection.Model,
NameTemplate extends valueof string = "{name}MergePatchUpdate"
> {}

@friendlyName(NameTemplate, T)
@mediaTypeHint("application/merge-patch+json")
@applyMergePatch(
T,
NameTemplate,
#{ visibilityMode: Private.MergePatchVisibilityMode.CreateOrUpdate }
)
model MergePatchCreateOrUpdate<
T extends Reflection.Model,
NameTemplate extends valueof string = "{name}MergePatchCreateOrUpdate"
> {}
36 changes: 36 additions & 0 deletions packages/http/lib/private.decorators.tsp
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,39 @@ extern dec httpPart(
* @param value If true, inapplicable metadata will be included in the payload.
*/
extern dec includeInapplicableMetadataInPayload(target: unknown, value: valueof boolean);

/**
* The visibility mode for the merge patch transform.
*/
enum MergePatchVisibilityMode {
/**
* The Update mode. This is used when a resource can be updated but must already exist.
*/
Update,

/**
* The Create or Update mode. This is used when a resource can be created OR updated in a single operation.
*/
CreateOrUpdate,
}

/**
* Options for the `@applyMergePatch` decorator.
*/
model ApplyMergePatchOptions {
/**
* The visibility mode to use.
*/
visibilityMode: MergePatchVisibilityMode;
}

/**
* Performs the canonical merge-patch transformation on the given model and injects its
* transformed properties into the target.
*/
extern dec applyMergePatch(
target: Reflection.Model,
source: Reflection.Model,
nameTemplate: valueof string,
options: valueof ApplyMergePatchOptions
);
8 changes: 4 additions & 4 deletions packages/http/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@ export { $linter } from "./linter.js";
export { setStatusCode } from "./decorators.js";
export type { HttpProperty } from "./http-property.js";
export {
HttpVisibilityProvider,
Visibility,
createMetadataInfo,
getVisibilitySuffix,
HttpVisibilityProvider,
isApplicableMetadata,
isApplicableMetadataOrBody,
isMetadata,
isVisible,
resolveRequestVisibility,
Visibility,
type MetadataInfo,
type MetadataInfoOptions,
} from "./metadata.js";
Expand All @@ -75,9 +75,9 @@ export {
} from "./operations.js";
export { getOperationParameters } from "./parameters.js";
export {
HttpPart,
getHttpFileModel,
getHttpPart,
HttpPart,
isHttpFile,
isOrExtendsHttpFile,
} from "./private.decorators.js";
Expand Down Expand Up @@ -135,11 +135,11 @@ export type {
ImplicitFlow,
NoAuth,
NoHttpAuthRef,
Oauth2Auth,
OAuth2Flow,
OAuth2FlowType,
OAuth2HttpAuthRef,
OAuth2Scope,
Oauth2Auth,
OpenIDConnectAuth,
OperationContainer,
OperationVerbSelector,
Expand Down
7 changes: 7 additions & 0 deletions packages/http/src/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,13 @@ export const $lib = createTypeSpecLibrary({
default: paramMessage`The 'contents' property of the file model must be a scalar type that extends 'string' or 'bytes'. Found '${"type"}'.`,
},
},
"merge-patch-contains-null": {
severity: "error",
messages: {
default:
"Cannot convert model to a merge-patch compatible shape because it contains the 'null' intrinsic type.",
},
},
},
state: {
authentication: { description: "State for the @auth decorator" },
Expand Down
Loading
Loading