From 41d48a757a85670d5345ee395594f80b1a21c3ba Mon Sep 17 00:00:00 2001 From: Francois Le Droff Date: Thu, 26 Mar 2026 12:12:19 +0100 Subject: [PATCH 1/5] GH-264 allowing the `ProviderService` to be built with custom (auth and headers) request interceptors --- .../adobe/aio/feign/AIOHeaderInterceptor.java | 34 +++++++++++++------ .../aio/event/management/ProviderService.java | 22 +++++++++++- .../feign/FeignProviderService.java | 5 +-- .../adobe/aio/ims/AccessTokenProvider.java | 20 +++++++++++ .../adobe/aio/ims/feign/AuthInterceptor.java | 17 +++++----- 5 files changed, 75 insertions(+), 23 deletions(-) create mode 100644 ims/src/main/java/com/adobe/aio/ims/AccessTokenProvider.java diff --git a/core/src/main/java/com/adobe/aio/feign/AIOHeaderInterceptor.java b/core/src/main/java/com/adobe/aio/feign/AIOHeaderInterceptor.java index d4b5f3cb..98be5407 100644 --- a/core/src/main/java/com/adobe/aio/feign/AIOHeaderInterceptor.java +++ b/core/src/main/java/com/adobe/aio/feign/AIOHeaderInterceptor.java @@ -20,10 +20,12 @@ public class AIOHeaderInterceptor implements RequestInterceptor { - private final Workspace workspace; + private final String apiKey; + private final String imsOrgId; - private AIOHeaderInterceptor(final Workspace workspace) { - this.workspace = workspace; + private AIOHeaderInterceptor(final String apiKey, final String imsOrgId) { + this.apiKey = apiKey; + this.imsOrgId = imsOrgId; } @Override @@ -34,14 +36,14 @@ public void apply(RequestTemplate requestTemplate) { private void applyApiKey(RequestTemplate requestTemplate) { - if (!requestTemplate.headers().containsKey(API_KEY_HEADER) && !StringUtils.isEmpty(workspace.getApiKey())) { - requestTemplate.header(API_KEY_HEADER, workspace.getApiKey()); + if (!requestTemplate.headers().containsKey(API_KEY_HEADER) && !StringUtils.isEmpty(apiKey)) { + requestTemplate.header(API_KEY_HEADER, apiKey); } } private void applyImsOrg(RequestTemplate requestTemplate) { - if (!requestTemplate.headers().containsKey(IMS_ORG_HEADER) && !StringUtils.isEmpty(workspace.getImsOrgId())) { - requestTemplate.header(IMS_ORG_HEADER, workspace.getImsOrgId()); + if (!requestTemplate.headers().containsKey(IMS_ORG_HEADER) && !StringUtils.isEmpty(imsOrgId)) { + requestTemplate.header(IMS_ORG_HEADER, imsOrgId); } } @@ -51,18 +53,30 @@ public static Builder builder() { public static class Builder { - private Workspace workspace;; + private String apiKey; + private String imsOrgId; private Builder() { } public Builder workspace(Workspace workspace) { - this.workspace = workspace; + this.apiKey = workspace.getApiKey(); + this.imsOrgId = workspace.getImsOrgId(); return this; } + public Builder apiKey(String apiKey) { + this.apiKey = apiKey; + return this; + } + + public Builder imsOrgId(String imsOrgId) { + this.imsOrgId = imsOrgId; + return this; + } + public AIOHeaderInterceptor build() { - return new AIOHeaderInterceptor(workspace); + return new AIOHeaderInterceptor(apiKey, imsOrgId); } } diff --git a/events_mgmt/src/main/java/com/adobe/aio/event/management/ProviderService.java b/events_mgmt/src/main/java/com/adobe/aio/event/management/ProviderService.java index 8b5995b3..516d50f6 100644 --- a/events_mgmt/src/main/java/com/adobe/aio/event/management/ProviderService.java +++ b/events_mgmt/src/main/java/com/adobe/aio/event/management/ProviderService.java @@ -13,6 +13,8 @@ import com.adobe.aio.event.management.feign.FeignProviderService; import com.adobe.aio.event.management.model.SampleEvent; +import com.adobe.aio.feign.AIOHeaderInterceptor; +import com.adobe.aio.ims.feign.AuthInterceptor; import com.adobe.aio.workspace.Workspace; import com.adobe.aio.event.management.model.EventMetadata; import com.adobe.aio.event.management.model.Provider; @@ -92,6 +94,8 @@ Optional getSampleEvent(String providerId, class Builder { private Workspace workspace; + private AuthInterceptor authInterceptor; + private AIOHeaderInterceptor aioHeaderInterceptor; private String url; public Builder() { @@ -102,13 +106,29 @@ public Builder workspace(Workspace workspace) { return this; } + public Builder authInterceptor(AuthInterceptor authInterceptor) { + this.authInterceptor = authInterceptor; + return this; + } + + public Builder aioHeaderInterceptor(AIOHeaderInterceptor aioHeaderInterceptor) { + this.aioHeaderInterceptor = aioHeaderInterceptor; + return this; + } + public Builder url(String url) { this.url = url; return this; } public ProviderService build() { - return new FeignProviderService(workspace, url); + if (authInterceptor == null) { + authInterceptor = AuthInterceptor.builder().workspace(workspace).build(); + } + if (aioHeaderInterceptor == null) { + aioHeaderInterceptor = AIOHeaderInterceptor.builder().workspace(workspace).build(); + } + return new FeignProviderService(authInterceptor, aioHeaderInterceptor, workspace, url); } } } diff --git a/events_mgmt/src/main/java/com/adobe/aio/event/management/feign/FeignProviderService.java b/events_mgmt/src/main/java/com/adobe/aio/event/management/feign/FeignProviderService.java index 6b68203d..879f8fee 100644 --- a/events_mgmt/src/main/java/com/adobe/aio/event/management/feign/FeignProviderService.java +++ b/events_mgmt/src/main/java/com/adobe/aio/event/management/feign/FeignProviderService.java @@ -48,15 +48,12 @@ public class FeignProviderService implements ProviderService { private final SampleEventApi sampleEventApi; private final Workspace workspace; - public FeignProviderService(final Workspace workspace, final String url) { + public FeignProviderService(final AuthInterceptor authInterceptor, final AIOHeaderInterceptor aioHeaderInterceptor, Workspace workspace, final String url) { String apiUrl = StringUtils.isEmpty(url) ? API_MANAGEMENT_URL : url; - if (workspace == null) { throw new IllegalArgumentException("ProviderService is missing a workspace context"); } workspace.validateWorkspaceContext(); - RequestInterceptor authInterceptor = AuthInterceptor.builder().workspace(workspace).build(); - RequestInterceptor aioHeaderInterceptor = AIOHeaderInterceptor.builder().workspace(workspace).build(); this.providerApi = FeignUtil.getDefaultBuilder() .requestInterceptor(authInterceptor) diff --git a/ims/src/main/java/com/adobe/aio/ims/AccessTokenProvider.java b/ims/src/main/java/com/adobe/aio/ims/AccessTokenProvider.java new file mode 100644 index 00000000..1f8e8e01 --- /dev/null +++ b/ims/src/main/java/com/adobe/aio/ims/AccessTokenProvider.java @@ -0,0 +1,20 @@ +/* + * Copyright 2017 Adobe. All rights reserved. + * This file is licensed to you 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 REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +package com.adobe.aio.ims; + +import com.adobe.aio.ims.model.AccessToken; + +@FunctionalInterface +public interface AccessTokenProvider { + + AccessToken getAccessToken(); +} \ No newline at end of file diff --git a/ims/src/main/java/com/adobe/aio/ims/feign/AuthInterceptor.java b/ims/src/main/java/com/adobe/aio/ims/feign/AuthInterceptor.java index 3a86a44e..10133276 100644 --- a/ims/src/main/java/com/adobe/aio/ims/feign/AuthInterceptor.java +++ b/ims/src/main/java/com/adobe/aio/ims/feign/AuthInterceptor.java @@ -1,5 +1,6 @@ package com.adobe.aio.ims.feign; +import com.adobe.aio.ims.AccessTokenProvider; import com.adobe.aio.ims.ImsService; import com.adobe.aio.ims.model.AccessToken; import com.adobe.aio.workspace.Workspace; @@ -12,10 +13,14 @@ public class AuthInterceptor implements RequestInterceptor { private volatile Long expirationTimeMillis; private volatile AccessToken accessToken; - private final ImsService imsService; + private final AccessTokenProvider accessTokenProvider; - protected AuthInterceptor (final Workspace workspace) { - this.imsService = ImsService.builder().workspace(workspace).build(); + protected AuthInterceptor(final Workspace workspace) { + this(ImsService.builder().workspace(workspace).build()::getAccessToken); + } + + public AuthInterceptor(final AccessTokenProvider accessTokenProvider) { + this.accessTokenProvider = accessTokenProvider; } @Override @@ -23,12 +28,8 @@ public void apply(RequestTemplate requestTemplate) { applyAuthorization(requestTemplate); } - ImsService getImsService() { - return this.imsService; - } - AccessToken fetchAccessToken() { - return getImsService().getAccessToken(); + return accessTokenProvider.getAccessToken(); } synchronized String getAccessToken() { From bb9ea0ea31d5fdf32ad176d05030afdecf11ab43 Mon Sep 17 00:00:00 2001 From: Francois Le Droff Date: Thu, 26 Mar 2026 14:09:47 +0100 Subject: [PATCH 2/5] GH-264 fixing typo --- ims/src/main/java/com/adobe/aio/ims/ImsService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ims/src/main/java/com/adobe/aio/ims/ImsService.java b/ims/src/main/java/com/adobe/aio/ims/ImsService.java index 58134b18..d2fdb7d3 100644 --- a/ims/src/main/java/com/adobe/aio/ims/ImsService.java +++ b/ims/src/main/java/com/adobe/aio/ims/ImsService.java @@ -61,11 +61,11 @@ public Builder workspace(Workspace workspace) { * Builds an IMS Service instance. * * @return a configured IMS Service - * @throws IllegalStateException if the Workspace authentication context is not valid. + * @throws IllegalArgumentException if the Workspace authentication context is not valid. */ public ImsService build() throws IllegalStateException { if (workspace == null) { - throw new IllegalStateException("Workspace is required to build ImsService"); + throw new IllegalArgumentException("Workspace is required to build ImsService"); } workspace.validateAll(); return new FeignImsService(this.workspace); From 383c3bd9a557867753a7301cca7807e1decf86eb Mon Sep 17 00:00:00 2001 From: Francois Le Droff Date: Thu, 26 Mar 2026 19:59:02 +0100 Subject: [PATCH 3/5] GH-264 loosening the workspace validation to ease its use --- .../com/adobe/aio/workspace/Workspace.java | 14 ++----- .../adobe/aio/workspace/WorkspaceTest.java | 40 ++++++++++--------- .../com/adobe/aio/util/WorkspaceUtil.java | 16 +++++--- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/core/src/main/java/com/adobe/aio/workspace/Workspace.java b/core/src/main/java/com/adobe/aio/workspace/Workspace.java index 28e02579..ed9570f5 100644 --- a/core/src/main/java/com/adobe/aio/workspace/Workspace.java +++ b/core/src/main/java/com/adobe/aio/workspace/Workspace.java @@ -58,6 +58,9 @@ public static Builder builder() { public void validateAll() { validateWorkspaceContext(); + if (StringUtils.isEmpty(apiKey)) { + throw new IllegalStateException("Your `Workspace` is missing an apiKey"); + } if (!isAuthOAuth() && !isAuthJWT()) { throw new IllegalStateException("Missing auth configuration, set either jwt or oauth..."); } @@ -76,23 +79,12 @@ public void validateWorkspaceContext() throws IllegalStateException { if (StringUtils.isEmpty(this.getConsumerOrgId())) { throw new IllegalStateException("Your `Workspace` is missing a consumerOrgId"); } - if (StringUtils.isEmpty(apiKey)) { - throw new IllegalStateException("Your `Workspace` is missing an apiKey"); - } if (StringUtils.isEmpty(this.getProjectId())) { throw new IllegalStateException("Your `Workspace` is missing a projectId"); } if (StringUtils.isEmpty(this.getWorkspaceId())) { throw new IllegalStateException("Your `Workspace` is missing a workspaceId"); } - // note that the credentialId is optional - // but it might be handy to have it in your `Workspace` POJO, - // to avoid confusion when you have multiple credentials, - // and to eventually in some Adobe API calls - - if (authContext == null) { - throw new IllegalStateException("Missing auth configuration ..."); - } } public String getProjectUrl() { diff --git a/core/src/test/java/com/adobe/aio/workspace/WorkspaceTest.java b/core/src/test/java/com/adobe/aio/workspace/WorkspaceTest.java index a8295792..99c717d4 100644 --- a/core/src/test/java/com/adobe/aio/workspace/WorkspaceTest.java +++ b/core/src/test/java/com/adobe/aio/workspace/WorkspaceTest.java @@ -15,8 +15,11 @@ import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; +import java.util.HashSet; +import java.util.Set; import com.adobe.aio.auth.Context; +import com.adobe.aio.auth.OAuthContext; import com.adobe.aio.util.Constants; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -38,14 +41,8 @@ public static void beforeClass() throws Exception { @Test public void successFullBuilder() throws IOException { - - class MockContext implements Context { - @Override - public void validate() { - - } - } - + Set scopes = new HashSet<>(); + scopes.add("scope1"); Workspace actual = Workspace.builder() .imsUrl(Constants.PROD_IMS_URL) .imsOrgId(Workspace.IMS_ORG_ID + TEST_VALUE) @@ -53,7 +50,7 @@ public void validate() { .consumerOrgId(Workspace.CONSUMER_ORG_ID + TEST_VALUE) .projectId(Workspace.PROJECT_ID + TEST_VALUE) .workspaceId(Workspace.WORKSPACE_ID + TEST_VALUE) - .authContext(new MockContext()) + .authContext(new OAuthContext("someClientSecret", scopes)) .build(); assertEquals(Workspace.IMS_ORG_ID + TEST_VALUE, actual.getImsOrgId()); @@ -63,6 +60,21 @@ public void validate() { assertEquals(Workspace.WORKSPACE_ID + TEST_VALUE, actual.getWorkspaceId()); assertEquals(Constants.PROD_IMS_URL, actual.getImsUrl()); actual.validateWorkspaceContext(); + actual.validateAll(); + } + + @Test + public void missingApiKey() { + Workspace actual = Workspace.builder() + .imsUrl(Constants.PROD_IMS_URL) + .imsOrgId(Workspace.IMS_ORG_ID + TEST_VALUE) + .consumerOrgId(Workspace.CONSUMER_ORG_ID + TEST_VALUE) + .projectId(Workspace.PROJECT_ID + TEST_VALUE) + .workspaceId(Workspace.WORKSPACE_ID + TEST_VALUE) + .build(); + actual.validateWorkspaceContext(); + Exception ex = assertThrows(IllegalStateException.class, actual::validateAll); + assertEquals("Your `Workspace` is missing an apiKey", ex.getMessage()); } @Test @@ -81,16 +93,6 @@ public void missingConsumerOrgId() { assertEquals("Your `Workspace` is missing a consumerOrgId", ex.getMessage()); } - @Test - public void missingApiKey() { - Workspace actual = Workspace.builder() - .imsOrgId(Workspace.IMS_ORG_ID + TEST_VALUE) - .consumerOrgId(Workspace.CONSUMER_ORG_ID + TEST_VALUE) - .build(); - Exception ex = assertThrows(IllegalStateException.class, actual::validateWorkspaceContext); - assertEquals("Your `Workspace` is missing an apiKey", ex.getMessage()); - } - @Test public void missingProjectId() { Workspace actual = Workspace.builder() diff --git a/ims/src/main/java/com/adobe/aio/util/WorkspaceUtil.java b/ims/src/main/java/com/adobe/aio/util/WorkspaceUtil.java index 55cd68c9..0c21239b 100644 --- a/ims/src/main/java/com/adobe/aio/util/WorkspaceUtil.java +++ b/ims/src/main/java/com/adobe/aio/util/WorkspaceUtil.java @@ -72,7 +72,7 @@ public static Workspace.Builder getWorkspaceBuilder(Map configMa .projectId(configMap.get(PROJECT_ID)) .workspaceId(configMap.get(WORKSPACE_ID)) .credentialId(configMap.get(CREDENTIAL_ID)); - builder.authContext(getAuthContext(configMap)); + getAuthContext(configMap).ifPresent(builder::authContext); return builder; } @@ -80,11 +80,17 @@ public static boolean isOAuthConfig(Map configMap) { return configMap.containsKey(SCOPES); } - public static Context getAuthContext(Map configMap) { + public static boolean isJwtConfig(Map configMap) { + return configMap.containsKey(META_SCOPES); + } + + public static Optional getAuthContext(Map configMap) { if (isOAuthConfig(configMap)) { - return getOAuthContextBuilder(configMap).build(); - } else { - return getJwtContextBuilder(configMap).build(); + return Optional.of(getOAuthContextBuilder(configMap).build()); + } else if (isJwtConfig(configMap)) { + return Optional.of(getJwtContextBuilder(configMap).build()); + } else { + return Optional.empty(); } } From c87e32b663da68720488b66712c7cd3f4fb5b6d0 Mon Sep 17 00:00:00 2001 From: Francois Le Droff Date: Fri, 27 Mar 2026 17:09:38 +0100 Subject: [PATCH 4/5] GH-264 code review amendments: aligning and polishing javadocs according to this PR refactoring --- .../src/main/java/com/adobe/aio/workspace/Workspace.java | 9 ++++++++- ims/src/main/java/com/adobe/aio/ims/ImsService.java | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/adobe/aio/workspace/Workspace.java b/core/src/main/java/com/adobe/aio/workspace/Workspace.java index ed9570f5..043e6464 100644 --- a/core/src/main/java/com/adobe/aio/workspace/Workspace.java +++ b/core/src/main/java/com/adobe/aio/workspace/Workspace.java @@ -56,6 +56,10 @@ public static Builder builder() { return new Builder(); } + /** + * Validates that this workspace context is populated, including auth related properties. + * @see #validateWorkspaceContext() where basic validation that does not include auth related properties. + */ public void validateAll() { validateWorkspaceContext(); if (StringUtils.isEmpty(apiKey)) { @@ -70,7 +74,10 @@ public void validateAll() { /** * Validates that this workspace context is populated. * - * @throws IllegalStateException if any properties are not specified. + * @throws IllegalStateException if context/basic workspace properties are not specified. + * Note `auth` related properties will not be validated here. + * @see #validateAll() for a more comprehensive validation that includes auth related properties. + * */ public void validateWorkspaceContext() throws IllegalStateException { if (StringUtils.isEmpty(imsOrgId)) { diff --git a/ims/src/main/java/com/adobe/aio/ims/ImsService.java b/ims/src/main/java/com/adobe/aio/ims/ImsService.java index d2fdb7d3..4929dc10 100644 --- a/ims/src/main/java/com/adobe/aio/ims/ImsService.java +++ b/ims/src/main/java/com/adobe/aio/ims/ImsService.java @@ -63,7 +63,7 @@ public Builder workspace(Workspace workspace) { * @return a configured IMS Service * @throws IllegalArgumentException if the Workspace authentication context is not valid. */ - public ImsService build() throws IllegalStateException { + public ImsService build() throws IllegalArgumentException { if (workspace == null) { throw new IllegalArgumentException("Workspace is required to build ImsService"); } From ee3241f49173580f7b5e6b39439e8f1867849da0 Mon Sep 17 00:00:00 2001 From: Francois Le Droff Date: Fri, 27 Mar 2026 17:18:35 +0100 Subject: [PATCH 5/5] GH-264 code review amendments: more javadoc polishing --- .../java/com/adobe/aio/workspace/Workspace.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/com/adobe/aio/workspace/Workspace.java b/core/src/main/java/com/adobe/aio/workspace/Workspace.java index 043e6464..c283009d 100644 --- a/core/src/main/java/com/adobe/aio/workspace/Workspace.java +++ b/core/src/main/java/com/adobe/aio/workspace/Workspace.java @@ -57,8 +57,9 @@ public static Builder builder() { } /** - * Validates that this workspace context is populated, including auth related properties. - * @see #validateWorkspaceContext() where basic validation that does not include auth related properties. + * Validates that this workspace is properly defined, including its `auth` (credential and api-key) related properties. + * @throws IllegalStateException if any workspace `context` or `auth` properties is missing. + * @see #validateWorkspaceContext() where validation that does not include `auth` related properties. */ public void validateAll() { validateWorkspaceContext(); @@ -72,11 +73,11 @@ public void validateAll() { } /** - * Validates that this workspace context is populated. + * Validates that this workspace is properly defined. * - * @throws IllegalStateException if context/basic workspace properties are not specified. - * Note `auth` related properties will not be validated here. - * @see #validateAll() for a more comprehensive validation that includes auth related properties. + * @throws IllegalStateException if a context/basic properties is missing. + * Note that `auth` related properties will not be validated here. + * @see #validateAll() for a more comprehensive validation that includes `auth` (credential and api-key) related properties. * */ public void validateWorkspaceContext() throws IllegalStateException {