Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 14 additions & 12 deletions src/main/java/com/chargebee/sdk/java/v4/JavaV4Internal.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,40 +11,42 @@
import java.util.Map;

/**
* Internal SDK generator that extends the public JavaV4 generator with batch infrastructure
* support. Generates BatchRequest, BatchEntry, BatchResult, and BatchConstants classes into the
* {@code com.chargebee.v4.internal} package.
* Internal SDK generator that extends the public JavaV4 generator with internal-only
* infrastructure. Generates batch classes (BatchRequest, BatchEntry, BatchResult, BatchConstants)
* and the InternalChargebeeClient into the {@code com.chargebee.v4.internal} package.
*/
public class JavaV4Internal extends JavaV4 {

private static final String[] BATCH_TEMPLATE_FILES = {
"batch.request", "batch.entry", "batch.result", "batch.constants"
private static final String[] INTERNAL_TEMPLATE_FILES = {
"batch.request", "batch.entry", "batch.result", "batch.constants", "internal.client"
};

private static final Map<String, String> BATCH_FILE_NAMES =
private static final Map<String, String> INTERNAL_FILE_NAMES =
Map.of(
"batch.request", "BatchRequest.java",
"batch.entry", "BatchEntry.java",
"batch.result", "BatchResult.java",
"batch.constants", "BatchConstants.java");
"batch.constants", "BatchConstants.java",
"internal.client", "InternalChargebeeClient.java");

@Override
public List<FileOp> generateSDK(String outputDirectoryPath, Spec spec) throws IOException {
List<FileOp> fileOps = new ArrayList<>(super.generateSDK(outputDirectoryPath, spec));
fileOps.addAll(generateBatchInfrastructure(outputDirectoryPath));
fileOps.addAll(generateInternalInfrastructure(outputDirectoryPath));
return fileOps;
}

private List<FileOp> generateBatchInfrastructure(String outputDirectoryPath) throws IOException {
private List<FileOp> generateInternalInfrastructure(String outputDirectoryPath)
throws IOException {
List<FileOp> fileOps = new ArrayList<>();
String internalDir = outputDirectoryPath + "/com/chargebee/v4/internal";
fileOps.add(new FileOp.CreateDirectory(internalDir, ""));

for (String templateName : BATCH_TEMPLATE_FILES) {
for (String templateName : INTERNAL_TEMPLATE_FILES) {
String resourcePath = "/templates/java/next/" + templateName + ".hbs";
String content = readResourceFile(resourcePath);
String formatted = JavaFormatter.formatSafely(content);
String fileName = BATCH_FILE_NAMES.get(templateName);
String fileName = INTERNAL_FILE_NAMES.get(templateName);
fileOps.add(new FileOp.WriteString(internalDir, fileName, formatted));
}

Expand All @@ -63,7 +65,7 @@ private String readResourceFile(String resourcePath) throws IOException {
@Override
protected Map<String, String> templatesDefinition() {
Map<String, String> templates = new HashMap<>(super.templatesDefinition());
// Batch templates are read as raw files, not compiled as Handlebars
// Internal templates are read as raw files, not compiled as Handlebars
// so no need to add them here
return templates;
}
Expand Down
9 changes: 6 additions & 3 deletions src/main/resources/templates/java/next/batch.request.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,14 @@ public class BatchRequest {
public BatchResult request() throws ChargebeeException {
String jsonPayload = buildRequestPayload();
String fullUrl = UrlBuilder.buildUrl(client.getBaseUrl(), "/batch" + uri, null);
Request httpRequest = Request.builder()
Request.Builder reqBuilder = Request.builder()
.method("POST")
.url(fullUrl)
.jsonBody(jsonPayload)
.build();
.jsonBody(jsonPayload);
for (Map.Entry<String, String> h : client.getClientHeaders().getHeaders().entrySet()) {
reqBuilder.header(h.getKey(), h.getValue());
}
Request httpRequest = reqBuilder.build();
Response response = client.executeWithInterceptor(httpRequest);
return BatchResult.fromJson(response.getBodyAsString(), response);
}
Expand Down
169 changes: 169 additions & 0 deletions src/main/resources/templates/java/next/internal.client.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
* This file is auto-generated by Chargebee.
* For more information on how to make changes to this file, please see the README.
* Reach out to dx@chargebee.com for any questions.
* Copyright 2025 Chargebee Inc.
*/

package com.chargebee.v4.internal;

import com.chargebee.v4.client.ChargebeeClient;
import com.chargebee.v4.client.request.RequestInterceptor;
import com.chargebee.v4.exceptions.ConfigurationException;
import com.chargebee.v4.internal.RetryConfig;
import com.chargebee.v4.transport.RequestLogger;
import com.chargebee.v4.transport.Transport;

import java.io.InputStream;
import java.util.Map;
import java.util.Properties;

/**
* Factory for creating Chargebee internal SDK client instances.
* Requires a mandatory {@code serviceName} parameter that is included in the User-Agent header
* for Splunk observability.
*
* <p>The User-Agent format is: {@code Chargebee-Java-Internal-Client v{version}-{serviceName}}
*
* <pre>{@code
* ChargebeeClient client = InternalChargebeeClient.builder("api_key", "site", "billing-service")
* .build();
* // Sends: User-Agent: Chargebee-Java-Internal-Client v4.0.1-billing-service
* }</pre>
*/
public final class InternalChargebeeClient {

private static final String USER_AGENT_PREFIX = "Chargebee-Java-Internal-Client";

private InternalChargebeeClient() {
// Utility class, not instantiable
}

/**
* Create a new builder with mandatory serviceName.
*
* @param apiKey the Chargebee API key (required)
* @param siteName the Chargebee site name (required)
* @param serviceName the internal microservice name, used in User-Agent for Splunk (required)
* @return a new Builder instance
*/
public static Builder builder(String apiKey, String siteName, String serviceName) {
return new Builder(apiKey, siteName, serviceName);
}

/**
* Builder that wraps {@link ChargebeeClient.Builder} with mandatory serviceName.
* Builds and returns a standard {@link ChargebeeClient} with the internal User-Agent set.
*/
public static final class Builder {
private final String serviceName;
private final ChargebeeClient.Builder delegate;

private Builder(String apiKey, String siteName, String serviceName) {
if (serviceName == null || serviceName.trim().isEmpty()) {
throw new ConfigurationException(
"serviceName is required for internal SDK client. "
+ "It identifies the calling microservice in the User-Agent header.");
}
this.serviceName = serviceName;
this.delegate = ChargebeeClient.builder(apiKey, siteName);
}

public Builder endpoint(String endpoint) {
delegate.endpoint(endpoint);
return this;
}

public Builder transport(Transport transport) {
delegate.transport(transport);
return this;
}

public Builder retry(RetryConfig retry) {
delegate.retry(retry);
return this;
}

public Builder retryConfig(RetryConfig retryConfig) {
delegate.retryConfig(retryConfig);
return this;
}

public Builder connectTimeout(int connectTimeout) {
delegate.connectTimeout(connectTimeout);
return this;
}

public Builder readTimeout(int readTimeout) {
delegate.readTimeout(readTimeout);
return this;
}

public Builder timeout(int connectTimeoutMs, int readTimeoutMs) {
delegate.timeout(connectTimeoutMs, readTimeoutMs);
return this;
}

public Builder debugLogging(boolean debugLogging) {
delegate.debugLogging(debugLogging);
return this;
}

public Builder requestLogger(RequestLogger requestLogger) {
delegate.requestLogger(requestLogger);
return this;
}

public Builder domainSuffix(String domainSuffix) {
delegate.domainSuffix(domainSuffix);
return this;
}

public Builder protocol(String protocol) {
delegate.protocol(protocol);
return this;
}

public Builder requestInterceptor(RequestInterceptor requestInterceptor) {
delegate.requestInterceptor(requestInterceptor);
return this;
}

public Builder header(String name, String value) {
delegate.header(name, value);
return this;
}

public Builder headers(Map<String, String> headers) {
delegate.headers(headers);
return this;
}

/**
* Build the ChargebeeClient with the internal User-Agent header.
* The User-Agent will be: {@code Chargebee-Java-Internal-Client v{version}-{serviceName}}
*
* @return configured ChargebeeClient with internal User-Agent
*/
public ChargebeeClient build() {
String version = getVersion();
String userAgent = USER_AGENT_PREFIX + " v" + version + "-" + serviceName;
delegate.header("User-Agent", userAgent);
return delegate.build();
}

private static String getVersion() {
try (InputStream is = InternalChargebeeClient.class.getClassLoader()
.getResourceAsStream("version.properties")) {
if (is != null) {
Properties props = new Properties();
props.load(is);
return props.getProperty("version", "unknown");
}
} catch (Exception e) {
// Ignore and use default
}
return "unknown";
}
}
}
Loading