diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml
index 5f0fc09a..1fafc176 100644
--- a/.github/workflows/maven-publish.yml
+++ b/.github/workflows/maven-publish.yml
@@ -16,7 +16,7 @@ jobs:
if: ${{ github.ref == 'refs/heads/develop' }}
environment: dev
permissions:
- contents: read
+ contents: write
packages: write
steps:
@@ -39,7 +39,7 @@ jobs:
# Install gpg secret key
cat <(echo -e "${{ secrets.MAVEN_GPG_PRIVATE_KEY }}") | gpg --batch --import
# Verify gpg secret key
- gpg --list-secret-keys --keyid-format LONG
+ gpg --list-secret-keys --keyid-format=long || echo "❌ No secret keys found"
- id: ssh-setup
name: Set SSH key
@@ -81,13 +81,17 @@ jobs:
if: ${{ github.ref == 'refs/heads/main' }}
environment: dev
permissions:
- contents: read
+ contents: write
packages: write
steps:
- id: checkout
name: Checkout
uses: actions/checkout@v4
+ with:
+ ref: main
+ fetch-depth: 0 # Required for Maven Release Plugin
+ persist-credentials: false
- id: gpg-install
name: Install gpg secret key
@@ -95,7 +99,8 @@ jobs:
# Install gpg secret key
cat <(echo -e "${{ secrets.MAVEN_GPG_PRIVATE_KEY }}") | gpg --batch --import
# Verify gpg secret key
- gpg --list-secret-keys --keyid-format LONG
+ gpg --list-secret-keys --keyid-format=long || echo "❌ No secret keys found"
+ echo "Installed gpg secret key"
- id: ssh-setup
name: Set SSH key
@@ -131,7 +136,8 @@ jobs:
- id: release
name: Release to production
- run: mvn clean release:clean release:prepare release:perform -B -Darguments="-Ddevelop.api.key=${{ secrets.DEVELOP_SERVER_API_KEY }} -Dprod.eu1.api.key=${{ secrets.PROD_EU1_SERVER_API_KEY }} -Dprod.na1.api.key=${{ secrets.PROD_NA1_SERVER_API_KEY }}" -DpreparationGoals=install
+ run: mvn clean release:clean release:prepare release:perform -B -DskipTests=true -DpreparationGoals=install
+ # run: mvn clean release:clean release:prepare release:perform -B -Darguments="-Ddevelop.api.key=${{ secrets.DEVELOP_SERVER_API_KEY }} -Dprod.eu1.api.key=${{ secrets.PROD_EU1_SERVER_API_KEY }} -Dprod.na1.api.key=${{ secrets.PROD_NA1_SERVER_API_KEY }}" -DskipTests=true -X -DpreparationGoals=install
- id: rollback
name: Rollback
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b903e2ba..64ee4ccc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,25 @@
# Changelog
+##
+## [0.15.5] - 2025-06-04
+
+### Added
+
+- `classFqn` helper to compute fully-qualified names
+- import-filter to drop any illegal import strings
+- `typeRef` helper to pick simple name or FQN and avoid duplicates
+- `lastSegment` helper to filter out a context’s own class from imports
+
+### Changed
+
+- Updated `ResolvedContext.hbs` so that all generated-type references go through `typeRef` (simple names only when safe)
+- Updated constructor loops in `ResolvedContext.hbs` to call proxy methods with `classFqn` (true FQNs)
+
+### Fixed
+
+- “already defined in this compilation unit” compile errors—SDK now builds without name collisions
+
+##
## [0.15.4] - 2024-11-15
### Added
diff --git a/README.md b/README.md
index 662f2fba..88cbc32a 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# Java Client Library
-* Latest released version 0.15.3
-* Latest snapshot version 0.15.4-SNAPSHOT
+* Latest released version 0.15.5
+* Latest snapshot version 0.15.6-SNAPSHOT
## Introduction
This is the PolyAPI Java client GitHub page. If you are here, then it means you're familiar with what we do at Poly. If you aren't, you can always check [here](https://github.com/polyapi/poly-alpha).
diff --git a/commons/pom.xml b/commons/pom.xml
index 539fcfd0..141d4256 100644
--- a/commons/pom.xml
+++ b/commons/pom.xml
@@ -4,7 +4,7 @@
io.polyapi
parent-pom
- 0.15.4-SNAPSHOT
+ 0.15.6-SNAPSHOT
../parent-pom
@@ -154,6 +154,12 @@
+
+
+ --pinentry-mode
+ loopback
+
+
org.sonatype.central
diff --git a/library/pom.xml b/library/pom.xml
index eca445a0..17a60586 100644
--- a/library/pom.xml
+++ b/library/pom.xml
@@ -4,7 +4,7 @@
io.polyapi
parent-pom
- 0.15.4-SNAPSHOT
+ 0.15.6-SNAPSHOT
../parent-pom
library
@@ -104,6 +104,12 @@
+
+
+ --pinentry-mode
+ loopback
+
+
org.sonatype.central
diff --git a/parent-pom/pom.xml b/parent-pom/pom.xml
index 32862ca7..b7e6bc64 100644
--- a/parent-pom/pom.xml
+++ b/parent-pom/pom.xml
@@ -1,11 +1,9 @@
-
+
4.0.0
io.polyapi
parent-pom
- 0.15.4-SNAPSHOT
+ 0.15.6-SNAPSHOT
pom
PolyAPI Java parent POM
https://polyapi.io
@@ -130,6 +128,12 @@
+
+
+ --pinentry-mode
+ loopback
+
+
org.sonatype.central
diff --git a/polyapi-maven-plugin/pom.xml b/polyapi-maven-plugin/pom.xml
index 8ee5cb6a..449bc724 100644
--- a/polyapi-maven-plugin/pom.xml
+++ b/polyapi-maven-plugin/pom.xml
@@ -1,11 +1,10 @@
-
+
4.0.0
io.polyapi
parent-pom
- 0.15.4-SNAPSHOT
+ 0.15.6-SNAPSHOT
../parent-pom
polyapi-maven-plugin
@@ -127,9 +126,6 @@
false
-
- MAVEN_GPG_KEY
-
@@ -145,6 +141,12 @@
+
+
+ --pinentry-mode
+ loopback
+
+
org.sonatype.central
diff --git a/polyapi-maven-plugin/src/main/java/io/polyapi/plugin/service/generation/PolyObjectResolverService.java b/polyapi-maven-plugin/src/main/java/io/polyapi/plugin/service/generation/PolyObjectResolverService.java
index fc5190ad..7fb74f16 100644
--- a/polyapi-maven-plugin/src/main/java/io/polyapi/plugin/service/generation/PolyObjectResolverService.java
+++ b/polyapi-maven-plugin/src/main/java/io/polyapi/plugin/service/generation/PolyObjectResolverService.java
@@ -5,6 +5,7 @@
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
@@ -43,6 +44,8 @@
@Slf4j
public class PolyObjectResolverService {
private final JsonSchemaParser jsonSchemaParser;
+ private static final Pattern VALID_IMPORT =
+ Pattern.compile("^[a-zA-Z_][a-zA-Z0-9_]*(\\.[a-zA-Z_][a-zA-Z0-9_]*)+$");
public PolyObjectResolverService(JsonSchemaParser jsonSchemaParser) {
this.jsonSchemaParser = jsonSchemaParser;
@@ -92,12 +95,21 @@ public ResolvedServerVariableSpecification resolve(ServerVariableSpecification s
public ResolvedContext resolve(Context context) {
Set imports = new HashSet<>();
- context.getSubcontexts().stream().map(subcontext -> format("%s.%s", subcontext.getPackageName(), subcontext.getClassName())).forEach(imports::add);
+ context.getSubcontexts().stream()
+ .map(subcontext -> format("%s.%s", subcontext.getPackageName(), subcontext.getClassName()))
+ .filter(s -> !s.isBlank())
+ .forEach(imports::add);
context.getSpecifications().forEach(specification -> {
ImportsCollectorVisitor importsCollectorVisitor = new ImportsCollectorVisitor(specification.getPackageName(), specification.getClassName(), jsonSchemaParser);
importsCollectorVisitor.doVisit(specification);
- imports.addAll(importsCollectorVisitor.getImports());
+ importsCollectorVisitor.getImports().stream()
+ .filter(Objects::nonNull)
+ .filter(s -> !s.isBlank())
+ .filter(s -> VALID_IMPORT.matcher(s).matches())
+ .filter(s -> !s.substring(s.lastIndexOf('.') + 1).equals(context.getClassName()))
+ .forEach(imports::add);
});
+
return new ResolvedContext(context.getName(), context.getPackageName(), imports, context.getClassName(), context.getSubcontexts().stream().map(this::resolve).toList(), context.getSpecifications().stream().map(specification -> {
PolyObjectResolverVisitor visitor = new PolyObjectResolverVisitor(this);
visitor.doVisit(specification);
diff --git a/polyapi-maven-plugin/src/main/java/io/polyapi/plugin/service/template/PolyHandlebars.java b/polyapi-maven-plugin/src/main/java/io/polyapi/plugin/service/template/PolyHandlebars.java
index 5d812e8a..5781e0ba 100644
--- a/polyapi-maven-plugin/src/main/java/io/polyapi/plugin/service/template/PolyHandlebars.java
+++ b/polyapi-maven-plugin/src/main/java/io/polyapi/plugin/service/template/PolyHandlebars.java
@@ -7,6 +7,7 @@
import java.util.function.BiPredicate;
import java.util.function.Function;
+import java.lang.reflect.Method;
public class PolyHandlebars extends Handlebars {
@@ -15,6 +16,50 @@ public PolyHandlebars() {
registerSimpleHelper("toCamelCase", StringUtils::toCamelCase);
registerSimpleHelper("toPascalCase", StringUtils::toCamelCase);
registerConditionalHelper("ifIsType", (object, options) -> object.getClass().getSimpleName().equals(options.param(0)));
+ registerSimpleHelper("lastSegment", (Object fqn) -> {
+ if (fqn == null) {
+ return "";
+ }
+ String s = fqn.toString();
+ int idx = s.lastIndexOf('.');
+ return idx == -1 ? s : s.substring(idx + 1);
+ });
+ registerConditionalHelper("eq",
+ (obj, opts) -> {
+ Object other = opts.param(0, "");
+ return obj != null && obj.toString().equals(other == null ? "" : other.toString());
+ });
+ registerHelper("typeRef", (Object ctx, Options opts) -> {
+ if (ctx == null) {
+ return "";
+ }
+ String fqn = ctx.toString();
+ String parentSimple = opts.param(0, "").toString();
+ String simple = fqn.substring(fqn.lastIndexOf('.') + 1);
+ return simple.equals(parentSimple) ? fqn : simple;
+ });
+ registerHelper("classFqn", (Object ctx, Options o) -> {
+ if (ctx == null) return "";
+
+ for (String m : new String[]{"getFullClassName", "getFullName"}) {
+ try {
+ Method mm = ctx.getClass().getMethod(m);
+ Object val = mm.invoke(ctx);
+ if (val != null) return val.toString();
+ } catch (ReflectiveOperationException ignored) {}
+ }
+
+ try {
+ Method pm = ctx.getClass().getMethod("getPackageName");
+ Method cm = ctx.getClass().getMethod("getClassName");
+ Object pkg = pm.invoke(ctx);
+ Object cls = cm.invoke(ctx);
+ if (pkg != null && cls != null) return pkg + "." + cls;
+ if (cls != null) return cls.toString();
+ } catch (ReflectiveOperationException ignored) {}
+
+ return "";
+ });
}
private void registerSimpleHelper(String name, Function helper) {
diff --git a/polyapi-maven-plugin/src/main/resources/templates/ResolvedContext.hbs b/polyapi-maven-plugin/src/main/resources/templates/ResolvedContext.hbs
index 9765b8fd..92eff32b 100644
--- a/polyapi-maven-plugin/src/main/resources/templates/ResolvedContext.hbs
+++ b/polyapi-maven-plugin/src/main/resources/templates/ResolvedContext.hbs
@@ -8,68 +8,74 @@ import io.polyapi.client.api.model.PolyEntity;
import io.polyapi.client.api.AuthTokenOptions;
import io.polyapi.commons.api.model.PolyGeneratedClass;
{{~#each this.imports}}
+{{~#unless (eq (lastSegment this) ../className)}}
import {{{this}}};
+{{~/unless}}
{{~/each}}
@PolyGeneratedClass
public class {{className}} extends PolyContext {
{{~#each functionSpecifications}}
- private final {{this.className}} {{this.name}};
+ private final {{typeRef (classFqn this) ../className}} {{this.name}};
{{~/each}}
{{~#each standardAuthFunctionSpecifications}}
- private final {{this.className}} {{this.name}};
+ private final {{typeRef (classFqn this) ../className}} {{this.name}};
{{~/each}}
{{~#each subresourceAuthFunctionSpecifications}}
- private final {{this.className}} {{this.name}};
+ private final {{typeRef (classFqn this) ../className}} {{this.name}};
{{~/each}}
{{~#each serverVariableSpecifications}}
- public final {{this.className}} {{this.name}};
+ private final {{typeRef (classFqn this) ../className}} {{this.name}};
{{~/each}}
{{~#each webhookHandlerSpecifications}}
- private final {{this.className}} {{this.name}};
+ private final {{typeRef (classFqn this) ../className}} {{this.name}};
{{~/each}}
{{#each subcontexts}}
- public final {{this.className}} {{this.name}};
+ private final {{typeRef (classFqn this) ../className}} {{this.name}};
{{~/each}}
- public {{className}}(PolyProxyFactory proxyFactory, WebSocketClient webSocketClient) {
- super(proxyFactory, webSocketClient);
+public {{className}}(PolyProxyFactory proxyFactory, WebSocketClient webSocketClient) {
+super(proxyFactory, webSocketClient);
{{~#each serverFunctionSpecifications}}
- this.{{this.name}} = createServerFunctionProxy({{this.className}}.class);
+ this.{{this.name}} =
+ createServerFunctionProxy({{classFqn this}}.class);
{{~/each}}
{{~#each customFunctionSpecifications}}
- this.{{this.name}} = createCustomFunctionProxy({{this.className}}.class);
+ this.{{this.name}} =
+ createCustomFunctionProxy({{classFqn this}}.class);
{{~/each}}
{{~#each apiFunctionSpecifications}}
- this.{{this.name}} = createApiFunctionProxy({{this.className}}.class);
+ this.{{this.name}} =
+ createApiFunctionProxy({{classFqn this}}.class);
{{~/each}}
{{~#each subresourceAuthFunctionSpecifications}}
- this.{{this.name}} = createSubresourceAuthFunction({{this.className}}.class);
+ this.{{this.name}} =
+ createSubresourceAuthFunction({{classFqn this}}.class);
{{~/each}}
{{~#each standardAuthFunctionSpecifications}}
- this.{{this.name}} = create{{#if audienceRequired}}Audience{{/if}}TokenAuthFunction({{this.className}}.class);
+ this.{{this.name}} =
+ create{{#if audienceRequired}}Audience{{/if}}TokenAuthFunction({{classFqn this}}.class);
{{~/each}}
{{~#each serverVariableSpecifications}}
- this.{{this.name}} = createServerVariableHandler({{this.className}}.class);
+ this.{{this.name}} =
+ createServerVariableHandler({{classFqn this}}.class);
{{~/each}}
{{~#each webhookHandlerSpecifications}}
- this.{{this.name}} = createPolyTriggerProxy({{this.className}}.class);
+ this.{{this.name}} =
+ createPolyTriggerProxy({{classFqn this}}.class);
{{~/each}}
{{#each subcontexts}}
- this.{{this.name}} = new {{this.className}}(proxyFactory, webSocketClient);
+ this.{{this.name}} = new {{typeRef (classFqn this) ../className}}(proxyFactory, webSocketClient);
{{~/each}}
}
{{~#each functionSpecifications}}
public {{{this.returnType}}} {{{this.methodSignature}}} {
- {{~#if this.returnsValue}}
- return
- {{~else}}
- {{~/if}} this.{{this.name}}.{{this.name}}({{this.paramVariableNames}});
+ {{#if this.returnsValue}}return {{/if}}this.{{this.name}}.{{this.name}}({{this.paramVariableNames}});
}
- public {{{this.className}}} get{{{this.className}}}Function() {
- return this.{{{this.name}}};
+ public {{typeRef (classFqn this) ../className}} get{{this.className}}Function() {
+ return this.{{this.name}};
}
{{~/each}}
@@ -110,6 +116,9 @@ public class {{className}} extends PolyContext {
{{~#each specifications}}
{{~#ifIsType this "AuthFunctionSpecification"}}
+ public {{typeRef (classFqn this) ../className}} get{{this.className}}AuthFunction() {
+ return this.{{this.name}};
+ }
{{~#if subResource}}
public void {{name}}(String token) {
this.{{name}}.{{name}}(token);
@@ -127,8 +136,8 @@ public class {{className}} extends PolyContext {
}
{{~/if}}
- public {{{this.className}}} get{{{this.className}}}AuthFunction() {
- return this.{{{this.name}}};
+ public {{typeRef (classFqn this) ../className}} get{{this.className}}AuthFunction() {
+ return this.{{this.name}};
}
{{~/ifIsType}}
{{~/each}}
diff --git a/pom.xml b/pom.xml
index f69e2965..60c23271 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,12 +1,10 @@
-
+
4.0.0
io.polyapi
polyapi-java
- 0.15.4-SNAPSHOT
+ 0.15.6-SNAPSHOT
pom
parent-pom
@@ -193,6 +191,12 @@
+
+
+ --pinentry-mode
+ loopback
+
+
org.sonatype.central