model: eliminate double-mapping of generic data in response classes

Explicitly declare mapping of the "data" field in response classes.
Therefore, the JSON setter setData() is no longer used. SecretResponse
is split into subclasses for secret with and without metadata.
This commit is contained in:
Stefan Kalscheuer 2021-10-25 20:40:05 +02:00
parent 021421a54c
commit 4f3cb4b330
Signed by: stefan
GPG Key ID: 3887EC2A53B55430
26 changed files with 338 additions and 412 deletions

View File

@ -8,6 +8,7 @@
* Add support for `wrap_info` in data response models * Add support for `wrap_info` in data response models
* Dependency updates * Dependency updates
* model and response classes implement `Serializable` (#57) * model and response classes implement `Serializable` (#57)
* split `SercretResponse` into `PlainSecretResponse` and `MetaSecretResponse` subclasses (common API unchanged)
### Test ### Test
* Tested against Vault 1.10.0 * Tested against Vault 1.10.0

View File

@ -411,7 +411,7 @@ public class HTTPVaultConnector implements VaultConnector {
public final SecretResponse read(final String key) throws VaultConnectorException { public final SecretResponse read(final String key) throws VaultConnectorException {
requireAuth(); requireAuth();
/* Issue request and parse secret response */ /* Issue request and parse secret response */
return request.get(key, emptyMap(), token, SecretResponse.class); return request.get(key, emptyMap(), token, PlainSecretResponse.class);
} }
@Override @Override
@ -423,7 +423,7 @@ public class HTTPVaultConnector implements VaultConnector {
args.put("version", version.toString()); args.put("version", version.toString());
} }
return request.get(mount + PATH_DATA + key, args, token, SecretResponse.class); return request.get(mount + PATH_DATA + key, args, token, MetaSecretResponse.class);
} }
@Override @Override

View File

@ -17,13 +17,9 @@
package de.stklcode.jvault.connector.model.response; package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.AppRole; import de.stklcode.jvault.connector.model.AppRole;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
/** /**
@ -34,27 +30,11 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class AppRoleResponse extends VaultDataResponse { public final class AppRoleResponse extends VaultDataResponse {
private static final long serialVersionUID = -7817890935652200399L; private static final long serialVersionUID = -6536422219633829177L;
@JsonProperty("data")
private AppRole role; private AppRole role;
@Override
public void setData(final Map<String, Object> data) throws InvalidResponseException {
var mapper = new ObjectMapper();
try {
/* null empty strings on list objects */
Map<String, Object> filteredData = new HashMap<>(data.size(), 1);
data.forEach((k, v) -> {
if (!(v instanceof String && ((String) v).isEmpty())) {
filteredData.put(k, v);
}
});
this.role = mapper.readValue(mapper.writeValueAsString(filteredData), AppRole.class);
} catch (IOException e) {
throw new InvalidResponseException("Failed deserializing response", e);
}
}
/** /**
* @return The role * @return The role
*/ */

View File

@ -17,13 +17,9 @@
package de.stklcode.jvault.connector.model.response; package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.AppRoleSecret; import de.stklcode.jvault.connector.model.AppRoleSecret;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
/** /**
@ -34,27 +30,11 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class AppRoleSecretResponse extends VaultDataResponse { public final class AppRoleSecretResponse extends VaultDataResponse {
private static final long serialVersionUID = 7511563325431032667L; private static final long serialVersionUID = -2484103304072370585L;
@JsonProperty("data")
private AppRoleSecret secret; private AppRoleSecret secret;
@Override
public void setData(final Map<String, Object> data) throws InvalidResponseException {
var mapper = new ObjectMapper();
try {
/* null empty strings on list objects */
Map<String, Object> filteredData = new HashMap<>(data.size(), 1);
data.forEach((k, v) -> {
if (!(v instanceof String && ((String) v).isEmpty())) {
filteredData.put(k, v);
}
});
this.secret = mapper.readValue(mapper.writeValueAsString(filteredData), AppRoleSecret.class);
} catch (IOException e) {
throw new InvalidResponseException("Failed deserializing response", e);
}
}
/** /**
* @return The secret * @return The secret
*/ */

View File

@ -17,11 +17,9 @@
package de.stklcode.jvault.connector.model.response; package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.response.embedded.AuthMethod; import de.stklcode.jvault.connector.model.response.embedded.AuthMethod;
import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@ -34,8 +32,9 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class AuthMethodsResponse extends VaultDataResponse { public final class AuthMethodsResponse extends VaultDataResponse {
private static final long serialVersionUID = 5521702564857621352L; private static final long serialVersionUID = -1802724129533405375L;
@JsonProperty("data")
private Map<String, AuthMethod> supportedMethods; private Map<String, AuthMethod> supportedMethods;
/** /**
@ -45,19 +44,6 @@ public final class AuthMethodsResponse extends VaultDataResponse {
this.supportedMethods = new HashMap<>(); this.supportedMethods = new HashMap<>();
} }
@Override
public void setData(final Map<String, Object> data) throws InvalidResponseException {
var mapper = new ObjectMapper();
for (Map.Entry<String, Object> entry : data.entrySet()) {
try {
this.supportedMethods.put(entry.getKey(),
mapper.readValue(mapper.writeValueAsString(entry.getValue()), AuthMethod.class));
} catch (IOException e) {
throw new InvalidResponseException();
}
}
}
/** /**
* @return Supported authentication methods * @return Supported authentication methods
*/ */

View File

@ -18,12 +18,8 @@ package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.response.embedded.AuthData; import de.stklcode.jvault.connector.model.response.embedded.AuthData;
import java.io.IOException;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
/** /**
@ -34,39 +30,10 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class AuthResponse extends VaultDataResponse { public final class AuthResponse extends VaultDataResponse {
private static final long serialVersionUID = -6728387061352164781L; private static final long serialVersionUID = 1628851361067456715L;
private Map<String, Object> data;
private AuthData auth;
/**
* Set authentication data. The input will be mapped to the {@link AuthData} model.
*
* @param auth Raw authentication data
* @throws InvalidResponseException on mapping errors
*/
@JsonProperty("auth") @JsonProperty("auth")
public void setAuth(final Map<String, Object> auth) throws InvalidResponseException { private AuthData auth;
var mapper = new ObjectMapper();
try {
this.auth = mapper.readValue(mapper.writeValueAsString(auth), AuthData.class);
} catch (IOException e) {
throw new InvalidResponseException("Failed deserializing response", e);
}
}
@Override
public void setData(final Map<String, Object> data) {
this.data = data;
}
/**
* @return Raw data
*/
public Map<String, Object> getData() {
return data;
}
/** /**
* @return Authentication data * @return Authentication data
@ -83,11 +50,11 @@ public final class AuthResponse extends VaultDataResponse {
return false; return false;
} }
AuthResponse that = (AuthResponse) o; AuthResponse that = (AuthResponse) o;
return Objects.equals(data, that.data) && Objects.equals(auth, that.auth); return Objects.equals(auth, that.auth);
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(super.hashCode(), data, auth); return Objects.hash(super.hashCode(), auth);
} }
} }

View File

@ -25,7 +25,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
* @since 0.5.0 * @since 0.5.0
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class CredentialsResponse extends SecretResponse { public final class CredentialsResponse extends PlainSecretResponse {
private static final long serialVersionUID = -1439692963299045425L; private static final long serialVersionUID = -1439692963299045425L;
/** /**

View File

@ -0,0 +1,75 @@
/*
* Copyright 2016-2021 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.model.response.embedded.SecretWrapper;
import de.stklcode.jvault.connector.model.response.embedded.VersionMetadata;
import java.io.Serializable;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
/**
* Vault response for secret responses with metadata.
*
* @author Stefan Kalscheuer
* @since 1.1 abstract
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class MetaSecretResponse extends SecretResponse {
private static final long serialVersionUID = -1076542846391240162L;
@JsonProperty("data")
private SecretWrapper secret;
@Override
public final Map<String, Serializable> getData() {
if (secret != null) {
return secret.getData();
} else {
return Collections.emptyMap();
}
}
@Override
public final VersionMetadata getMetadata() {
if (secret != null) {
return secret.getMetadata();
} else {
return null;
}
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass() || !super.equals(o)) {
return false;
}
MetaSecretResponse that = (MetaSecretResponse) o;
return Objects.equals(secret, that.secret);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), secret);
}
}

View File

@ -17,14 +17,12 @@
package de.stklcode.jvault.connector.model.response; package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.response.embedded.SecretMetadata; import de.stklcode.jvault.connector.model.response.embedded.SecretMetadata;
import java.io.IOException;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
/** /**
* Vault response for secret metadata (KV v2). * Vault response for secret metadata (KV v2).
* *
@ -33,20 +31,11 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class MetadataResponse extends VaultDataResponse { public class MetadataResponse extends VaultDataResponse {
private static final long serialVersionUID = 3407081728744500975L; private static final long serialVersionUID = -3679762333630984679L;
@JsonProperty("data")
private SecretMetadata metadata; private SecretMetadata metadata;
@Override
public final void setData(final Map<String, Object> data) throws InvalidResponseException {
var mapper = new ObjectMapper();
try {
this.metadata = mapper.readValue(mapper.writeValueAsString(data), SecretMetadata.class);
} catch (IOException e) {
throw new InvalidResponseException("Failed deserializing response", e);
}
}
/** /**
* Get the actual metadata. * Get the actual metadata.
* *

View File

@ -0,0 +1,66 @@
/*
* Copyright 2016-2021 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.model.response.embedded.VersionMetadata;
import java.io.Serializable;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
/**
* Vault response for plain secret responses.
*
* @author Stefan Kalscheuer
* @since 1.1 abstract
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class PlainSecretResponse extends SecretResponse {
private static final long serialVersionUID = 3010138542437913023L;
@JsonProperty("data")
private Map<String, Serializable> data;
@Override
public final Map<String, Serializable> getData() {
return Objects.requireNonNullElseGet(data, Collections::emptyMap);
}
@Override
public final VersionMetadata getMetadata() {
return null;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass() || !super.equals(o)) {
return false;
}
PlainSecretResponse that = (PlainSecretResponse) o;
return Objects.equals(data, that.data);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), data);
}
}

View File

@ -17,7 +17,9 @@
package de.stklcode.jvault.connector.model.response; package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@ -29,19 +31,15 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class RawDataResponse extends VaultDataResponse { public final class RawDataResponse extends VaultDataResponse {
private static final long serialVersionUID = -5494734676257709074L; private static final long serialVersionUID = -319727427792124071L;
private Map<String, Object> data; @JsonProperty("data")
private Map<String, Serializable> data;
@Override
public void setData(final Map<String, Object> data) {
this.data = data;
}
/** /**
* @return Raw data {@link Map} * @return Raw data {@link Map}
*/ */
public Map<String, Object> getData() { public Map<String, Serializable> getData() {
return data; return data;
} }

View File

@ -18,10 +18,10 @@ package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.exception.InvalidResponseException; import de.stklcode.jvault.connector.model.response.embedded.SecretListWrapper;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
/** /**
@ -32,29 +32,19 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class SecretListResponse extends VaultDataResponse { public final class SecretListResponse extends VaultDataResponse {
private static final long serialVersionUID = -5279146643326713976L;
private List<String> keys; private static final long serialVersionUID = 8597121175002967213L;
/**
* Set data. Extracts list of keys from raw response data.
*
* @param data Raw data
*/
@JsonProperty("data") @JsonProperty("data")
public void setData(final Map<String, Object> data) throws InvalidResponseException { private SecretListWrapper data;
try {
this.keys = (List<String>) data.get("keys");
} catch (ClassCastException e) {
throw new InvalidResponseException("Keys could not be parsed from data.", e);
}
}
/** /**
* @return List of secret keys * @return List of secret keys
*/ */
public List<String> getKeys() { public List<String> getKeys() {
return keys; if (data == null) {
return Collections.emptyList();
}
return Objects.requireNonNullElseGet(data.getKeys(), Collections::emptyList);
} }
@Override @Override
@ -65,11 +55,11 @@ public final class SecretListResponse extends VaultDataResponse {
return false; return false;
} }
SecretListResponse that = (SecretListResponse) o; SecretListResponse that = (SecretListResponse) o;
return Objects.equals(keys, that.keys); return Objects.equals(data, that.data);
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(super.hashCode(), keys); return Objects.hash(super.hashCode(), data);
} }
} }

View File

@ -22,57 +22,28 @@ import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.response.embedded.VersionMetadata; import de.stklcode.jvault.connector.model.response.embedded.VersionMetadata;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.io.Serializable;
import java.util.Map; import java.util.Map;
import java.util.Objects;
/** /**
* Vault response for secret request. * Vault response for secret request.
* *
* @author Stefan Kalscheuer * @author Stefan Kalscheuer
* @since 0.1 * @since 0.1
* @since 1.1 abstract
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class SecretResponse extends VaultDataResponse { public abstract class SecretResponse extends VaultDataResponse {
private static final long serialVersionUID = -8215178956885015265L; private static final long serialVersionUID = 5198088815871692951L;
private static final String KEY_DATA = "data";
private static final String KEY_METADATA = "metadata";
private Map<String, Object> data;
private VersionMetadata metadata;
@Override
public final void setData(final Map<String, Object> data) throws InvalidResponseException {
if (data.size() == 2
&& data.containsKey(KEY_DATA) && data.get(KEY_DATA) instanceof Map
&& data.containsKey(KEY_METADATA) && data.get(KEY_METADATA) instanceof Map) {
var mapper = new ObjectMapper();
try {
// This is apparently a KV v2 value.
this.data = (Map<String, Object>) data.get(KEY_DATA);
this.metadata = mapper.readValue(mapper.writeValueAsString(data.get(KEY_METADATA)), VersionMetadata.class);
} catch (ClassCastException | IOException e) {
throw new InvalidResponseException("Failed deserializing response", e);
}
} else {
// For KV v1 without metadata just store the data map.
this.data = data;
}
}
/** /**
* Get complete data object. * Get complete data object.
* *
* @return data map * @return data map
* @since 0.4.0 * @since 0.4.0
* @since 1.1 Serializable map value.
*/ */
public final Map<String, Object> getData() { public abstract Map<String, Serializable> getData();
if (data == null) {
return Collections.emptyMap();
}
return data;
}
/** /**
* Get secret metadata. This is only available for KV v2 secrets. * Get secret metadata. This is only available for KV v2 secrets.
@ -80,9 +51,7 @@ public class SecretResponse extends VaultDataResponse {
* @return Metadata of the secret. * @return Metadata of the secret.
* @since 0.8 * @since 0.8
*/ */
public final VersionMetadata getMetadata() { public abstract VersionMetadata getMetadata();
return metadata;
}
/** /**
* Get a single value for given key. * Get a single value for given key.
@ -92,9 +61,6 @@ public class SecretResponse extends VaultDataResponse {
* @since 0.4.0 * @since 0.4.0
*/ */
public final Object get(final String key) { public final Object get(final String key) {
if (data == null) {
return null;
}
return getData().get(key); return getData().get(key);
} }
@ -103,12 +69,12 @@ public class SecretResponse extends VaultDataResponse {
* *
* @param key the key * @param key the key
* @param type Class to parse response * @param type Class to parse response
* @param <T> Class to parse response * @param <C> Class to parse response
* @return Parsed object or {@code null} if absent * @return Parsed object or {@code null} if absent
* @throws InvalidResponseException on parsing error * @throws InvalidResponseException on parsing error
* @since 0.4.0 * @since 0.4.0
*/ */
public final <T> T get(final String key, final Class<T> type) throws InvalidResponseException { public final <C> C get(final String key, final Class<C> type) throws InvalidResponseException {
try { try {
Object rawValue = get(key); Object rawValue = get(key);
if (rawValue == null) { if (rawValue == null) {
@ -119,20 +85,4 @@ public class SecretResponse extends VaultDataResponse {
throw new InvalidResponseException("Unable to parse response payload: " + e.getMessage()); throw new InvalidResponseException("Unable to parse response payload: " + e.getMessage());
} }
} }
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass() || !super.equals(o)) {
return false;
}
SecretResponse that = (SecretResponse) o;
return Objects.equals(data, that.data) && Objects.equals(metadata, that.metadata);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), data, metadata);
}
} }

View File

@ -17,12 +17,9 @@
package de.stklcode.jvault.connector.model.response; package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.response.embedded.VersionMetadata; import de.stklcode.jvault.connector.model.response.embedded.VersionMetadata;
import java.io.IOException;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
/** /**
@ -33,20 +30,11 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class SecretVersionResponse extends VaultDataResponse { public class SecretVersionResponse extends VaultDataResponse {
private static final long serialVersionUID = -6681638207727120184L; private static final long serialVersionUID = 2748635005258576174L;
@JsonProperty("data")
private VersionMetadata metadata; private VersionMetadata metadata;
@Override
public final void setData(final Map<String, Object> data) throws InvalidResponseException {
var mapper = new ObjectMapper();
try {
this.metadata = mapper.readValue(mapper.writeValueAsString(data), VersionMetadata.class);
} catch (IOException e) {
throw new InvalidResponseException("Failed deserializing response", e);
}
}
/** /**
* Get the actual metadata. * Get the actual metadata.
* *

View File

@ -18,12 +18,8 @@ package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.response.embedded.TokenData; import de.stklcode.jvault.connector.model.response.embedded.TokenData;
import java.io.IOException;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
/** /**
@ -34,29 +30,14 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class TokenResponse extends VaultDataResponse { public final class TokenResponse extends VaultDataResponse {
private static final long serialVersionUID = 2248288114849229479L; private static final long serialVersionUID = -4053126653764241197L;
@JsonProperty("data")
private TokenData data; private TokenData data;
@JsonProperty("auth") @JsonProperty("auth")
private Boolean auth; private Boolean auth;
/**
* Set data. Parses response data map to {@link TokenData}.
*
* @param data Raw response data
* @throws InvalidResponseException on parsing errors
*/
@Override
public void setData(final Map<String, Object> data) throws InvalidResponseException {
var mapper = new ObjectMapper();
try {
this.data = mapper.readValue(mapper.writeValueAsString(data), TokenData.class);
} catch (IOException e) {
throw new InvalidResponseException("Failed deserializing response", e);
}
}
/** /**
* @return Token data * @return Token data
*/ */
@ -64,6 +45,13 @@ public final class TokenResponse extends VaultDataResponse {
return data; return data;
} }
/**
* @return Auth data
*/
public Boolean getAuth() {
return auth;
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) { if (this == o) {

View File

@ -17,13 +17,10 @@
package de.stklcode.jvault.connector.model.response; package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.TokenRole; import de.stklcode.jvault.connector.model.TokenRole;
import de.stklcode.jvault.connector.model.response.embedded.TokenData; import de.stklcode.jvault.connector.model.response.embedded.TokenData;
import java.io.IOException;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
/** /**
@ -34,26 +31,11 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class TokenRoleResponse extends VaultDataResponse { public final class TokenRoleResponse extends VaultDataResponse {
private static final long serialVersionUID = -6622498881812517596L; private static final long serialVersionUID = 5265363857731948626L;
@JsonProperty("data")
private TokenRole data; private TokenRole data;
/**
* Set data. Parses response data map to {@link TokenRole}.
*
* @param data Raw response data
* @throws InvalidResponseException on parsing errors
*/
@Override
public void setData(final Map<String, Object> data) throws InvalidResponseException {
var mapper = new ObjectMapper();
try {
this.data = mapper.readValue(mapper.writeValueAsString(data), TokenRole.class);
} catch (IOException e) {
throw new InvalidResponseException("Failed deserializing response", e);
}
}
/** /**
* @return TokenRole data * @return TokenRole data
*/ */

View File

@ -17,11 +17,9 @@
package de.stklcode.jvault.connector.model.response; package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.response.embedded.WrapInfo; import de.stklcode.jvault.connector.model.response.embedded.WrapInfo;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
/** /**
@ -48,15 +46,6 @@ public abstract class VaultDataResponse implements VaultResponse {
@JsonProperty("wrap_info") @JsonProperty("wrap_info")
private WrapInfo wrapInfo; private WrapInfo wrapInfo;
/**
* Set data. To be implemented in the specific subclasses, as data can be of arbitrary structure.
*
* @param data Raw response data
* @throws InvalidResponseException on parsing errors
*/
@JsonProperty("data")
public abstract void setData(final Map<String, Object> data) throws InvalidResponseException;
/** /**
* @return Lease ID * @return Lease ID
*/ */

View File

@ -0,0 +1,42 @@
package de.stklcode.jvault.connector.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.util.List;
import java.util.Objects;
/**
* Wrapper object for secret key lists.
*
* @author Stefan Kalscheuer
* @since 1.1
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class SecretListWrapper implements Serializable {
private static final long serialVersionUID = -8777605197063766125L;
@JsonProperty("keys")
private List<String> keys;
public List<String> getKeys() {
return keys;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass()) {
return false;
}
SecretListWrapper that = (SecretListWrapper) o;
return Objects.equals(keys, that.keys);
}
@Override
public int hashCode() {
return Objects.hash(keys);
}
}

View File

@ -0,0 +1,49 @@
package de.stklcode.jvault.connector.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.util.Map;
import java.util.Objects;
/**
* Wrapper object for secret data and metadata.
*
* @author Stefan Kalscheuer
* @since 1.1
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class SecretWrapper implements Serializable {
private static final long serialVersionUID = 8600413181758893378L;
@JsonProperty("data")
private Map<String, Serializable> data;
@JsonProperty("metadata")
private VersionMetadata metadata;
public Map<String, Serializable> getData() {
return data;
}
public VersionMetadata getMetadata() {
return metadata;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass()) {
return false;
}
SecretWrapper that = (SecretWrapper) o;
return Objects.equals(data, that.data) && Objects.equals(metadata, that.metadata);
}
@Override
public int hashCode() {
return Objects.hash(data, metadata);
}
}

View File

@ -17,13 +17,11 @@
package de.stklcode.jvault.connector.model.response; package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.AppRole; import de.stklcode.jvault.connector.model.AppRole;
import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.EqualsVerifier;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.util.List; import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
@ -63,8 +61,6 @@ class AppRoleResponseTest {
" \"lease_id\": \"\"\n" + " \"lease_id\": \"\"\n" +
"}"; "}";
private static final Map<String, Object> INVALID_DATA = Map.of("token_policies", "fancy-policy");
/** /**
* Test getter, setter and get-methods for response data. * Test getter, setter and get-methods for response data.
*/ */
@ -73,13 +69,6 @@ class AppRoleResponseTest {
// Create empty Object. // Create empty Object.
AppRoleResponse res = new AppRoleResponse(); AppRoleResponse res = new AppRoleResponse();
assertNull(res.getRole(), "Initial data should be empty"); assertNull(res.getRole(), "Initial data should be empty");
// Parsing invalid auth data map should fail.
assertThrows(
InvalidResponseException.class,
() -> res.setData(INVALID_DATA),
"Parsing invalid data succeeded"
);
} }
/** /**

View File

@ -63,8 +63,6 @@ class AuthMethodsResponseTest {
" }\n" + " }\n" +
"}"; "}";
private static final Map<String, Object> INVALID_DATA = Map.of("dummy/", new Dummy());
/** /**
* Test getter, setter and get-methods for response data. * Test getter, setter and get-methods for response data.
*/ */
@ -73,13 +71,6 @@ class AuthMethodsResponseTest {
// Create empty Object. // Create empty Object.
AuthMethodsResponse res = new AuthMethodsResponse(); AuthMethodsResponse res = new AuthMethodsResponse();
assertEquals(Collections.emptyMap(), res.getSupportedMethods(), "Initial method map should be empty"); assertEquals(Collections.emptyMap(), res.getSupportedMethods(), "Initial method map should be empty");
// Parsing invalid data map should fail.
assertThrows(
InvalidResponseException.class,
() -> res.setData(INVALID_DATA),
"Parsing invalid data succeeded"
);
} }
/** /**

View File

@ -17,7 +17,6 @@
package de.stklcode.jvault.connector.model.response; package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.response.embedded.AuthData; import de.stklcode.jvault.connector.model.response.embedded.AuthData;
import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.EqualsVerifier;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -69,8 +68,6 @@ class AuthResponseTest {
" }\n" + " }\n" +
"}"; "}";
private static final Map<String, Object> INVALID_AUTH_DATA = Map.of("policies", "fancy-policy");
/** /**
* Test getter, setter and get-methods for response data. * Test getter, setter and get-methods for response data.
*/ */
@ -78,18 +75,8 @@ class AuthResponseTest {
void getDataRoundtrip() { void getDataRoundtrip() {
// Create empty Object. // Create empty Object.
AuthResponse res = new AuthResponse(); AuthResponse res = new AuthResponse();
assertNull(res.getData(), "Initial data should be empty"); // TODO
// assertNull(res.getData(), "Initial data should be empty");
// Parsing invalid auth data map should fail.
assertThrows(
InvalidResponseException.class,
() -> res.setAuth(INVALID_AUTH_DATA),
"Parsing invalid auth data succeeded"
);
// Data method should be agnostic.
res.setData(INVALID_AUTH_DATA);
assertEquals(INVALID_AUTH_DATA, res.getData(), "Data not passed through");
} }
/** /**

View File

@ -16,14 +16,12 @@
package de.stklcode.jvault.connector.model.response; package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException; import de.stklcode.jvault.connector.exception.InvalidResponseException;
import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.EqualsVerifier;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.util.Map; import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
/** /**
* JUnit Test for {@link CredentialsResponse} model. * JUnit Test for {@link CredentialsResponse} model.
@ -34,10 +32,17 @@ import static org.junit.jupiter.api.Assertions.assertNull;
class CredentialsResponseTest { class CredentialsResponseTest {
private static final String VAL_USER = "testUserName"; private static final String VAL_USER = "testUserName";
private static final String VAL_PASS = "5up3r5ecr3tP455"; private static final String VAL_PASS = "5up3r5ecr3tP455";
private static final Map<String, Object> DATA = Map.of( private static final String JSON = "{\n" +
"username", VAL_USER, " \"request_id\": \"68315073-6658-e3ff-2da7-67939fb91bbd\",\n" +
"password", VAL_PASS " \"lease_id\": \"\",\n" +
); " \"lease_duration\": 2764800,\n" +
" \"renewable\": false,\n" +
" \"data\": {\n" +
" \"username\": \"" + VAL_USER + "\",\n" +
" \"password\": \"" + VAL_PASS + "\"\n" +
" },\n" +
" \"warnings\": null\n" +
"}";
/** /**
* Test getter, setter and get-methods for response data. * Test getter, setter and get-methods for response data.
@ -51,8 +56,10 @@ class CredentialsResponseTest {
assertNull(res.getUsername(), "Username not present in data map should not return anything"); assertNull(res.getUsername(), "Username not present in data map should not return anything");
assertNull(res.getPassword(), "Password not present in data map should not return anything"); assertNull(res.getPassword(), "Password not present in data map should not return anything");
// Fill data map. res = assertDoesNotThrow(
res.setData(DATA); () -> new ObjectMapper().readValue(JSON, CredentialsResponse.class),
"Deserialization of CredentialsResponse failed"
);
assertEquals(VAL_USER, res.getUsername(), "Incorrect username"); assertEquals(VAL_USER, res.getUsername(), "Incorrect username");
assertEquals(VAL_PASS, res.getPassword(), "Incorrect password"); assertEquals(VAL_PASS, res.getPassword(), "Incorrect password");
} }

View File

@ -16,16 +16,14 @@
package de.stklcode.jvault.connector.model.response; package de.stklcode.jvault.connector.model.response;
import de.stklcode.jvault.connector.exception.InvalidResponseException; import com.fasterxml.jackson.databind.ObjectMapper;
import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.EqualsVerifier;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
/** /**
* JUnit Test for {@link SecretListResponse} model. * JUnit Test for {@link SecretListResponse} model.
@ -36,33 +34,30 @@ import static org.junit.jupiter.api.Assertions.*;
class SecretListResponseTest { class SecretListResponseTest {
private static final String KEY1 = "key1"; private static final String KEY1 = "key1";
private static final String KEY2 = "key-2"; private static final String KEY2 = "key-2";
private static final List<String> KEYS = Arrays.asList(KEY1, KEY2); private static final String JSON = "{\n" +
private static final Map<String, Object> DATA = Map.of("keys", KEYS); " \"auth\": null,\n" +
" \"data\": {\n" +
" \"keys\": [" +
" \"" + KEY1 + "\",\n" +
" \"" + KEY2 + "\"\n" +
" ]\n" +
" },\n" +
" \"lease_duration\": 2764800,\n" +
" \"lease_id\": \"\",\n" +
" \"renewable\": false\n" +
"}";
/** /**
* Test getter, setter and get-methods for response data. * Test JSON deserialization and key getter.
*
* @throws InvalidResponseException Should not occur
*/ */
@Test @Test
void getKeysTest() throws InvalidResponseException { void getKeysTest() {
// Create empty Object. SecretListResponse res = assertDoesNotThrow(
SecretListResponse res = new SecretListResponse(); () -> new ObjectMapper().readValue(JSON, SecretListResponse.class),
assertNull(res.getKeys(), "Keys should be null without initialization"); "SecretListResponse deserialization failed"
// Provoke internal ClassCastException.
Map<String, Object> invalidData = Map.of("keys", "some string");
assertThrows(
InvalidResponseException.class,
() -> res.setData(invalidData),
"Setting incorrect class succeeded"
); );
// Fill correct data. assertEquals(List.of(KEY1, KEY2), res.getKeys(), "Unexpected secret keys");
res.setData(DATA);
assertNotNull(res.getKeys(), "Keys should be filled here");
assertEquals(2, res.getKeys().size(), "Unexpected number of keys");
assertTrue(res.getKeys().containsAll(Set.of(KEY1, KEY2)), "Unexpected keys");
} }
@Test @Test

View File

@ -17,13 +17,10 @@
package de.stklcode.jvault.connector.model.response; package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.EqualsVerifier;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
@ -34,20 +31,6 @@ import static org.junit.jupiter.api.Assertions.*;
* @since 0.6.2 * @since 0.6.2
*/ */
class SecretResponseTest { class SecretResponseTest {
private static final String KEY_UNKNOWN = "unknown";
private static final String KEY_STRING = "test1";
private static final String VAL_STRING = "testvalue";
private static final String KEY_INTEGER = "test2";
private static final Integer VAL_INTEGER = 42;
private static final String KEY_LIST = "list";
private static final String VAL_LIST = "[\"first\",\"second\"]";
private static final Map<String, Object> DATA = Map.of(
KEY_STRING, VAL_STRING,
KEY_INTEGER, VAL_INTEGER,
KEY_LIST, VAL_LIST
);
private static final String SECRET_REQUEST_ID = "68315073-6658-e3ff-2da7-67939fb91bbd"; private static final String SECRET_REQUEST_ID = "68315073-6658-e3ff-2da7-67939fb91bbd";
private static final String SECRET_LEASE_ID = ""; private static final String SECRET_LEASE_ID = "";
private static final Integer SECRET_LEASE_DURATION = 2764800; private static final Integer SECRET_LEASE_DURATION = 2764800;
@ -109,58 +92,20 @@ class SecretResponseTest {
" \"warnings\": " + SECRET_WARNINGS + "\n" + " \"warnings\": " + SECRET_WARNINGS + "\n" +
"}"; "}";
/**
* Test getter, setter and get-methods for response data.
*
* @throws InvalidResponseException Should not occur
*/
@Test
@SuppressWarnings("unchecked")
void getDataRoundtrip() throws InvalidResponseException {
// Create empty Object.
SecretResponse res = new SecretResponse();
assertNotNull(res.getData(), "Initial data should be Map");
assertTrue(res.getData().isEmpty(), "Initial data should be empty");
assertNull(res.get(KEY_STRING), "Getter should return NULL on empty data map");
// Fill data map.
res.setData(DATA);
assertEquals(DATA, res.getData(), "Data setter/getter not transparent");
assertEquals(DATA.size(), res.getData().keySet().size(), "Data size modified");
assertTrue(res.getData().keySet().containsAll(Set.of(KEY_STRING, KEY_INTEGER, KEY_LIST)), "Data keys not passed correctly");
assertEquals(VAL_STRING, res.get(KEY_STRING), "Data values not passed correctly");
assertEquals(VAL_INTEGER, res.get(KEY_INTEGER), "Data values not passed correctly");
assertNull(res.get(KEY_UNKNOWN), "Non-Null returned on unknown key");
// Try explicit JSON conversion.
final List<?> list = res.get(KEY_LIST, List.class);
assertNotNull(list, "JSON parsing of list failed");
assertEquals(2, list.size(), "JSON parsing of list returned incorrect size");
assertTrue(list.containsAll(List.of("first", "second")), "JSON parsing of list returned incorrect elements");
assertNull(res.get(KEY_UNKNOWN, Object.class), "Non-Null returned on unknown key");
// Requesting invalid class should result in Exception.
assertThrows(
InvalidResponseException.class,
() -> res.get(KEY_LIST, Double.class),
"JSON parsing to incorrect type succeeded"
);
}
/** /**
* Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation). * Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation).
*/ */
@Test @Test
void jsonRoundtrip() { void jsonRoundtrip() {
SecretResponse res = assertDoesNotThrow( SecretResponse res = assertDoesNotThrow(
() -> new ObjectMapper().readValue(SECRET_JSON, SecretResponse.class), () -> new ObjectMapper().readValue(SECRET_JSON, PlainSecretResponse.class),
"SecretResponse deserialization failed" "SecretResponse deserialization failed"
); );
assertSecretData(res); assertSecretData(res);
// KV v2 secret. // KV v2 secret.
res = assertDoesNotThrow( res = assertDoesNotThrow(
() -> new ObjectMapper().readValue(SECRET_JSON_V2, SecretResponse.class), () -> new ObjectMapper().readValue(SECRET_JSON_V2, MetaSecretResponse.class),
"SecretResponse deserialization failed" "SecretResponse deserialization failed"
); );
assertSecretData(res); assertSecretData(res);
@ -174,7 +119,7 @@ class SecretResponseTest {
// Deleted KV v2 secret. // Deleted KV v2 secret.
res = assertDoesNotThrow( res = assertDoesNotThrow(
() -> new ObjectMapper().readValue(SECRET_JSON_V2_2, SecretResponse.class), () -> new ObjectMapper().readValue(SECRET_JSON_V2_2, MetaSecretResponse.class),
"SecretResponse deserialization failed" "SecretResponse deserialization failed"
); );
assertSecretData(res); assertSecretData(res);
@ -190,6 +135,8 @@ class SecretResponseTest {
@Test @Test
void testEqualsHashcode() { void testEqualsHashcode() {
EqualsVerifier.simple().forClass(SecretResponse.class).verify(); EqualsVerifier.simple().forClass(SecretResponse.class).verify();
EqualsVerifier.simple().forClass(PlainSecretResponse.class).verify();
EqualsVerifier.simple().forClass(MetaSecretResponse.class).verify();
} }
private void assertSecretData(SecretResponse res) { private void assertSecretData(SecretResponse res) {

View File

@ -17,7 +17,6 @@
package de.stklcode.jvault.connector.model.response; package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.response.embedded.TokenData; import de.stklcode.jvault.connector.model.response.embedded.TokenData;
import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.EqualsVerifier;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -89,8 +88,6 @@ class TokenResponseTest {
" \"auth\": null\n" + " \"auth\": null\n" +
"}"; "}";
private static final Map<String, Object> INVALID_TOKEN_DATA = Map.of("num_uses", "fourtytwo");
/** /**
* Test getter, setter and get-methods for response data. * Test getter, setter and get-methods for response data.
*/ */
@ -99,13 +96,6 @@ class TokenResponseTest {
// Create empty Object. // Create empty Object.
TokenResponse res = new TokenResponse(); TokenResponse res = new TokenResponse();
assertNull(res.getData(), "Initial data should be empty"); assertNull(res.getData(), "Initial data should be empty");
// Parsing invalid data map should fail.
assertThrows(
InvalidResponseException.class,
() -> res.setData(INVALID_TOKEN_DATA),
"Parsing invalid token data succeeded"
);
} }
/** /**