Skip to content

Commit 2c0a9d9

Browse files
committed
feat: make variant type converting to YDB native working
1 parent 5320024 commit 2c0a9d9

File tree

2 files changed

+130
-33
lines changed

2 files changed

+130
-33
lines changed

src/__tests__/types.test.ts

Lines changed: 117 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,12 @@ describe('Types', () => {
310310
});
311311

312312
it('Tuple variant value', () => {
313-
expect(TypedValues.fromNative(Types.variant(Types.tuple(Types.UINT32, Types.BOOL)), [3, null])).toEqual({
313+
expect(
314+
TypedValues.fromNative(Types.variant(Types.tuple(Types.UINT32, Types.BOOL)), [
315+
3,
316+
null,
317+
]),
318+
).toEqual({
314319
type: {
315320
variantType: {
316321
tupleItems: {
@@ -323,19 +328,18 @@ describe('Types', () => {
323328
},
324329
value: {
325330
variantIndex: 0,
326-
items: [
327-
{uint32Value: 3},
328-
{nullFlagValue: 0},
329-
],
331+
nestedValue: {uint32Value: 3},
330332
},
331333
});
332334
});
333335

334336
it('Struct variant value', () => {
335-
expect(TypedValues.fromNative(Types.variant(Types.struct({
336-
a: Types.UINT32,
337-
b: Types.BOOL
338-
})), [3, null])).toEqual({
337+
expect(
338+
TypedValues.fromNative(
339+
Types.variant(Types.struct({a: Types.UINT32, b: Types.BOOL})),
340+
{a: 3},
341+
),
342+
).toEqual({
339343
type: {
340344
variantType: {
341345
structItems: {
@@ -348,10 +352,7 @@ describe('Types', () => {
348352
},
349353
value: {
350354
variantIndex: 0,
351-
items: [
352-
{uint32Value: 3},
353-
{nullFlagValue: 0},
354-
],
355+
nestedValue: {uint32Value: 3},
355356
},
356357
});
357358
});
@@ -599,6 +600,109 @@ describe('Types', () => {
599600
});
600601
});
601602

603+
it('Variant YDB -> SDK value', async () => {
604+
await driver.tableClient.withSession(async (session) => {
605+
const query = `$var_type_struct = Variant<foo: UInt32, bar: String>;
606+
$var_type_tuple = Variant<Int32,Bool>;
607+
SELECT
608+
Variant(12345678, "foo", $var_type_struct) as v1,
609+
Variant("AbCdEfGh", "bar", $var_type_struct) as v2,
610+
Variant(-12345678, "0", $var_type_tuple) as v3,
611+
Variant(false, "1", $var_type_tuple) as v4;`;
612+
613+
const sdkValues = {
614+
v1: TypedValues.fromNative(
615+
Types.variant(Types.struct({foo: Types.UINT32, bar: Types.STRING})),
616+
{foo: 12345678},
617+
),
618+
v2: TypedValues.fromNative(
619+
Types.variant(Types.struct({foo: Types.UINT32, bar: Types.STRING})),
620+
{bar: 'AbCdEfGh'},
621+
),
622+
v3: TypedValues.fromNative(
623+
Types.variant(Types.tuple(Types.INT32, Types.BOOL)),
624+
[-12345678, null],
625+
),
626+
v4: TypedValues.fromNative(
627+
Types.variant(Types.tuple(Types.INT32, Types.BOOL)),
628+
[null, false],
629+
),
630+
};
631+
632+
const response = await session.executeQuery(query);
633+
const actual = TypedData.createNativeObjects(response.resultSets[0]);
634+
635+
// SDK writes string as given, while YDB returns it in base64 encoding.
636+
// though, we need to change it in future
637+
if (!sdkValues.v2.value) sdkValues.v2.value = {};
638+
sdkValues.v2.value.nestedValue = {
639+
bytesValue: Buffer.from(
640+
sdkValues.v2.value?.nestedValue?.bytesValue as unknown as string,
641+
).toString('base64') as unknown as Uint8Array,
642+
};
643+
644+
Object.values(sdkValues).map((v, idx) => {
645+
expect(JSON.stringify(v.value?.nestedValue)).toEqual(
646+
JSON.stringify(response.resultSets[0].rows?.[0].items?.[idx]?.nestedValue),
647+
);
648+
});
649+
650+
expect(actual).toEqual([
651+
{
652+
v1: {foo: 12345678},
653+
v2: {bar: 'AbCdEfGh'},
654+
v3: [-12345678, undefined],
655+
v4: [undefined, false],
656+
},
657+
]);
658+
});
659+
});
660+
661+
// // TODO: Enable in future versions of YDB
662+
// // now throws error `Failed to export parameter type: $var1
663+
// // Unsupported protobuf type: Variant<'bar':Bool,'foo':Int32>`
664+
// it('Variant SDK -> YDB value', async () => {
665+
// await driver.tableClient.withSession(async (session) => {
666+
// const query = `
667+
// DECLARE $var1 AS Variant<foo: Int32, bar: Bool>;
668+
// DECLARE $var2 AS Variant<foo: Int32, bar: Bool>;
669+
// DECLARE $var3 AS Variant<Int32,String>;
670+
// DECLARE $var4 AS Variant<Int32,String>;
671+
// SELECT $var1 as var1, $var2 as var2, $var3 as var3, $var4 as var4;`;
672+
673+
// const params = {
674+
// $var1: TypedValues.fromNative(
675+
// Types.variant(Types.struct({foo: Types.INT32, bar: Types.BOOL})),
676+
// [111, null],
677+
// ),
678+
// $var2: TypedValues.fromNative(
679+
// Types.variant(Types.struct({foo: Types.INT32, bar: Types.BOOL})),
680+
// [null, true],
681+
// ),
682+
// $var3: TypedValues.fromNative(
683+
// Types.variant(Types.tuple(Types.INT32, Types.STRING)),
684+
// [333, null],
685+
// ),
686+
// $var4: TypedValues.fromNative(
687+
// Types.variant(Types.tuple(Types.INT32, Types.STRING)),
688+
// [null, '444'],
689+
// ),
690+
// };
691+
692+
// const response = await session.executeQuery(query, params);
693+
// const actual = TypedData.createNativeObjects(response.resultSets[0]);
694+
695+
// const data = {
696+
// var1: {foo: 6},
697+
// var2: {bar: false},
698+
// var3: [-123, null],
699+
// var4: [null, 'abcdef'],
700+
// };
701+
// const expected = [new TypedData(data)];
702+
// expect(expected).toEqual(actual);
703+
// });
704+
// });
705+
602706
it('Enum value', async () => {
603707
await driver.tableClient.withSession(async (session) => {
604708
const query = `$enum_type = Enum<Foo, Bar>;

src/types.ts

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -574,32 +574,25 @@ function typeToValue(type: IType | null | undefined, value: any): IValue {
574574
}))
575575
}
576576
} else if (type.variantType) {
577-
let variantIndex = -1;
578577
if (type.variantType.tupleItems) {
579578
const elements = type.variantType.tupleItems.elements as IType[];
579+
const variantIndex = (value as Array<any>).findIndex((v) => v !== null);
580580
return {
581-
items: _.map(value, (item, index: number) => {
582-
if (item) {
583-
variantIndex = index;
584-
return typeToValue(elements[index], item);
585-
}
586-
return {nullFlagValue: NullValue.NULL_VALUE};
587-
}),
588-
variantIndex
589-
}
581+
nestedValue: typeToValue(elements[variantIndex], value[variantIndex]),
582+
variantIndex,
583+
};
590584
} else if (type.variantType.structItems) {
591585
const members = type.variantType.structItems.members as IStructMember[];
586+
const variantKey = Object.keys(value)[0];
587+
const variantIndex = members.findIndex((a) => variantKey === a.name);
588+
589+
if (variantKey === undefined)
590+
throw new Error("Variant type doesn't have not null fields");
591+
592592
return {
593-
items: _.map(value, (item, index: number)=> {
594-
if (item) {
595-
variantIndex = index;
596-
const type = members[index].type;
597-
return typeToValue(type, item);
598-
}
599-
return {nullFlagValue: NullValue.NULL_VALUE};
600-
}),
601-
variantIndex
602-
}
593+
nestedValue: typeToValue(members[variantIndex].type, value[variantKey]),
594+
variantIndex,
595+
};
603596
}
604597
throw new Error('Either tupleItems or structItems should be present in VariantType!');
605598
} else if (type.voidType === NullValue.NULL_VALUE) {

0 commit comments

Comments
 (0)