Skip to content

Commit 60afb6a

Browse files
committed
Merge branch 'ch-schema' of https://github.com/firebase/firebase-js-sdk into ch-schema
2 parents 11756ee + b14737f commit 60afb6a

File tree

4 files changed

+194
-52
lines changed

4 files changed

+194
-52
lines changed

packages/vertexai/src/requests/schema-builder.test.ts

Lines changed: 151 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,28 +24,28 @@ use(sinonChai);
2424
describe.only('Schema builder', () => {
2525
it('builds integer schema', () => {
2626
const schema = Schema.integer();
27-
expect(schema.toJSON()).to.eql({
27+
expect(schema.toRequest()).to.eql({
2828
type: 'integer',
2929
nullable: false
3030
});
3131
});
3232
it('builds number schema', () => {
3333
const schema = Schema.number();
34-
expect(schema.toJSON()).to.eql({
34+
expect(schema.toRequest()).to.eql({
3535
type: 'number',
3636
nullable: false
3737
});
3838
});
3939
it('builds boolean schema', () => {
4040
const schema = Schema.boolean();
41-
expect(schema.toJSON()).to.eql({
41+
expect(schema.toRequest()).to.eql({
4242
type: 'boolean',
4343
nullable: false
4444
});
4545
});
4646
it('builds string schema', () => {
4747
const schema = Schema.string({ description: 'hey' });
48-
expect(schema.toJSON()).to.eql({
48+
expect(schema.toRequest()).to.eql({
4949
type: 'string',
5050
description: 'hey',
5151
nullable: false
@@ -56,7 +56,7 @@ describe.only('Schema builder', () => {
5656
example: 'east',
5757
enum: ['east', 'west']
5858
});
59-
expect(schema.toJSON()).to.eql({
59+
expect(schema.toRequest()).to.eql({
6060
type: 'string',
6161
example: 'east',
6262
enum: ['east', 'west'],
@@ -69,7 +69,7 @@ describe.only('Schema builder', () => {
6969
'someInput': Schema.string()
7070
}
7171
});
72-
expect(schema.toJSON()).to.eql({
72+
expect(schema.toRequest()).to.eql({
7373
type: 'object',
7474
nullable: false,
7575
properties: {
@@ -81,13 +81,12 @@ describe.only('Schema builder', () => {
8181
required: ['someInput']
8282
});
8383
});
84-
it('builds layered schema', () => {
84+
it('builds layered schema - partially filled out', () => {
8585
const schema = Schema.array({
8686
items: Schema.object({
8787
properties: {
8888
country: Schema.string({
89-
description: 'some country',
90-
required: true
89+
description: 'A country name'
9190
}),
9291
population: Schema.integer(),
9392
coordinates: Schema.object({
@@ -106,18 +105,74 @@ describe.only('Schema builder', () => {
106105
}
107106
})
108107
});
108+
expect(schema.toRequest()).to.eql(layeredSchemaOutputPartial);
109+
});
110+
it('builds layered schema - fully filled out', () => {
111+
const schema = Schema.array({
112+
items: Schema.object({
113+
description: 'A country profile',
114+
nullable: false,
115+
properties: {
116+
country: Schema.string({
117+
nullable: false,
118+
description: 'Country name',
119+
format: undefined
120+
}),
121+
population: Schema.integer({
122+
nullable: false,
123+
description: 'Number of people in country',
124+
format: 'int64'
125+
}),
126+
coordinates: Schema.object({
127+
nullable: false,
128+
description: 'Latitude and longitude',
129+
properties: {
130+
latitude: Schema.number({
131+
nullable: false,
132+
description: 'Latitude of capital',
133+
format: 'float'
134+
}),
135+
longitude: Schema.number({
136+
nullable: false,
137+
description: 'Longitude of capital',
138+
format: 'double'
139+
})
140+
}
141+
}),
142+
hemisphere: Schema.object({
143+
nullable: false,
144+
description: 'Hemisphere(s) country is in',
145+
properties: {
146+
latitudinal: Schema.enumString({ enum: ['N', 'S'] }),
147+
longitudinal: Schema.enumString({ enum: ['E', 'W'] })
148+
}
149+
}),
150+
isCapital: Schema.boolean({
151+
nullable: false,
152+
description: "This doesn't make a lot of sense but it's a demo"
153+
}),
154+
elevation: Schema.integer({
155+
nullable: false,
156+
description: 'Average elevation',
157+
format: 'float'
158+
})
159+
},
160+
optionalProperties: []
161+
})
162+
});
109163

110-
expect(schema.toJSON()).to.eql(layeredSchemaOutput);
164+
expect(schema.toRequest()).to.eql(layeredSchemaOutput);
111165
});
112-
it('can override the "required" and "nullable" properties', () => {
166+
it('can override "nullable" and set optional properties', () => {
113167
const schema = Schema.object({
114168
properties: {
115169
country: Schema.string(),
116-
elevation: Schema.number({ required: false }),
170+
elevation: Schema.number(),
117171
population: Schema.integer({ nullable: true })
118-
}
172+
},
173+
optionalProperties: ['elevation']
119174
});
120-
expect(schema.toJSON()).to.eql({
175+
expect(schema.toRequest()).to.eql({
121176
'type': 'object',
122177
'nullable': false,
123178
'properties': {
@@ -139,7 +194,7 @@ describe.only('Schema builder', () => {
139194
});
140195
});
141196

142-
const layeredSchemaOutput = {
197+
const layeredSchemaOutputPartial = {
143198
'type': 'array',
144199
'nullable': false,
145200
'items': {
@@ -148,7 +203,7 @@ const layeredSchemaOutput = {
148203
'properties': {
149204
'country': {
150205
'type': 'string',
151-
'description': 'some country',
206+
'description': 'A country name',
152207
'nullable': false
153208
},
154209
'population': {
@@ -203,3 +258,83 @@ const layeredSchemaOutput = {
203258
]
204259
}
205260
};
261+
262+
const layeredSchemaOutput = {
263+
'type': 'array',
264+
'nullable': false,
265+
'items': {
266+
'type': 'object',
267+
'description': 'A country profile',
268+
'nullable': false,
269+
'required': [
270+
'country',
271+
'population',
272+
'coordinates',
273+
'hemisphere',
274+
'isCapital',
275+
'elevation'
276+
],
277+
'properties': {
278+
'country': {
279+
'type': 'string',
280+
'description': 'Country name',
281+
'nullable': false
282+
},
283+
'population': {
284+
'type': 'integer',
285+
'format': 'int64',
286+
'description': 'Number of people in country',
287+
'nullable': false
288+
},
289+
'coordinates': {
290+
'type': 'object',
291+
'description': 'Latitude and longitude',
292+
'nullable': false,
293+
'required': ['latitude', 'longitude'],
294+
'properties': {
295+
'latitude': {
296+
'type': 'number',
297+
'format': 'float',
298+
'description': 'Latitude of capital',
299+
'nullable': false
300+
},
301+
'longitude': {
302+
'type': 'number',
303+
'format': 'double',
304+
'description': 'Longitude of capital',
305+
'nullable': false
306+
}
307+
}
308+
},
309+
'hemisphere': {
310+
'type': 'object',
311+
'description': 'Hemisphere(s) country is in',
312+
'nullable': false,
313+
'required': ['latitudinal', 'longitudinal'],
314+
'properties': {
315+
'latitudinal': {
316+
'type': 'string',
317+
'nullable': false,
318+
'enum': ['N', 'S']
319+
},
320+
'longitudinal': {
321+
'type': 'string',
322+
'nullable': false,
323+
'enum': ['E', 'W']
324+
}
325+
}
326+
},
327+
'isCapital': {
328+
'type': 'boolean',
329+
'description': "This doesn't make a lot of sense but it's a demo",
330+
'nullable': false
331+
},
332+
'elevation': {
333+
'type': 'integer',
334+
'format': 'float',
335+
'description': 'Average elevation',
336+
'nullable': false
337+
}
338+
}
339+
}
340+
};

packages/vertexai/src/requests/schema-builder.ts

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import {
22
SchemaInterface,
33
SchemaType,
44
SchemaParams,
5-
_SchemaRequest
5+
_SchemaRequest,
6+
ObjectSchemaInterface
67
} from '../types/schema';
78

89
export abstract class Schema implements SchemaInterface {
@@ -22,8 +23,6 @@ export abstract class Schema implements SchemaInterface {
2223
description?: string;
2324
/** Optional. Whether the property is nullable. Defaults to false. */
2425
nullable: boolean;
25-
/** Optional. Defaults to true. */
26-
required: boolean;
2726
/** Optional. The example of the property. */
2827
example?: unknown;
2928

@@ -35,13 +34,10 @@ export abstract class Schema implements SchemaInterface {
3534
this.nullable = schemaParams.hasOwnProperty('nullable')
3635
? !!schemaParams.nullable
3736
: false;
38-
this.required = schemaParams.hasOwnProperty('required')
39-
? !!schemaParams.required
40-
: true;
4137
}
4238

4339
/** Converts class to a plain JSON object (not a string). */
44-
toJSON(): _SchemaRequest {
40+
toRequest(): _SchemaRequest {
4541
const obj: { type: SchemaType; [key: string]: unknown } = {
4642
type: this.type
4743
};
@@ -55,6 +51,10 @@ export abstract class Schema implements SchemaInterface {
5551
return obj as _SchemaRequest;
5652
}
5753

54+
toJSON(): string {
55+
return JSON.stringify(this.toRequest());
56+
}
57+
5858
static array(arrayParams: SchemaParams & { items: Schema }): ArraySchema {
5959
return new ArraySchema(arrayParams, arrayParams.items);
6060
}
@@ -64,16 +64,22 @@ export abstract class Schema implements SchemaInterface {
6464
properties: {
6565
[k: string]: Schema;
6666
};
67+
optionalProperties?: string[];
6768
}
6869
): ObjectSchema {
69-
return new ObjectSchema(objectParams, objectParams.properties);
70+
return new ObjectSchema(
71+
objectParams,
72+
objectParams.properties,
73+
objectParams.optionalProperties
74+
);
7075
}
7176

7277
static functionDeclaration(
7378
objectParams: SchemaParams & {
7479
properties: {
7580
[k: string]: Schema;
7681
};
82+
optionalProperties?: string[];
7783
}
7884
): ObjectSchema {
7985
return this.object(objectParams);
@@ -148,8 +154,8 @@ export class StringSchema extends Schema {
148154
this.enum = enumValues;
149155
}
150156

151-
toJSON(): _SchemaRequest {
152-
const obj = super.toJSON();
157+
toRequest(): _SchemaRequest {
158+
const obj = super.toRequest();
153159
if (this.enum) {
154160
obj['enum'] = this.enum;
155161
}
@@ -165,9 +171,9 @@ export class ArraySchema extends Schema {
165171
});
166172
}
167173

168-
toJSON(): _SchemaRequest {
169-
const obj = super.toJSON();
170-
obj.items = this.items.toJSON();
174+
toRequest(): _SchemaRequest {
175+
const obj = super.toRequest();
176+
obj.items = this.items.toRequest();
171177
return obj;
172178
}
173179
}
@@ -177,24 +183,25 @@ export class ObjectSchema extends Schema {
177183
schemaParams: SchemaParams,
178184
public properties: {
179185
[k: string]: TypedSchema;
180-
}
186+
},
187+
public optionalProperties: string[] = []
181188
) {
182189
super({
183190
type: SchemaType.OBJECT,
184191
...schemaParams
185192
});
186193
}
187194

188-
toJSON(): _SchemaRequest {
189-
const obj = super.toJSON();
195+
toRequest(): _SchemaRequest {
196+
const obj = super.toRequest();
190197
const properties: Record<string, _SchemaRequest> = {};
191198
const required = [];
192199
for (const propertyKey in this.properties) {
193200
if (this.properties.hasOwnProperty(propertyKey)) {
194201
properties[propertyKey] = this.properties[
195202
propertyKey
196-
].toJSON() as _SchemaRequest;
197-
if (this.properties[propertyKey].required) {
203+
].toRequest() as _SchemaRequest;
204+
if (!this.optionalProperties.includes(propertyKey)) {
198205
required.push(propertyKey);
199206
}
200207
}
@@ -203,6 +210,7 @@ export class ObjectSchema extends Schema {
203210
if (required.length > 0) {
204211
obj.required = required;
205212
}
213+
delete (obj as ObjectSchemaInterface).optionalProperties;
206214
return obj as _SchemaRequest;
207215
}
208216
}

0 commit comments

Comments
 (0)