Skip to content

Commit 4d15917

Browse files
committed
Core: Add LoadCredentialsResponse class/parser
1 parent 8dc9eac commit 4d15917

File tree

6 files changed

+253
-15
lines changed

6 files changed

+253
-15
lines changed

core/src/main/java/org/apache/iceberg/rest/RESTSerializers.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,10 @@
5959
import org.apache.iceberg.rest.responses.ConfigResponseParser;
6060
import org.apache.iceberg.rest.responses.ErrorResponse;
6161
import org.apache.iceberg.rest.responses.ErrorResponseParser;
62+
import org.apache.iceberg.rest.responses.ImmutableLoadCredentialsResponse;
6263
import org.apache.iceberg.rest.responses.ImmutableLoadViewResponse;
64+
import org.apache.iceberg.rest.responses.LoadCredentialsResponse;
65+
import org.apache.iceberg.rest.responses.LoadCredentialsResponseParser;
6366
import org.apache.iceberg.rest.responses.LoadTableResponse;
6467
import org.apache.iceberg.rest.responses.LoadTableResponseParser;
6568
import org.apache.iceberg.rest.responses.LoadViewResponse;
@@ -119,7 +122,13 @@ public static void registerAll(ObjectMapper mapper) {
119122
.addSerializer(ConfigResponse.class, new ConfigResponseSerializer<>())
120123
.addDeserializer(ConfigResponse.class, new ConfigResponseDeserializer<>())
121124
.addSerializer(LoadTableResponse.class, new LoadTableResponseSerializer<>())
122-
.addDeserializer(LoadTableResponse.class, new LoadTableResponseDeserializer<>());
125+
.addDeserializer(LoadTableResponse.class, new LoadTableResponseDeserializer<>())
126+
.addSerializer(LoadCredentialsResponse.class, new LoadCredentialsResponseSerializer<>())
127+
.addSerializer(
128+
ImmutableLoadCredentialsResponse.class, new LoadCredentialsResponseSerializer<>())
129+
.addDeserializer(LoadCredentialsResponse.class, new LoadCredentialsResponseDeserializer<>())
130+
.addDeserializer(
131+
ImmutableLoadCredentialsResponse.class, new LoadCredentialsResponseDeserializer<>());
123132

124133
mapper.registerModule(module);
125134
}
@@ -443,4 +452,22 @@ public T deserialize(JsonParser p, DeserializationContext context) throws IOExce
443452
return (T) LoadTableResponseParser.fromJson(jsonNode);
444453
}
445454
}
455+
456+
static class LoadCredentialsResponseSerializer<T extends LoadCredentialsResponse>
457+
extends JsonSerializer<T> {
458+
@Override
459+
public void serialize(T request, JsonGenerator gen, SerializerProvider serializers)
460+
throws IOException {
461+
LoadCredentialsResponseParser.toJson(request, gen);
462+
}
463+
}
464+
465+
static class LoadCredentialsResponseDeserializer<T extends LoadCredentialsResponse>
466+
extends JsonDeserializer<T> {
467+
@Override
468+
public T deserialize(JsonParser p, DeserializationContext context) throws IOException {
469+
JsonNode jsonNode = p.getCodec().readTree(p);
470+
return (T) LoadCredentialsResponseParser.fromJson(jsonNode);
471+
}
472+
}
446473
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.iceberg.rest.responses;
20+
21+
import java.util.List;
22+
import org.apache.iceberg.rest.RESTResponse;
23+
import org.apache.iceberg.rest.credentials.Credential;
24+
import org.immutables.value.Value;
25+
26+
@Value.Immutable
27+
public interface LoadCredentialsResponse extends RESTResponse {
28+
List<Credential> credentials();
29+
30+
@Override
31+
default void validate() {
32+
// nothing to validate
33+
}
34+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.iceberg.rest.responses;
20+
21+
import com.fasterxml.jackson.core.JsonGenerator;
22+
import com.fasterxml.jackson.databind.JsonNode;
23+
import java.io.IOException;
24+
import org.apache.iceberg.relocated.com.google.common.base.Preconditions;
25+
import org.apache.iceberg.rest.credentials.Credential;
26+
import org.apache.iceberg.rest.credentials.CredentialParser;
27+
import org.apache.iceberg.util.JsonUtil;
28+
29+
public class LoadCredentialsResponseParser {
30+
private static final String STORAGE_CREDENTIALS = "storage-credentials";
31+
32+
private LoadCredentialsResponseParser() {}
33+
34+
public static String toJson(LoadCredentialsResponse response) {
35+
return toJson(response, false);
36+
}
37+
38+
public static String toJson(LoadCredentialsResponse response, boolean pretty) {
39+
return JsonUtil.generate(gen -> toJson(response, gen), pretty);
40+
}
41+
42+
public static void toJson(LoadCredentialsResponse response, JsonGenerator gen)
43+
throws IOException {
44+
Preconditions.checkArgument(null != response, "Invalid load credentials response: null");
45+
46+
gen.writeStartObject();
47+
48+
gen.writeArrayFieldStart(STORAGE_CREDENTIALS);
49+
for (Credential credential : response.credentials()) {
50+
CredentialParser.toJson(credential, gen);
51+
}
52+
53+
gen.writeEndArray();
54+
55+
gen.writeEndObject();
56+
}
57+
58+
public static LoadCredentialsResponse fromJson(String json) {
59+
return JsonUtil.parse(json, LoadCredentialsResponseParser::fromJson);
60+
}
61+
62+
public static LoadCredentialsResponse fromJson(JsonNode json) {
63+
Preconditions.checkArgument(
64+
null != json, "Cannot parse load credentials response from null object");
65+
66+
JsonNode credentials = JsonUtil.get(STORAGE_CREDENTIALS, json);
67+
Preconditions.checkArgument(
68+
credentials.isArray(), "Cannot parse credentials from non-array: %s", credentials);
69+
70+
ImmutableLoadCredentialsResponse.Builder builder = ImmutableLoadCredentialsResponse.builder();
71+
for (JsonNode credential : credentials) {
72+
builder.addCredentials(CredentialParser.fromJson(credential));
73+
}
74+
75+
return builder.build();
76+
}
77+
}

core/src/main/java/org/apache/iceberg/rest/responses/LoadTableResponseParser.java

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,7 @@ public static LoadTableResponse fromJson(JsonNode json) {
9898
}
9999

100100
if (json.hasNonNull(STORAGE_CREDENTIALS)) {
101-
JsonNode credentials = JsonUtil.get(STORAGE_CREDENTIALS, json);
102-
Preconditions.checkArgument(
103-
credentials.isArray(), "Cannot parse credentials from non-array: %s", credentials);
104-
105-
for (JsonNode credential : credentials) {
106-
builder.addCredential(CredentialParser.fromJson(credential));
107-
}
101+
builder.addAllCredentials(LoadCredentialsResponseParser.fromJson(json).credentials());
108102
}
109103

110104
return builder.build();

core/src/main/java/org/apache/iceberg/rest/responses/LoadViewResponseParser.java

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,7 @@ public static LoadViewResponse fromJson(JsonNode json) {
9393
}
9494

9595
if (json.hasNonNull(STORAGE_CREDENTIALS)) {
96-
JsonNode credentials = JsonUtil.get(STORAGE_CREDENTIALS, json);
97-
Preconditions.checkArgument(
98-
credentials.isArray(), "Cannot parse credentials from non-array: %s", credentials);
99-
100-
for (JsonNode credential : credentials) {
101-
builder.addCredentials(CredentialParser.fromJson(credential));
102-
}
96+
builder.addAllCredentials(LoadCredentialsResponseParser.fromJson(json).credentials());
10397
}
10498

10599
return builder.build();
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.iceberg.rest.responses;
20+
21+
import static org.assertj.core.api.Assertions.assertThat;
22+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
23+
24+
import com.fasterxml.jackson.databind.JsonNode;
25+
import org.apache.iceberg.relocated.com.google.common.collect.ImmutableMap;
26+
import org.apache.iceberg.rest.credentials.ImmutableCredential;
27+
import org.junit.jupiter.api.Test;
28+
29+
public class TestLoadCredentialsResponseParser {
30+
@Test
31+
public void nullCheck() {
32+
assertThatThrownBy(() -> LoadCredentialsResponseParser.toJson(null))
33+
.isInstanceOf(IllegalArgumentException.class)
34+
.hasMessage("Invalid load credentials response: null");
35+
36+
assertThatThrownBy(() -> LoadCredentialsResponseParser.fromJson((JsonNode) null))
37+
.isInstanceOf(IllegalArgumentException.class)
38+
.hasMessage("Cannot parse load credentials response from null object");
39+
}
40+
41+
@Test
42+
public void missingFields() {
43+
assertThatThrownBy(() -> LoadCredentialsResponseParser.fromJson("{}"))
44+
.isInstanceOf(IllegalArgumentException.class)
45+
.hasMessage("Cannot parse missing field: storage-credentials");
46+
47+
assertThatThrownBy(() -> LoadCredentialsResponseParser.fromJson("{\"x\": \"val\"}"))
48+
.isInstanceOf(IllegalArgumentException.class)
49+
.hasMessage("Cannot parse missing field: storage-credentials");
50+
}
51+
52+
@Test
53+
public void roundTripSerde() {
54+
LoadCredentialsResponse response =
55+
ImmutableLoadCredentialsResponse.builder()
56+
.addCredentials(
57+
ImmutableCredential.builder()
58+
.prefix("s3://custom-uri")
59+
.config(
60+
ImmutableMap.of(
61+
"s3.access-key-id",
62+
"keyId",
63+
"s3.secret-access-key",
64+
"accessKey",
65+
"s3.session-token",
66+
"sessionToken"))
67+
.build())
68+
.addCredentials(
69+
ImmutableCredential.builder()
70+
.prefix("gs://custom-uri")
71+
.config(
72+
ImmutableMap.of(
73+
"gcs.oauth2.token", "gcsToken1", "gcs.oauth2.token-expires-at", "1000"))
74+
.build())
75+
.addCredentials(
76+
ImmutableCredential.builder()
77+
.prefix("gs")
78+
.config(
79+
ImmutableMap.of(
80+
"gcs.oauth2.token", "gcsToken2", "gcs.oauth2.token-expires-at", "2000"))
81+
.build())
82+
.build();
83+
84+
String expectedJson =
85+
"{\n"
86+
+ " \"storage-credentials\" : [ {\n"
87+
+ " \"prefix\" : \"s3://custom-uri\",\n"
88+
+ " \"config\" : {\n"
89+
+ " \"s3.access-key-id\" : \"keyId\",\n"
90+
+ " \"s3.secret-access-key\" : \"accessKey\",\n"
91+
+ " \"s3.session-token\" : \"sessionToken\"\n"
92+
+ " }\n"
93+
+ " }, {\n"
94+
+ " \"prefix\" : \"gs://custom-uri\",\n"
95+
+ " \"config\" : {\n"
96+
+ " \"gcs.oauth2.token\" : \"gcsToken1\",\n"
97+
+ " \"gcs.oauth2.token-expires-at\" : \"1000\"\n"
98+
+ " }\n"
99+
+ " }, {\n"
100+
+ " \"prefix\" : \"gs\",\n"
101+
+ " \"config\" : {\n"
102+
+ " \"gcs.oauth2.token\" : \"gcsToken2\",\n"
103+
+ " \"gcs.oauth2.token-expires-at\" : \"2000\"\n"
104+
+ " }\n"
105+
+ " } ]\n"
106+
+ "}";
107+
108+
String json = LoadCredentialsResponseParser.toJson(response, true);
109+
assertThat(json).isEqualTo(expectedJson);
110+
assertThat(LoadCredentialsResponseParser.fromJson(json)).isEqualTo(response);
111+
}
112+
}

0 commit comments

Comments
 (0)