Skip to content

Commit d61facb

Browse files
authored
feat: adding default values (#26)
1 parent b707b81 commit d61facb

File tree

3 files changed

+163
-16
lines changed

3 files changed

+163
-16
lines changed

src/emitter.ts

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type {
88
Scalar,
99
Type,
1010
Union,
11+
Value,
1112
} from "@typespec/compiler";
1213
import {
1314
type EmitContext,
@@ -21,6 +22,29 @@ import * as ts from "typescript";
2122
import { StateKeys } from "./lib.js";
2223
import { RawCode, stringifyObject } from "./stringify.js";
2324

25+
/**
26+
* Extracts a primitive default value from a TypeSpec Value.
27+
* Returns undefined if the value cannot be converted to a simple default.
28+
*/
29+
function extractDefaultValue(
30+
value: Value,
31+
): string | number | boolean | undefined {
32+
switch (value.valueKind) {
33+
case "StringValue":
34+
return value.value;
35+
case "NumericValue":
36+
return Number(value.value.asNumber());
37+
case "BooleanValue":
38+
return value.value;
39+
case "EnumValue":
40+
// For enum values, use the value if specified, otherwise use the member name
41+
return value.value.value ?? value.value.name;
42+
default:
43+
// Complex values (objects, arrays) are not supported as simple defaults
44+
return undefined;
45+
}
46+
}
47+
2448
function emitIntrinsincScalar(type: Scalar) {
2549
switch (type.name) {
2650
case "boolean":
@@ -225,10 +249,21 @@ function emitType(type: Type): Attribute {
225249
}
226250

227251
function emitModelProperty(prop: ModelProperty): Attribute {
228-
return {
252+
const attr: Attribute = {
229253
...emitType(prop.type),
230254
required: !prop.optional,
231255
};
256+
257+
// Add default value if present
258+
if (prop.defaultValue) {
259+
const defaultValue = extractDefaultValue(prop.defaultValue);
260+
if (defaultValue !== undefined) {
261+
// @ts-expect-error - default is a valid ElectroDB attribute property
262+
attr.default = defaultValue;
263+
}
264+
}
265+
266+
return attr;
232267
}
233268

234269
const getLabel = (ctx: EmitContext, prop: ModelProperty) =>
@@ -274,6 +309,15 @@ function emitAttribute(ctx: EmitContext, prop: ModelProperty): Attribute {
274309
attr.label = label;
275310
}
276311

312+
// Add default value if present
313+
if (prop.defaultValue) {
314+
const defaultValue = extractDefaultValue(prop.defaultValue);
315+
if (defaultValue !== undefined) {
316+
// @ts-expect-error - default is a valid ElectroDB attribute property
317+
attr.default = defaultValue;
318+
}
319+
}
320+
277321
return attr;
278322
}
279323

test/entities.test.js

Lines changed: 89 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import assert from "node:assert/strict";
22
import { suite, test } from "node:test";
3-
import { Job, Person } from "../build/entities/index.js";
3+
import { Job, Person, Task } from "../build/entities/index.js";
44

55
suite("Job Entity", () => {
66
test("Job entity has correct model configuration", () => {
@@ -53,6 +53,86 @@ suite("Job Entity", () => {
5353
});
5454
});
5555

56+
suite("Task Entity - Default Values", () => {
57+
test("Task entity has correct model configuration", () => {
58+
assert.deepEqual(Task.model, {
59+
entity: "task",
60+
service: "org",
61+
version: "1",
62+
});
63+
});
64+
65+
suite("String default values", () => {
66+
test("optional description has string default", () => {
67+
assert.deepEqual(Task.attributes.description, {
68+
type: "string",
69+
required: false,
70+
default: "No description provided",
71+
});
72+
});
73+
});
74+
75+
suite("Enum default values", () => {
76+
test("priority has enum default value", () => {
77+
assert.deepEqual(Task.attributes.priority, {
78+
type: ["LOW", "MEDIUM", "HIGH"],
79+
required: true,
80+
default: "MEDIUM",
81+
});
82+
});
83+
});
84+
85+
suite("Number default values", () => {
86+
test("count has number default value", () => {
87+
assert.deepEqual(Task.attributes.count, {
88+
type: "number",
89+
required: true,
90+
default: 0,
91+
});
92+
});
93+
});
94+
95+
suite("Boolean default values", () => {
96+
test("active has boolean default value", () => {
97+
assert.deepEqual(Task.attributes.active, {
98+
type: "boolean",
99+
required: true,
100+
default: true,
101+
});
102+
});
103+
});
104+
105+
suite("Nested model default values", () => {
106+
test("settings is a list type", () => {
107+
assert.equal(Task.attributes.settings.type, "list");
108+
assert.equal(Task.attributes.settings.items.type, "map");
109+
});
110+
111+
test("settings item value property has string default", () => {
112+
assert.deepEqual(Task.attributes.settings.items.properties.value, {
113+
type: "string",
114+
required: true,
115+
default: "default",
116+
});
117+
});
118+
119+
test("settings item enabled property has boolean default", () => {
120+
assert.deepEqual(Task.attributes.settings.items.properties.enabled, {
121+
type: "boolean",
122+
required: true,
123+
default: true,
124+
});
125+
});
126+
127+
test("settings item key property has no default", () => {
128+
assert.deepEqual(Task.attributes.settings.items.properties.key, {
129+
type: "string",
130+
required: true,
131+
});
132+
});
133+
});
134+
});
135+
56136
suite("Person Entity", () => {
57137
test("Person entity has correct model configuration", () => {
58138
assert.deepEqual(Person.model, {
@@ -195,13 +275,10 @@ suite("Person Entity", () => {
195275
});
196276

197277
test("contact item has description property", () => {
198-
assert.deepEqual(
199-
Person.attributes.contact.items.properties.description,
200-
{
201-
type: "string",
202-
required: true,
203-
},
204-
);
278+
assert.deepEqual(Person.attributes.contact.items.properties.description, {
279+
type: "string",
280+
required: true,
281+
});
205282
});
206283
});
207284

@@ -239,13 +316,10 @@ suite("Person Entity", () => {
239316
});
240317

241318
test("additionalInfo item has name property as string", () => {
242-
assert.deepEqual(
243-
Person.attributes.additionalInfo.items.properties.name,
244-
{
245-
type: "string",
246-
required: true,
247-
},
248-
);
319+
assert.deepEqual(Person.attributes.additionalInfo.items.properties.name, {
320+
type: "string",
321+
required: true,
322+
});
249323
});
250324

251325
test("additionalInfo item value property uses CustomAttributeType for union", () => {

test/main.tsp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,35 @@ model Job {
6767
description: string;
6868
}
6969

70+
enum Priority {
71+
LOW,
72+
MEDIUM,
73+
HIGH,
74+
}
75+
76+
model SettingsItem {
77+
key: string;
78+
value: string = "default";
79+
enabled: boolean = true;
80+
}
81+
82+
@entity("task", "org")
83+
@index(
84+
"tasks",
85+
{
86+
pk: [Task.pk],
87+
}
88+
)
89+
model Task {
90+
pk: UUID;
91+
title: string;
92+
description?: string = "No description provided";
93+
priority: Priority = Priority.MEDIUM;
94+
count: int32 = 0;
95+
active: boolean = true;
96+
settings: SettingsItem[];
97+
}
98+
7099
@entity("person", "org")
71100
@index(
72101
"persons",

0 commit comments

Comments
 (0)