Skip to content

Commit 7de8464

Browse files
author
ablx
committed
feat: GoogleCloudPlatform#2576 Added typeadapter to read and write Instant values.
1 parent dcec57a commit 7de8464

File tree

6 files changed

+408
-181
lines changed

6 files changed

+408
-181
lines changed

spring-cloud-gcp-data-spanner/src/main/java/com/google/cloud/spring/data/spanner/core/mapping/SpannerMappingContext.java

+10-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818

1919
import com.google.cloud.spring.data.spanner.core.convert.ConverterAwareMappingSpannerEntityProcessor;
2020
import com.google.cloud.spring.data.spanner.core.convert.SpannerEntityProcessor;
21+
import com.google.cloud.spring.data.spanner.core.mapping.typeadapter.InstantTypeAdapter;
2122
import com.google.gson.Gson;
23+
import com.google.gson.TypeAdapter;
2224
import org.springframework.beans.BeansException;
2325
import org.springframework.context.ApplicationContext;
2426
import org.springframework.context.ApplicationContextAware;
@@ -31,6 +33,8 @@
3133
import org.springframework.lang.NonNull;
3234
import org.springframework.util.Assert;
3335

36+
import java.time.Instant;
37+
3438
/**
3539
* A mapping context for Cloud Spanner that provides ways to create persistent entities and
3640
* properties.
@@ -55,7 +59,7 @@ public SpannerMappingContext() {
5559

5660
public SpannerMappingContext(Gson gson) {
5761
Assert.notNull(gson, "A non-null gson is required.");
58-
this.gson = gson;
62+
this.gson = addTypeAdapter(gson, new InstantTypeAdapter(), Instant.class);
5963
}
6064

6165
@NonNull
@@ -124,4 +128,9 @@ public SpannerPersistentEntity<?> getPersistentEntityOrFail(Class<?> entityClass
124128
}
125129
return entity;
126130
}
131+
132+
private <T> Gson addTypeAdapter(Gson gson, TypeAdapter<T> typeAdapter, Class<T> type) {
133+
134+
return gson.newBuilder().registerTypeAdapter(type, typeAdapter).create();
135+
}
127136
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.google.cloud.spring.data.spanner.core.mapping.typeadapter;
2+
3+
import com.google.gson.TypeAdapter;
4+
import com.google.gson.stream.JsonReader;
5+
import com.google.gson.stream.JsonToken;
6+
import com.google.gson.stream.JsonWriter;
7+
8+
import java.io.IOException;
9+
import java.time.Instant;
10+
11+
public class InstantTypeAdapter extends TypeAdapter<Instant> {
12+
@Override
13+
public void write(JsonWriter jsonWriter, Instant instant) throws IOException {
14+
if (instant == null) {
15+
jsonWriter.nullValue();
16+
} else {
17+
jsonWriter.value(instant.toString());
18+
}
19+
}
20+
21+
@Override
22+
public Instant read(JsonReader jsonReader) throws IOException {
23+
System.out.println(jsonReader.peek());
24+
if (jsonReader.peek() == JsonToken.NULL) {
25+
return null;
26+
}
27+
return Instant.parse(jsonReader.nextString());
28+
}
29+
}

spring-cloud-gcp-data-spanner/src/test/java/com/google/cloud/spring/data/spanner/core/convert/ConverterAwareMappingSpannerEntityReaderTests.java

+27
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import static org.assertj.core.api.Assertions.assertThat;
2020
import static org.assertj.core.api.Assertions.assertThatNoException;
2121
import static org.assertj.core.api.Assertions.assertThatThrownBy;
22+
import static org.assertj.core.api.InstanceOfAssertFactories.INSTANT;
2223
import static org.mockito.Mockito.mock;
2324
import static org.mockito.Mockito.times;
2425
import static org.mockito.Mockito.verify;
@@ -41,6 +42,8 @@
4142
import com.google.cloud.spring.data.spanner.core.mapping.SpannerDataException;
4243
import com.google.cloud.spring.data.spanner.core.mapping.SpannerMappingContext;
4344
import com.google.gson.Gson;
45+
46+
import java.time.Instant;
4447
import java.util.Arrays;
4548
import java.util.List;
4649
import org.junit.jupiter.api.BeforeEach;
@@ -397,6 +400,30 @@ void readJsonFieldTest() {
397400
assertThat(result.params.p2).isEqualTo("5");
398401
}
399402

403+
@Test
404+
void readJsonInstantFieldTest() {
405+
Struct row = mock(Struct.class);
406+
when(row.getString("id")).thenReturn("1234");
407+
when(row.getType())
408+
.thenReturn(
409+
Type.struct(
410+
Arrays.asList(
411+
Type.StructField.of("id", Type.string()),
412+
Type.StructField.of("params", Type.json()))));
413+
when(row.getColumnType("id")).thenReturn(Type.string());
414+
415+
when(row.getJson("params")).thenReturn("{\"instant\":\"1970-01-01T00:00:00Z\"}");
416+
417+
TestEntities.TestEntityInstantInJson result =
418+
this.spannerEntityReader.read(TestEntities.TestEntityInstantInJson.class, row);
419+
420+
assertThat(result.id).isEqualTo("1234");
421+
422+
assertThat(result.params.instant).isEqualTo(Instant.ofEpochSecond(0));
423+
}
424+
425+
426+
400427
@Test
401428
void readArrayJsonFieldTest() {
402429
Struct row = mock(Struct.class);

spring-cloud-gcp-data-spanner/src/test/java/com/google/cloud/spring/data/spanner/core/convert/ConverterAwareMappingSpannerEntityWriterTests.java

+16
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,22 @@ void writeJsonTest() {
366366
verify(valueBinder).to(testEntity.id);
367367
verify(valueBinder).to(Value.json("{\"p1\":\"some value\",\"p2\":\"some other value\"}"));
368368
}
369+
@Test
370+
void writeJsonWithInstantTest() {
371+
TestEntities.InstantParam parameters = new TestEntities.InstantParam(Instant.ofEpochSecond(0));
372+
TestEntities.TestEntityInstantInJson testEntity = new TestEntities.TestEntityInstantInJson("id1", parameters);
373+
374+
WriteBuilder writeBuilder = mock(WriteBuilder.class);
375+
ValueBinder<WriteBuilder> valueBinder = mock(ValueBinder.class);
376+
377+
when(writeBuilder.set("id")).thenReturn(valueBinder);
378+
when(writeBuilder.set("params")).thenReturn(valueBinder);
379+
380+
this.spannerEntityWriter.write(testEntity, writeBuilder::set);
381+
382+
verify(valueBinder).to(testEntity.id);
383+
verify(valueBinder).to(Value.json("{\"instant\":\"1970-01-01T00:00:00Z\"}"));
384+
}
369385

370386
@Test
371387
void writeNullJsonTest() {

0 commit comments

Comments
 (0)