diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml index d330260..65ddbaa 100644 --- a/dependency-reduced-pom.xml +++ b/dependency-reduced-pom.xml @@ -3,7 +3,7 @@ 4.0.0 io.mymetaverse.sdk java-sdk - 2.4.0 + 2.5.2 package install ${project.name}-${project.version} diff --git a/pom.xml b/pom.xml index 498cf4b..199b4c2 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,8 @@ io.mymetaverse.sdk java-sdk - 2.4.0 + 2.5.2 + 8 diff --git a/src/main/java/io/mymetavese/metaapi/api/RestAction.java b/src/main/java/io/mymetavese/metaapi/api/RestAction.java index 6580a03..77acd5c 100644 --- a/src/main/java/io/mymetavese/metaapi/api/RestAction.java +++ b/src/main/java/io/mymetavese/metaapi/api/RestAction.java @@ -3,7 +3,9 @@ import io.mymetavese.metaapi.MetaAPIImpl; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.Consumer; public interface RestAction { @@ -23,7 +25,6 @@ public interface RestAction { /** * Submits a request for execution in asynchronous logic. * - * * @param success The success callback from the request. * @param failure The callback error from the request. */ @@ -32,7 +33,6 @@ public interface RestAction { /** * Submits a request for execution in asynchronous logic. * - * * @param success The success callback from the request. */ default void queue(Consumer success) { @@ -46,6 +46,13 @@ default void queue(Consumer success) { */ T complete(); + /** + * Submits a Request in synchronous logic with a Timeout + * + * @return The response of the Request. + */ + T complete(long timeout, TimeUnit timeUnit) throws ExecutionException, InterruptedException, TimeoutException; + /** * Submits a request for execution after some time delay. * @param time The time diff --git a/src/main/java/io/mymetavese/metaapi/api/actions/GetLinkingLink.java b/src/main/java/io/mymetavese/metaapi/api/actions/GetLinkingLinkAction.java similarity index 67% rename from src/main/java/io/mymetavese/metaapi/api/actions/GetLinkingLink.java rename to src/main/java/io/mymetavese/metaapi/api/actions/GetLinkingLinkAction.java index 4756f98..92ca2ae 100644 --- a/src/main/java/io/mymetavese/metaapi/api/actions/GetLinkingLink.java +++ b/src/main/java/io/mymetavese/metaapi/api/actions/GetLinkingLinkAction.java @@ -3,5 +3,5 @@ import io.mymetavese.metaapi.api.RestAction; import io.mymetavese.metaapi.api.entities.LinkingLink; -public interface GetLinkingLink extends RestAction { +public interface GetLinkingLinkAction extends RestAction { } diff --git a/src/main/java/io/mymetavese/metaapi/api/entities/v2/GameEntity.java b/src/main/java/io/mymetavese/metaapi/api/entities/v2/GameEntity.java index 0118f4c..376dd5f 100644 --- a/src/main/java/io/mymetavese/metaapi/api/entities/v2/GameEntity.java +++ b/src/main/java/io/mymetavese/metaapi/api/entities/v2/GameEntity.java @@ -59,7 +59,7 @@ public interface GameEntity extends ApiEntity { * * @return An action that represents the linking link. */ - GetLinkingLink getLinkingLink(); + GetLinkingLinkAction getLinkingLink(); /** * Get the currently active metacitizen for this game entity. diff --git a/src/main/java/io/mymetavese/metaapi/requests/RequestGenerator.java b/src/main/java/io/mymetavese/metaapi/requests/RequestGenerator.java index 4edf7d4..f12ae14 100644 --- a/src/main/java/io/mymetavese/metaapi/requests/RequestGenerator.java +++ b/src/main/java/io/mymetavese/metaapi/requests/RequestGenerator.java @@ -8,7 +8,6 @@ import okhttp3.internal.http.HttpMethod; import java.io.IOException; -import java.util.Objects; public class RequestGenerator { @@ -29,6 +28,7 @@ public void asyncRequest(Request request) { } public void request(Request request) { + String route = request.getCompiledRoute(); okhttp3.Request.Builder builder = new okhttp3.Request.Builder(); @@ -52,28 +52,29 @@ else if(bodyObject != null) if (request.getHeaders() != null && !request.getHeaders().isEmpty()) request.getHeaders().forEach(builder::addHeader); - // Maybe should add attempts here. - try(Response response = httpClient.newCall(builder.build()).execute()) { + try (Response response = httpClient.newCall(builder.build()).execute()) { + if (response.code() >= 500) { - throw new IOException("Internal server error: " + Objects.requireNonNull(response.body()).string()); + String responseBodyString = response.peekBody(Long.MAX_VALUE).string(); + request.handleResponse(response); + throw new IOException("Internal server error: " + responseBodyString); } - if(response.code() == 401 && request.getAttempts() < 2) { + // Try to Re-authenticate if the token is invalid and the re-authentication has not been attempted for more + // than 2 times. + if (response.code() == 401 && request.getAttempts() < 2) { + request.addAttempt(); - api.getTokenHandler().reauthenticate(); + api.getTokenHandler().authenticate(); this.request(request); - return; + + } else { + request.handleResponse(response); } - request.handleResponse(response); } catch (IOException ex) { ex.printStackTrace(); - - if(request.getAttempts() < 2) { - request.addAttempt(); - this.request(request); - } } } diff --git a/src/main/java/io/mymetavese/metaapi/requests/RestActionImpl.java b/src/main/java/io/mymetavese/metaapi/requests/RestActionImpl.java index e64ea4c..544ceee 100644 --- a/src/main/java/io/mymetavese/metaapi/requests/RestActionImpl.java +++ b/src/main/java/io/mymetavese/metaapi/requests/RestActionImpl.java @@ -15,6 +15,9 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.Consumer; public abstract class RestActionImpl extends Transformable implements RestAction { @@ -104,5 +107,11 @@ public void queue(Consumer success, Consumer fa public T complete() { return submit().join(); } + + @Override + public T complete(long timeout, TimeUnit timeUnit) throws ExecutionException, InterruptedException, TimeoutException { + return submit().get(timeout, timeUnit); + } + } diff --git a/src/main/java/io/mymetavese/metaapi/requests/actions/GetLinkingLinkActionImpl.java b/src/main/java/io/mymetavese/metaapi/requests/actions/GetLinkingLinkActionImpl.java index b606df5..54ecaf1 100644 --- a/src/main/java/io/mymetavese/metaapi/requests/actions/GetLinkingLinkActionImpl.java +++ b/src/main/java/io/mymetavese/metaapi/requests/actions/GetLinkingLinkActionImpl.java @@ -1,14 +1,14 @@ package io.mymetavese.metaapi.requests.actions; import io.mymetavese.metaapi.api.MetaAPI; -import io.mymetavese.metaapi.api.actions.GetLinkingLink; +import io.mymetavese.metaapi.api.actions.GetLinkingLinkAction; import io.mymetavese.metaapi.api.entities.v2.GameEntity; import io.mymetavese.metaapi.api.entities.LinkingLink; import io.mymetavese.metaapi.requests.RestActionImpl; import io.mymetavese.metaapi.requests.entities.LinkingLinkImpl; import io.mymetavese.metaapi.requests.routes.Routes; -public class GetLinkingLinkActionImpl extends RestActionImpl implements GetLinkingLink { +public class GetLinkingLinkActionImpl extends RestActionImpl implements GetLinkingLinkAction { private final GameEntity gameEntity; diff --git a/src/main/java/io/mymetavese/metaapi/requests/actions/drops/ConsumeDropActionImpl.java b/src/main/java/io/mymetavese/metaapi/requests/actions/drops/ConsumeDropActionImpl.java index e6a0995..02c7510 100644 --- a/src/main/java/io/mymetavese/metaapi/requests/actions/drops/ConsumeDropActionImpl.java +++ b/src/main/java/io/mymetavese/metaapi/requests/actions/drops/ConsumeDropActionImpl.java @@ -1,6 +1,5 @@ package io.mymetavese.metaapi.requests.actions.drops; -import com.google.gson.Gson; import com.google.gson.JsonArray; import io.mymetavese.metaapi.api.MetaAPI; import io.mymetavese.metaapi.api.actions.drops.ConsumeDropAction; @@ -11,12 +10,8 @@ import io.mymetavese.metaapi.requests.RestActionImpl; import io.mymetavese.metaapi.requests.entities.drops.responses.DropConsumedResponseImpl; import io.mymetavese.metaapi.requests.routes.Routes; -import okhttp3.Response; -import java.io.IOException; -import java.io.Reader; import java.util.List; -import java.util.Objects; public class ConsumeDropActionImpl extends RestActionImpl implements ConsumeDropAction { @@ -31,24 +26,6 @@ public ConsumeDropActionImpl(MetaAPI api, String dropId, GameEntity dropReceiver this.dropEntryRequirements = dropEntryRequirements; } - @Override - public DropConsumedResponse transform(Response response) { - - if (response == null || response.body() == null) { - throw new NullPointerException("Response cannot be null"); - } - - Gson gson = new Gson(); - try (Reader reader = Objects.requireNonNull(response.body()).charStream()) { - return gson.fromJson(reader, DropConsumedResponseImpl.class); - } catch (IOException ex) { - ex.printStackTrace(); - } - - return null; - - } - @Override protected String compileRoute() { return route.compileRoute(dropId); diff --git a/src/main/java/io/mymetavese/metaapi/requests/entities/GameEntityImpl.java b/src/main/java/io/mymetavese/metaapi/requests/entities/GameEntityImpl.java index 65f5444..976737a 100644 --- a/src/main/java/io/mymetavese/metaapi/requests/entities/GameEntityImpl.java +++ b/src/main/java/io/mymetavese/metaapi/requests/entities/GameEntityImpl.java @@ -56,7 +56,7 @@ public CreateLinkingLinkAction createLinkingLink() { } @Override - public GetLinkingLink getLinkingLink() { + public GetLinkingLinkAction getLinkingLink() { return new GetLinkingLinkActionImpl(metaAPI, this); } diff --git a/src/main/java/io/mymetavese/metaapi/requests/token/StaticToken.java b/src/main/java/io/mymetavese/metaapi/requests/token/StaticToken.java index d1fae2b..4c813df 100644 --- a/src/main/java/io/mymetavese/metaapi/requests/token/StaticToken.java +++ b/src/main/java/io/mymetavese/metaapi/requests/token/StaticToken.java @@ -15,5 +15,5 @@ public static StaticToken create(String token) { } @Override - public void reauthenticate() { } + public void authenticate() { } } diff --git a/src/main/java/io/mymetavese/metaapi/requests/token/TokenHandler.java b/src/main/java/io/mymetavese/metaapi/requests/token/TokenHandler.java index db4210d..85c3e93 100644 --- a/src/main/java/io/mymetavese/metaapi/requests/token/TokenHandler.java +++ b/src/main/java/io/mymetavese/metaapi/requests/token/TokenHandler.java @@ -4,6 +4,6 @@ public interface TokenHandler { String getToken(); - void reauthenticate(); + void authenticate(); } diff --git a/src/main/java/io/mymetavese/metaapi/requests/token/oauth/OAuthToken.java b/src/main/java/io/mymetavese/metaapi/requests/token/oauth/OAuthToken.java new file mode 100644 index 0000000..6cd64d8 --- /dev/null +++ b/src/main/java/io/mymetavese/metaapi/requests/token/oauth/OAuthToken.java @@ -0,0 +1,66 @@ +package io.mymetavese.metaapi.requests.token.oauth; + +import io.mymetavese.metaapi.requests.token.TokenHandler; +import io.mymetavese.metaapi.requests.token.oauth.scopes.OAuthScope; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public interface OAuthToken extends TokenHandler { + + List getOAuthScopes(); + + String getBaseAuthUrl(); + + final class Builder { + + private String clientID; + private String clientSecret; + private String authenticationAddress = "cloud.mymetaverse.io"; + private final List scopes = new ArrayList<>(); + + private Builder() { } + + public static OAuthToken.Builder createBuilder() { + return new OAuthToken.Builder(); + } + + public OAuthToken.Builder withClientID(String clientID) { + this.clientID = clientID; + return this; + } + + public OAuthToken.Builder withClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + return this; + } + + public OAuthToken.Builder withAuthenticationAddress(String authenticationAddress) { + this.authenticationAddress = authenticationAddress; + return this; + } + + public OAuthToken.Builder useScopes(OAuthScope... scopes) { + Arrays.stream(scopes).forEach(scope -> this.scopes.add(scope.getScope())); + return this; + } + + public OAuthToken.Builder useScopes(String... scopes) { + this.scopes.addAll(Arrays.asList(scopes)); + return this; + } + + public OAuthTokenImpl build() { + return new OAuthTokenImpl(clientID, clientSecret, authenticationAddress, scopes); + } + + public OAuthTokenImpl buildAuthenticated() { + OAuthTokenImpl oAuthToken = new OAuthTokenImpl(clientID, clientSecret, authenticationAddress, scopes); + oAuthToken.authenticate(); + return oAuthToken; + } + + } + +} diff --git a/src/main/java/io/mymetavese/metaapi/requests/token/OAuthToken.java b/src/main/java/io/mymetavese/metaapi/requests/token/oauth/OAuthTokenImpl.java similarity index 54% rename from src/main/java/io/mymetavese/metaapi/requests/token/OAuthToken.java rename to src/main/java/io/mymetavese/metaapi/requests/token/oauth/OAuthTokenImpl.java index c8969b3..8dffd63 100644 --- a/src/main/java/io/mymetavese/metaapi/requests/token/OAuthToken.java +++ b/src/main/java/io/mymetavese/metaapi/requests/token/oauth/OAuthTokenImpl.java @@ -1,55 +1,52 @@ -package io.mymetavese.metaapi.requests.token; +package io.mymetavese.metaapi.requests.token.oauth; import com.google.gson.Gson; -import com.google.gson.annotations.SerializedName; +import lombok.Getter; import okhttp3.*; import java.io.IOException; -import java.util.Arrays; +import java.util.List; import java.util.Objects; -public class OAuthToken implements TokenHandler { +public class OAuthTokenImpl implements OAuthToken { - private final static MediaType JSON_MEDIA_TYPE = MediaType.get("application/x-www-form-urlencoded; charset=utf-8"); - private static final RequestBody EMPTY_BODY = RequestBody.create(new byte[0], JSON_MEDIA_TYPE); - // Data to generate token private final OkHttpClient httpClient; - private final Gson gson; private final String clientId; private final String clientSecret; - private final String[] oAuthScopes; - private final String baseAuthUrl; + @Getter private final List oAuthScopes; + @Getter private final String baseAuthUrl; - private OAuthToken(final String clientId, final String clientSecret, final String baseAuthUrl) { + private long tokenExpireTime = 0; + private String accessToken; + private String refreshToken; + + OAuthTokenImpl(final String clientId, final String clientSecret, final String baseAuthUrl, final List oAuthScopes) { this.clientId = clientId; this.clientSecret = clientSecret; this.baseAuthUrl = baseAuthUrl; - this.oAuthScopes = new String[] {"wallet.read", "p2e.read", "p2e.add", "linkinglink.create", "linkinglink.read", "linkinglink.implicit"}; + this.oAuthScopes = oAuthScopes; this.httpClient = new OkHttpClient(); - this.gson = new Gson(); } - // WalletIndex Hybrid - private long tokenExpireTime = 0; - private String accessToken; - private String refreshToken; - private boolean shouldUpdateToken() { long currentTime = System.currentTimeMillis(); - return this.tokenExpireTime - currentTime <= 0; } private void requestToken(boolean withCredentials) { - Request.Builder builder = new Request.Builder(); + Gson gson = new Gson(); + Request.Builder builder = new Request.Builder(); HttpUrl.Builder urlBuilder = new HttpUrl.Builder(); + if (this.oAuthScopes == null || this.oAuthScopes.isEmpty()) + throw new RuntimeException("OAuth scopes are not available, please provide at least one OAuth Scope."); + urlBuilder .scheme("https") .host(baseAuthUrl) @@ -76,19 +73,19 @@ private void requestToken(boolean withCredentials) { } builder.url(urlBuilder.build()); - builder.post(formBody.build()); try (Response response = httpClient.newCall(builder.build()).execute()) { + if (response.code() != 200) { throw new IOException("Internal server error: " + Objects.requireNonNull(response.body()).string()); } - TokenResponse tokenResponse = this.gson.fromJson(Objects.requireNonNull(response.body()).charStream(), TokenResponse.class); + TokenResponse tokenResponse = gson.fromJson(Objects.requireNonNull(response.body()).charStream(), TokenResponse.class); - this.accessToken = tokenResponse.accessToken; - this.refreshToken = tokenResponse.refreshToken; - this.tokenExpireTime = System.currentTimeMillis() + (tokenResponse.expiresIn * 1000L); + this.accessToken = tokenResponse.getAccessToken(); + this.refreshToken = tokenResponse.getRefreshToken(); + this.tokenExpireTime = System.currentTimeMillis() + (tokenResponse.getExpiresIn() * 1000L); } catch (IOException ex) { ex.printStackTrace(); @@ -96,30 +93,11 @@ private void requestToken(boolean withCredentials) { } - public static OAuthToken create(final String clientId, final String clientSecret) { - OAuthToken tokenHandler = new OAuthToken(clientId, clientSecret, "cloud.mymetaverse.io"); - tokenHandler.requestToken(true); - return tokenHandler; - } - - public static OAuthToken create(final String clientId, final String clientSecret, final String baseAuthUrl) { - OAuthToken tokenHandler = new OAuthToken(clientId, clientSecret, baseAuthUrl); - tokenHandler.requestToken(true); - return tokenHandler; - } - - @Override - public String getToken() { - if (shouldUpdateToken()) - this.requestToken(false); - return accessToken; - } - - public String getOAuthScopesAsString() { + private String getOAuthScopesAsString() { StringBuilder oAuthScopesStringBuilder = new StringBuilder(); - Arrays.stream(this.oAuthScopes).forEach(scope -> { + this.oAuthScopes.forEach(scope -> { oAuthScopesStringBuilder.append(scope); oAuthScopesStringBuilder.append(" "); }); @@ -129,19 +107,15 @@ public String getOAuthScopesAsString() { } @Override - public void reauthenticate() { - this.requestToken(true); + public String getToken() { + if (shouldUpdateToken()) + this.requestToken(false); + return accessToken; } - private static class TokenResponse { - @SerializedName("access_token") - private String accessToken; - @SerializedName("token_type") - private String tokenType; - @SerializedName("expires_in") - private int expiresIn; - @SerializedName("refresh_token") - private String refreshToken; + @Override + public void authenticate() { + this.requestToken(true); } } diff --git a/src/main/java/io/mymetavese/metaapi/requests/token/oauth/TokenResponse.java b/src/main/java/io/mymetavese/metaapi/requests/token/oauth/TokenResponse.java new file mode 100644 index 0000000..8081765 --- /dev/null +++ b/src/main/java/io/mymetavese/metaapi/requests/token/oauth/TokenResponse.java @@ -0,0 +1,18 @@ +package io.mymetavese.metaapi.requests.token.oauth; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +@Data +public class TokenResponse { + + @SerializedName("access_token") + private String accessToken; + @SerializedName("token_type") + private String tokenType; + @SerializedName("expires_in") + private int expiresIn; + @SerializedName("refresh_token") + private String refreshToken; + +} diff --git a/src/main/java/io/mymetavese/metaapi/requests/token/oauth/scopes/OAuthScope.java b/src/main/java/io/mymetavese/metaapi/requests/token/oauth/scopes/OAuthScope.java new file mode 100644 index 0000000..13e19f6 --- /dev/null +++ b/src/main/java/io/mymetavese/metaapi/requests/token/oauth/scopes/OAuthScope.java @@ -0,0 +1,20 @@ +package io.mymetavese.metaapi.requests.token.oauth.scopes; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.ToString; + +@RequiredArgsConstructor +@Getter +@ToString +public enum OAuthScope { + + WALLET_READ("wallet.read"), + P2E_READ("p2e.read"), + P2E_ADD("p2e.add"), + LINKING_CREATE("linkinglink.create"), + LINKING_READ("linkinglink.read"); + + private final String scope; + +} diff --git a/src/test/java/io/mymetavese/metaapi/requests/token/oauth/OAuthTokenTest.java b/src/test/java/io/mymetavese/metaapi/requests/token/oauth/OAuthTokenTest.java new file mode 100644 index 0000000..3d1ddb6 --- /dev/null +++ b/src/test/java/io/mymetavese/metaapi/requests/token/oauth/OAuthTokenTest.java @@ -0,0 +1,39 @@ +package io.mymetavese.metaapi.requests.token.oauth; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class OAuthTokenTest { + + @Test + @DisplayName("Test OAuthTokenBuilder") + void testOAuthTokenBuilder() { + + String clientID = "dummyClientID"; + String clientSecret = "dummyClientSecret"; + String authenticationAddress = "dummyAuthenticationAddress"; + String[] scopes = new String[] {"dummyScope1", "dummyScope2"}; + + OAuthToken oAuthToken = OAuthToken.Builder.createBuilder() + .withClientID(clientID) + .withClientSecret(clientSecret) + .withAuthenticationAddress(authenticationAddress) + .useScopes(scopes) + .build(); + + assertThat(oAuthToken) + .isNotNull() + .isInstanceOf(OAuthTokenImpl.class) + .hasFieldOrPropertyWithValue("clientId", clientID) + .hasFieldOrPropertyWithValue("clientSecret", clientSecret) + .hasFieldOrPropertyWithValue("baseAuthUrl", authenticationAddress); + + assertThat(oAuthToken.getOAuthScopes()) + .hasSize(2) + .containsExactly(scopes); + + } + +} \ No newline at end of file