Skip to content

Commit 4b05b53

Browse files
GH-264 allowing the ProviderService to be built with custom (auth and headers) request interceptors (#265)
## Related Issue * #264
1 parent 48eeb23 commit 4b05b53

File tree

9 files changed

+123
-63
lines changed

9 files changed

+123
-63
lines changed

core/src/main/java/com/adobe/aio/feign/AIOHeaderInterceptor.java

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@
2020

2121
public class AIOHeaderInterceptor implements RequestInterceptor {
2222

23-
private final Workspace workspace;
23+
private final String apiKey;
24+
private final String imsOrgId;
2425

25-
private AIOHeaderInterceptor(final Workspace workspace) {
26-
this.workspace = workspace;
26+
private AIOHeaderInterceptor(final String apiKey, final String imsOrgId) {
27+
this.apiKey = apiKey;
28+
this.imsOrgId = imsOrgId;
2729
}
2830

2931
@Override
@@ -34,14 +36,14 @@ public void apply(RequestTemplate requestTemplate) {
3436

3537

3638
private void applyApiKey(RequestTemplate requestTemplate) {
37-
if (!requestTemplate.headers().containsKey(API_KEY_HEADER) && !StringUtils.isEmpty(workspace.getApiKey())) {
38-
requestTemplate.header(API_KEY_HEADER, workspace.getApiKey());
39+
if (!requestTemplate.headers().containsKey(API_KEY_HEADER) && !StringUtils.isEmpty(apiKey)) {
40+
requestTemplate.header(API_KEY_HEADER, apiKey);
3941
}
4042
}
4143

4244
private void applyImsOrg(RequestTemplate requestTemplate) {
43-
if (!requestTemplate.headers().containsKey(IMS_ORG_HEADER) && !StringUtils.isEmpty(workspace.getImsOrgId())) {
44-
requestTemplate.header(IMS_ORG_HEADER, workspace.getImsOrgId());
45+
if (!requestTemplate.headers().containsKey(IMS_ORG_HEADER) && !StringUtils.isEmpty(imsOrgId)) {
46+
requestTemplate.header(IMS_ORG_HEADER, imsOrgId);
4547
}
4648
}
4749

@@ -51,18 +53,30 @@ public static Builder builder() {
5153

5254
public static class Builder {
5355

54-
private Workspace workspace;;
56+
private String apiKey;
57+
private String imsOrgId;
5558

5659
private Builder() {
5760
}
5861

5962
public Builder workspace(Workspace workspace) {
60-
this.workspace = workspace;
63+
this.apiKey = workspace.getApiKey();
64+
this.imsOrgId = workspace.getImsOrgId();
6165
return this;
6266
}
6367

68+
public Builder apiKey(String apiKey) {
69+
this.apiKey = apiKey;
70+
return this;
71+
}
72+
73+
public Builder imsOrgId(String imsOrgId) {
74+
this.imsOrgId = imsOrgId;
75+
return this;
76+
}
77+
6478
public AIOHeaderInterceptor build() {
65-
return new AIOHeaderInterceptor(workspace);
79+
return new AIOHeaderInterceptor(apiKey, imsOrgId);
6680
}
6781
}
6882

core/src/main/java/com/adobe/aio/workspace/Workspace.java

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -56,18 +56,29 @@ public static Builder builder() {
5656
return new Builder();
5757
}
5858

59+
/**
60+
* Validates that this workspace is properly defined, including its `auth` (credential and api-key) related properties.
61+
* @throws IllegalStateException if any workspace `context` or `auth` properties is missing.
62+
* @see #validateWorkspaceContext() where validation that does not include `auth` related properties.
63+
*/
5964
public void validateAll() {
6065
validateWorkspaceContext();
66+
if (StringUtils.isEmpty(apiKey)) {
67+
throw new IllegalStateException("Your `Workspace` is missing an apiKey");
68+
}
6169
if (!isAuthOAuth() && !isAuthJWT()) {
6270
throw new IllegalStateException("Missing auth configuration, set either jwt or oauth...");
6371
}
6472
authContext.validate();
6573
}
6674

6775
/**
68-
* Validates that this workspace context is populated.
76+
* Validates that this workspace is properly defined.
6977
*
70-
* @throws IllegalStateException if any properties are not specified.
78+
* @throws IllegalStateException if a context/basic properties is missing.
79+
* Note that `auth` related properties will not be validated here.
80+
* @see #validateAll() for a more comprehensive validation that includes `auth` (credential and api-key) related properties.
81+
*
7182
*/
7283
public void validateWorkspaceContext() throws IllegalStateException {
7384
if (StringUtils.isEmpty(imsOrgId)) {
@@ -76,23 +87,12 @@ public void validateWorkspaceContext() throws IllegalStateException {
7687
if (StringUtils.isEmpty(this.getConsumerOrgId())) {
7788
throw new IllegalStateException("Your `Workspace` is missing a consumerOrgId");
7889
}
79-
if (StringUtils.isEmpty(apiKey)) {
80-
throw new IllegalStateException("Your `Workspace` is missing an apiKey");
81-
}
8290
if (StringUtils.isEmpty(this.getProjectId())) {
8391
throw new IllegalStateException("Your `Workspace` is missing a projectId");
8492
}
8593
if (StringUtils.isEmpty(this.getWorkspaceId())) {
8694
throw new IllegalStateException("Your `Workspace` is missing a workspaceId");
8795
}
88-
// note that the credentialId is optional
89-
// but it might be handy to have it in your `Workspace` POJO,
90-
// to avoid confusion when you have multiple credentials,
91-
// and to eventually in some Adobe API calls
92-
93-
if (authContext == null) {
94-
throw new IllegalStateException("Missing auth configuration ...");
95-
}
9696
}
9797

9898
public String getProjectUrl() {

core/src/test/java/com/adobe/aio/workspace/WorkspaceTest.java

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@
1515
import java.security.KeyPair;
1616
import java.security.KeyPairGenerator;
1717
import java.security.PrivateKey;
18+
import java.util.HashSet;
19+
import java.util.Set;
1820

1921
import com.adobe.aio.auth.Context;
22+
import com.adobe.aio.auth.OAuthContext;
2023
import com.adobe.aio.util.Constants;
2124
import org.junit.jupiter.api.BeforeAll;
2225
import org.junit.jupiter.api.Test;
@@ -38,22 +41,16 @@ public static void beforeClass() throws Exception {
3841

3942
@Test
4043
public void successFullBuilder() throws IOException {
41-
42-
class MockContext implements Context {
43-
@Override
44-
public void validate() {
45-
46-
}
47-
}
48-
44+
Set<String> scopes = new HashSet<>();
45+
scopes.add("scope1");
4946
Workspace actual = Workspace.builder()
5047
.imsUrl(Constants.PROD_IMS_URL)
5148
.imsOrgId(Workspace.IMS_ORG_ID + TEST_VALUE)
5249
.apiKey(Workspace.API_KEY + TEST_VALUE)
5350
.consumerOrgId(Workspace.CONSUMER_ORG_ID + TEST_VALUE)
5451
.projectId(Workspace.PROJECT_ID + TEST_VALUE)
5552
.workspaceId(Workspace.WORKSPACE_ID + TEST_VALUE)
56-
.authContext(new MockContext())
53+
.authContext(new OAuthContext("someClientSecret", scopes))
5754
.build();
5855

5956
assertEquals(Workspace.IMS_ORG_ID + TEST_VALUE, actual.getImsOrgId());
@@ -63,6 +60,21 @@ public void validate() {
6360
assertEquals(Workspace.WORKSPACE_ID + TEST_VALUE, actual.getWorkspaceId());
6461
assertEquals(Constants.PROD_IMS_URL, actual.getImsUrl());
6562
actual.validateWorkspaceContext();
63+
actual.validateAll();
64+
}
65+
66+
@Test
67+
public void missingApiKey() {
68+
Workspace actual = Workspace.builder()
69+
.imsUrl(Constants.PROD_IMS_URL)
70+
.imsOrgId(Workspace.IMS_ORG_ID + TEST_VALUE)
71+
.consumerOrgId(Workspace.CONSUMER_ORG_ID + TEST_VALUE)
72+
.projectId(Workspace.PROJECT_ID + TEST_VALUE)
73+
.workspaceId(Workspace.WORKSPACE_ID + TEST_VALUE)
74+
.build();
75+
actual.validateWorkspaceContext();
76+
Exception ex = assertThrows(IllegalStateException.class, actual::validateAll);
77+
assertEquals("Your `Workspace` is missing an apiKey", ex.getMessage());
6678
}
6779

6880
@Test
@@ -81,16 +93,6 @@ public void missingConsumerOrgId() {
8193
assertEquals("Your `Workspace` is missing a consumerOrgId", ex.getMessage());
8294
}
8395

84-
@Test
85-
public void missingApiKey() {
86-
Workspace actual = Workspace.builder()
87-
.imsOrgId(Workspace.IMS_ORG_ID + TEST_VALUE)
88-
.consumerOrgId(Workspace.CONSUMER_ORG_ID + TEST_VALUE)
89-
.build();
90-
Exception ex = assertThrows(IllegalStateException.class, actual::validateWorkspaceContext);
91-
assertEquals("Your `Workspace` is missing an apiKey", ex.getMessage());
92-
}
93-
9496
@Test
9597
public void missingProjectId() {
9698
Workspace actual = Workspace.builder()

events_mgmt/src/main/java/com/adobe/aio/event/management/ProviderService.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
import com.adobe.aio.event.management.feign.FeignProviderService;
1515
import com.adobe.aio.event.management.model.SampleEvent;
16+
import com.adobe.aio.feign.AIOHeaderInterceptor;
17+
import com.adobe.aio.ims.feign.AuthInterceptor;
1618
import com.adobe.aio.workspace.Workspace;
1719
import com.adobe.aio.event.management.model.EventMetadata;
1820
import com.adobe.aio.event.management.model.Provider;
@@ -92,6 +94,8 @@ Optional<SampleEvent> getSampleEvent(String providerId,
9294
class Builder {
9395

9496
private Workspace workspace;
97+
private AuthInterceptor authInterceptor;
98+
private AIOHeaderInterceptor aioHeaderInterceptor;
9599
private String url;
96100

97101
public Builder() {
@@ -102,13 +106,29 @@ public Builder workspace(Workspace workspace) {
102106
return this;
103107
}
104108

109+
public Builder authInterceptor(AuthInterceptor authInterceptor) {
110+
this.authInterceptor = authInterceptor;
111+
return this;
112+
}
113+
114+
public Builder aioHeaderInterceptor(AIOHeaderInterceptor aioHeaderInterceptor) {
115+
this.aioHeaderInterceptor = aioHeaderInterceptor;
116+
return this;
117+
}
118+
105119
public Builder url(String url) {
106120
this.url = url;
107121
return this;
108122
}
109123

110124
public ProviderService build() {
111-
return new FeignProviderService(workspace, url);
125+
if (authInterceptor == null) {
126+
authInterceptor = AuthInterceptor.builder().workspace(workspace).build();
127+
}
128+
if (aioHeaderInterceptor == null) {
129+
aioHeaderInterceptor = AIOHeaderInterceptor.builder().workspace(workspace).build();
130+
}
131+
return new FeignProviderService(authInterceptor, aioHeaderInterceptor, workspace, url);
112132
}
113133
}
114134
}

events_mgmt/src/main/java/com/adobe/aio/event/management/feign/FeignProviderService.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,12 @@ public class FeignProviderService implements ProviderService {
4848
private final SampleEventApi sampleEventApi;
4949
private final Workspace workspace;
5050

51-
public FeignProviderService(final Workspace workspace, final String url) {
51+
public FeignProviderService(final AuthInterceptor authInterceptor, final AIOHeaderInterceptor aioHeaderInterceptor, Workspace workspace, final String url) {
5252
String apiUrl = StringUtils.isEmpty(url) ? API_MANAGEMENT_URL : url;
53-
5453
if (workspace == null) {
5554
throw new IllegalArgumentException("ProviderService is missing a workspace context");
5655
}
5756
workspace.validateWorkspaceContext();
58-
RequestInterceptor authInterceptor = AuthInterceptor.builder().workspace(workspace).build();
59-
RequestInterceptor aioHeaderInterceptor = AIOHeaderInterceptor.builder().workspace(workspace).build();
6057

6158
this.providerApi = FeignUtil.getDefaultBuilder()
6259
.requestInterceptor(authInterceptor)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2017 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
package com.adobe.aio.ims;
13+
14+
import com.adobe.aio.ims.model.AccessToken;
15+
16+
@FunctionalInterface
17+
public interface AccessTokenProvider {
18+
19+
AccessToken getAccessToken();
20+
}

ims/src/main/java/com/adobe/aio/ims/ImsService.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,11 @@ public Builder workspace(Workspace workspace) {
6161
* Builds an IMS Service instance.
6262
*
6363
* @return a configured IMS Service
64-
* @throws IllegalStateException if the Workspace authentication context is not valid.
64+
* @throws IllegalArgumentException if the Workspace authentication context is not valid.
6565
*/
66-
public ImsService build() throws IllegalStateException {
66+
public ImsService build() throws IllegalArgumentException {
6767
if (workspace == null) {
68-
throw new IllegalStateException("Workspace is required to build ImsService");
68+
throw new IllegalArgumentException("Workspace is required to build ImsService");
6969
}
7070
workspace.validateAll();
7171
return new FeignImsService(this.workspace);

ims/src/main/java/com/adobe/aio/ims/feign/AuthInterceptor.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.adobe.aio.ims.feign;
22

3+
import com.adobe.aio.ims.AccessTokenProvider;
34
import com.adobe.aio.ims.ImsService;
45
import com.adobe.aio.ims.model.AccessToken;
56
import com.adobe.aio.workspace.Workspace;
@@ -12,23 +13,23 @@ public class AuthInterceptor implements RequestInterceptor {
1213

1314
private volatile Long expirationTimeMillis;
1415
private volatile AccessToken accessToken;
15-
private final ImsService imsService;
16+
private final AccessTokenProvider accessTokenProvider;
1617

17-
protected AuthInterceptor (final Workspace workspace) {
18-
this.imsService = ImsService.builder().workspace(workspace).build();
18+
protected AuthInterceptor(final Workspace workspace) {
19+
this(ImsService.builder().workspace(workspace).build()::getAccessToken);
20+
}
21+
22+
public AuthInterceptor(final AccessTokenProvider accessTokenProvider) {
23+
this.accessTokenProvider = accessTokenProvider;
1924
}
2025

2126
@Override
2227
public void apply(RequestTemplate requestTemplate) {
2328
applyAuthorization(requestTemplate);
2429
}
2530

26-
ImsService getImsService() {
27-
return this.imsService;
28-
}
29-
3031
AccessToken fetchAccessToken() {
31-
return getImsService().getAccessToken();
32+
return accessTokenProvider.getAccessToken();
3233
}
3334

3435
synchronized String getAccessToken() {

ims/src/main/java/com/adobe/aio/util/WorkspaceUtil.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,19 +72,25 @@ public static Workspace.Builder getWorkspaceBuilder(Map<String, String> configMa
7272
.projectId(configMap.get(PROJECT_ID))
7373
.workspaceId(configMap.get(WORKSPACE_ID))
7474
.credentialId(configMap.get(CREDENTIAL_ID));
75-
builder.authContext(getAuthContext(configMap));
75+
getAuthContext(configMap).ifPresent(builder::authContext);
7676
return builder;
7777
}
7878

7979
public static boolean isOAuthConfig(Map<String, String> configMap) {
8080
return configMap.containsKey(SCOPES);
8181
}
8282

83-
public static Context getAuthContext(Map<String, String> configMap) {
83+
public static boolean isJwtConfig(Map<String, String> configMap) {
84+
return configMap.containsKey(META_SCOPES);
85+
}
86+
87+
public static Optional<Context> getAuthContext(Map<String, String> configMap) {
8488
if (isOAuthConfig(configMap)) {
85-
return getOAuthContextBuilder(configMap).build();
86-
} else {
87-
return getJwtContextBuilder(configMap).build();
89+
return Optional.of(getOAuthContextBuilder(configMap).build());
90+
} else if (isJwtConfig(configMap)) {
91+
return Optional.of(getJwtContextBuilder(configMap).build());
92+
} else {
93+
return Optional.empty();
8894
}
8995
}
9096

0 commit comments

Comments
 (0)