Skip to content

Extract common functionality into core#6

Merged
adamlogic merged 3 commits intomainfrom
am-extract-to-core
Feb 3, 2026
Merged

Extract common functionality into core#6
adamlogic merged 3 commits intomainfrom
am-extract-to-core

Conversation

@adamlogic
Copy link
Contributor

@adamlogic adamlogic commented Feb 2, 2026

We ended up with a lot of duplicated code when we added judoscale-spring-boot-2-starter. This PR extracts what we can into judoscale-core.

There's still some functionality that feels like it should be in core (like the API clients), but we're constrained that core must remain compatible with Java 8, which means we can't use java.net.http.HttpClient like judoscale-spring-boot-starter can.


Note

Medium Risk
Refactors core metrics reporting/serialization and request queue-time parsing used by both starters, so regressions could impact telemetry accuracy or reporting behavior despite largely equivalent logic.

Overview
Extracts duplicated functionality into judoscale-core by adding ConfigBase, QueueTimeCalculator, ReportBuilder, Reporter, and an Adapter model (with Jackson added as a core dependency).

Updates both judoscale-spring-boot-starter and judoscale-spring-boot-2-starter to extend/use these core utilities: configs now inherit from ConfigBase, filters call QueueTimeCalculator (and warn on unparseable headers), API clients build payloads via ReportBuilder (including per-starter adapter name/version), and Spring reporters become thin wrappers over core Reporter. Adds comprehensive unit tests in core and removes now-redundant JSON-building tests from the Spring Boot starter.

Written by Cursor Bugbot for commit 6df6964. This will update automatically on new commits. Configure here.

@adamlogic adamlogic marked this pull request as ready for review February 3, 2026 12:51
Copy link
Member

@carlosantoniodasilva carlosantoniodasilva left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice cleanup, just a few bits of food for thought.

String configuredUrl = getApiBaseUrl();
return configuredUrl != null && !configuredUrl.trim().isEmpty();
}
public class JudoscaleConfig extends ConfigBase {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do these still need to have their own class rather than using the config one? Seems like it's still necessary due to specific framework things?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, it's the @ConfigurationProperties line above that's specific to Spring Boot.

@ConfigurationProperties is Spring Boot's way of automatically binding external configuration to Java objects. With @ConfigurationProperties(prefix = "judoscale"):

# application.properties
judoscale.api-base-url=https://api.judoscale.com/abc123

or environment variables:

JUDOSCALE_URL=https://api.judoscale.com/abc123

...automatically get mapped to the corresponding setters (setApiBaseUrl(), etc.).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha, thanks for clarifying 👍

ObjectNode adapters = objectMapper.createObjectNode();
ObjectNode springBootAdapter = objectMapper.createObjectNode();
springBootAdapter.put("adapter_version", adapterVersion);
adapters.set("judoscale-spring-boot", springBootAdapter);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this handle multiple adapters somehow? I think our report builder on other packages pass the list of adapters if there are multiple (it's not the case on the java package yet because we can only have one, but it might be in the future)

e.g.: https://github.com/judoscale/judoscale-ruby/blob/aeb67bdff4370665543a8b6b5bd3abb5381dea2f/judoscale-ruby/lib/judoscale/report.rb#L18

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes definitely and this was an oversight. Core should not have any knowledge about the SpringBootAdapter. Good catch!

private static final ObjectMapper objectMapper = new ObjectMapper();
private static final int MAX_RETRIES = 3;
private static final String ADAPTER_VERSION = loadAdapterVersion();
private static final String ADAPTER_VERSION = ReportBuilder.loadAdapterVersion(JudoscaleApiClient.class);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might be able to extract more Api client functionality into core to send these requests, but I think we might need to handle the adapters reporting in the process as well (see my other comment).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The challenge with the API client is that our core library needs to support Java 8 as does the Spring Boot 2 starter, but the Spring Boot starter can use the built-in HTTP client from Java 11. For now I've kept them separate.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, makes sense... well I guess in the future we could revisit so that each lib provides the http interface, and core provides the base api client that handles the flow of request & retrying.... anyway, problem for later.

Copy link
Member

@carlosantoniodasilva carlosantoniodasilva Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, this is me playing with Claude 😄: a refactoring extracting a base class, where each implementation handles the http stuff: https://github.com/judoscale/judoscale-java/compare/ca-refactor-api-client

Copy link
Contributor Author

@adamlogic adamlogic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

String configuredUrl = getApiBaseUrl();
return configuredUrl != null && !configuredUrl.trim().isEmpty();
}
public class JudoscaleConfig extends ConfigBase {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, it's the @ConfigurationProperties line above that's specific to Spring Boot.

@ConfigurationProperties is Spring Boot's way of automatically binding external configuration to Java objects. With @ConfigurationProperties(prefix = "judoscale"):

# application.properties
judoscale.api-base-url=https://api.judoscale.com/abc123

or environment variables:

JUDOSCALE_URL=https://api.judoscale.com/abc123

...automatically get mapped to the corresponding setters (setApiBaseUrl(), etc.).

ObjectNode adapters = objectMapper.createObjectNode();
ObjectNode springBootAdapter = objectMapper.createObjectNode();
springBootAdapter.put("adapter_version", adapterVersion);
adapters.set("judoscale-spring-boot", springBootAdapter);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes definitely and this was an oversight. Core should not have any knowledge about the SpringBootAdapter. Good catch!

private static final ObjectMapper objectMapper = new ObjectMapper();
private static final int MAX_RETRIES = 3;
private static final String ADAPTER_VERSION = loadAdapterVersion();
private static final String ADAPTER_VERSION = ReportBuilder.loadAdapterVersion(JudoscaleApiClient.class);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The challenge with the API client is that our core library needs to support Java 8 as does the Spring Boot 2 starter, but the Spring Boot starter can use the built-in HTTP client from Java 11. For now I've kept them separate.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

@adamlogic adamlogic merged commit 24a11db into main Feb 3, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants