diff --git a/packages/samples/README.md b/packages/samples/README.md index e26203e4753..4f1f277fe40 100644 --- a/packages/samples/README.md +++ b/packages/samples/README.md @@ -4,6 +4,14 @@ This project has a collection of samples used to demonstrate and test various Ty It is not published as an npm package. +Samples should teach a distinct TypeSpec concept or realistic workflow. Narrow protocol test +cases belong in the relevant package test suite instead of this collection. + +Each sample directory has a `sample-config.yaml` containing a unique `title` and a +`description`. Set `playground: false` for samples that require multiple files or are not suitable +for the playground. A directory config uses `directory: true` and can provide a gallery `label`, +`order`, or an inherited `playground: false`. + ```bash npm run test # Check Samples match snapshots npm run test:ci # run test same as CI diff --git a/packages/samples/package.json b/packages/samples/package.json index 438b6c5afd0..d039df0934f 100644 --- a/packages/samples/package.json +++ b/packages/samples/package.json @@ -46,6 +46,7 @@ "@typespec/best-practices": "workspace:^", "@typespec/compiler": "workspace:^", "@typespec/events": "workspace:^", + "@typespec/graphql": "workspace:^", "@typespec/html-program-viewer": "workspace:^", "@typespec/http": "workspace:^", "@typespec/http-server-csharp": "workspace:^", @@ -57,7 +58,8 @@ "@typespec/rest": "workspace:^", "@typespec/sse": "workspace:^", "@typespec/streams": "workspace:^", - "@typespec/versioning": "workspace:^" + "@typespec/versioning": "workspace:^", + "yaml": "catalog:" }, "devDependencies": { "@types/node": "catalog:", diff --git a/packages/samples/specs/authentication/sample-config.yaml b/packages/samples/specs/authentication/sample-config.yaml new file mode 100644 index 00000000000..d446705ee5b --- /dev/null +++ b/packages/samples/specs/authentication/sample-config.yaml @@ -0,0 +1,3 @@ +title: Authentication +description: Apply authentication schemes at service, interface, and operation scope. +playground: false diff --git a/packages/samples/specs/binary/sample-config.yaml b/packages/samples/specs/binary/sample-config.yaml new file mode 100644 index 00000000000..9a863c88598 --- /dev/null +++ b/packages/samples/specs/binary/sample-config.yaml @@ -0,0 +1,3 @@ +title: Binary data +description: Model binary request and response payloads. +playground: false diff --git a/packages/samples/specs/documentation/sample-config.yaml b/packages/samples/specs/documentation/sample-config.yaml new file mode 100644 index 00000000000..b8b4726775d --- /dev/null +++ b/packages/samples/specs/documentation/sample-config.yaml @@ -0,0 +1,3 @@ +title: Documentation +description: Add documentation to TypeSpec declarations and emitted API descriptions. +playground: false diff --git a/packages/samples/specs/emitters/graphql/main.tsp b/packages/samples/specs/emitters/graphql/main.tsp new file mode 100644 index 00000000000..066e48d67ec --- /dev/null +++ b/packages/samples/specs/emitters/graphql/main.tsp @@ -0,0 +1,46 @@ +import "@typespec/graphql"; + +using GraphQL; + +@schema +namespace PetStore; + +/** A pet in the store. */ +model Pet { + id: string; + name: string; + tag?: string; + status: PetStatus; +} + +/** A pet's status in the store. */ +enum PetStatus { + Available, + Pending, + Sold, +} + +/** A toy that belongs to a pet. */ +model Toy { + id: string; + name: string; + pet: Pet; +} + +@query +op getPet(id: string): Pet | null; + +@query +op listPets(status?: PetStatus, limit?: int32): Pet[]; + +@query +op getPetToys(petId: string): Toy[]; + +@mutation +op createPet(name: string, tag: string, status: PetStatus): Pet; + +@mutation +op updatePet(id: string, name?: string, status?: PetStatus): Pet | null; + +@mutation +op deletePet(id: string): boolean; diff --git a/packages/samples/specs/emitters/graphql/sample-config.yaml b/packages/samples/specs/emitters/graphql/sample-config.yaml new file mode 100644 index 00000000000..e441137849d --- /dev/null +++ b/packages/samples/specs/emitters/graphql/sample-config.yaml @@ -0,0 +1,3 @@ +title: GraphQL +description: Generate a GraphQL schema with queries, mutations, models, and enums. +order: 3 diff --git a/packages/samples/specs/emitters/graphql/tspconfig.yaml b/packages/samples/specs/emitters/graphql/tspconfig.yaml new file mode 100644 index 00000000000..c8676afee2a --- /dev/null +++ b/packages/samples/specs/emitters/graphql/tspconfig.yaml @@ -0,0 +1,2 @@ +emit: + - "@typespec/graphql" diff --git a/packages/samples/specs/emitters/json-schema/main.tsp b/packages/samples/specs/emitters/json-schema/main.tsp new file mode 100644 index 00000000000..95c5e4f635d --- /dev/null +++ b/packages/samples/specs/emitters/json-schema/main.tsp @@ -0,0 +1,40 @@ +import "@typespec/json-schema"; + +using JsonSchema; + +@jsonSchema +namespace Schemas; + +model Person { + /** The person's first name. */ + firstName: string; + + /** The person's last name. */ + lastName: string; + + /** Age in years, which must be zero or greater. */ + @minValue(0) + age: int32; + + /** The person's address. */ + address: Address; + + /** The person's nicknames. */ + @uniqueItems + nickNames?: string[]; + + /** Cars owned by the person. */ + cars?: Car[]; +} + +model Address { + street: string; + city: string; + country: string; +} + +model Car { + kind: "ev" | "ice"; + brand: string; + `model`: string; +} diff --git a/packages/samples/specs/emitters/json-schema/sample-config.yaml b/packages/samples/specs/emitters/json-schema/sample-config.yaml new file mode 100644 index 00000000000..767b7ab8d1e --- /dev/null +++ b/packages/samples/specs/emitters/json-schema/sample-config.yaml @@ -0,0 +1,3 @@ +title: JSON Schema +description: Generate JSON Schema documents from TypeSpec models and constraints. +order: 2 diff --git a/packages/samples/specs/emitters/json-schema/tspconfig.yaml b/packages/samples/specs/emitters/json-schema/tspconfig.yaml new file mode 100644 index 00000000000..a67708da567 --- /dev/null +++ b/packages/samples/specs/emitters/json-schema/tspconfig.yaml @@ -0,0 +1,2 @@ +emit: + - "@typespec/json-schema" diff --git a/packages/samples/specs/emitters/protobuf-kiosk/main.tsp b/packages/samples/specs/emitters/protobuf-kiosk/main.tsp new file mode 100644 index 00000000000..7dc5dfae896 --- /dev/null +++ b/packages/samples/specs/emitters/protobuf-kiosk/main.tsp @@ -0,0 +1,74 @@ +import "@typespec/protobuf"; + +using Protobuf; + +@package({ + name: "kiosk", +}) +namespace KioskExample; + +@TypeSpec.Protobuf.service +interface Display { + /** Create a new kiosk and enroll it for sign display. */ + createKiosk(...Kiosk): Kiosk; + + /** List active kiosks. */ + listKiosks(...WellKnown.Empty): { + @field(1) kiosks: Kiosk[]; + }; + + /** Get a kiosk. */ + getKiosk(@field(1) id: int32): Kiosk; + + /** Delete a kiosk. */ + deleteKiosk(@field(1) id: int32): void; + + /** Create a new sign. */ + createSign(...Sign): Sign; + + /** List active signs. */ + listSigns(...WellKnown.Empty): { + @field(1) signs: Sign[]; + }; + + /** Get a sign. */ + getSign(@field(1) id: int32): Sign; + + /** Delete a sign. */ + deleteSign(@field(1) id: int32): void; + + /** Set a sign for display on one or more kiosks. */ + setSignIdForKioskIds(@field(1) kioskIds: int32[], @field(2) signId: int32): void; + + /** Get the sign that should be displayed on a kiosk. */ + getSignIdForKioskId(@field(1) kioskId: int32): GetSignIdResponse; + + /** Stream the signs that should be displayed on a kiosk. */ + @stream(StreamMode.Out) + getSignIdsForKioskId(@field(1) kioskId: int32): GetSignIdResponse; +} + +model Kiosk { + @field(1) id?: int32; + @field(2) name: string; + @field(3) size: ScreenSize; + @field(4) location: WellKnown.LatLng; + @field(5) createTime?: WellKnown.Timestamp; +} + +model Sign { + @field(1) id?: int32; + @field(2) name: string; + @field(3) text: string; + @field(4) image: bytes; + @field(5) createTime?: WellKnown.Timestamp; +} + +model ScreenSize { + @field(1) width: int32; + @field(2) height: int32; +} + +model GetSignIdResponse { + @field(1) signId: int32; +} diff --git a/packages/samples/specs/emitters/protobuf-kiosk/sample-config.yaml b/packages/samples/specs/emitters/protobuf-kiosk/sample-config.yaml new file mode 100644 index 00000000000..5015ed97628 --- /dev/null +++ b/packages/samples/specs/emitters/protobuf-kiosk/sample-config.yaml @@ -0,0 +1,3 @@ +title: Protobuf kiosk +description: Generate Protocol Buffer service and message definitions for a kiosk system. +order: 1 diff --git a/packages/samples/specs/emitters/protobuf-kiosk/tspconfig.yaml b/packages/samples/specs/emitters/protobuf-kiosk/tspconfig.yaml new file mode 100644 index 00000000000..daedd9b709b --- /dev/null +++ b/packages/samples/specs/emitters/protobuf-kiosk/tspconfig.yaml @@ -0,0 +1,2 @@ +emit: + - "@typespec/protobuf" diff --git a/packages/samples/specs/emitters/sample-config.yaml b/packages/samples/specs/emitters/sample-config.yaml new file mode 100644 index 00000000000..0e970f53d70 --- /dev/null +++ b/packages/samples/specs/emitters/sample-config.yaml @@ -0,0 +1,3 @@ +directory: true +label: Emitters +order: 3 diff --git a/packages/samples/specs/encoded-names/sample-config.yaml b/packages/samples/specs/encoded-names/sample-config.yaml new file mode 100644 index 00000000000..ed17c381cd0 --- /dev/null +++ b/packages/samples/specs/encoded-names/sample-config.yaml @@ -0,0 +1,3 @@ +title: Encoded names +description: Customize declaration names for specific emitters. +playground: false diff --git a/packages/samples/specs/encoding/sample-config.yaml b/packages/samples/specs/encoding/sample-config.yaml new file mode 100644 index 00000000000..7dc42c72e36 --- /dev/null +++ b/packages/samples/specs/encoding/sample-config.yaml @@ -0,0 +1,3 @@ +title: Value encoding +description: Configure serialization formats for scalar values. +playground: false diff --git a/packages/samples/specs/getting-started/http-service/main.tsp b/packages/samples/specs/getting-started/http-service/main.tsp new file mode 100644 index 00000000000..0135d7e9492 --- /dev/null +++ b/packages/samples/specs/getting-started/http-service/main.tsp @@ -0,0 +1,44 @@ +import "@typespec/http"; + +using Http; + +@service(#{ title: "Widget Service" }) +namespace DemoService; + +model Widget { + @visibility(Lifecycle.Read) + id: string; + + weight: int32; + color: "red" | "blue"; +} + +@error +model Error { + code: int32; + message: string; +} + +@route("/widgets") +@tag("Widgets") +interface Widgets { + /** List widgets. */ + @get list(): Widget[] | Error; + + /** Read a widget. */ + @get read(@path id: Widget.id): Widget | Error; + + /** Create a widget. */ + @post create(@body widget: Widget): Widget | Error; + + /** Update a widget. */ + @patch update(@path id: Widget.id, @body widget: MergePatchUpdate): Widget | Error; + + /** Delete a widget. */ + @delete delete(@path id: Widget.id): void | Error; + + /** Analyze a widget. */ + @route("{id}/analyze") + @post + analyze(@path id: Widget.id): string | Error; +} diff --git a/packages/samples/specs/getting-started/http-service/sample-config.yaml b/packages/samples/specs/getting-started/http-service/sample-config.yaml new file mode 100644 index 00000000000..114db21f9a6 --- /dev/null +++ b/packages/samples/specs/getting-started/http-service/sample-config.yaml @@ -0,0 +1,3 @@ +title: HTTP service +description: Build an HTTP service with routes, parameters, request bodies, and responses. +order: 1 diff --git a/packages/samples/specs/getting-started/http-service/tspconfig.yaml b/packages/samples/specs/getting-started/http-service/tspconfig.yaml new file mode 100644 index 00000000000..c603bb9c94f --- /dev/null +++ b/packages/samples/specs/getting-started/http-service/tspconfig.yaml @@ -0,0 +1,5 @@ +emit: + - "@typespec/openapi3" +linter: + extends: + - "@typespec/http/all" diff --git a/packages/samples/specs/getting-started/rest-framework/main.tsp b/packages/samples/specs/getting-started/rest-framework/main.tsp new file mode 100644 index 00000000000..431495db274 --- /dev/null +++ b/packages/samples/specs/getting-started/rest-framework/main.tsp @@ -0,0 +1,26 @@ +import "@typespec/http"; +import "@typespec/rest"; + +using Http; +using Rest; + +@service(#{ title: "Widget Service" }) +namespace DemoService; + +model Widget { + @key id: string; + weight: int32; + color: "red" | "blue"; +} + +@error +model Error { + code: int32; + message: string; +} + +interface WidgetService extends Resource.ResourceOperations { + @get + @route("customGet") + customGet(): Widget; +} diff --git a/packages/samples/specs/getting-started/rest-framework/sample-config.yaml b/packages/samples/specs/getting-started/rest-framework/sample-config.yaml new file mode 100644 index 00000000000..6183192fd70 --- /dev/null +++ b/packages/samples/specs/getting-started/rest-framework/sample-config.yaml @@ -0,0 +1,3 @@ +title: REST framework +description: Use resource-oriented REST templates to define a service with less repetition. +order: 2 diff --git a/packages/samples/specs/getting-started/rest-framework/tspconfig.yaml b/packages/samples/specs/getting-started/rest-framework/tspconfig.yaml new file mode 100644 index 00000000000..c603bb9c94f --- /dev/null +++ b/packages/samples/specs/getting-started/rest-framework/tspconfig.yaml @@ -0,0 +1,5 @@ +emit: + - "@typespec/openapi3" +linter: + extends: + - "@typespec/http/all" diff --git a/packages/samples/specs/getting-started/sample-config.yaml b/packages/samples/specs/getting-started/sample-config.yaml new file mode 100644 index 00000000000..6ad35826c10 --- /dev/null +++ b/packages/samples/specs/getting-started/sample-config.yaml @@ -0,0 +1,3 @@ +directory: true +label: Getting Started +order: 1 diff --git a/packages/samples/specs/grpc-kiosk-example/sample-config.yaml b/packages/samples/specs/grpc-kiosk-example/sample-config.yaml new file mode 100644 index 00000000000..c1fdfdfc354 --- /dev/null +++ b/packages/samples/specs/grpc-kiosk-example/sample-config.yaml @@ -0,0 +1,3 @@ +title: Advanced gRPC kiosk +description: Demonstrate a multi-file Protobuf-oriented kiosk service. +playground: false diff --git a/packages/samples/specs/grpc-library-example/sample-config.yaml b/packages/samples/specs/grpc-library-example/sample-config.yaml new file mode 100644 index 00000000000..6b8b2d29b6b --- /dev/null +++ b/packages/samples/specs/grpc-library-example/sample-config.yaml @@ -0,0 +1,3 @@ +title: Protobuf library +description: Build and consume reusable Protobuf declarations. +playground: false diff --git a/packages/samples/specs/init/sample-config.yaml b/packages/samples/specs/init/sample-config.yaml new file mode 100644 index 00000000000..09c903dd11a --- /dev/null +++ b/packages/samples/specs/init/sample-config.yaml @@ -0,0 +1,3 @@ +title: Project initialization +description: Show the service generated by the TypeSpec project initializer. +playground: false diff --git a/packages/samples/specs/language/api-versioning/main.tsp b/packages/samples/specs/language/api-versioning/main.tsp new file mode 100644 index 00000000000..0a5283d487a --- /dev/null +++ b/packages/samples/specs/language/api-versioning/main.tsp @@ -0,0 +1,38 @@ +import "@typespec/http"; +import "@typespec/rest"; +import "@typespec/versioning"; + +using Http; +using Rest; +using Versioning; + +@versioned(Versions) +@service(#{ title: "Widget Service" }) +namespace DemoService; + +enum Versions { + v1, + v2, +} + +model Widget { + @key id: string; + weight: int32; + color: "red" | "blue"; + + @added(Versions.v2) + name: string; +} + +@error +model Error { + code: int32; + message: string; +} + +interface WidgetService extends Resource.ResourceOperations { + @added(Versions.v2) + @get + @route("customGet") + customGet(): Widget; +} diff --git a/packages/samples/specs/language/api-versioning/sample-config.yaml b/packages/samples/specs/language/api-versioning/sample-config.yaml new file mode 100644 index 00000000000..fd3788cf1c9 --- /dev/null +++ b/packages/samples/specs/language/api-versioning/sample-config.yaml @@ -0,0 +1,3 @@ +title: API versioning +description: Evolve an API across versions with version-aware types and operations. +order: 1 diff --git a/packages/samples/specs/language/api-versioning/tspconfig.yaml b/packages/samples/specs/language/api-versioning/tspconfig.yaml new file mode 100644 index 00000000000..a3fe48f13e9 --- /dev/null +++ b/packages/samples/specs/language/api-versioning/tspconfig.yaml @@ -0,0 +1,2 @@ +emit: + - "@typespec/openapi3" diff --git a/packages/samples/specs/language/discriminated-unions/main.tsp b/packages/samples/specs/language/discriminated-unions/main.tsp new file mode 100644 index 00000000000..a33fc673bbc --- /dev/null +++ b/packages/samples/specs/language/discriminated-unions/main.tsp @@ -0,0 +1,41 @@ +import "@typespec/http"; +import "@typespec/openapi3"; + +using Http; + +@service(#{ title: "Widget Service" }) +namespace DemoService; + +model WidgetBase { + @key id: string; + weight: int32; + color: "red" | "blue"; +} + +enum WidgetKind { + Heavy, + Light, +} + +model HeavyWidget extends WidgetBase { + kind: WidgetKind.Heavy; +} + +model LightWidget extends WidgetBase { + kind: WidgetKind.Light; +} + +@discriminated +union Widget { + heavy: HeavyWidget, + light: LightWidget, +} + +@error +model Error { + code: int32; + message: string; +} + +@get +op read(@path id: string): Widget | Error; diff --git a/packages/samples/specs/language/discriminated-unions/sample-config.yaml b/packages/samples/specs/language/discriminated-unions/sample-config.yaml new file mode 100644 index 00000000000..a9ce4bda7ae --- /dev/null +++ b/packages/samples/specs/language/discriminated-unions/sample-config.yaml @@ -0,0 +1,3 @@ +title: Discriminated unions +description: Model polymorphic values with a discriminated union. +order: 2 diff --git a/packages/samples/specs/language/discriminated-unions/tspconfig.yaml b/packages/samples/specs/language/discriminated-unions/tspconfig.yaml new file mode 100644 index 00000000000..a3fe48f13e9 --- /dev/null +++ b/packages/samples/specs/language/discriminated-unions/tspconfig.yaml @@ -0,0 +1,2 @@ +emit: + - "@typespec/openapi3" diff --git a/packages/samples/specs/language/sample-config.yaml b/packages/samples/specs/language/sample-config.yaml new file mode 100644 index 00000000000..9c19a8697ea --- /dev/null +++ b/packages/samples/specs/language/sample-config.yaml @@ -0,0 +1,3 @@ +directory: true +label: Language +order: 2 diff --git a/packages/samples/specs/local-typespec/sample-config.yaml b/packages/samples/specs/local-typespec/sample-config.yaml new file mode 100644 index 00000000000..f0f127fea64 --- /dev/null +++ b/packages/samples/specs/local-typespec/sample-config.yaml @@ -0,0 +1,3 @@ +title: Local TypeSpec installation +description: Configure an editor to use a project-local TypeSpec compiler. +playground: false diff --git a/packages/samples/specs/multipart/sample-config.yaml b/packages/samples/specs/multipart/sample-config.yaml new file mode 100644 index 00000000000..821e94f5001 --- /dev/null +++ b/packages/samples/specs/multipart/sample-config.yaml @@ -0,0 +1,3 @@ +title: Multipart requests +description: Define multipart form data with files and structured fields. +playground: false diff --git a/packages/samples/specs/multiple-types-union/main.tsp b/packages/samples/specs/multiple-types-union/main.tsp deleted file mode 100644 index 346f8e28d2b..00000000000 --- a/packages/samples/specs/multiple-types-union/main.tsp +++ /dev/null @@ -1,34 +0,0 @@ -import "@typespec/rest"; -import "@typespec/openapi"; - -@service(#{ title: "Pet Store Service" }) -namespace PetStore; -using Http; - -model ApiVersionParam { - @header apiVersion: string; -} - -model PetBase { - name: string; -} - -model Dog extends PetBase { - type: "dog"; - nextWalkTime: utcDateTime; -} - -model Cat extends PetBase { - type: "cat"; - catnipDose: int32; -} - -union Pet { - cat: Cat, - dog: Dog, -} - -@route("/") -interface MyService { - getPet(...ApiVersionParam): Body; -} diff --git a/packages/samples/specs/nested/main.tsp b/packages/samples/specs/nested/main.tsp deleted file mode 100644 index c7f142b0742..00000000000 --- a/packages/samples/specs/nested/main.tsp +++ /dev/null @@ -1 +0,0 @@ -import "./nested.tsp"; diff --git a/packages/samples/specs/nested/nested.tsp b/packages/samples/specs/nested/nested.tsp deleted file mode 100644 index 8b8de6e48e4..00000000000 --- a/packages/samples/specs/nested/nested.tsp +++ /dev/null @@ -1,37 +0,0 @@ -import "@typespec/rest"; - -using Http; - -@service(#{ title: "Nested sample" }) -namespace Root { - @route("/sub/a") - namespace SubA { - model Thing { - id: int64; - } - - @route("subsub") - namespace SubSubA { - @post op doSomething(@body thing: Thing): OkResponse & Body; - - // Doesn't conflict with parent namespace! - model Thing { - name: string; - } - } - } - - @route("/sub/b") - namespace SubB { - // Uses the same name as - model Thing { - id: int64; - } - - @post op doSomething(@body thing: Thing): string; - } - - namespace SubC { - @post op anotherOp(thing: Root.SubA.Thing, thing2: SubA.Thing): string; - } -} diff --git a/packages/samples/specs/nullable/main.tsp b/packages/samples/specs/nullable/main.tsp deleted file mode 100644 index e4190004fff..00000000000 --- a/packages/samples/specs/nullable/main.tsp +++ /dev/null @@ -1 +0,0 @@ -import "./nullable.tsp"; diff --git a/packages/samples/specs/nullable/nullable.tsp b/packages/samples/specs/nullable/nullable.tsp deleted file mode 100644 index fb177513100..00000000000 --- a/packages/samples/specs/nullable/nullable.tsp +++ /dev/null @@ -1,31 +0,0 @@ -import "@typespec/rest"; -import "@typespec/openapi"; - -using Http; - -@service(#{ title: "Nullable sample" }) -namespace NullableSample; - -model HasNullables { - str: string; - when: plainTime; - strOrNull: string | null; - modelOrNull: AnotherModel | null; - literalsOrNull: "one" | "two" | null; - manyNullsOneString: null | null | string | null; - manyNullsSomeValues: null | 42 | null | 100 | null; - arr: string[] | null; - // thisWillFail: AnotherModel | string | null; -} - -@route("/test") -namespace NullableMethods { - @get op read( - @query someParam: string | null, - @body modelOrNull: AnotherModel | null, - ): HasNullables; -} - -model AnotherModel { - num: int32; -} diff --git a/packages/samples/specs/openapi-extensions/sample-config.yaml b/packages/samples/specs/openapi-extensions/sample-config.yaml new file mode 100644 index 00000000000..d4e66f4dd5c --- /dev/null +++ b/packages/samples/specs/openapi-extensions/sample-config.yaml @@ -0,0 +1,3 @@ +title: OpenAPI extensions +description: Attach custom OpenAPI extensions to emitted declarations. +playground: false diff --git a/packages/samples/specs/optional/main.tsp b/packages/samples/specs/optional/main.tsp deleted file mode 100644 index 953235bacef..00000000000 --- a/packages/samples/specs/optional/main.tsp +++ /dev/null @@ -1 +0,0 @@ -import "./optional.tsp"; diff --git a/packages/samples/specs/optional/optional.tsp b/packages/samples/specs/optional/optional.tsp deleted file mode 100644 index ddfd3a05e2d..00000000000 --- a/packages/samples/specs/optional/optional.tsp +++ /dev/null @@ -1,29 +0,0 @@ -import "@typespec/rest"; - -using Http; - -@service(#{ title: "Optional sample" }) -namespace OptionalSample; - -model HasOptional { - optionalNoDefault?: string; - optionalString?: string = "default string"; - optionalNumber?: int32 = 123; - optionalBoolean?: boolean = true; - optionalArray?: string[] = #["foo", "bar"]; - optionalUnion?: "foo" | "bar" = "foo"; - optionalEnum?: MyEnum = MyEnum.a; -} - -@route("/test") -namespace OptionalMethods { - @get op read( - @query queryString?: string = "defaultQueryString", - @body queryNumber?: int64 = 123, - ): HasOptional; -} - -enum MyEnum { - a, - b, -} diff --git a/packages/samples/specs/param-decorators/sample-config.yaml b/packages/samples/specs/param-decorators/sample-config.yaml new file mode 100644 index 00000000000..32eb0c2abd2 --- /dev/null +++ b/packages/samples/specs/param-decorators/sample-config.yaml @@ -0,0 +1,3 @@ +title: Parameter decorators +description: Apply decorators through reusable operation parameters. +playground: false diff --git a/packages/samples/specs/petstore/sample-config.yaml b/packages/samples/specs/petstore/sample-config.yaml new file mode 100644 index 00000000000..b32cb866a1d --- /dev/null +++ b/packages/samples/specs/petstore/sample-config.yaml @@ -0,0 +1,3 @@ +title: Extensible Pet Store +description: Extend TypeSpec with a local decorator and custom project files. +playground: false diff --git a/packages/samples/specs/polymorphism/sample-config.yaml b/packages/samples/specs/polymorphism/sample-config.yaml new file mode 100644 index 00000000000..70c2bc8fca3 --- /dev/null +++ b/packages/samples/specs/polymorphism/sample-config.yaml @@ -0,0 +1,3 @@ +title: Polymorphic models +description: Model inheritance and discriminated polymorphic responses. +playground: false diff --git a/packages/samples/specs/rest-metadata-emitter/sample-config.yaml b/packages/samples/specs/rest-metadata-emitter/sample-config.yaml new file mode 100644 index 00000000000..cc8b0a0ed3a --- /dev/null +++ b/packages/samples/specs/rest-metadata-emitter/sample-config.yaml @@ -0,0 +1,3 @@ +title: REST metadata emitter +description: Inspect REST metadata from a custom emitter. +playground: false diff --git a/packages/samples/specs/rest/petstore/sample-config.yaml b/packages/samples/specs/rest/petstore/sample-config.yaml new file mode 100644 index 00000000000..347b239cb9c --- /dev/null +++ b/packages/samples/specs/rest/petstore/sample-config.yaml @@ -0,0 +1,3 @@ +title: REST Pet Store +description: Define a resource-oriented Pet Store service. +playground: false diff --git a/packages/samples/specs/signatures/sample-config.yaml b/packages/samples/specs/signatures/sample-config.yaml new file mode 100644 index 00000000000..a08e5308e92 --- /dev/null +++ b/packages/samples/specs/signatures/sample-config.yaml @@ -0,0 +1,3 @@ +title: Operation signatures +description: Compare operation declaration and template signatures. +playground: false diff --git a/packages/samples/specs/simple/main.tsp b/packages/samples/specs/simple/main.tsp deleted file mode 100644 index 2f519503468..00000000000 --- a/packages/samples/specs/simple/main.tsp +++ /dev/null @@ -1 +0,0 @@ -import "./simple.tsp"; diff --git a/packages/samples/specs/simple/simple.tsp b/packages/samples/specs/simple/simple.tsp deleted file mode 100644 index de103cc73eb..00000000000 --- a/packages/samples/specs/simple/simple.tsp +++ /dev/null @@ -1,9 +0,0 @@ -import "@typespec/rest"; - -using Http; - -@route("/alpha/{id}") -op doAlpha(id: string): string; - -@route("/beta/{id}") -op doBeta(id: string): string; diff --git a/packages/samples/specs/status-code-ranges/sample-config.yaml b/packages/samples/specs/status-code-ranges/sample-config.yaml new file mode 100644 index 00000000000..8bde09833b2 --- /dev/null +++ b/packages/samples/specs/status-code-ranges/sample-config.yaml @@ -0,0 +1,3 @@ +title: Status code ranges +description: Describe HTTP responses covering status code ranges. +playground: false diff --git a/packages/samples/specs/string-template/sample-config.yaml b/packages/samples/specs/string-template/sample-config.yaml new file mode 100644 index 00000000000..0283f256347 --- /dev/null +++ b/packages/samples/specs/string-template/sample-config.yaml @@ -0,0 +1,3 @@ +title: String templates +description: Construct values using TypeSpec string templates. +playground: false diff --git a/packages/samples/specs/tags/sample-config.yaml b/packages/samples/specs/tags/sample-config.yaml new file mode 100644 index 00000000000..b33bcb3e2fa --- /dev/null +++ b/packages/samples/specs/tags/sample-config.yaml @@ -0,0 +1,3 @@ +title: OpenAPI tags +description: Group emitted operations with tags. +playground: false diff --git a/packages/samples/specs/testserver/body-boolean/body-boolean.tsp b/packages/samples/specs/testserver/body-boolean/body-boolean.tsp deleted file mode 100644 index c6f860712c2..00000000000 --- a/packages/samples/specs/testserver/body-boolean/body-boolean.tsp +++ /dev/null @@ -1,67 +0,0 @@ -import "@typespec/rest"; -import "@typespec/openapi"; - -using Http; -using OpenAPI; - -@service(#{ title: "sample" }) -namespace Sample; - -@doc("Error") -@error -model Error { - code: int32; - message: string; -} - -alias NullableBoolean = boolean | null; - -@doc("AutoRest Bool Test Service") -@route("/bool/true") -namespace BoolsTrue { - @doc("Get true Boolean value") - @operationId("bool_getTrue") - @get - op getTrue(): true | Error; - - @doc("Set Boolean value true") - @operationId("bool_putTrue") - @put - op setTrue(@body value: true): - | { - ...Response<200>; - } - | Error; -} - -@doc("AutoRest Bool Test Service") -@route("/bool/false") -namespace BoolsFalse { - @doc("Get true Boolean false") - @operationId("bool_getFalse") - @get - op getFalse(): false | Error; - - @doc("Set Boolean value false") - @operationId("bool_putFalse") - @put - op setFalse(@body value: false): boolean | Error; -} - -@doc("AutoRest Bool Test Service") -@route("/bool/null") -namespace BoolsNull { - @doc("Get null Boolean value") - @operationId("bool_getNull") - @get - op getNull(): NullableBoolean | Error; -} - -@doc("AutoRest Bool Test Service") -@route("/bool/invalid") -namespace BoolsInvalid { - @doc("Get invalid Boolean value") - @operationId("bool_getInvalid") - @get - op getInvalid(): boolean | Error; -} diff --git a/packages/samples/specs/testserver/body-boolean/main.tsp b/packages/samples/specs/testserver/body-boolean/main.tsp deleted file mode 100644 index 7c9a2440442..00000000000 --- a/packages/samples/specs/testserver/body-boolean/main.tsp +++ /dev/null @@ -1 +0,0 @@ -import "./body-boolean.tsp"; diff --git a/packages/samples/specs/testserver/body-complex/body-complex.tsp b/packages/samples/specs/testserver/body-complex/body-complex.tsp deleted file mode 100644 index af6842e1fb8..00000000000 --- a/packages/samples/specs/testserver/body-complex/body-complex.tsp +++ /dev/null @@ -1,284 +0,0 @@ -import "@typespec/rest"; -import "@typespec/openapi"; - -using Http; - -@service(#{ title: "sample" }) -namespace Sample; - -@doc("Error") -@error -model Error { - message: string; -} - -model Basic { - @doc("Basic Id") - id: int32 | null; - - @doc("Name property with a very long description that does not fit on a single line and a line break.") - name: string; - - color: Colors; -} - -enum Colors { - cyan, - Magenta, - YELLOW, - blacK, -} - -model Pet { - id: int32; - name: string; -} - -model IntWrapper { - field1: int32; - field2: int32; -} - -model LongWrapper { - field1: int64; - field2: int64; -} - -model FloatWrapper { - field1: float32; - field2: float32; -} - -model DoubleWrapper { - field1: float64; - field_56_zeros_after_the_dot_and_negative_zero_before_dot_and_this_is_a_long_field_name_on_purpose: float64; -} - -model BooleanWrapper { - field_true: boolean; - field_false: boolean; -} - -model StringWrapper { - field: string; - empty: string; - null: string | null; -} - -model PlainDateWrapper { - field: plainDate; - leap: plainDate; -} - -model DateTimeWrapper { - field: utcDateTime; - now: utcDateTime; -} - -model DurationWrapper { - field: duration; -} - -model BytesWrapper { - field: bytes; -} - -model ArrayWrapper { - field: string[]; -} - -model DictionaryWrapper { - field: Record; -} - -model ReadonlyObj { - @visibility(Lifecycle.Read) id: int32; - @visibility(Lifecycle.Read) size: int32; -} - -@doc("AutoRest Complex Body Test Service") -@route("/complex") -namespace Complex { - @doc("Get complex type {id: 2, name: 'abc', color: 'YELLOW'}") - @route("/basic/valid") - @get - op getValid(): Basic | Error; - - @doc("Please put {id: 2, name: 'abc', color: 'Magenta'}") - @route("/basic/valid") - @put - op putValid(@body value: Basic): - | { - ...Response<200>; - } - | Error; - - @doc("Get complex types with integer properties") - @route("/primitive/integer") - op getInt(): IntWrapper | Error; - - @doc("Put complex types with integer properties") - @route("/primitive/integer") - @put - op putInt(@body value: IntWrapper): - | { - ...Response<200>; - } - | Error; - - @doc("Get complex types with long properties") - @route("/primitive/long") - @get - op getLong(): LongWrapper | Error; - - @doc("Put complex types with long properties") - @route("/primitive/long") - @put - op putLong(@body value: LongWrapper): - | { - ...Response<200>; - } - | Error; - - @doc("Get complex types with float properties") - @route("/primitive/float") - @get - op getFloat(): FloatWrapper | Error; - - @doc("Put complex types with float properties") - @route("/primitive/float") - @put - op putFloat(@body value: FloatWrapper): - | { - ...Response<200>; - } - | Error; - - @doc("Get complex types with double properties") - @route("/primitive/double") - @get - op getDouble(): DoubleWrapper | Error; - - @doc("Put complex types with double properties") - @route("/primitive/double") - @put - op putDouble(@body value: DoubleWrapper): - | { - ...Response<200>; - } - | Error; - - @doc("Get complex types with bool properties") - @route("/primitive/bool") - @get - op getBool(): BooleanWrapper | Error; - - @doc("Put complex types with bool properties") - @route("/primitive/bool") - @put - op putBool(@body value: BooleanWrapper): - | { - ...Response<200>; - } - | Error; - - @doc("Get complex types with string properties") - @route("/primitive/string") - @get - op getString(): StringWrapper | Error; - - @doc("Put complex types with string properties") - @route("/primitive/string") - @put - op putString(@body value: StringWrapper): - | { - ...Response<200>; - } - | Error; - - @doc("Get complex types with date properties") - @route("/primitive/date") - @get - op getPlainDate(): PlainDateWrapper | Error; - - @doc("Put complex types with date properties") - @route("/primitive/date") - @put - op putPlainDate(@body value: PlainDateWrapper): - | { - ...Response<200>; - } - | Error; - - @doc("Get complex types with DateTime properties") - @route("/primitive/datetime") - @get - op getZonedDateTime(): DateTimeWrapper | Error; - - @doc("Put complex types with DateTime properties") - @route("/primitive/datetime") - @put - op putZonedDateTime(@body value: DateTimeWrapper): - | { - ...Response<200>; - } - | Error; - - @doc("Get complex types with Duration properties") - @route("/primitive/duration") - @get - op getZonedDuration(): DurationWrapper | Error; - - @doc("Put complex types with Duration properties") - @route("/primitive/duration") - @put - op putZonedDuration(@body value: DurationWrapper): - | { - ...Response<200>; - } - | Error; - - @doc("Get complex types with bytes properties") - @route("/primitive/bytes") - @get - op getBytes(): BytesWrapper | Error; - - @doc("Put complex types with bytes properties") - @route("/primitive/bytes") - @put - op putBytes(@body value: BytesWrapper): - | { - ...Response<200>; - } - | Error; - - @doc("Get complex types with array properties") - @route("/array/valid") - op getArray(): ArrayWrapper | Error; - - @doc("Put complex types with array properties") - @route("/array/valid") - @put - op putArray(@body value: ArrayWrapper): - | { - ...Response<200>; - } - | Error; - - @route("/dictionary") - namespace Dictionary { - @doc("Get complex types with dictionary property") - @route("/typed/valid") - @get - op getDictionary(): DictionaryWrapper | Error; - - @doc("Put complex types with dictionary property") - @route("/typed/valid") - @put - op putArray(@body value: DictionaryWrapper): - | { - ...Response<200>; - } - | Error; - } -} diff --git a/packages/samples/specs/testserver/body-complex/main.tsp b/packages/samples/specs/testserver/body-complex/main.tsp deleted file mode 100644 index 5714a37045e..00000000000 --- a/packages/samples/specs/testserver/body-complex/main.tsp +++ /dev/null @@ -1 +0,0 @@ -import "./body-complex.tsp"; diff --git a/packages/samples/specs/testserver/body-string/body-string.tsp b/packages/samples/specs/testserver/body-string/body-string.tsp deleted file mode 100644 index 6b2a05632d3..00000000000 --- a/packages/samples/specs/testserver/body-string/body-string.tsp +++ /dev/null @@ -1,139 +0,0 @@ -import "@typespec/rest"; -import "@typespec/openapi"; -/* cspell:disable */ - -using Http; - -@service(#{ title: "sample" }) -namespace Sample; - -@doc("Error") -@error -model Error { - status: int32; - message: string; -} - -@doc("AutoRest String Test Service") -@tag("String Operations") -@route("/string") -namespace String { - @doc("Get null string value") - @route("null") - @get - op getNull(): Body | Error; - - @doc("Put null string value") - @route("null") - @put - op putNull(@body value: string | null): - | { - ...Response<200>; - } - | Error; - - @doc("Get empty string value") - @route("empty") - @get - op getEmpty(): Body<""> | Error; - - @doc("Put empty string value") - @route("empty") - @put - op putEmpty(@body value: ""): - | { - ...Response<200>; - } - | Error; - - @doc("Get mbcs string value '啊齄丂狛狜隣郎隣兀﨩ˊ〞〡¦℡㈱‐ー﹡﹢﹫、〓ⅰⅹ⒈€㈠㈩ⅠⅫ! ̄ぁんァヶΑ︴АЯаяāɡㄅㄩ─╋︵﹄︻︱︳︴ⅰⅹɑɡ〇〾⿻⺁䜣€'") - @route("mbcs") - @get - op getMbcs(): - | Body<"啊齄丂狛狜隣郎隣兀﨩ˊ〞〡¦℡㈱‐ー﹡﹢﹫、〓ⅰⅹ⒈€㈠㈩ⅠⅫ! ̄ぁんァヶΑ︴АЯаяāɡㄅㄩ─╋︵﹄︻︱︳︴ⅰⅹɑɡ〇〾⿻⺁䜣€"> - | Error; - - @doc("Put mbcs string value '啊齄丂狛狜隣郎隣兀﨩ˊ〞〡¦℡㈱‐ー﹡﹢﹫、〓ⅰⅹ⒈€㈠㈩ⅠⅫ! ̄ぁんァヶΑ︴АЯаяāɡㄅㄩ─╋︵﹄︻︱︳︴ⅰⅹɑɡ〇〾⿻⺁䜣€'") - @route("mbcs") - @put - op putMbCs( - @body - value: "啊齄丂狛狜隣郎隣兀﨩ˊ〞〡¦℡㈱‐ー﹡﹢﹫、〓ⅰⅹ⒈€㈠㈩ⅠⅫ! ̄ぁんァヶΑ︴АЯаяāɡㄅㄩ─╋︵﹄︻︱︳︴ⅰⅹɑɡ〇〾⿻⺁䜣€", - ): - | { - ...Response<200>; - } - | Error; - - @doc("Get string value with leading and trailing whitespace 'Now is the time for all good men to come to the aid of their country'") - @route("whitespace") - @get - op getWhitespace(): - | Body<" Now is the time for all good men to come to the aid of their country "> - | Error; - - @doc("Get string value with leading and trailing whitespace 'Now is the time for all good men to come to the aid of their country'") - @route("whitespace") - @put - op putWhitespace( - @body value: " Now is the time for all good men to come to the aid of their country ", - ): - | { - ...Response<200>; - } - | Error; - - @doc("Get value that is base64 encoded") - @route("base64Encoding") - @get - op getBase64Encoding(): Body | Error; - - @doc("Put value that is base64 encoded") - @route("base64Encoding") - @put - op putBase64Encoding(@body value: bytes): - | { - ...Response<200>; - } - | Error; - - @route("enum") - namespace Enums { - @doc("Get non expandable string enum value") - @route("empty") - @get - op getNotExpandable(): Body | Error; - - @doc("Put non expandable string enum value") - @route("empty") - @put - op putNotExpandable(@body value: Colors): - | { - ...Response<200>; - } - | Error; - - @doc("Gets value 'green-color' from a constant") - @route("constant") - @get - op getConstant(): Body | Error; - - @doc("Sends value 'green-color' from a constant") - @route("constant") - @put - op putConstant(@body value: Colors): - | { - ...Response<200>; - } - | Error; - } -} - -// TODO: closed enum https://github.com/Azure/typespec-azure/issues/1036 -enum Colors { - `red color`, - `green-color`, - `blue-color`, -} - -alias RefColorConstant = "green-color"; diff --git a/packages/samples/specs/testserver/body-string/main.tsp b/packages/samples/specs/testserver/body-string/main.tsp deleted file mode 100644 index b767a0fe158..00000000000 --- a/packages/samples/specs/testserver/body-string/main.tsp +++ /dev/null @@ -1 +0,0 @@ -import "./body-string.tsp"; diff --git a/packages/samples/specs/testserver/body-time/body-time.tsp b/packages/samples/specs/testserver/body-time/body-time.tsp deleted file mode 100644 index 3e2e24ed3b8..00000000000 --- a/packages/samples/specs/testserver/body-time/body-time.tsp +++ /dev/null @@ -1,26 +0,0 @@ -import "@typespec/rest"; -import "@typespec/openapi"; - -using Http; - -@service(#{ title: "sample" }) -namespace Sample; - -@doc("Error") -@error -model Error { - status: int32; - message: string; -} - -@doc("AutoRest Time Test Service") -@route("/time") -namespace Time { - @doc("Get time value \"11:34:56\"") - @get - op get(): plainTime | Error; - - @doc("Put time value \"08:07:56\"") - @put - op put(@body value: plainTime): OkResponse | Error; -} diff --git a/packages/samples/specs/testserver/body-time/main.tsp b/packages/samples/specs/testserver/body-time/main.tsp deleted file mode 100644 index 1d39d7f896e..00000000000 --- a/packages/samples/specs/testserver/body-time/main.tsp +++ /dev/null @@ -1 +0,0 @@ -import "./body-time.tsp"; diff --git a/packages/samples/specs/testserver/media-types/main.tsp b/packages/samples/specs/testserver/media-types/main.tsp deleted file mode 100644 index 7a3bdd93322..00000000000 --- a/packages/samples/specs/testserver/media-types/main.tsp +++ /dev/null @@ -1 +0,0 @@ -import "./media-types.tsp"; diff --git a/packages/samples/specs/testserver/media-types/media-types.tsp b/packages/samples/specs/testserver/media-types/media-types.tsp deleted file mode 100644 index acdfd6d5a0f..00000000000 --- a/packages/samples/specs/testserver/media-types/media-types.tsp +++ /dev/null @@ -1,45 +0,0 @@ -import "@typespec/rest"; -import "@typespec/openapi"; - -using Http; -using OpenAPI; - -@service(#{ title: "sample" }) -namespace Sample; - -@doc("Uri or local path to source data.") -model SourcePath { - @minLength(0) - @maxLength(2048) - @doc("File source path.") - source: string; -} - -model Input { - @doc("Input parameter.") - @body - input: SourcePath; -} - -@route("/mediatypes") -namespace MediaTypes { - @doc("Analyze body, that could be different media types.") - @route("analyze") - @operationId("AnalyzeBody") - @post - op analyzeBody( - ...Input, - @header contentType: - | "application/pdf" - | "application/json" - | "image/jpeg" - | "image/png" - | "image/tiff", - ): string; - - @doc("Pass in contentType 'text/plain; encoding=UTF-8' to pass test. Value for input does not matter") - @route("contentTypeWithEncoding") - @operationId("contentTypeWithEncoding") - @post - op contentTypeWithEncoding(...Input, @header contentType: "text/plain"): string; -} diff --git a/packages/samples/specs/testserver/multiple-inheritance/main.tsp b/packages/samples/specs/testserver/multiple-inheritance/main.tsp deleted file mode 100644 index 347345ebc5a..00000000000 --- a/packages/samples/specs/testserver/multiple-inheritance/main.tsp +++ /dev/null @@ -1 +0,0 @@ -import "./multiple-inheritance.tsp"; diff --git a/packages/samples/specs/testserver/multiple-inheritance/multiple-inheritance.tsp b/packages/samples/specs/testserver/multiple-inheritance/multiple-inheritance.tsp deleted file mode 100644 index d87d5b25da0..00000000000 --- a/packages/samples/specs/testserver/multiple-inheritance/multiple-inheritance.tsp +++ /dev/null @@ -1,82 +0,0 @@ -import "@typespec/rest"; -import "@typespec/openapi"; - -using Http; - -@service(#{ title: "sample" }) -namespace Sample; - -model Pet { - name: string; -} - -model Feline extends Pet { - meows: boolean; - hisses: boolean; -} - -model Cat extends Feline { - likesMilk: boolean; -} - -model Kitten extends Cat { - eatsMiceYet: boolean; -} - -model Horse extends Pet { - isAShowHorse: boolean; -} - -@doc("Unexpected error") -@error -model ErrorResponse { - @body body: string; -} - -@route("/multipleInheritance") -namespace MultipleInheritance { - @doc("Get a horse with name 'Fred' and isAShowHorse true") - @route("horse") - op getHorse(): Horse | ErrorResponse; - - @doc("Put a horse with name 'General' and isAShowHorse false") - @route("horse") - @put - op putHorse(@body horse: Horse): string | ErrorResponse; - - @doc("Get a pet with name 'Peanut'") - @route("pet") - op getPet(): Pet | ErrorResponse; - - @doc("Put a pet with name 'Butter'") - @route("pet") - @put - op putPet(@body horse: Pet): string | ErrorResponse; - - @doc("Get a feline where meows and hisses are true") - @route("feline") - op getFeline(): Feline | ErrorResponse; - - @doc("Put a feline who hisses and doesn't meow") - @route("feline") - @put - op putFeline(@body feline: Feline): string | ErrorResponse; - - @doc("Get a cat with name 'Whiskers' where likesMilk, meows, and hisses is true") - @route("cat") - op getCat(): Cat | ErrorResponse; - - @doc("Put a cat with name 'Boots' where likesMilk and hisses is false, meows is true") - @route("cat") - @put - op putCat(@body cat: Cat): string | ErrorResponse; - - @doc("Get a kitten with name 'Gatito' where likesMilk and meows is true, and hisses and eatsMiceYet is false") - @route("kitten") - op getKitten(): Kitten | ErrorResponse; - - @doc("Put a kitten with name 'Kitty' where likesMilk and hisses is false, meows and eatsMiceYet is true") - @route("kitten") - @put - op putKitten(@body kitten: Kitten): string | ErrorResponse; -} diff --git a/packages/samples/specs/todoApp/sample-config.yaml b/packages/samples/specs/todoApp/sample-config.yaml new file mode 100644 index 00000000000..568693e6875 --- /dev/null +++ b/packages/samples/specs/todoApp/sample-config.yaml @@ -0,0 +1,3 @@ +title: Todo application +description: Define a complete HTTP API for a small todo application. +playground: false diff --git a/packages/samples/specs/use-versioned-lib/sample-config.yaml b/packages/samples/specs/use-versioned-lib/sample-config.yaml new file mode 100644 index 00000000000..0558bdb7e51 --- /dev/null +++ b/packages/samples/specs/use-versioned-lib/sample-config.yaml @@ -0,0 +1,3 @@ +title: Versioned library +description: Consume a library whose API evolves across versions. +playground: false diff --git a/packages/samples/specs/versioning/sample-config.yaml b/packages/samples/specs/versioning/sample-config.yaml new file mode 100644 index 00000000000..ec681dc7090 --- /dev/null +++ b/packages/samples/specs/versioning/sample-config.yaml @@ -0,0 +1,3 @@ +title: Advanced versioning +description: Coordinate service and library versions across multiple files. +playground: false diff --git a/packages/samples/specs/visibility/sample-config.yaml b/packages/samples/specs/visibility/sample-config.yaml new file mode 100644 index 00000000000..5629bbf9674 --- /dev/null +++ b/packages/samples/specs/visibility/sample-config.yaml @@ -0,0 +1,3 @@ +title: Lifecycle visibility +description: Control how model properties participate in lifecycle operations. +playground: false diff --git a/packages/samples/src/index.ts b/packages/samples/src/index.ts index 4a2e207b030..9c67c0047c6 100644 --- a/packages/samples/src/index.ts +++ b/packages/samples/src/index.ts @@ -1 +1,2 @@ +export * from "./sample-config.js"; export * from "./sample-snapshot-testing.js"; diff --git a/packages/samples/src/sample-config.ts b/packages/samples/src/sample-config.ts new file mode 100644 index 00000000000..ee54d2c6eee --- /dev/null +++ b/packages/samples/src/sample-config.ts @@ -0,0 +1,288 @@ +import { readdir, readFile } from "node:fs/promises"; +import { dirname, relative, resolve, sep } from "node:path"; +import { parse } from "yaml"; + +export interface SampleConfig { + directory?: false; + title: string; + description: string; + llmstxt?: boolean; + danger?: string; + order?: number; + playground?: boolean; +} + +export interface SampleDirectoryConfig { + directory: true; + label?: string; + danger?: string; + order?: number; + playground?: boolean; +} + +export interface SampleCategory { + id: string; + label: string; + order?: number; +} + +export interface SampleEntry { + id: string; + directory: string; + config: SampleConfig; + category?: SampleCategory; + playground: boolean; + tspFiles: string[]; +} + +export interface SampleCatalog { + samples: SampleEntry[]; + directories: Map; +} + +interface LoadedConfig { + id: string; + directory: string; + config: SampleConfig | SampleDirectoryConfig; +} + +/** + * Discover and validate samples below the given specs directory. + */ +export async function loadSampleCatalog(specsDir: string): Promise { + const root = resolve(specsDir); + const loadedConfigs: LoadedConfig[] = []; + const entrypointDirs = new Set(); + + await walk(root); + + const configByDirectory = new Map(loadedConfigs.map((x) => [x.directory, x])); + const directories = new Map(); + const samples: SampleEntry[] = []; + const titles = new Map(); + + for (const loaded of loadedConfigs) { + if (loaded.config.directory === true) { + directories.set(loaded.id, loaded.config); + continue; + } + + const existingTitle = titles.get(loaded.config.title); + if (existingTitle) { + throw new Error( + `Sample "${loaded.id}" has duplicate title "${loaded.config.title}" also used by "${existingTitle}".`, + ); + } + titles.set(loaded.config.title, loaded.id); + + const tspFiles = await findTspFiles(loaded.directory); + const hasMain = tspFiles.some((x) => x === resolve(loaded.directory, "main.tsp")); + const hasPackage = entrypointDirs.has(loaded.directory); + if (!hasMain && !hasPackage) { + throw new Error( + `Sample "${loaded.id}" must contain main.tsp or a package.json TypeSpec entrypoint.`, + ); + } + + const ancestors = getAncestorDirectories(loaded.directory, root); + const directoryConfigs = ancestors + .map((x) => configByDirectory.get(x)) + .filter( + (x): x is LoadedConfig & { config: SampleDirectoryConfig } => x?.config.directory === true, + ); + const categoryConfig = directoryConfigs.at(-1); + + samples.push({ + id: loaded.id, + directory: loaded.directory, + config: loaded.config, + category: categoryConfig + ? { + id: categoryConfig.id, + label: categoryConfig.config.label ?? formatLabel(categoryConfig.id.split("/").at(-1)!), + order: categoryConfig.config.order, + } + : undefined, + playground: + loaded.config.playground !== false && + directoryConfigs.every((x) => x.config.playground !== false), + tspFiles, + }); + } + + for (const entrypointDir of entrypointDirs) { + const loaded = configByDirectory.get(entrypointDir); + if (!loaded || loaded.config.directory === true) { + throw new Error( + `Sample at "${toPosix(relative(root, entrypointDir))}" is missing sample-config.yaml.`, + ); + } + } + + samples.sort( + (a, b) => + (a.category?.order ?? Number.POSITIVE_INFINITY) - + (b.category?.order ?? Number.POSITIVE_INFINITY) || + (a.config.order ?? Number.POSITIVE_INFINITY) - (b.config.order ?? Number.POSITIVE_INFINITY) || + a.id.localeCompare(b.id), + ); + + return { samples, directories }; + + async function walk(directory: string): Promise { + const entries = await readdir(directory, { withFileTypes: true }); + for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) { + const fullPath = resolve(directory, entry.name); + if (entry.isDirectory()) { + await walk(fullPath); + } else if (entry.name === "sample-config.yaml") { + const id = toPosix(relative(root, directory)); + loadedConfigs.push({ + id, + directory, + config: parseSampleConfig(await readFile(fullPath, "utf-8"), fullPath), + }); + } else if (entry.name === "main.tsp" || entry.name === "package.json") { + entrypointDirs.add(directory); + } + } + } +} + +function parseSampleConfig( + content: string, + configPath: string, +): SampleConfig | SampleDirectoryConfig { + let value: unknown; + try { + value = parse(content); + } catch (error) { + throw new Error(`Invalid YAML in ${configPath}.`, { cause: error }); + } + + if (!isRecord(value)) { + throw new Error(`Sample config at ${configPath} must be a YAML object.`); + } + + if (value.directory === true) { + validateKnownKeys( + value, + new Set(["directory", "label", "danger", "order", "playground"]), + configPath, + ); + validateOptionalString(value, "label", configPath); + validateOptionalString(value, "danger", configPath); + validateOptionalNumber(value, "order", configPath); + validateOptionalBoolean(value, "playground", configPath); + return value as unknown as SampleDirectoryConfig; + } + + validateKnownKeys( + value, + new Set(["directory", "title", "description", "llmstxt", "danger", "order", "playground"]), + configPath, + ); + if (value.directory !== undefined && value.directory !== false) { + throw new Error(`Sample config at ${configPath} has invalid "directory"; expected false.`); + } + validateRequiredString(value, "title", configPath); + validateRequiredString(value, "description", configPath); + validateOptionalBoolean(value, "llmstxt", configPath); + validateOptionalString(value, "danger", configPath); + validateOptionalNumber(value, "order", configPath); + validateOptionalBoolean(value, "playground", configPath); + return value as unknown as SampleConfig; +} + +function validateKnownKeys( + value: Record, + keys: Set, + configPath: string, +): void { + const unknown = Object.keys(value).find((x) => !keys.has(x)); + if (unknown) { + throw new Error(`Sample config at ${configPath} has unknown field "${unknown}".`); + } +} + +async function findTspFiles(directory: string): Promise { + const files: string[] = []; + await walk(directory); + return files.sort(); + + async function walk(current: string): Promise { + for (const entry of await readdir(current, { withFileTypes: true })) { + const fullPath = resolve(current, entry.name); + if (entry.isDirectory()) { + await walk(fullPath); + } else if (entry.name.endsWith(".tsp")) { + files.push(fullPath); + } + } + } +} + +function getAncestorDirectories(directory: string, root: string): string[] { + const ancestors: string[] = []; + let current = dirname(directory); + while (current !== root && current.startsWith(`${root}${sep}`)) { + ancestors.unshift(current); + current = dirname(current); + } + return ancestors; +} + +function formatLabel(value: string): string { + return value + .split("-") + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(" "); +} + +function toPosix(path: string): string { + return path.split(sep).join("/"); +} + +function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null && !Array.isArray(value); +} + +function validateRequiredString( + value: Record, + key: string, + configPath: string, +): void { + if (typeof value[key] !== "string" || value[key].trim() === "") { + throw new Error(`Sample config at ${configPath} requires a non-empty "${key}" field.`); + } +} + +function validateOptionalString( + value: Record, + key: string, + configPath: string, +): void { + if (value[key] !== undefined && typeof value[key] !== "string") { + throw new Error(`Sample config at ${configPath} has invalid "${key}"; expected a string.`); + } +} + +function validateOptionalNumber( + value: Record, + key: string, + configPath: string, +): void { + if (value[key] !== undefined && typeof value[key] !== "number") { + throw new Error(`Sample config at ${configPath} has invalid "${key}"; expected a number.`); + } +} + +function validateOptionalBoolean( + value: Record, + key: string, + configPath: string, +): void { + if (value[key] !== undefined && typeof value[key] !== "boolean") { + throw new Error(`Sample config at ${configPath} has invalid "${key}"; expected a boolean.`); + } +} diff --git a/packages/samples/test/output/emitters/json-schema/@typespec/json-schema/Address.yaml b/packages/samples/test/output/emitters/json-schema/@typespec/json-schema/Address.yaml new file mode 100644 index 00000000000..66d24b90332 --- /dev/null +++ b/packages/samples/test/output/emitters/json-schema/@typespec/json-schema/Address.yaml @@ -0,0 +1,14 @@ +$schema: https://json-schema.org/draft/2020-12/schema +$id: Address.yaml +type: object +properties: + street: + type: string + city: + type: string + country: + type: string +required: + - street + - city + - country diff --git a/packages/samples/test/output/emitters/json-schema/@typespec/json-schema/Car.yaml b/packages/samples/test/output/emitters/json-schema/@typespec/json-schema/Car.yaml new file mode 100644 index 00000000000..102dbc176a5 --- /dev/null +++ b/packages/samples/test/output/emitters/json-schema/@typespec/json-schema/Car.yaml @@ -0,0 +1,18 @@ +$schema: https://json-schema.org/draft/2020-12/schema +$id: Car.yaml +type: object +properties: + kind: + anyOf: + - type: string + const: ev + - type: string + const: ice + brand: + type: string + model: + type: string +required: + - kind + - brand + - model diff --git a/packages/samples/test/output/emitters/json-schema/@typespec/json-schema/Person.yaml b/packages/samples/test/output/emitters/json-schema/@typespec/json-schema/Person.yaml new file mode 100644 index 00000000000..e7de614ac05 --- /dev/null +++ b/packages/samples/test/output/emitters/json-schema/@typespec/json-schema/Person.yaml @@ -0,0 +1,34 @@ +$schema: https://json-schema.org/draft/2020-12/schema +$id: Person.yaml +type: object +properties: + firstName: + type: string + description: The person's first name. + lastName: + type: string + description: The person's last name. + age: + type: integer + minimum: 0 + maximum: 2147483647 + description: Age in years, which must be zero or greater. + address: + $ref: Address.yaml + description: The person's address. + nickNames: + type: array + items: + type: string + uniqueItems: true + description: The person's nicknames. + cars: + type: array + items: + $ref: Car.yaml + description: Cars owned by the person. +required: + - firstName + - lastName + - age + - address diff --git a/packages/samples/test/output/emitters/protobuf-kiosk/@typespec/protobuf/kiosk.proto b/packages/samples/test/output/emitters/protobuf-kiosk/@typespec/protobuf/kiosk.proto new file mode 100644 index 00000000000..97a48c1c746 --- /dev/null +++ b/packages/samples/test/output/emitters/protobuf-kiosk/@typespec/protobuf/kiosk.proto @@ -0,0 +1,96 @@ +// Generated by Microsoft TypeSpec + +syntax = "proto3"; + +package kiosk; + +import "google/type/latlng.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/empty.proto"; + +message ScreenSize { + int32 width = 1; + int32 height = 2; +} + +message Kiosk { + optional int32 id = 1; + string name = 2; + ScreenSize size = 3; + google.type.LatLng location = 4; + google.protobuf.Timestamp createTime = 5; +} + +message Sign { + optional int32 id = 1; + string name = 2; + string text = 3; + bytes image = 4; + google.protobuf.Timestamp createTime = 5; +} + +message GetSignIdResponse { + int32 signId = 1; +} + +message ListKiosksResponse { + repeated Kiosk kiosks = 1; +} + +message GetKioskRequest { + int32 id = 1; +} + +message DeleteKioskRequest { + int32 id = 1; +} + +message ListSignsResponse { + repeated Sign signs = 1; +} + +message GetSignRequest { + int32 id = 1; +} + +message DeleteSignRequest { + int32 id = 1; +} + +message SetSignIdForKioskIdsRequest { + repeated int32 kioskIds = 1; + int32 signId = 2; +} + +message GetSignIdForKioskIdRequest { + int32 kioskId = 1; +} + +message GetSignIdsForKioskIdRequest { + int32 kioskId = 1; +} + +service Display { + // Create a new kiosk and enroll it for sign display. + rpc CreateKiosk(Kiosk) returns (Kiosk); + // List active kiosks. + rpc ListKiosks(google.protobuf.Empty) returns (ListKiosksResponse); + // Get a kiosk. + rpc GetKiosk(GetKioskRequest) returns (Kiosk); + // Delete a kiosk. + rpc DeleteKiosk(DeleteKioskRequest) returns (google.protobuf.Empty); + // Create a new sign. + rpc CreateSign(Sign) returns (Sign); + // List active signs. + rpc ListSigns(google.protobuf.Empty) returns (ListSignsResponse); + // Get a sign. + rpc GetSign(GetSignRequest) returns (Sign); + // Delete a sign. + rpc DeleteSign(DeleteSignRequest) returns (google.protobuf.Empty); + // Set a sign for display on one or more kiosks. + rpc SetSignIdForKioskIds(SetSignIdForKioskIdsRequest) returns (google.protobuf.Empty); + // Get the sign that should be displayed on a kiosk. + rpc GetSignIdForKioskId(GetSignIdForKioskIdRequest) returns (GetSignIdResponse); + // Stream the signs that should be displayed on a kiosk. + rpc GetSignIdsForKioskId(GetSignIdsForKioskIdRequest) returns (stream GetSignIdResponse); +} diff --git a/packages/samples/test/output/getting-started/http-service/@typespec/openapi3/openapi.yaml b/packages/samples/test/output/getting-started/http-service/@typespec/openapi3/openapi.yaml new file mode 100644 index 00000000000..bcc0f66dfa0 --- /dev/null +++ b/packages/samples/test/output/getting-started/http-service/@typespec/openapi3/openapi.yaml @@ -0,0 +1,200 @@ +openapi: 3.0.0 +info: + title: Widget Service + version: 0.0.0 +tags: + - name: Widgets +paths: + /widgets: + get: + operationId: Widgets_list + description: List widgets. + parameters: [] + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Widget' + default: + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - Widgets + post: + operationId: Widgets_create + description: Create a widget. + parameters: [] + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/Widget' + default: + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - Widgets + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Widget' + /widgets/{id}: + get: + operationId: Widgets_read + description: Read a widget. + parameters: + - name: id + in: path + required: true + schema: + type: string + readOnly: true + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/Widget' + default: + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - Widgets + patch: + operationId: Widgets_update + description: Update a widget. + parameters: + - name: id + in: path + required: true + schema: + type: string + readOnly: true + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/Widget' + default: + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - Widgets + requestBody: + required: true + content: + application/merge-patch+json: + schema: + $ref: '#/components/schemas/WidgetMergePatchUpdate' + delete: + operationId: Widgets_delete + description: Delete a widget. + parameters: + - name: id + in: path + required: true + schema: + type: string + readOnly: true + responses: + '204': + description: 'There is no content to send for this request, but the headers may be useful. ' + default: + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - Widgets + /widgets/{id}/analyze: + post: + operationId: Widgets_analyze + description: Analyze a widget. + parameters: + - name: id + in: path + required: true + schema: + type: string + readOnly: true + responses: + '200': + description: The request has succeeded. + content: + text/plain: + schema: + type: string + default: + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - Widgets +components: + schemas: + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string + Widget: + type: object + required: + - id + - weight + - color + properties: + id: + type: string + readOnly: true + weight: + type: integer + format: int32 + color: + type: string + enum: + - red + - blue + WidgetMergePatchUpdate: + type: object + properties: + weight: + type: integer + format: int32 + color: + type: string + enum: + - red + - blue diff --git a/packages/samples/test/output/getting-started/rest-framework/@typespec/openapi3/openapi.yaml b/packages/samples/test/output/getting-started/rest-framework/@typespec/openapi3/openapi.yaml new file mode 100644 index 00000000000..db2259c7177 --- /dev/null +++ b/packages/samples/test/output/getting-started/rest-framework/@typespec/openapi3/openapi.yaml @@ -0,0 +1,200 @@ +openapi: 3.0.0 +info: + title: Widget Service + version: 0.0.0 +tags: [] +paths: + /: + post: + operationId: WidgetService_create + description: Creates a new instance of the resource. + parameters: [] + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/Widget' + '201': + description: Resource create operation completed successfully. + content: + application/json: + schema: + $ref: '#/components/schemas/Widget' + default: + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreate' + get: + operationId: WidgetService_list + description: Lists all instances of the resource. + parameters: [] + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCollectionWithNextLink' + default: + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /customGet: + get: + operationId: WidgetService_customGet + parameters: [] + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/Widget' + /{id}: + get: + operationId: WidgetService_get + description: Gets an instance of the resource. + parameters: + - $ref: '#/components/parameters/WidgetKey' + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/Widget' + default: + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + patch: + operationId: WidgetService_update + description: Updates an existing instance of the resource. + parameters: + - $ref: '#/components/parameters/WidgetKey' + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/Widget' + default: + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetUpdate' + delete: + operationId: WidgetService_delete + description: Deletes an existing instance of the resource. + parameters: + - $ref: '#/components/parameters/WidgetKey' + responses: + '200': + description: Resource deleted successfully. + default: + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' +components: + parameters: + WidgetKey: + name: id + in: path + required: true + schema: + type: string + schemas: + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string + Widget: + type: object + required: + - id + - weight + - color + properties: + id: + type: string + weight: + type: integer + format: int32 + color: + type: string + enum: + - red + - blue + WidgetCollectionWithNextLink: + type: object + required: + - value + properties: + value: + type: array + items: + $ref: '#/components/schemas/Widget' + description: The items on this page + nextLink: + type: string + format: uri + description: The link to the next page of items + description: Paged response of Widget items + WidgetCreate: + type: object + required: + - weight + - color + properties: + weight: + type: integer + format: int32 + color: + type: string + enum: + - red + - blue + description: Resource create operation model. + WidgetUpdate: + type: object + properties: + weight: + type: integer + format: int32 + color: + type: string + enum: + - red + - blue + description: Resource create or update operation model. diff --git a/packages/samples/test/output/language/api-versioning/@typespec/openapi3/openapi.v1.yaml b/packages/samples/test/output/language/api-versioning/@typespec/openapi3/openapi.v1.yaml new file mode 100644 index 00000000000..53be9c284fa --- /dev/null +++ b/packages/samples/test/output/language/api-versioning/@typespec/openapi3/openapi.v1.yaml @@ -0,0 +1,194 @@ +openapi: 3.0.0 +info: + title: Widget Service + version: v1 +tags: [] +paths: + /: + post: + operationId: WidgetService_create + description: Creates a new instance of the resource. + parameters: [] + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/Widget' + '201': + description: Resource create operation completed successfully. + content: + application/json: + schema: + $ref: '#/components/schemas/Widget' + default: + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreate' + get: + operationId: WidgetService_list + description: Lists all instances of the resource. + parameters: [] + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCollectionWithNextLink' + default: + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /{id}: + get: + operationId: WidgetService_get + description: Gets an instance of the resource. + parameters: + - $ref: '#/components/parameters/WidgetKey' + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/Widget' + default: + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + patch: + operationId: WidgetService_update + description: Updates an existing instance of the resource. + parameters: + - $ref: '#/components/parameters/WidgetKey' + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/Widget' + default: + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetUpdate' + delete: + operationId: WidgetService_delete + description: Deletes an existing instance of the resource. + parameters: + - $ref: '#/components/parameters/WidgetKey' + responses: + '200': + description: Resource deleted successfully. + default: + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' +components: + parameters: + WidgetKey: + name: id + in: path + required: true + schema: + type: string + schemas: + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string + Versions: + type: string + enum: + - v1 + - v2 + Widget: + type: object + required: + - id + - weight + - color + properties: + id: + type: string + weight: + type: integer + format: int32 + color: + type: string + enum: + - red + - blue + WidgetCollectionWithNextLink: + type: object + required: + - value + properties: + value: + type: array + items: + $ref: '#/components/schemas/Widget' + description: The items on this page + nextLink: + type: string + format: uri + description: The link to the next page of items + description: Paged response of Widget items + WidgetCreate: + type: object + required: + - weight + - color + properties: + weight: + type: integer + format: int32 + color: + type: string + enum: + - red + - blue + description: Resource create operation model. + WidgetUpdate: + type: object + properties: + weight: + type: integer + format: int32 + color: + type: string + enum: + - red + - blue + description: Resource create or update operation model. diff --git a/packages/samples/test/output/language/api-versioning/@typespec/openapi3/openapi.v2.yaml b/packages/samples/test/output/language/api-versioning/@typespec/openapi3/openapi.v2.yaml new file mode 100644 index 00000000000..4ec506447ac --- /dev/null +++ b/packages/samples/test/output/language/api-versioning/@typespec/openapi3/openapi.v2.yaml @@ -0,0 +1,213 @@ +openapi: 3.0.0 +info: + title: Widget Service + version: v2 +tags: [] +paths: + /: + post: + operationId: WidgetService_create + description: Creates a new instance of the resource. + parameters: [] + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/Widget' + '201': + description: Resource create operation completed successfully. + content: + application/json: + schema: + $ref: '#/components/schemas/Widget' + default: + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreate' + get: + operationId: WidgetService_list + description: Lists all instances of the resource. + parameters: [] + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCollectionWithNextLink' + default: + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /customGet: + get: + operationId: WidgetService_customGet + parameters: [] + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/Widget' + /{id}: + get: + operationId: WidgetService_get + description: Gets an instance of the resource. + parameters: + - $ref: '#/components/parameters/WidgetKey' + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/Widget' + default: + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + patch: + operationId: WidgetService_update + description: Updates an existing instance of the resource. + parameters: + - $ref: '#/components/parameters/WidgetKey' + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/Widget' + default: + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetUpdate' + delete: + operationId: WidgetService_delete + description: Deletes an existing instance of the resource. + parameters: + - $ref: '#/components/parameters/WidgetKey' + responses: + '200': + description: Resource deleted successfully. + default: + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' +components: + parameters: + WidgetKey: + name: id + in: path + required: true + schema: + type: string + schemas: + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string + Versions: + type: string + enum: + - v1 + - v2 + Widget: + type: object + required: + - id + - weight + - color + - name + properties: + id: + type: string + weight: + type: integer + format: int32 + color: + type: string + enum: + - red + - blue + name: + type: string + WidgetCollectionWithNextLink: + type: object + required: + - value + properties: + value: + type: array + items: + $ref: '#/components/schemas/Widget' + description: The items on this page + nextLink: + type: string + format: uri + description: The link to the next page of items + description: Paged response of Widget items + WidgetCreate: + type: object + required: + - weight + - color + - name + properties: + weight: + type: integer + format: int32 + color: + type: string + enum: + - red + - blue + name: + type: string + description: Resource create operation model. + WidgetUpdate: + type: object + properties: + weight: + type: integer + format: int32 + color: + type: string + enum: + - red + - blue + name: + type: string + description: Resource create or update operation model. diff --git a/packages/samples/test/output/language/discriminated-unions/@typespec/openapi3/openapi.yaml b/packages/samples/test/output/language/discriminated-unions/@typespec/openapi3/openapi.yaml new file mode 100644 index 00000000000..bb9ab0a8cd5 --- /dev/null +++ b/packages/samples/test/output/language/discriminated-unions/@typespec/openapi3/openapi.yaml @@ -0,0 +1,119 @@ +openapi: 3.0.0 +info: + title: Widget Service + version: 0.0.0 +tags: [] +paths: + /{id}: + get: + operationId: read + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/Widget' + default: + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' +components: + schemas: + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string + HeavyWidget: + type: object + required: + - kind + properties: + kind: + type: string + enum: + - Heavy + allOf: + - $ref: '#/components/schemas/WidgetBase' + LightWidget: + type: object + required: + - kind + properties: + kind: + type: string + enum: + - Light + allOf: + - $ref: '#/components/schemas/WidgetBase' + Widget: + type: object + oneOf: + - $ref: '#/components/schemas/WidgetHeavy' + - $ref: '#/components/schemas/WidgetLight' + discriminator: + propertyName: kind + mapping: + heavy: '#/components/schemas/WidgetHeavy' + light: '#/components/schemas/WidgetLight' + WidgetBase: + type: object + required: + - id + - weight + - color + properties: + id: + type: string + weight: + type: integer + format: int32 + color: + type: string + enum: + - red + - blue + WidgetHeavy: + type: object + required: + - kind + - value + properties: + kind: + type: string + enum: + - heavy + value: + $ref: '#/components/schemas/HeavyWidget' + WidgetKind: + type: string + enum: + - Heavy + - Light + WidgetLight: + type: object + required: + - kind + - value + properties: + kind: + type: string + enum: + - light + value: + $ref: '#/components/schemas/LightWidget' diff --git a/packages/samples/test/output/multiple-types-union/@typespec/openapi3/openapi.yaml b/packages/samples/test/output/multiple-types-union/@typespec/openapi3/openapi.yaml deleted file mode 100644 index e1accc666c2..00000000000 --- a/packages/samples/test/output/multiple-types-union/@typespec/openapi3/openapi.yaml +++ /dev/null @@ -1,68 +0,0 @@ -openapi: 3.0.0 -info: - title: Pet Store Service - version: 0.0.0 -tags: [] -paths: - /: - get: - operationId: MyService_getPet - parameters: - - $ref: '#/components/parameters/ApiVersionParam' - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Pet' -components: - parameters: - ApiVersionParam: - name: api-version - in: header - required: true - schema: - type: string - schemas: - Cat: - type: object - required: - - type - - catnipDose - properties: - type: - type: string - enum: - - cat - catnipDose: - type: integer - format: int32 - allOf: - - $ref: '#/components/schemas/PetBase' - Dog: - type: object - required: - - type - - nextWalkTime - properties: - type: - type: string - enum: - - dog - nextWalkTime: - type: string - format: date-time - allOf: - - $ref: '#/components/schemas/PetBase' - Pet: - anyOf: - - $ref: '#/components/schemas/Cat' - - $ref: '#/components/schemas/Dog' - PetBase: - type: object - required: - - name - properties: - name: - type: string diff --git a/packages/samples/test/output/nested/@typespec/openapi3/openapi.yaml b/packages/samples/test/output/nested/@typespec/openapi3/openapi.yaml deleted file mode 100644 index 58bd9245d5e..00000000000 --- a/packages/samples/test/output/nested/@typespec/openapi3/openapi.yaml +++ /dev/null @@ -1,90 +0,0 @@ -openapi: 3.0.0 -info: - title: Nested sample - version: 0.0.0 -tags: [] -paths: - /: - post: - operationId: SubC_anotherOp - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - text/plain: - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - thing: - $ref: '#/components/schemas/SubA.Thing' - thing2: - $ref: '#/components/schemas/SubA.Thing' - required: - - thing - - thing2 - /sub/a/subsub: - post: - operationId: SubSubA_doSomething - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - text/plain: - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/SubA.SubSubA.Thing' - /sub/b: - post: - operationId: SubB_doSomething - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - text/plain: - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/SubB.Thing' -components: - schemas: - SubA.SubSubA.Thing: - type: object - required: - - name - properties: - name: - type: string - SubA.Thing: - type: object - required: - - id - properties: - id: - type: integer - format: int64 - SubB.Thing: - type: object - required: - - id - properties: - id: - type: integer - format: int64 diff --git a/packages/samples/test/output/nullable/@typespec/openapi3/openapi.yaml b/packages/samples/test/output/nullable/@typespec/openapi3/openapi.yaml deleted file mode 100644 index 1c82d2ee049..00000000000 --- a/packages/samples/test/output/nullable/@typespec/openapi3/openapi.yaml +++ /dev/null @@ -1,88 +0,0 @@ -openapi: 3.0.0 -info: - title: Nullable sample - version: 0.0.0 -tags: [] -paths: - /test: - get: - operationId: NullableMethods_read - parameters: - - name: someParam - in: query - required: true - schema: - type: string - nullable: true - explode: false - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/HasNullables' - requestBody: - required: true - content: - application/json: - schema: - type: object - allOf: - - $ref: '#/components/schemas/AnotherModel' - nullable: true -components: - schemas: - AnotherModel: - type: object - required: - - num - properties: - num: - type: integer - format: int32 - HasNullables: - type: object - required: - - str - - when - - strOrNull - - modelOrNull - - literalsOrNull - - manyNullsOneString - - manyNullsSomeValues - - arr - properties: - str: - type: string - when: - type: string - format: time - strOrNull: - type: string - nullable: true - modelOrNull: - type: object - allOf: - - $ref: '#/components/schemas/AnotherModel' - nullable: true - literalsOrNull: - type: string - enum: - - one - - two - nullable: true - manyNullsOneString: - type: string - nullable: true - manyNullsSomeValues: - type: number - enum: - - 42 - - 100 - nullable: true - arr: - type: array - items: - type: string - nullable: true diff --git a/packages/samples/test/output/optional/@typespec/openapi3/openapi.yaml b/packages/samples/test/output/optional/@typespec/openapi3/openapi.yaml deleted file mode 100644 index 8a424c0fcd1..00000000000 --- a/packages/samples/test/output/optional/@typespec/openapi3/openapi.yaml +++ /dev/null @@ -1,70 +0,0 @@ -openapi: 3.0.0 -info: - title: Optional sample - version: 0.0.0 -tags: [] -paths: - /test: - get: - operationId: OptionalMethods_read - parameters: - - name: queryString - in: query - required: false - schema: - type: string - default: defaultQueryString - explode: false - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/HasOptional' - requestBody: - required: false - content: - text/plain: - schema: - type: integer - format: int64 -components: - schemas: - HasOptional: - type: object - properties: - optionalNoDefault: - type: string - optionalString: - type: string - default: default string - optionalNumber: - type: integer - format: int32 - default: 123 - optionalBoolean: - type: boolean - default: true - optionalArray: - type: array - items: - type: string - default: - - foo - - bar - optionalUnion: - type: string - enum: - - foo - - bar - default: foo - optionalEnum: - allOf: - - $ref: '#/components/schemas/MyEnum' - default: a - MyEnum: - type: string - enum: - - a - - b diff --git a/packages/samples/test/output/simple/@typespec/openapi3/openapi.yaml b/packages/samples/test/output/simple/@typespec/openapi3/openapi.yaml deleted file mode 100644 index 3a30689e243..00000000000 --- a/packages/samples/test/output/simple/@typespec/openapi3/openapi.yaml +++ /dev/null @@ -1,39 +0,0 @@ -openapi: 3.0.0 -info: - title: (title) - version: 0.0.0 -tags: [] -paths: - /alpha/{id}: - get: - operationId: doAlpha - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: The request has succeeded. - content: - text/plain: - schema: - type: string - /beta/{id}: - get: - operationId: doBeta - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: The request has succeeded. - content: - text/plain: - schema: - type: string -components: {} diff --git a/packages/samples/test/output/testserver/body-boolean/@typespec/openapi3/openapi.yaml b/packages/samples/test/output/testserver/body-boolean/@typespec/openapi3/openapi.yaml deleted file mode 100644 index 87331fa8aef..00000000000 --- a/packages/samples/test/output/testserver/body-boolean/@typespec/openapi3/openapi.yaml +++ /dev/null @@ -1,142 +0,0 @@ -openapi: 3.0.0 -info: - title: sample - version: 0.0.0 -tags: [] -paths: - /bool/false: - get: - operationId: bool_getFalse - description: Get true Boolean false - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - text/plain: - schema: - type: boolean - enum: - - false - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - put: - operationId: bool_putFalse - description: Set Boolean value false - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - text/plain: - schema: - type: boolean - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - requestBody: - required: true - content: - text/plain: - schema: - type: boolean - enum: - - false - /bool/invalid: - get: - operationId: bool_getInvalid - description: Get invalid Boolean value - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - text/plain: - schema: - type: boolean - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - /bool/null: - get: - operationId: bool_getNull - description: Get null Boolean value - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - text/plain: - schema: - type: boolean - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - /bool/true: - get: - operationId: bool_getTrue - description: Get true Boolean value - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - text/plain: - schema: - type: boolean - enum: - - true - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - put: - operationId: bool_putTrue - description: Set Boolean value true - parameters: [] - responses: - '200': - description: The request has succeeded. - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - requestBody: - required: true - content: - text/plain: - schema: - type: boolean - enum: - - true -components: - schemas: - Error: - type: object - required: - - code - - message - properties: - code: - type: integer - format: int32 - message: - type: string - description: Error diff --git a/packages/samples/test/output/testserver/body-complex/@typespec/openapi3/openapi.yaml b/packages/samples/test/output/testserver/body-complex/@typespec/openapi3/openapi.yaml deleted file mode 100644 index 86af522a240..00000000000 --- a/packages/samples/test/output/testserver/body-complex/@typespec/openapi3/openapi.yaml +++ /dev/null @@ -1,676 +0,0 @@ -openapi: 3.0.0 -info: - title: sample - version: 0.0.0 -tags: [] -paths: - /complex/array/valid: - get: - operationId: Complex_getArray - description: Get complex types with array properties - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/ArrayWrapper' - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - put: - operationId: Complex_putArray - description: Put complex types with array properties - parameters: [] - responses: - '200': - description: The request has succeeded. - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ArrayWrapper' - /complex/basic/valid: - get: - operationId: Complex_getValid - description: "Get complex type {id: 2, name: 'abc', color: 'YELLOW'}" - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Basic' - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - put: - operationId: Complex_putValid - description: "Please put {id: 2, name: 'abc', color: 'Magenta'}" - parameters: [] - responses: - '200': - description: The request has succeeded. - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Basic' - /complex/dictionary/typed/valid: - get: - operationId: Dictionary_getDictionary - description: Get complex types with dictionary property - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/DictionaryWrapper' - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - put: - operationId: Dictionary_putArray - description: Put complex types with dictionary property - parameters: [] - responses: - '200': - description: The request has succeeded. - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/DictionaryWrapper' - /complex/primitive/bool: - get: - operationId: Complex_getBool - description: Get complex types with bool properties - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/BooleanWrapper' - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - put: - operationId: Complex_putBool - description: Put complex types with bool properties - parameters: [] - responses: - '200': - description: The request has succeeded. - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/BooleanWrapper' - /complex/primitive/bytes: - get: - operationId: Complex_getBytes - description: Get complex types with bytes properties - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/BytesWrapper' - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - put: - operationId: Complex_putBytes - description: Put complex types with bytes properties - parameters: [] - responses: - '200': - description: The request has succeeded. - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/BytesWrapper' - /complex/primitive/date: - get: - operationId: Complex_getPlainDate - description: Get complex types with date properties - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/PlainDateWrapper' - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - put: - operationId: Complex_putPlainDate - description: Put complex types with date properties - parameters: [] - responses: - '200': - description: The request has succeeded. - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/PlainDateWrapper' - /complex/primitive/datetime: - get: - operationId: Complex_getZonedDateTime - description: Get complex types with DateTime properties - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/DateTimeWrapper' - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - put: - operationId: Complex_putZonedDateTime - description: Put complex types with DateTime properties - parameters: [] - responses: - '200': - description: The request has succeeded. - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/DateTimeWrapper' - /complex/primitive/double: - get: - operationId: Complex_getDouble - description: Get complex types with double properties - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/DoubleWrapper' - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - put: - operationId: Complex_putDouble - description: Put complex types with double properties - parameters: [] - responses: - '200': - description: The request has succeeded. - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/DoubleWrapper' - /complex/primitive/duration: - get: - operationId: Complex_getZonedDuration - description: Get complex types with Duration properties - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/DurationWrapper' - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - put: - operationId: Complex_putZonedDuration - description: Put complex types with Duration properties - parameters: [] - responses: - '200': - description: The request has succeeded. - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/DurationWrapper' - /complex/primitive/float: - get: - operationId: Complex_getFloat - description: Get complex types with float properties - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/FloatWrapper' - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - put: - operationId: Complex_putFloat - description: Put complex types with float properties - parameters: [] - responses: - '200': - description: The request has succeeded. - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/FloatWrapper' - /complex/primitive/integer: - get: - operationId: Complex_getInt - description: Get complex types with integer properties - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/IntWrapper' - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - put: - operationId: Complex_putInt - description: Put complex types with integer properties - parameters: [] - responses: - '200': - description: The request has succeeded. - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/IntWrapper' - /complex/primitive/long: - get: - operationId: Complex_getLong - description: Get complex types with long properties - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/LongWrapper' - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - put: - operationId: Complex_putLong - description: Put complex types with long properties - parameters: [] - responses: - '200': - description: The request has succeeded. - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/LongWrapper' - /complex/primitive/string: - get: - operationId: Complex_getString - description: Get complex types with string properties - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/StringWrapper' - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - put: - operationId: Complex_putString - description: Put complex types with string properties - parameters: [] - responses: - '200': - description: The request has succeeded. - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/StringWrapper' -components: - schemas: - ArrayWrapper: - type: object - required: - - field - properties: - field: - type: array - items: - type: string - Basic: - type: object - required: - - id - - name - - color - properties: - id: - type: integer - format: int32 - nullable: true - description: Basic Id - name: - type: string - description: Name property with a very long description that does not fit on a single line and a line break. - color: - $ref: '#/components/schemas/Colors' - BooleanWrapper: - type: object - required: - - field_true - - field_false - properties: - field_true: - type: boolean - field_false: - type: boolean - BytesWrapper: - type: object - required: - - field - properties: - field: - type: string - format: byte - Colors: - type: string - enum: - - cyan - - Magenta - - YELLOW - - blacK - DateTimeWrapper: - type: object - required: - - field - - now - properties: - field: - type: string - format: date-time - now: - type: string - format: date-time - DictionaryWrapper: - type: object - required: - - field - properties: - field: - type: object - additionalProperties: - type: string - DoubleWrapper: - type: object - required: - - field1 - - field_56_zeros_after_the_dot_and_negative_zero_before_dot_and_this_is_a_long_field_name_on_purpose - properties: - field1: - type: number - format: double - field_56_zeros_after_the_dot_and_negative_zero_before_dot_and_this_is_a_long_field_name_on_purpose: - type: number - format: double - DurationWrapper: - type: object - required: - - field - properties: - field: - type: string - format: duration - Error: - type: object - required: - - message - properties: - message: - type: string - description: Error - FloatWrapper: - type: object - required: - - field1 - - field2 - properties: - field1: - type: number - format: float - field2: - type: number - format: float - IntWrapper: - type: object - required: - - field1 - - field2 - properties: - field1: - type: integer - format: int32 - field2: - type: integer - format: int32 - LongWrapper: - type: object - required: - - field1 - - field2 - properties: - field1: - type: integer - format: int64 - field2: - type: integer - format: int64 - Pet: - type: object - required: - - id - - name - properties: - id: - type: integer - format: int32 - name: - type: string - PlainDateWrapper: - type: object - required: - - field - - leap - properties: - field: - type: string - format: date - leap: - type: string - format: date - ReadonlyObj: - type: object - required: - - id - - size - properties: - id: - type: integer - format: int32 - readOnly: true - size: - type: integer - format: int32 - readOnly: true - StringWrapper: - type: object - required: - - field - - empty - - 'null' - properties: - field: - type: string - empty: - type: string - 'null': - type: string - nullable: true diff --git a/packages/samples/test/output/testserver/body-string/@typespec/openapi3/openapi.yaml b/packages/samples/test/output/testserver/body-string/@typespec/openapi3/openapi.yaml deleted file mode 100644 index f1b759fc6c7..00000000000 --- a/packages/samples/test/output/testserver/body-string/@typespec/openapi3/openapi.yaml +++ /dev/null @@ -1,330 +0,0 @@ -openapi: 3.0.0 -info: - title: sample - version: 0.0.0 -tags: - - name: String Operations -paths: - /string/base64Encoding: - get: - operationId: String_getBase64Encoding - description: Get value that is base64 encoded - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - application/octet-stream: - schema: - type: string - format: binary - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - tags: - - String Operations - put: - operationId: String_putBase64Encoding - description: Put value that is base64 encoded - parameters: [] - responses: - '200': - description: The request has succeeded. - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - tags: - - String Operations - requestBody: - required: true - content: - application/octet-stream: - schema: - type: string - format: binary - /string/empty: - get: - operationId: String_getEmpty - description: Get empty string value - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - text/plain: - schema: - type: string - enum: - - '' - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - tags: - - String Operations - put: - operationId: String_putEmpty - description: Put empty string value - parameters: [] - responses: - '200': - description: The request has succeeded. - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - tags: - - String Operations - requestBody: - required: true - content: - text/plain: - schema: - type: string - enum: - - '' - /string/enum/constant: - get: - operationId: Enums_getConstant - description: Gets value 'green-color' from a constant - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Colors' - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - tags: - - String Operations - put: - operationId: Enums_putConstant - description: Sends value 'green-color' from a constant - parameters: [] - responses: - '200': - description: The request has succeeded. - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - tags: - - String Operations - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Colors' - /string/enum/empty: - get: - operationId: Enums_getNotExpandable - description: Get non expandable string enum value - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Colors' - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - tags: - - String Operations - put: - operationId: Enums_putNotExpandable - description: Put non expandable string enum value - parameters: [] - responses: - '200': - description: The request has succeeded. - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - tags: - - String Operations - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Colors' - /string/mbcs: - get: - operationId: String_getMbcs - description: Get mbcs string value '啊齄丂狛狜隣郎隣兀﨩ˊ〞〡¦℡㈱‐ー﹡﹢﹫、〓ⅰⅹ⒈€㈠㈩ⅠⅫ! ̄ぁんァヶΑ︴АЯаяāɡㄅㄩ─╋︵﹄︻︱︳︴ⅰⅹɑɡ〇〾⿻⺁䜣€' - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - text/plain: - schema: - type: string - enum: - - 啊齄丂狛狜隣郎隣兀﨩ˊ〞〡¦℡㈱‐ー﹡﹢﹫、〓ⅰⅹ⒈€㈠㈩ⅠⅫ! ̄ぁんァヶΑ︴АЯаяāɡㄅㄩ─╋︵﹄︻︱︳︴ⅰⅹɑɡ〇〾⿻⺁䜣€ - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - tags: - - String Operations - put: - operationId: String_putMbCs - description: Put mbcs string value '啊齄丂狛狜隣郎隣兀﨩ˊ〞〡¦℡㈱‐ー﹡﹢﹫、〓ⅰⅹ⒈€㈠㈩ⅠⅫ! ̄ぁんァヶΑ︴АЯаяāɡㄅㄩ─╋︵﹄︻︱︳︴ⅰⅹɑɡ〇〾⿻⺁䜣€' - parameters: [] - responses: - '200': - description: The request has succeeded. - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - tags: - - String Operations - requestBody: - required: true - content: - text/plain: - schema: - type: string - enum: - - 啊齄丂狛狜隣郎隣兀﨩ˊ〞〡¦℡㈱‐ー﹡﹢﹫、〓ⅰⅹ⒈€㈠㈩ⅠⅫ! ̄ぁんァヶΑ︴АЯаяāɡㄅㄩ─╋︵﹄︻︱︳︴ⅰⅹɑɡ〇〾⿻⺁䜣€ - /string/null: - get: - operationId: String_getNull - description: Get null string value - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - type: string - nullable: true - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - tags: - - String Operations - put: - operationId: String_putNull - description: Put null string value - parameters: [] - responses: - '200': - description: The request has succeeded. - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - tags: - - String Operations - requestBody: - required: true - content: - application/json: - schema: - type: string - nullable: true - /string/whitespace: - get: - operationId: String_getWhitespace - description: Get string value with leading and trailing whitespace 'Now is the time for all good men to come to the aid of their country' - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - text/plain: - schema: - type: string - enum: - - ' Now is the time for all good men to come to the aid of their country ' - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - tags: - - String Operations - put: - operationId: String_putWhitespace - description: Get string value with leading and trailing whitespace 'Now is the time for all good men to come to the aid of their country' - parameters: [] - responses: - '200': - description: The request has succeeded. - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - tags: - - String Operations - requestBody: - required: true - content: - text/plain: - schema: - type: string - enum: - - ' Now is the time for all good men to come to the aid of their country ' -components: - schemas: - Colors: - type: string - enum: - - red color - - green-color - - blue-color - Error: - type: object - required: - - status - - message - properties: - status: - type: integer - format: int32 - message: - type: string - description: Error diff --git a/packages/samples/test/output/testserver/body-time/@typespec/openapi3/openapi.yaml b/packages/samples/test/output/testserver/body-time/@typespec/openapi3/openapi.yaml deleted file mode 100644 index fe72294ef95..00000000000 --- a/packages/samples/test/output/testserver/body-time/@typespec/openapi3/openapi.yaml +++ /dev/null @@ -1,59 +0,0 @@ -openapi: 3.0.0 -info: - title: sample - version: 0.0.0 -tags: [] -paths: - /time: - get: - operationId: Time_get - description: Get time value "11:34:56" - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - text/plain: - schema: - type: string - format: time - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - put: - operationId: Time_put - description: Put time value "08:07:56" - parameters: [] - responses: - '200': - description: The request has succeeded. - default: - description: An unexpected error response. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - requestBody: - required: true - content: - text/plain: - schema: - type: string - format: time -components: - schemas: - Error: - type: object - required: - - status - - message - properties: - status: - type: integer - format: int32 - message: - type: string - description: Error diff --git a/packages/samples/test/output/testserver/media-types/@typespec/openapi3/openapi.yaml b/packages/samples/test/output/testserver/media-types/@typespec/openapi3/openapi.yaml deleted file mode 100644 index 8e7c881d4e1..00000000000 --- a/packages/samples/test/output/testserver/media-types/@typespec/openapi3/openapi.yaml +++ /dev/null @@ -1,78 +0,0 @@ -openapi: 3.0.0 -info: - title: sample - version: 0.0.0 -tags: [] -paths: - /mediatypes/analyze: - post: - operationId: AnalyzeBody - description: Analyze body, that could be different media types. - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - text/plain: - schema: - type: string - requestBody: - required: true - content: - application/pdf: - schema: - $ref: '#/components/schemas/SourcePath' - application/json: - schema: - $ref: '#/components/schemas/SourcePath' - image/jpeg: - schema: - $ref: '#/components/schemas/SourcePath' - image/png: - schema: - $ref: '#/components/schemas/SourcePath' - image/tiff: - schema: - $ref: '#/components/schemas/SourcePath' - description: Input parameter. - /mediatypes/contentTypeWithEncoding: - post: - operationId: contentTypeWithEncoding - description: Pass in contentType 'text/plain; encoding=UTF-8' to pass test. Value for input does not matter - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - text/plain: - schema: - type: string - requestBody: - required: true - content: - text/plain: - schema: - $ref: '#/components/schemas/SourcePath' - description: Input parameter. -components: - schemas: - Input: - type: object - required: - - input - properties: - input: - allOf: - - $ref: '#/components/schemas/SourcePath' - description: Input parameter. - SourcePath: - type: object - required: - - source - properties: - source: - type: string - minLength: 0 - maxLength: 2048 - description: File source path. - description: Uri or local path to source data. diff --git a/packages/samples/test/output/testserver/multiple-inheritance/@typespec/openapi3/openapi.yaml b/packages/samples/test/output/testserver/multiple-inheritance/@typespec/openapi3/openapi.yaml deleted file mode 100644 index 0e262ffaec4..00000000000 --- a/packages/samples/test/output/testserver/multiple-inheritance/@typespec/openapi3/openapi.yaml +++ /dev/null @@ -1,259 +0,0 @@ -openapi: 3.0.0 -info: - title: sample - version: 0.0.0 -tags: [] -paths: - /multipleInheritance/cat: - get: - operationId: MultipleInheritance_getCat - description: Get a cat with name 'Whiskers' where likesMilk, meows, and hisses is true - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Cat' - default: - description: Unexpected error - content: - text/plain: - schema: - type: string - put: - operationId: MultipleInheritance_putCat - description: Put a cat with name 'Boots' where likesMilk and hisses is false, meows is true - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - text/plain: - schema: - type: string - default: - description: Unexpected error - content: - text/plain: - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Cat' - /multipleInheritance/feline: - get: - operationId: MultipleInheritance_getFeline - description: Get a feline where meows and hisses are true - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Feline' - default: - description: Unexpected error - content: - text/plain: - schema: - type: string - put: - operationId: MultipleInheritance_putFeline - description: Put a feline who hisses and doesn't meow - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - text/plain: - schema: - type: string - default: - description: Unexpected error - content: - text/plain: - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Feline' - /multipleInheritance/horse: - get: - operationId: MultipleInheritance_getHorse - description: Get a horse with name 'Fred' and isAShowHorse true - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Horse' - default: - description: Unexpected error - content: - text/plain: - schema: - type: string - put: - operationId: MultipleInheritance_putHorse - description: Put a horse with name 'General' and isAShowHorse false - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - text/plain: - schema: - type: string - default: - description: Unexpected error - content: - text/plain: - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Horse' - /multipleInheritance/kitten: - get: - operationId: MultipleInheritance_getKitten - description: Get a kitten with name 'Gatito' where likesMilk and meows is true, and hisses and eatsMiceYet is false - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Kitten' - default: - description: Unexpected error - content: - text/plain: - schema: - type: string - put: - operationId: MultipleInheritance_putKitten - description: Put a kitten with name 'Kitty' where likesMilk and hisses is false, meows and eatsMiceYet is true - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - text/plain: - schema: - type: string - default: - description: Unexpected error - content: - text/plain: - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Kitten' - /multipleInheritance/pet: - get: - operationId: MultipleInheritance_getPet - description: Get a pet with name 'Peanut' - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - application/json: - schema: - $ref: '#/components/schemas/Pet' - default: - description: Unexpected error - content: - text/plain: - schema: - type: string - put: - operationId: MultipleInheritance_putPet - description: Put a pet with name 'Butter' - parameters: [] - responses: - '200': - description: The request has succeeded. - content: - text/plain: - schema: - type: string - default: - description: Unexpected error - content: - text/plain: - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Pet' -components: - schemas: - Cat: - type: object - required: - - likesMilk - properties: - likesMilk: - type: boolean - allOf: - - $ref: '#/components/schemas/Feline' - Feline: - type: object - required: - - meows - - hisses - properties: - meows: - type: boolean - hisses: - type: boolean - allOf: - - $ref: '#/components/schemas/Pet' - Horse: - type: object - required: - - isAShowHorse - properties: - isAShowHorse: - type: boolean - allOf: - - $ref: '#/components/schemas/Pet' - Kitten: - type: object - required: - - eatsMiceYet - properties: - eatsMiceYet: - type: boolean - allOf: - - $ref: '#/components/schemas/Cat' - Pet: - type: object - required: - - name - properties: - name: - type: string diff --git a/packages/samples/test/sample-config.test.ts b/packages/samples/test/sample-config.test.ts new file mode 100644 index 00000000000..762d238863a --- /dev/null +++ b/packages/samples/test/sample-config.test.ts @@ -0,0 +1,114 @@ +import { findTestPackageRoot } from "@typespec/compiler/testing"; +import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { resolve } from "node:path"; +import { describe, expect, it } from "vitest"; +import { loadSampleCatalog } from "../src/sample-config.js"; + +describe("sample config", () => { + it("loads the canonical playground samples in configured order", async () => { + const packageRoot = await findTestPackageRoot(import.meta.url); + const catalog = await loadSampleCatalog(resolve(packageRoot, "specs")); + + expect(catalog.samples.filter((x) => x.playground).map((x) => x.config.title)).toEqual([ + "HTTP service", + "REST framework", + "API versioning", + "Discriminated unions", + "Protobuf kiosk", + "JSON Schema", + "GraphQL", + ]); + }); + + it("inherits playground exclusions from directory configs", async () => { + await withTempCatalog( + { + "legacy/sample-config.yaml": "directory: true\nplayground: false\n", + "legacy/widget/sample-config.yaml": "title: Legacy widget\ndescription: A legacy sample.\n", + "legacy/widget/main.tsp": "model Widget {}", + }, + async (root) => { + const catalog = await loadSampleCatalog(root); + expect(catalog.samples[0].playground).toBe(false); + }, + ); + }); + + it("reports missing required metadata", async () => { + await withTempCatalog( + { + "widget/sample-config.yaml": "title: Widget\n", + "widget/main.tsp": "model Widget {}", + }, + async (root) => { + await expect(loadSampleCatalog(root)).rejects.toThrow( + 'requires a non-empty "description" field', + ); + }, + ); + }); + + it("reports samples without metadata", async () => { + await withTempCatalog({ "widget/main.tsp": "model Widget {}" }, async (root) => { + await expect(loadSampleCatalog(root)).rejects.toThrow("is missing sample-config.yaml"); + }); + }); + + it("reports duplicate sample titles", async () => { + await withTempCatalog( + { + "one/sample-config.yaml": "title: Widget\ndescription: First sample.\n", + "one/main.tsp": "model One {}", + "two/sample-config.yaml": "title: Widget\ndescription: Second sample.\n", + "two/main.tsp": "model Two {}", + }, + async (root) => { + await expect(loadSampleCatalog(root)).rejects.toThrow('has duplicate title "Widget"'); + }, + ); + }); + + it("reports malformed and unknown metadata", async () => { + await withTempCatalog( + { + "widget/sample-config.yaml": + "title: Widget\ndescription: A widget sample.\nunexpected: true\n", + "widget/main.tsp": "model Widget {}", + }, + async (root) => { + await expect(loadSampleCatalog(root)).rejects.toThrow('unknown field "unexpected"'); + }, + ); + }); + + it("reports metadata without a sample entrypoint", async () => { + await withTempCatalog( + { + "widget/sample-config.yaml": "title: Widget\ndescription: A widget sample.\n", + }, + async (root) => { + await expect(loadSampleCatalog(root)).rejects.toThrow( + "must contain main.tsp or a package.json TypeSpec entrypoint", + ); + }, + ); + }); +}); + +async function withTempCatalog( + files: Record, + callback: (root: string) => Promise, +): Promise { + const root = await mkdtemp(resolve(tmpdir(), "typespec-samples-")); + try { + for (const [name, content] of Object.entries(files)) { + const path = resolve(root, name); + await mkdir(resolve(path, ".."), { recursive: true }); + await writeFile(path, content); + } + await callback(root); + } finally { + await rm(root, { recursive: true }); + } +} diff --git a/packages/samples/test/samples.test.ts b/packages/samples/test/samples.test.ts index 734c09a0b39..0d70c0c0cb1 100644 --- a/packages/samples/test/samples.test.ts +++ b/packages/samples/test/samples.test.ts @@ -6,6 +6,8 @@ import { defineSampleSnaphotTests } from "../src/sample-snapshot-testing.js"; const excludedSamples = [ // fails compilation by design to demo language server "local-typespec", + // The GraphQL emitter's Alloy output is validated in its package; the playground compiles this sample. + "emitters/graphql", ]; const pkgRoot = await findTestPackageRoot(import.meta.url); diff --git a/packages/samples/vitest.config.ts b/packages/samples/vitest.config.ts index bd5b317b122..6151b95852f 100644 --- a/packages/samples/vitest.config.ts +++ b/packages/samples/vitest.config.ts @@ -2,6 +2,7 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ test: { + include: ["test/**/*.test.ts"], environment: "node", testTimeout: 10000, isolate: false, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c60b4fdeade..220c76ac4a1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2214,6 +2214,9 @@ importers: '@typespec/events': specifier: workspace:^ version: link:../events + '@typespec/graphql': + specifier: workspace:^ + version: link:../graphql '@typespec/html-program-viewer': specifier: workspace:^ version: link:../html-program-viewer @@ -2250,6 +2253,9 @@ importers: '@typespec/versioning': specifier: workspace:^ version: link:../versioning + yaml: + specifier: 'catalog:' + version: 2.9.0 devDependencies: '@types/node': specifier: 'catalog:'