Skip to content

Commit

Permalink
Improve tests and so on.
Browse files Browse the repository at this point in the history
  • Loading branch information
SebastianStehle committed Aug 15, 2023
1 parent eade748 commit 10459f0
Show file tree
Hide file tree
Showing 15 changed files with 353 additions and 33 deletions.
11 changes: 4 additions & 7 deletions .fernignore
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
README.md

# Wrappers
ClientOptions.java
**/ClientOptions.java

AccessToken.java
AuthInterceptor.java
InMemoryTokenStore.java
SquidexApiClient.java
SquidexApiClientBuilder.java
TokenStore.java
# Client files
src/main/java/com/squidex/api/*.*

# Workflows
.github/workflows/check-updates.yml
.github/workflows/ci.yml
.github/workflows/test.yml

#tests
Expand Down
23 changes: 23 additions & 0 deletions .github/workflows/check-updates.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Check Update
concurrency: check

on:
workflow_dispatch:
schedule:
# Automatically run on every Sunday
- cron: '0 0 * * 0'

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
with:
token: ${{ secrets.WORKFLOW_SECRET }}

- name: Check for Update
uses: saadmk11/[email protected]
with:
token: ${{ secrets.WORKFLOW_SECRET }}
release_types: 'major'
37 changes: 35 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,25 @@ jobs:
java-version: "11"
architecture: x64

- name: Test - Start Compose
run: docker-compose up -d
working-directory: tests

- name: Test
run: ./gradlew test

- name: Test - Dump docker logs on failure
if: failure()
uses: jwalton/[email protected]
with:
images: 'squidex,squidex/resizer'
tail: '100'

- name: Test - Cleanup
if: always()
run: docker-compose down
working-directory: tests

publish:
needs: [ compile, test ]
if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
Expand All @@ -52,10 +69,26 @@ jobs:
java-version: "11"
architecture: x64

- name: Test - Start Compose
run: docker-compose up -d
working-directory: tests

- name: Publish to maven
run: |
./gradlew publish
./gradlew publish
env:
MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
MAVEN_PUBLISH_REGISTRY_URL: "https://s01.oss.sonatype.org/content/repositories/releases/"
MAVEN_PUBLISH_REGISTRY_URL: "https://s01.oss.sonatype.org/content/repositories/releases/"

- name: Test - Dump docker logs on failure
if: failure()
uses: jwalton/[email protected]
with:
images: 'squidex,squidex/resizer'
tail: '100'

- name: Test - Cleanup
if: always()
run: docker-compose down
working-directory: tests
37 changes: 37 additions & 0 deletions src/main/java/com/squidex/api/AccessToken.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.squidex.api;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;

import java.time.Instant;

public final class AccessToken {
private final String accessToken;
private final int expiresIn;
private final Instant expiresAt;

@JsonCreator()
public AccessToken(
@JsonProperty("access_token")String accessToken,
@JsonProperty("expires_in")int expiresIn) {
this.accessToken = accessToken;
this.expiresIn = expiresIn;
this.expiresAt = Instant.now().plusSeconds(expiresIn);
}


public String getAccessToken() {
return accessToken;
}


public int getExpiresIn() {
return expiresIn;
}

@JsonIgnore()
public Instant getExpiresAt() {
return expiresAt;
}
}
84 changes: 84 additions & 0 deletions src/main/java/com/squidex/api/AuthInterceptor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package com.squidex.api;

import com.squidex.api.core.Environment;
import com.squidex.api.core.ObjectMappers;
import okhttp3.*;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.time.Instant;
import java.util.Objects;

public final class AuthInterceptor implements Interceptor {
private final Environment environment;
private final String clientId;
private final String clientSecret;
private final TokenStore tokenStore;
private final OkHttpClient httpClient;

public AuthInterceptor(OkHttpClient httpClient, Environment environment, String clientId, String clientSecret, TokenStore tokenStore) {
this.httpClient = httpClient;
this.environment = environment;
this.clientId = clientId;
this.clientSecret = clientSecret;
this.tokenStore = tokenStore;
}

@NotNull
@Override
public Response intercept(@NotNull Chain chain) throws IOException {
Request originalRequest = chain.request();

AccessToken token = this.tokenStore.get();
if (token != null && token.getExpiresAt().isBefore(Instant.now())) {
// The token has been expired, therefore also remove it from the store for other calls.
token = null;
this.tokenStore.clear();
}

if (token == null) {
token = acquireToken();
this.tokenStore.set(token);
}

Request requestWithHeader = originalRequest.newBuilder()
.header("Authorization", String.format("Bearer %s", token.getAccessToken()))
.build();

Response response = chain.proceed(requestWithHeader);
if (response.code() == 401) {
this.tokenStore.clear();

return intercept(chain);
}

return response;
}

private AccessToken acquireToken() throws IOException {
RequestBody formBody = new FormBody.Builder()
.add("grant_type", "client_credentials")
.add("client_id", this.clientId)
.add("client_secret", this.clientSecret)
.add("scope", "squidex-api")
.build();

HttpUrl tokenUrl = Objects.requireNonNull(HttpUrl.parse(this.environment.getUrl()))
.newBuilder()
.addPathSegments("identity-server/connect/token")
.build();

Request tokenRequest = new Request.Builder()
.url(tokenUrl.url())
.post(formBody)
.build();

AccessToken token;
try (Response response = this.httpClient.newCall(tokenRequest).execute()) {
assert response.body() != null;
token = ObjectMappers.JSON_MAPPER.readValue(response.body().string(), AccessToken.class);
}

return token;
}
}
20 changes: 20 additions & 0 deletions src/main/java/com/squidex/api/InMemoryTokenStore.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.squidex.api;

public class InMemoryTokenStore implements TokenStore {
private AccessToken currentToken;

@Override
public AccessToken get() {
return this.currentToken;
}

@Override
public void set(AccessToken token) {
this.currentToken = token;
}

@Override
public void clear() {
this.currentToken = null;
}
}
102 changes: 95 additions & 7 deletions src/main/java/com/squidex/api/SquidexApiClientBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,21 @@

import com.squidex.api.core.ClientOptions;
import com.squidex.api.core.Environment;
import okhttp3.OkHttpClient;

import javax.net.ssl.*;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;

public final class SquidexApiClientBuilder {
private ClientOptions.Builder clientOptionsBuilder = ClientOptions.builder();
private final ClientOptions.Builder clientOptionsBuilder = ClientOptions.builder();

private Environment environment = Environment.DEFAULT;

public SquidexApiClientBuilder token(String token) {
this.clientOptionsBuilder.addHeader("Authorization", "Bearer " + token);
return this;
}
private String clientId;
private String clientSecret;
private TokenStore tokenStore;
private OkHttpClient httpClient;
private boolean trustAllCerts;

public SquidexApiClientBuilder environment(Environment environment) {
this.environment = environment;
Expand All @@ -23,13 +28,96 @@ public SquidexApiClientBuilder url(String url) {
return this;
}

public SquidexApiClientBuilder clientId(String clientId) {
this.clientId = clientId;
return this;
}

public String clientId() {
return this.clientId;
}

public SquidexApiClientBuilder clientSecret(String clientSecret) {
this.clientSecret = clientSecret;
return this;
}

public String clientSecret() {
return this.clientSecret;
}

public SquidexApiClientBuilder tokenStore(TokenStore tokenStore) {
this.tokenStore = tokenStore;
return this;
}

public SquidexApiClientBuilder httpClient(OkHttpClient httpClient) {
this.httpClient = httpClient;
return this;
}

public SquidexApiClientBuilder appName(String appName) {
clientOptionsBuilder.appName(appName);
this.clientOptionsBuilder.appName(appName);
return this;
}

public SquidexApiClientBuilder trustAllCerts() {
this.trustAllCerts = true;
return this;
}

public SquidexApiClient build() {
clientOptionsBuilder.environment(this.environment);

if (this.tokenStore == null) {
this.tokenStore = new InMemoryTokenStore();
}

if (this.httpClient == null) {
this.httpClient = new OkHttpClient();
}

if (this.trustAllCerts) {
X509TrustManager trustAllCerts = new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {
}

@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {
}

@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[] {};
}
};

try {
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[] { trustAllCerts }, new java.security.SecureRandom());

this.httpClient = this.httpClient.newBuilder()
.sslSocketFactory(sslContext.getSocketFactory(), trustAllCerts)
.build();
} catch (NoSuchAlgorithmException | KeyManagementException e) {
throw new RuntimeException(e);
}
}

AuthInterceptor interceptor = new AuthInterceptor(
this.httpClient,
this.environment,
this.clientId,
this.clientSecret,
this.tokenStore);

this.httpClient = this.httpClient.newBuilder()
.addInterceptor(interceptor)
.build();

clientOptionsBuilder.httpClient(this.httpClient);

return new SquidexApiClient(clientOptionsBuilder.build());
}
}
10 changes: 10 additions & 0 deletions src/main/java/com/squidex/api/TokenStore.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.squidex.api;

public interface TokenStore {
AccessToken get();

void set(AccessToken token);

void clear();
}

Loading

0 comments on commit 10459f0

Please sign in to comment.