Skip to content

Commit c2e95e6

Browse files
committed
Improve spec resource composability and reusability with improved templates
Using some workarounds, the following features are now usable in Protobuf emitter: - direct use of templated models without intermediaries - operation templates - interface templates
1 parent dc38489 commit c2e95e6

3 files changed

Lines changed: 90 additions & 7 deletions

File tree

packages/spec/api/resource.tsp

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ import "../lib";
44

55
import "./change.tsp";
66

7-
using TypeSpec.Protobuf;
87
using TypeSpec.Reflection;
9-
using TypeSpec.Rest.Resource;
108

119
namespace API.Resource;
1210

@@ -37,30 +35,59 @@ model Change<Resource extends Model> {
3735
...OptionalProperties<OmitKey<Resource>>;
3836
}
3937

38+
interface CRUD<Resource extends Model>
39+
extends List.Operation<Resource>,
40+
Get.Operation<Resource>,
41+
Create.Operation<Resource>,
42+
Update.Operation<Resource>,
43+
Delete.Operation<Resource> {}
44+
4045
namespace List {
46+
interface Operation<Resource extends Model> {
47+
@useFriendlyName
48+
@friendlyName("{name}List", Resource)
49+
List(...Request<Resource>): Response<Resource>;
50+
}
51+
52+
@move(Resource)
53+
@friendlyName("{name}ListItem", Resource)
4154
@normalize(Resource)
4255
@withVisibility(Query.List)
4356
model Item<Resource extends Model> {
4457
...Resource;
4558
}
4659

47-
model Request<Resource> {
60+
@move(Resource)
61+
@friendlyName("{name}ListRequest", Resource)
62+
model Request<Resource extends Model> {
4863
...ParentKeyOf<Resource>;
4964
}
5065

66+
@move(Resource)
67+
@friendlyName("{name}ListResponse", Resource)
5168
@autoFields
5269
@normalize
53-
model Response<Resource, Item = Resource> {
70+
model Response<Resource extends Model, Item = List.Item<Resource>> {
5471
...Request<Resource>;
5572
items: Item[];
5673
}
5774
}
5875

5976
namespace Get {
60-
model Request<Resource> {
77+
interface Operation<Resource extends Model> {
78+
@useFriendlyName
79+
@friendlyName("{name}Get", Resource)
80+
Get(...Request<Resource>): Response<Resource>;
81+
}
82+
83+
@move(Resource)
84+
@friendlyName("{name}GetRequest", Resource)
85+
model Request<Resource extends Model> {
6186
...KeyOf<Resource>;
6287
}
6388

89+
@move(Resource)
90+
@friendlyName("{name}GetResponse", Resource)
6491
@normalize(Resource)
6592
@withVisibility(Query.Get)
6693
model Response<Resource extends Model> {
@@ -69,13 +96,23 @@ namespace Get {
6996
}
7097

7198
namespace Create {
99+
interface Operation<Resource extends Model> {
100+
@useFriendlyName
101+
@friendlyName("{name}Create", Resource)
102+
Create(...Request<Resource>): Response<Resource>;
103+
}
104+
105+
@move(Resource)
106+
@friendlyName("{name}CreateRequest", Resource)
72107
@normalize(Resource)
73108
@withVisibility(Mutation.Create)
74109
model Request<Resource extends Model> {
75110
...ParentKeyOf<Resource>;
76111
...Resource;
77112
}
78113

114+
@move(Resource)
115+
@friendlyName("{name}CreateResponse", Resource)
79116
@autoChange({
80117
$data: {
81118
kind: API.Change.SourceKind.MERGE,
@@ -98,13 +135,23 @@ namespace Create {
98135
}
99136

100137
namespace Update {
138+
interface Operation<Resource extends Model> {
139+
@useFriendlyName
140+
@friendlyName("{name}Update", Resource)
141+
Update(...Request<Resource>): Response<Resource>;
142+
}
143+
144+
@move(Resource)
145+
@friendlyName("{name}UpdateRequest", Resource)
101146
@normalize(Resource)
102147
@withVisibility(Mutation.Update)
103148
model Request<Resource extends Model> {
104149
...KeyOf<Resource>;
105150
...OptionalProperties<OmitKey<Resource>>;
106151
}
107152

153+
@move(Resource)
154+
@friendlyName("{name}UpdateResponse", Resource)
108155
@autoChange({
109156
kind: API.Change.ChangeKind.CHANGE_KIND_UPDATE,
110157
$data: {
@@ -118,11 +165,21 @@ namespace Update {
118165
}
119166

120167
namespace Delete {
168+
interface Operation<Resource extends Model> {
169+
@useFriendlyName
170+
@friendlyName("{name}Delete", Resource)
171+
Delete(...Request<Resource>): Response<Resource>;
172+
}
173+
174+
@move(Resource)
175+
@friendlyName("{name}DeleteRequest", Resource)
121176
@normalize(Resource)
122177
model Request<Resource extends Model> {
123178
...KeyOf<Resource>;
124179
}
125180

181+
@move(Resource)
182+
@friendlyName("{name}DeleteResponse", Resource)
126183
@autoChange({
127184
kind: API.Change.ChangeKind.CHANGE_KIND_DELETE,
128185
$data: {

packages/spec/lib/decorators.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import { getKeyName } from '@typespec/compiler';
1+
import { getFriendlyName, getKeyName } from '@typespec/compiler';
22
import { $field } from '@typespec/protobuf';
33
import { getParentResource, getResourceTypeKey } from '@typespec/rest';
44
import { Array, Hash, Number, Option, pipe, Record } from 'effect';
55

66
import { $lib } from './lib.js';
77

8-
/** @import { DecoratorApplication, DecoratorContext, Model, ModelProperty, Type } from '@typespec/compiler' */
8+
/** @import { DecoratorApplication, DecoratorContext, Model, ModelProperty, Operation, Type } from '@typespec/compiler' */
99

1010
/**
1111
* @param {DecoratorContext} context
@@ -198,3 +198,20 @@ export function $autoChange(context, target, value) {
198198
const autoChangesMap = context.program.stateMap($lib.stateKeys.autoChanges);
199199
pipe(autoChangesMap.get(target) ?? [], Array.append(change), (_) => autoChangesMap.set(target, _));
200200
}
201+
202+
/**
203+
* @param {DecoratorContext} context
204+
* @param {Model} target
205+
* @param {Model} destination
206+
*/
207+
export function $move(context, target, destination) {
208+
target.namespace = destination.namespace;
209+
}
210+
211+
/**
212+
* @param {DecoratorContext} context
213+
* @param {Operation} target
214+
*/
215+
export function $useFriendlyName(context, target) {
216+
target.name = getFriendlyName(context.program, target);
217+
}

packages/spec/lib/main.tsp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,12 @@ extern dec normalize(target: Model, base?: Model);
3030
extern dec normalKey(target: ModelProperty);
3131

3232
extern dec autoChange(target: TypeSpec.Reflection.Model, change: API.Change.AutoChange);
33+
34+
/** Move model to specified namespace. Useful for collocating templated models */
35+
extern dec move(target: Model, destination: Model);
36+
37+
/**
38+
* Replace name with friendly name. Useful for when friendly names are
39+
* unsupported, f.e. operations in Protobuf emitter
40+
*/
41+
extern dec useFriendlyName(target: Operation);

0 commit comments

Comments
 (0)