Skip to content

Commit 4563fcb

Browse files
feat(spanner): Add support for UUID data type.
1 parent 4d2a380 commit 4563fcb

File tree

11 files changed

+118
-11
lines changed

11 files changed

+118
-11
lines changed

apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.IntegrationTests/AllTypesTableFixture.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public string CreateInsertCommand(bool skipProtobufValue = false) =>
5252
ProtobufRectangleValue,
5353
ProtobufPersonValue,
5454
ProtobufValueWrapperValue,
55+
{EmptyOnEmulator("UuidValue,")}
5556
BoolArrayValue,
5657
Int64ArrayValue,
5758
Float32ArrayValue,
@@ -67,6 +68,7 @@ public string CreateInsertCommand(bool skipProtobufValue = false) =>
6768
ProtobufDurationArrayValue,
6869
ProtobufRectangleArrayValue,
6970
ProtobufPersonArrayValue,
71+
{EmptyOnEmulator("UuidArrayValue,")}
7072
ProtobufValueWrapperArrayValue) VALUES(
7173
@K,
7274
@BoolValue,
@@ -84,6 +86,7 @@ public string CreateInsertCommand(bool skipProtobufValue = false) =>
8486
@ProtobufRectangleValue,
8587
@ProtobufPersonValue,
8688
@ProtobufValueWrapperValue,
89+
{EmptyOnEmulator("@UuidValue,")}
8790
@BoolArrayValue,
8891
@Int64ArrayValue,
8992
@Float32ArrayValue,
@@ -99,7 +102,8 @@ public string CreateInsertCommand(bool skipProtobufValue = false) =>
99102
@ProtobufDurationArrayValue,
100103
@ProtobufRectangleArrayValue,
101104
@ProtobufPersonArrayValue,
102-
@ProtobufValueWrapperArrayValue
105+
@ProtobufValueWrapperArrayValue,
106+
{EmptyOnEmulator("@UuidArrayValue")}
103107
)";
104108

105109
protected override void CreateTable() =>
@@ -120,6 +124,7 @@ BytesValue BYTES(MAX),
120124
ProtobufRectangleValue {Rectangle.Descriptor.FullName},
121125
ProtobufPersonValue {Person.Descriptor.FullName},
122126
ProtobufValueWrapperValue {ValueWrapper.Descriptor.FullName},
127+
{EmptyOnEmulator("UuidValue UUID,")}
123128
BoolArrayValue ARRAY<BOOL>,
124129
Int64ArrayValue ARRAY<INT64>,
125130
Float32ArrayValue ARRAY<FLOAT32>,
@@ -135,9 +140,12 @@ BytesValue BYTES(MAX),
135140
ProtobufDurationArrayValue ARRAY<{Duration.Descriptor.FullName}>,
136141
ProtobufRectangleArrayValue ARRAY<{Rectangle.Descriptor.FullName}>,
137142
ProtobufPersonArrayValue ARRAY<{Person.Descriptor.FullName}>,
138-
ProtobufValueWrapperArrayValue ARRAY<{ValueWrapper.Descriptor.FullName}>
143+
ProtobufValueWrapperArrayValue ARRAY<{ValueWrapper.Descriptor.FullName}>,
144+
{EmptyOnEmulator("UuidArrayValue ARRAY<UUID>")}
139145
) PRIMARY KEY(K)");
140146

141147
private string MaybeEmptyOnProduction(string text, bool skip) => skip && !RunningOnEmulator ? "" : text;
148+
149+
private string EmptyOnEmulator(string text) => RunningOnEmulator ? "" : text;
142150
}
143151
}

apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.IntegrationTests/BindingTests.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public BindingTests(SpannerDatabaseFixture fixture) =>
6161
SpannerDbType.Float32,
6262
SpannerDbType.Json,
6363
SpannerDbType.Interval,
64+
SpannerDbType.Uuid,
6465
SpannerDbType.FromClrType(typeof(Duration)),
6566
SpannerDbType.FromClrType(typeof(Rectangle)),
6667
SpannerDbType.FromClrType(typeof(Person)),
@@ -76,6 +77,7 @@ public BindingTests(SpannerDatabaseFixture fixture) =>
7677
SpannerDbType.ArrayOf(SpannerDbType.Float32),
7778
SpannerDbType.ArrayOf(SpannerDbType.Json),
7879
SpannerDbType.ArrayOf(SpannerDbType.Interval),
80+
SpannerDbType.ArrayOf(SpannerDbType.Uuid),
7981
SpannerDbType.ArrayOf(SpannerDbType.FromClrType(typeof(Duration))),
8082
SpannerDbType.ArrayOf(SpannerDbType.FromClrType(typeof(Rectangle))),
8183
SpannerDbType.ArrayOf(SpannerDbType.FromClrType(typeof(Person))),
@@ -393,5 +395,21 @@ public async Task BindProtobufValueWrapperEmptyArray() => await TestBindNonNull(
393395
private void MaybeSkipIfOnProduction(SpannerDbType spannerDbType) =>
394396
Skip.If(!_fixture.RunningOnEmulator && BindProductionUnsupportedNullData.Any<SpannerDbType>(spannerDbType.Equals),
395397
$"Production does not support {spannerDbType}.");
398+
399+
[Fact]
400+
public Task BindUuid() => TestBindNonNull(
401+
SpannerDbType.Uuid,
402+
Guid.NewGuid(),
403+
r => r.GetGuid(0));
404+
405+
[Fact]
406+
public Task BindUuidArray() => TestBindNonNull(
407+
SpannerDbType.ArrayOf(SpannerDbType.Uuid),
408+
new Guid?[] { Guid.NewGuid(), null, Guid.NewGuid() });
409+
410+
[Fact]
411+
public Task BindUuidEmptyArray() => TestBindNonNull(
412+
SpannerDbType.ArrayOf(SpannerDbType.Uuid),
413+
new Guid[] { });
396414
}
397415
}

apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/KeysTests.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ public void CreateKeyFromParameterCollection()
4040
{ "", SpannerDbType.Interval, Interval.Parse("P1Y2M3D") },
4141
{ "", SpannerDbType.PgOid, 2 },
4242
{ "", SpannerDbType.String, "test" },
43-
{ "", SpannerDbType.Timestamp, new DateTime(2021, 9, 10, 9, 37, 10, DateTimeKind.Utc) }
43+
{ "", SpannerDbType.Timestamp, new DateTime(2021, 9, 10, 9, 37, 10, DateTimeKind.Utc) },
44+
{ "", SpannerDbType.Uuid, Guid.Parse("8f8c4746-17b1-4d9f-a634-58e11942095f") }
4445
});
4546

4647
var actual = key.ToProtobuf(SpannerConversionOptions.Default);
@@ -61,7 +62,8 @@ public void CreateKeyFromParameterCollection()
6162
Value.ForString("P1Y2M3D"),
6263
Value.ForString("2"),
6364
Value.ForString("test"),
64-
Value.ForString("2021-09-10T09:37:10Z")
65+
Value.ForString("2021-09-10T09:37:10Z"),
66+
Value.ForString("8f8c4746-17b1-4d9f-a634-58e11942095f")
6567
}
6668
};
6769
Assert.Equal(expected, actual);

apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/SpannerCommandTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1344,6 +1344,7 @@ public async Task CanExecuteReadCommand()
13441344
{"timestamp", SpannerDbType.Timestamp, new DateTime(2021, 9, 8, 15, 22, 59, DateTimeKind.Utc)},
13451345
{"bool", SpannerDbType.Bool, true},
13461346
{"interval", SpannerDbType.Interval, Interval.Parse("P1Y2M3D")},
1347+
{"uuid", SpannerDbType.Uuid, new Guid("8f8c4746-17b1-4d9f-a634-58e11942095f")},
13471348
}));
13481349
using var reader = await command.ExecuteReaderAsync();
13491350
Assert.True(reader.HasRows);
@@ -1365,6 +1366,7 @@ public async Task CanExecuteReadCommand()
13651366
new Value { StringValue = "2021-09-08T15:22:59Z" },
13661367
new Value { BoolValue = true },
13671368
new Value { StringValue = "P1Y2M3D" },
1369+
new Value { StringValue = "8f8c4746-17b1-4d9f-a634-58e11942095f"},
13681370
} } } })),
13691371
Arg.Any<CallSettings>());
13701372
}

apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/SpannerDbTypeTests.ValueConversions.cs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,14 @@ public enum TestType
6161
{ "JsonField", SpannerDbType.Json, "{\"field\": \"value\"}" },
6262
{ "PgJsonbField", SpannerDbType.PgJsonb, "{\"field1\": \"value1\"}" },
6363
{ "PgOidField", SpannerDbType.PgOid, 3L },
64-
{ "IntervalField", SpannerDbType.Interval, Interval.Parse("P1Y2M3D") }
64+
{ "IntervalField", SpannerDbType.Interval, Interval.Parse("P1Y2M3D") },
65+
{ "Uuid", SpannerDbType.Uuid, new Guid("8f8c4746-17b1-4d9f-a634-58e11942095f")},
6566
};
6667

6768
// Structs are serialized as lists of their values. The field names aren't present, as they're
6869
// specified in the type.
6970
private static readonly string s_sampleStructSerialized =
70-
"[ \"stringValue\", \"2\", \"NaN\", \"NaN\", true, \"2017-01-31\", \"2017-01-31T03:15:30Z\", \"99999999999999999999999999999.999999999\", \"NaN\", \"{\\\"field\\\": \\\"value\\\"}\", \"{\\\"field1\\\": \\\"value1\\\"}\", \"3\", \"P1Y2M3D\" ]";
71+
"[ \"stringValue\", \"2\", \"NaN\", \"NaN\", true, \"2017-01-31\", \"2017-01-31T03:15:30Z\", \"99999999999999999999999999999.999999999\", \"NaN\", \"{\\\"field\\\": \\\"value\\\"}\", \"{\\\"field1\\\": \\\"value1\\\"}\", \"3\", \"P1Y2M3D\", \"8f8c4746-17b1-4d9f-a634-58e11942095f\" ]";
7172

7273
private static string Quote(string s) => $"\"{s}\"";
7374

@@ -141,6 +142,12 @@ private static IEnumerable<Interval> GetIntervalsForArray()
141142
yield return Interval.Parse("PT0.9S");
142143
}
143144

145+
private static IEnumerable<Guid> GetGuidsForArray()
146+
{
147+
yield return Guid.Empty;
148+
yield return Guid.Parse("8f8c4746-17b1-4d9f-a634-58e11942095f");
149+
}
150+
144151
private static readonly BigInteger MaxValueForPgNumeric = BigInteger.Pow(10, 147455) - 1;
145152

146153
private static readonly string ExpectedMaxValueForPgNumeric = MaxValueForPgNumeric.ToString();
@@ -331,6 +338,17 @@ public static IEnumerable<object[]> GetValidValueConversions()
331338
// Interval tests
332339
yield return new object[] { "P0Y", SpannerDbType.Interval, Quote("P0Y") };
333340
yield return new object[] { Interval.Parse("P1Y2M3DT4H5M6S"), SpannerDbType.Interval, Quote("P1Y2M3DT4H5M6S") };
341+
// UUID tests
342+
yield return new object[] { "8f8c4746-17b1-4d9f-a634-58e11942095f", SpannerDbType.Uuid, Quote("8f8c4746-17b1-4d9f-a634-58e11942095f")};
343+
yield return new object[] { Guid.Parse("8f8c4746-17b1-4d9f-a634-58e11942095f"), SpannerDbType.Uuid, Quote("8f8c4746-17b1-4d9f-a634-58e11942095f") };
344+
yield return new object[] { Guid.Parse("8f8c4746-17b1-4d9f-a634-58e11942095f"), SpannerDbType.Uuid, Quote("8f8c474617b14d9fa63458e11942095f"), TestType.ValueToClr };
345+
yield return new object[] { Guid.Parse("8f8c4746-17b1-4d9f-a634-58e11942095f"), SpannerDbType.Uuid, Quote("{8f8c4746-17b1-4d9f-a634-58e11942095f}"), TestType.ValueToClr };
346+
yield return new object[] { Guid.Parse("8f8c4746-17b1-4d9f-a634-58e11942095f"), SpannerDbType.Uuid, Quote("(8f8c4746-17b1-4d9f-a634-58e11942095f)"), TestType.ValueToClr };
347+
yield return new object[] { Guid.Parse("8f8c4746-17b1-4d9f-a634-58e11942095f"), SpannerDbType.Uuid, Quote("8F8C4746-17B1-4D9F-A634-58E11942095F"), TestType.ValueToClr };
348+
yield return new object[] { "{8f8c4746-17b1-4d9f-a634-58e11942095f}", SpannerDbType.Uuid, Quote("8f8c4746-17b1-4d9f-a634-58e11942095f"), TestType.ClrToValue};
349+
yield return new object[] { "(8f8c4746-17b1-4d9f-a634-58e11942095f)", SpannerDbType.Uuid, Quote("8f8c4746-17b1-4d9f-a634-58e11942095f"), TestType.ClrToValue};
350+
yield return new object[] { "8F8C4746-17B1-4D9F-A634-58E11942095F", SpannerDbType.Uuid, Quote("8f8c4746-17b1-4d9f-a634-58e11942095f"), TestType.ClrToValue};
351+
yield return new object[] { "8f8c474617b14d9fa63458e11942095f", SpannerDbType.Uuid, Quote("8f8c4746-17b1-4d9f-a634-58e11942095f"), TestType.ClrToValue };
334352

335353
// Note the difference in C# conversions from special floats and doubles.
336354
yield return new object[] { float.NegativeInfinity, SpannerDbType.String, Quote("-Infinity") };
@@ -420,6 +438,11 @@ public static IEnumerable<object[]> GetValidValueConversions()
420438
new List<Interval>(GetIntervalsForArray()), SpannerDbType.ArrayOf(SpannerDbType.Interval),
421439
"[ \"P1Y\", \"P1Y2M3DT4H5M6.5S\", \"PT0.9S\" ]"
422440
};
441+
yield return new object[]
442+
{
443+
new List<Guid>(GetGuidsForArray()), SpannerDbType.ArrayOf(SpannerDbType.Uuid),
444+
"[ \"00000000-0000-0000-0000-000000000000\", \"8f8c4746-17b1-4d9f-a634-58e11942095f\" ]"
445+
};
423446
// JSON can not be converted from Value to Clr, as there is no unique Clr type for JSON.
424447
yield return new object[]
425448
{
@@ -683,6 +706,11 @@ public static IEnumerable<object[]> GetInvalidValueConversions()
683706
yield return new object[] { "1Y2M", SpannerDbType.Interval };
684707
yield return new object[] { "P1H", SpannerDbType.Interval };
685708
yield return new object[] { TimeSpan.FromDays(3), SpannerDbType.Interval };
709+
710+
// Spanner type = UUID tests.
711+
yield return new object[] { "invalid", SpannerDbType.Uuid };
712+
yield return new object[] { "", SpannerDbType.Uuid };
713+
yield return new object[] { "0", SpannerDbType.Uuid };
686714
}
687715

688716
private static readonly CultureInfo[] s_cultures = new[]

apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/SpannerDbTypeTests.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ public static IEnumerable<object[]> GetSpannerStringConversions()
8484
yield return new object[] { "NUMERIC", SpannerDbType.Numeric };
8585
yield return new object[] { "NUMERIC{PG}", SpannerDbType.PgNumeric };
8686
yield return new object[] { "INTERVAL", SpannerDbType.Interval };
87+
yield return new object[] { "UUID", SpannerDbType.Uuid };
8788

8889
yield return new object[] { " STRING ", SpannerDbType.String };
8990
yield return new object[] { " BOOL ", SpannerDbType.Bool };
@@ -99,6 +100,7 @@ public static IEnumerable<object[]> GetSpannerStringConversions()
99100
yield return new object[] { " NUMERIC ", SpannerDbType.Numeric };
100101
yield return new object[] { " NUMERIC{PG} ", SpannerDbType.PgNumeric };
101102
yield return new object[] { " INTERVAL ", SpannerDbType.Interval };
103+
yield return new object[] { " UUID ", SpannerDbType.Uuid };
102104

103105
yield return new object[] { "STRING(2)", SpannerDbType.String.WithSize(2) };
104106
yield return new object[] { "STRING(100)", SpannerDbType.String.WithSize(100) };
@@ -126,6 +128,7 @@ public static IEnumerable<object[]> GetSpannerStringConversions()
126128
yield return new object[] { "ARRAY<OID{PG}>", SpannerDbType.ArrayOf(SpannerDbType.PgOid) };
127129
yield return new object[] { "ARRAY<TIMESTAMP>", SpannerDbType.ArrayOf(SpannerDbType.Timestamp) };
128130
yield return new object[] { "ARRAY<INTERVAL>", SpannerDbType.ArrayOf(SpannerDbType.Interval) };
131+
yield return new object[] { "ARRAY<UUID>", SpannerDbType.ArrayOf(SpannerDbType.Uuid) };
129132

130133
yield return new object[] { "ARRAY<STRING(5)>", SpannerDbType.ArrayOf(SpannerDbType.String), false };
131134
yield return new object[] { "ARRAY<BYTES(5)>", SpannerDbType.ArrayOf(SpannerDbType.Bytes), false };
@@ -186,9 +189,10 @@ public static IEnumerable<object[]> GetSpannerStringConversions()
186189
{ "F11", SpannerDbType.PgNumeric, null },
187190
{ "F12", SpannerDbType.PgJsonb, null },
188191
{ "F13", SpannerDbType.PgOid, null },
189-
{ "F14", SpannerDbType.Interval, null}
192+
{ "F14", SpannerDbType.Interval, null},
193+
{ "F15", SpannerDbType.Uuid, null}
190194
};
191-
yield return new object[] { "STRUCT<F1:STRING,F2:INT64,F3:BOOL,F4:BYTES,F5:DATE,F6:FLOAT32,F7:FLOAT64,F8:TIMESTAMP,F9:NUMERIC,F10:JSON,F11:NUMERIC{PG},F12:JSONB{PG},F13:OID{PG},F14:INTERVAL>", sampleStruct.GetSpannerDbType() };
195+
yield return new object[] { "STRUCT<F1:STRING,F2:INT64,F3:BOOL,F4:BYTES,F5:DATE,F6:FLOAT32,F7:FLOAT64,F8:TIMESTAMP,F9:NUMERIC,F10:JSON,F11:NUMERIC{PG},F12:JSONB{PG},F13:OID{PG},F14:INTERVAL,F15:UUID>", sampleStruct.GetSpannerDbType() };
192196

193197
sampleStruct = new SpannerStruct
194198
{

apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/SpannerParameterTests.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public static IEnumerable<object[]> GetDbTypeConversions()
4545
yield return new object[] { SpannerDbType.Numeric, DbType.VarNumeric, true };
4646
yield return new object[] { SpannerDbType.Unspecified, DbType.Object, true };
4747
yield return new object[] { SpannerDbType.String, DbType.String, true };
48+
yield return new object[] { SpannerDbType.Uuid, DbType.Guid, true };
4849
// There is no DbType that will map automatically to SpannerDbType.Json, SpannerDbType.PgJsonb,
4950
// SpannerDbType.PgOid, SpannerDbType protobuf or SpannerDbType.Interval.
5051
yield return new object[] { SpannerDbType.Json, DbType.String, false };
@@ -99,6 +100,8 @@ public static IEnumerable<object[]> GetValueConversions()
99100

100101
yield return new object[] { Interval.Parse("P1Y2M3D"), SpannerDbType.Interval, DbType.Object, typeof(Interval) };
101102

103+
yield return new object[] { Guid.Parse("8f8c4746-17b1-4d9f-a634-58e11942095f"), SpannerDbType.Uuid, DbType.Guid, typeof(Guid) };
104+
102105
// Tests for protobuf
103106
// Note that the default CLR type here is always Value, because in general, we only know the the name of the protobuf type and from there
104107
// we can't get the CLR type.
@@ -208,6 +211,7 @@ public static IEnumerable<object[]> AllSpannerTypes()
208211
yield return new object[] { SpannerDbType.Json };
209212
yield return new object[] { SpannerDbType.PgJsonb };
210213
yield return new object[] { SpannerDbType.Interval };
214+
yield return new object[] { SpannerDbType.Uuid };
211215
yield return new object[] { SpannerDbType.ArrayOf(SpannerDbType.Bytes) };
212216
yield return new object[] { SpannerDbType.ArrayOf(SpannerDbType.String) };
213217
yield return new object[] { SpannerDbType.ArrayOf(SpannerDbType.Bool) };
@@ -222,6 +226,7 @@ public static IEnumerable<object[]> AllSpannerTypes()
222226
yield return new object[] { SpannerDbType.ArrayOf(SpannerDbType.Json) };
223227
yield return new object[] { SpannerDbType.ArrayOf(SpannerDbType.PgJsonb) };
224228
yield return new object[] { SpannerDbType.ArrayOf(SpannerDbType.Interval) };
229+
yield return new object[] { SpannerDbType.ArrayOf(SpannerDbType.Uuid) };
225230
}
226231

227232
[Theory]

apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerDbType.StringParsing.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,10 @@ public static bool TryParse(string spannerType, out SpannerDbType spannerDbType)
5151
case TypeCode.Json:
5252
case TypeCode.Numeric:
5353
case TypeCode.Interval:
54+
case TypeCode.Uuid:
5455
if (!string.IsNullOrEmpty(remainder))
5556
{
56-
//unexepected inner remainder on simple type
57+
// Unexpected inner remainder on simple type.
5758
return false;
5859
}
5960
// If there's no size, we can use cached values.

apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerDbType.ValueConversion.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,16 @@ internal Value ToProtobufValue(object value)
191191
StringValue = StripTimePart(
192192
XmlConvert.ToString(Convert.ToDateTime(value, InvariantCulture), XmlDateTimeSerializationMode.Utc))
193193
};
194+
case TypeCode.Uuid:
195+
if (value is Guid guidValue)
196+
{
197+
return new Value { StringValue = guidValue.ToString() };
198+
}
199+
if (value is string guidStringValue)
200+
{
201+
return new Value { StringValue = Guid.Parse(guidStringValue).ToString() };
202+
}
203+
throw new ArgumentException($"TypeCode.Uuid only supports {typeof(string).FullName} and {typeof(Guid).FullName}", nameof(value));
194204
case TypeCode.Array:
195205
if (value is IEnumerable enumerable)
196206
{
@@ -526,6 +536,20 @@ private object ConvertToClrTypeImpl(Value wireValue, System.Type targetClrType,
526536
}
527537
}
528538

539+
if (targetClrType == typeof(Guid))
540+
{
541+
switch (wireValue.KindCase)
542+
{
543+
case Value.KindOneofCase.NullValue:
544+
return null;
545+
case Value.KindOneofCase.StringValue:
546+
return Guid.Parse(wireValue.StringValue);
547+
default:
548+
throw new InvalidOperationException(
549+
$"Invalid Type conversion from {wireValue.KindCase} to {targetClrType.FullName}");
550+
}
551+
}
552+
529553
if (targetClrType == typeof(string))
530554
{
531555
switch (wireValue.KindCase)

0 commit comments

Comments
 (0)