Skip to content

Commit 30ac9f7

Browse files
committed
Changes to mutation node and canonicalization implementations.
1 parent 97ba887 commit 30ac9f7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+2509
-1309
lines changed

.vscode/settings.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,6 @@
5656
},
5757
"typescript.tsdk": "./node_modules/typescript/lib",
5858
"git.ignoreLimitWarning": true,
59-
"vitest.workspaceConfig": "./vitest.config.ts",
60-
"typespec.tsp-server.path": "${workspaceFolder}/packages/compiler"
59+
"typespec.tsp-server.path": "${workspaceFolder}/packages/compiler",
60+
"vitest.rootConfig": "vitest.config.js"
6161
}

packages/http-canonicalization/src/codecs.ts

Lines changed: 52 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { getEncode, type MemberType, type Program, type Type } from "@typespec/compiler";
22
import type { Typekit } from "@typespec/compiler/typekit";
33
import { isHeader } from "@typespec/http";
4-
import type { HttpCanonicalization } from "./http-canonicalization-classes.js";
54

65
export interface CodecEncodeResult {
76
codec: Codec;
@@ -19,9 +18,9 @@ export class CodecRegistry {
1918
this.#codecs.push(codec);
2019
}
2120

22-
detect(type: HttpCanonicalization): Codec {
21+
detect(sourceType: Type, referenceTypes: MemberType[]): Codec {
2322
for (const codec of this.#codecs) {
24-
const codecInstance = codec.detect(this.$, type);
23+
const codecInstance = codec.detect(this.$, sourceType, referenceTypes);
2524
if (codecInstance) {
2625
return codecInstance;
2726
}
@@ -33,15 +32,17 @@ export class CodecRegistry {
3332

3433
export abstract class Codec {
3534
abstract id: string;
36-
canonicalization: HttpCanonicalization;
3735
$: Typekit;
36+
sourceType: Type;
37+
referenceTypes: MemberType[];
3838

39-
constructor($: Typekit, canonicalization: HttpCanonicalization) {
40-
this.canonicalization = canonicalization;
39+
constructor($: Typekit, sourceType: Type, referenceTypes: MemberType[]) {
4140
this.$ = $;
41+
this.sourceType = sourceType;
42+
this.referenceTypes = referenceTypes;
4243
}
4344

44-
static detect($: Typekit, canonicalization: HttpCanonicalization): Codec | undefined {
45+
static detect($: Typekit, sourceType: Type, referenceTypes: MemberType[]): Codec | undefined {
4546
return undefined;
4647
}
4748

@@ -84,31 +85,29 @@ export abstract class Codec {
8485

8586
export class IdentityCodec extends Codec {
8687
readonly id = "identity";
87-
static detect($: Typekit, canonicalization: HttpCanonicalization) {
88-
return new IdentityCodec($, canonicalization);
88+
static detect($: Typekit, sourceType: Type, referenceTypes: MemberType[]) {
89+
return new IdentityCodec($, sourceType, referenceTypes);
8990
}
9091

9192
encode() {
9293
return {
93-
wireType: this.canonicalization.sourceType,
94-
languageType: this.canonicalization.sourceType,
94+
wireType: this.sourceType,
95+
languageType: this.sourceType,
9596
};
9697
}
9798
}
9899

99100
export class UnixTimestamp64Codec extends Codec {
100101
readonly id = "unix-timestamp-64";
101-
static detect($: Typekit, canonicalization: HttpCanonicalization) {
102-
const type = canonicalization.sourceType;
103-
104-
if (!$.scalar.is(type) || !$.type.isAssignableTo(type, $.builtin.utcDateTime)) {
102+
static detect($: Typekit, sourceType: Type, referenceTypes: MemberType[]) {
103+
if (!$.scalar.is(sourceType) || !$.type.isAssignableTo(sourceType, $.builtin.utcDateTime)) {
105104
return;
106105
}
107106

108107
const encodingInfo = this.getMetadata(
109108
$,
110-
type,
111-
canonicalization.referenceTypes,
109+
sourceType,
110+
referenceTypes,
112111
$.modelProperty.is,
113112
getEncode,
114113
);
@@ -118,7 +117,7 @@ export class UnixTimestamp64Codec extends Codec {
118117
}
119118

120119
if (encodingInfo.encoding === "unix-timestamp" && encodingInfo.type === $.builtin.int64) {
121-
return new UnixTimestamp64Codec($, canonicalization);
120+
return new UnixTimestamp64Codec($, sourceType, referenceTypes);
122121
}
123122
}
124123

@@ -132,17 +131,15 @@ export class UnixTimestamp64Codec extends Codec {
132131

133132
export class UnixTimestamp32Codec extends Codec {
134133
readonly id = "unix-timestamp-32";
135-
static detect($: Typekit, canonicalization: HttpCanonicalization) {
136-
const type = canonicalization.sourceType;
137-
138-
if (!$.scalar.is(type) || !$.type.isAssignableTo(type, $.builtin.utcDateTime)) {
134+
static detect($: Typekit, sourceType: Type, referenceTypes: MemberType[]) {
135+
if (!$.scalar.is(sourceType) || !$.type.isAssignableTo(sourceType, $.builtin.utcDateTime)) {
139136
return;
140137
}
141138

142139
const encodingInfo = this.getMetadata(
143140
$,
144-
type,
145-
canonicalization.referenceTypes,
141+
sourceType,
142+
referenceTypes,
146143
$.modelProperty.is,
147144
getEncode,
148145
);
@@ -152,7 +149,7 @@ export class UnixTimestamp32Codec extends Codec {
152149
}
153150

154151
if (encodingInfo.encoding === "unix-timestamp" && encodingInfo.type === $.builtin.int32) {
155-
return new UnixTimestamp32Codec($, canonicalization);
152+
return new UnixTimestamp32Codec($, sourceType, referenceTypes);
156153
}
157154
}
158155

@@ -167,19 +164,17 @@ export class UnixTimestamp32Codec extends Codec {
167164
export class Rfc3339Codec extends Codec {
168165
readonly id = "rfc3339";
169166

170-
static detect($: Typekit, canonicalization: HttpCanonicalization) {
171-
const type = canonicalization.sourceType;
172-
173-
if (!$.scalar.is(type) || !$.type.isAssignableTo(type, $.builtin.utcDateTime)) {
167+
static detect($: Typekit, sourceType: Type, referenceTypes: MemberType[]) {
168+
if (!$.scalar.is(sourceType) || !$.type.isAssignableTo(sourceType, $.builtin.utcDateTime)) {
174169
return;
175170
}
176171

177-
return new Rfc3339Codec($, canonicalization);
172+
return new Rfc3339Codec($, sourceType, referenceTypes);
178173
}
179174

180175
encode() {
181176
return {
182-
languageType: this.canonicalization.sourceType,
177+
languageType: this.sourceType,
183178
wireType: this.$.builtin.string,
184179
};
185180
}
@@ -188,44 +183,34 @@ export class Rfc3339Codec extends Codec {
188183
export class Rfc7231Codec extends Codec {
189184
readonly id = "rfc7231";
190185

191-
static detect($: Typekit, canonicalization: HttpCanonicalization) {
192-
const type = canonicalization.sourceType;
193-
194-
if (!$.scalar.is(type) || !$.type.isAssignableTo(type, $.builtin.utcDateTime)) {
186+
static detect($: Typekit, sourceType: Type, referenceTypes: MemberType[]) {
187+
if (!$.scalar.is(sourceType) || !$.type.isAssignableTo(sourceType, $.builtin.utcDateTime)) {
195188
return;
196189
}
197190

198191
const encodingInfo = this.getMetadata(
199192
$,
200-
type,
201-
canonicalization.referenceTypes,
193+
sourceType,
194+
referenceTypes,
202195
$.modelProperty.is,
203196
getEncode,
204197
);
205198

206199
if (!encodingInfo) {
207-
if (
208-
this.getMetadata(
209-
$,
210-
undefined,
211-
canonicalization.referenceTypes,
212-
$.modelProperty.is,
213-
isHeader,
214-
)
215-
) {
216-
return new Rfc7231Codec($, canonicalization);
200+
if (this.getMetadata($, undefined, referenceTypes, $.modelProperty.is, isHeader)) {
201+
return new Rfc7231Codec($, sourceType, referenceTypes);
217202
}
218203
return;
219204
}
220205

221206
if (encodingInfo.encoding === "rfc7231") {
222-
return new Rfc7231Codec($, canonicalization);
207+
return new Rfc7231Codec($, sourceType, referenceTypes);
223208
}
224209
}
225210

226211
encode() {
227212
return {
228-
languageType: this.canonicalization.sourceType,
213+
languageType: this.sourceType,
229214
wireType: this.$.builtin.string,
230215
};
231216
}
@@ -234,19 +219,17 @@ export class Rfc7231Codec extends Codec {
234219
export class Base64Codec extends Codec {
235220
readonly id = "base64";
236221

237-
static detect($: Typekit, canonicalization: HttpCanonicalization) {
238-
const type = canonicalization.sourceType;
239-
240-
if (!$.type.isAssignableTo(type, $.builtin.bytes)) {
222+
static detect($: Typekit, sourceType: Type, referenceTypes: MemberType[]) {
223+
if (!$.type.isAssignableTo(sourceType, $.builtin.bytes)) {
241224
return;
242225
}
243226

244-
return new Base64Codec($, canonicalization);
227+
return new Base64Codec($, sourceType, referenceTypes);
245228
}
246229

247230
encode() {
248231
return {
249-
languageType: this.canonicalization.sourceType,
232+
languageType: this.sourceType,
250233
wireType: this.$.builtin.string,
251234
};
252235
}
@@ -255,19 +238,17 @@ export class Base64Codec extends Codec {
255238
export class CoerceToFloat64Codec extends Codec {
256239
readonly id = "coerce-to-float64";
257240

258-
static detect($: Typekit, canonicalization: HttpCanonicalization) {
259-
const type = canonicalization.sourceType;
260-
261-
if (!$.type.isAssignableTo(type, $.builtin.numeric)) {
241+
static detect($: Typekit, sourceType: Type, referenceTypes: MemberType[]) {
242+
if (!$.type.isAssignableTo(sourceType, $.builtin.numeric)) {
262243
return;
263244
}
264245

265-
return new CoerceToFloat64Codec($, canonicalization);
246+
return new CoerceToFloat64Codec($, sourceType, referenceTypes);
266247
}
267248

268249
encode() {
269250
return {
270-
languageType: this.canonicalization.sourceType,
251+
languageType: this.sourceType,
271252
wireType: this.$.builtin.float64,
272253
};
273254
}
@@ -276,45 +257,37 @@ export class CoerceToFloat64Codec extends Codec {
276257
export class NumericToStringCodec extends Codec {
277258
readonly id = "numeric-to-string";
278259

279-
static detect($: Typekit, canonicalization: HttpCanonicalization) {
280-
const type = canonicalization.sourceType;
281-
282-
if (!$.type.isAssignableTo(type, $.builtin.numeric)) {
260+
static detect($: Typekit, sourceType: Type, referenceTypes: MemberType[]) {
261+
if (!$.type.isAssignableTo(sourceType, $.builtin.numeric)) {
283262
return;
284263
}
285264

286-
return new NumericToStringCodec($, canonicalization);
265+
return new NumericToStringCodec($, sourceType, referenceTypes);
287266
}
288267

289268
encode() {
290269
return {
291-
languageType: this.canonicalization.sourceType,
270+
languageType: this.sourceType,
292271
wireType: this.$.builtin.string,
293272
};
294273
}
295274
}
296275

297276
export class ArrayJoinCodec extends Codec {
298277
readonly id = "array-join";
299-
static detect($: Typekit, canonicalization: HttpCanonicalization) {
300-
const type = canonicalization.sourceType;
301-
302-
if (!$.array.is(type)) {
278+
static detect($: Typekit, sourceType: Type, referenceTypes: MemberType[]) {
279+
if (!$.array.is(sourceType)) {
303280
return;
304281
}
305282

306-
if (
307-
canonicalization.options.location === "query" ||
308-
canonicalization.options.location === "header" ||
309-
canonicalization.options.location === "path"
310-
) {
311-
return new ArrayJoinCodec($, canonicalization);
312-
}
283+
// Note: This codec previously checked canonicalization.options.location
284+
// This logic may need to be refactored to pass location info differently
285+
return new ArrayJoinCodec($, sourceType, referenceTypes);
313286
}
314287

315288
encode() {
316289
return {
317-
languageType: this.canonicalization.sourceType,
290+
languageType: this.sourceType,
318291
wireType: this.$.builtin.string,
319292
};
320293
}

packages/http-canonicalization/src/http-canonicalization.test.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ it("canonicalizes models for read visibility", async () => {
3434
// validate mutation node
3535
expect(read.properties.size).toBe(2);
3636
const deletedProperty = read.properties.get("name")! as ModelPropertyHttpCanonicalization;
37-
expect(deletedProperty.languageType).toBe(tk.intrinsic.never);
37+
expect((deletedProperty.languageType as any) === tk.intrinsic.never).toBe(true);
3838

3939
// validate language type
4040
expect(read.languageType.name).toBe("Foo");
@@ -97,3 +97,42 @@ it("returns the same canonicalization for the same type", async () => {
9797

9898
expect(read1 === read2).toBe(true);
9999
});
100+
101+
it("handles referring to the same canonicalization", async () => {
102+
const runner = await Tester.createInstance();
103+
const { Foo, Bar, Baz, program } = await runner.compile(t.code`
104+
model ${t.model("Foo")} {
105+
@visibility(Lifecycle.Read) createdAt: utcDateTime;
106+
name: string;
107+
}
108+
109+
model ${t.model("Bar")} {
110+
foo: Foo;
111+
}
112+
113+
model ${t.model("Baz")} {
114+
foo: Foo;
115+
}
116+
`);
117+
118+
const tk = $(program);
119+
120+
const canonicalizer = new HttpCanonicalizer(tk);
121+
122+
const createFoo = canonicalizer.canonicalize(Foo, {
123+
visibility: Visibility.Create,
124+
}) as ModelHttpCanonicalization;
125+
126+
const createBar = canonicalizer.canonicalize(Bar, {
127+
visibility: Visibility.Create,
128+
}) as ModelHttpCanonicalization;
129+
130+
expect(createBar.properties.get("foo")!.type === createFoo).toBe(true);
131+
expect(createBar.properties.get("foo")!.languageType.type === createFoo.languageType).toBe(true);
132+
133+
const createBaz = canonicalizer.canonicalize(Baz, {
134+
visibility: Visibility.Create,
135+
}) as ModelHttpCanonicalization;
136+
137+
expect(createBaz.properties.get("foo")!.type === createFoo).toBe(true);
138+
});

0 commit comments

Comments
 (0)