Skip to content

feat: add ArtifactUtils utility class and corresponding tests (#303)#350

Merged
fjuma merged 4 commits intoa2aproject:mainfrom
leehuiseok:main
Oct 27, 2025
Merged

feat: add ArtifactUtils utility class and corresponding tests (#303)#350
fjuma merged 4 commits intoa2aproject:mainfrom
leehuiseok:main

Conversation

@leehuiseok
Copy link
Contributor

@leehuiseok leehuiseok commented Oct 15, 2025

feat: add ArtifactUtils utility class and corresponding unit tests

Description

This PR introduces the ArtifactUtils utility class, which provides a set of static helper methods for creating Artifact instances in a standardized and concise way.
It simplifies the creation of artifacts with different content types (TextPart, DataPart, etc.) while ensuring consistent ID generation and description handling.

Details

  • Added ArtifactUtils as a final utility class with a private constructor to prevent instantiation.
  • Implemented multiple overloaded factory methods for flexible artifact creation:
    • newArtifact(List<Part<?>> parts, String name, String description)
    • newArtifact(List<Part<?>> parts, String name)
    • newTextArtifact(String name, String text, String description)
    • newTextArtifact(String name, String text)
    • newDataArtifact(String name, Map<String, Object> data, String description)
    • newDataArtifact(String name, Map<String, Object> data)
  • Each artifact automatically receives a unique artifactId via UUID.randomUUID().
  • Uses List.of() for concise creation of single-part artifacts (TextPart, DataPart).

Tests

Added comprehensive unit tests in ArtifactUtilsTest to verify:

  • UUID generation through mocking
  • Proper assignment of name, description, and parts
  • Correct creation of both text-based and data-based artifacts
  • Non-null and non-empty artifactId values

Example Usage

Artifact textArtifact = ArtifactUtils.newTextArtifact("User Profile", "Basic user info");
Artifact dataArtifact = ArtifactUtils.newDataArtifact("Config", Map.of("version", "1.0"));

Fixes:#303

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @leehuiseok, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a new ArtifactUtils utility class to streamline the process of creating Artifact objects within the system. By providing a set of static factory methods, it aims to standardize artifact construction, ensure consistent ID generation, and simplify the handling of various content types, ultimately improving code readability and maintainability when working with Artifact instances.

Highlights

  • New ArtifactUtils Class: Introduces ArtifactUtils, a final utility class with a private constructor, designed to simplify and standardize the creation of Artifact instances.
  • Flexible Artifact Creation: Implements several overloaded static factory methods (newArtifact, newTextArtifact, newDataArtifact) to support various content types (TextPart, DataPart) and optional descriptions.
  • Automated ID Generation: Ensures each new Artifact automatically receives a unique artifactId generated using UUID.randomUUID(), promoting consistency.
  • Comprehensive Unit Tests: Adds ArtifactUtilsTest with extensive unit tests to verify correct artifactId generation (with mocking), proper assignment of properties, and accurate creation of different artifact types.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a helpful ArtifactUtils class for creating Artifact instances. The implementation is clean and the tests are comprehensive. I have a few suggestions to improve consistency and maintainability:

  • Use null instead of an empty string for optional descriptions to better represent their absence.
  • Update the corresponding tests to assert for null.
  • Refactor test data creation to consistently use Map.of() for conciseness.
  • Remove a couple of redundant tests to streamline the test suite.

Overall, this is a solid addition.

* @return A new {@code Artifact} object with a generated artifact_id.
*/
public static Artifact newArtifact(List<Part<?>> parts, String name) {
return newArtifact(parts, name, "");
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

It's better to use null instead of an empty string ("") for an optional parameter that is not provided. This indicates the absence of a value more clearly and works well with Jackson's NON_ABSENT inclusion strategy on the Artifact record, preventing the serialization of an empty description field.

Suggested change
return newArtifact(parts, name, "");
return newArtifact(parts, name, null);

* @return A new {@code Artifact} object with a generated artifact_id.
*/
public static Artifact newTextArtifact(String name, String text) {
return newTextArtifact(name, text, "");
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

For consistency with using null to represent an absent description, please use null here instead of an empty string (""). This aligns with the NON_ABSENT serialization strategy on the Artifact record.

Suggested change
return newTextArtifact(name, text, "");
return newTextArtifact(name, text, null);

* @return A new {@code Artifact} object with a generated artifact_id.
*/
public static Artifact newDataArtifact(String name, Map<String, Object> data) {
return newDataArtifact(name, data, "");
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

For consistency with using null to represent an absent description, please use null here instead of an empty string (""). This aligns with the NON_ABSENT serialization strategy on the Artifact record.

Suggested change
return newDataArtifact(name, data, "");
return newDataArtifact(name, data, null);

Artifact artifact = ArtifactUtils.newArtifact(parts, name);

// Then
assertEquals("", artifact.description());
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

Following the suggestion to use null for absent descriptions instead of an empty string, this assertion should be updated to check for null. You will need to add import static org.junit.jupiter.api.Assertions.assertNull;.

Suggested change
assertEquals("", artifact.description());
assertNull(artifact.description());

Comment on lines 118 to 120
Map<String, Object> sampleData = new HashMap<>();
sampleData.put("key", "value");
sampleData.put("number", 123);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

For creating small, immutable maps for test data, Map.of() is more concise and generally preferred over instantiating a HashMap and populating it. This also improves consistency with other tests in this file that use Map.of().

Suggested change
Map<String, Object> sampleData = new HashMap<>();
sampleData.put("key", "value");
sampleData.put("number", 123);
Map<String, Object> sampleData = Map.of("key", "value", "number", 123);

Comment on lines 134 to 136
Map<String, Object> sampleData = new HashMap<>();
sampleData.put("content", "test_data");
sampleData.put("is_valid", true);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

For creating small, immutable maps for test data, Map.of() is more concise and generally preferred over instantiating a HashMap and populating it.

Suggested change
Map<String, Object> sampleData = new HashMap<>();
sampleData.put("content", "test_data");
sampleData.put("is_valid", true);
Map<String, Object> sampleData = Map.of("content", "test_data", "is_valid", true);

Comment on lines 150 to 151
Map<String, Object> sampleData = new HashMap<>();
sampleData.put("info", "some details");
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

For creating small, immutable maps for test data, Map.of() is more concise and generally preferred over instantiating a HashMap and populating it.

Suggested change
Map<String, Object> sampleData = new HashMap<>();
sampleData.put("info", "some details");
Map<String, Object> sampleData = Map.of("info", "some details");

Comment on lines 163 to 187
@Test
void testNewTextArtifactWithEmptyDescription() {
// Given
String text = "Test content";
String name = "Test_Artifact";

// When
Artifact artifact = ArtifactUtils.newTextArtifact(name, text);

// Then
assertEquals("", artifact.description());
}

@Test
void testNewDataArtifactWithEmptyDescription() {
// Given
Map<String, Object> data = Map.of("test", "value");
String name = "Test_Data_Artifact";

// When
Artifact artifact = ArtifactUtils.newDataArtifact(name, data);

// Then
assertEquals("", artifact.description());
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The tests testNewTextArtifactWithEmptyDescription and testNewDataArtifactWithEmptyDescription are redundant. The logic for handling an empty (or null) description is centralized in ArtifactUtils.newArtifact(...), which is already tested by testNewArtifactEmptyDescriptionIfNotProvided. Since the other factory methods delegate to this one, these additional tests don't add new coverage and can be removed to keep the test suite concise.

@ehsavoie
Copy link
Collaborator

@leehuiseok may you address Gemini's comments ?

@leehuiseok
Copy link
Contributor Author

@ehsavoie
I’ve reviewed Gemini’s comments and updated the document accordingly.

@ehsavoie
Copy link
Collaborator

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces the ArtifactUtils utility class with corresponding unit tests to simplify the creation of Artifact instances. The utility class provides static helper methods for creating artifacts with different content types, ensuring consistent ID generation and description handling. The unit tests cover UUID generation, assignment of name, description, and parts, and correct creation of text-based and data-based artifacts. I have provided comments to address potential issues related to null description handling and test coverage.

* @return A new {@code Artifact} object with a generated artifact_id.
*/
public static Artifact newArtifact(List<Part<?>> parts, String name) {
return newArtifact(parts, name, null);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The current implementation passes null as the description when it's not provided. It might be better to pass an empty string ("") instead to avoid potential null pointer exceptions when the description is accessed elsewhere. This provides a default value and ensures consistency.

Consider updating the newArtifact method to handle null descriptions by defaulting to an empty string.

    public static Artifact newArtifact(List<Part<?>> parts, String name) {
        return newArtifact(parts, name, ""); // Provide an empty string as default description
    }

Copy link
Collaborator

Choose a reason for hiding this comment

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

null avoid serializing it to json

Copy link
Contributor Author

@leehuiseok leehuiseok Oct 22, 2025

Choose a reason for hiding this comment

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

I aligned with your suggestion to keep description as null by default so that it’s omitted from JSON (@JsonInclude(NON_NULL)). Tests now assert null accordingly.

* @return A new {@code Artifact} object with a generated artifact_id.
*/
public static Artifact newTextArtifact(String name, String text) {
return newTextArtifact(name, text, null);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The current implementation passes null as the description when it's not provided. It might be better to pass an empty string ("") instead to avoid potential null pointer exceptions when the description is accessed elsewhere. This provides a default value and ensures consistency.

Consider updating the newTextArtifact method to handle null descriptions by defaulting to an empty string.

    public static Artifact newTextArtifact(String name, String text) {
        return newTextArtifact(name, text, ""); // Provide an empty string as default description
    }

Copy link
Collaborator

Choose a reason for hiding this comment

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

null avoid serializing the description in json

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I aligned with your suggestion to keep description as null by default so that it’s omitted from JSON (@JsonInclude(NON_NULL)). Tests now assert null accordingly.

Copy link
Collaborator

@ehsavoie ehsavoie left a comment

Choose a reason for hiding this comment

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

Maybe add methods with a Metadata parameter

* @param name The human-readable name of the artifact.
* @return A new {@code Artifact} object with a generated artifact_id.
*/
public static Artifact newArtifact(List<Part<?>> parts, String name) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe switch the parameters order to match the other methods

* @param description An optional description of the artifact.
* @return A new {@code Artifact} object with a generated artifact_id.
*/
public static Artifact newArtifact(List<Part<?>> parts, String name, String description) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe switch the parameters order to match the other methods

Copy link
Collaborator

@fjuma fjuma left a comment

Choose a reason for hiding this comment

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

Thanks @leehuiseok!

@fjuma fjuma merged commit 15fa456 into a2aproject:main Oct 27, 2025
8 checks passed
kabir pushed a commit to kabir/a2a-java that referenced this pull request Dec 23, 2025
…ject#303) (a2aproject#350)

## feat: add `ArtifactUtils` utility class and corresponding unit tests

### Description

This PR introduces the `ArtifactUtils` utility class, which provides a
set of static helper methods for creating `Artifact` instances in a
standardized and concise way.
It simplifies the creation of artifacts with different content types
(`TextPart`, `DataPart`, etc.) while ensuring consistent ID generation
and description handling.

### Details

- Added `ArtifactUtils` as a `final` utility class with a private
constructor to prevent instantiation.
- Implemented multiple overloaded factory methods for flexible artifact
creation:
  - `newArtifact(List<Part<?>> parts, String name, String description)`
  - `newArtifact(List<Part<?>> parts, String name)`
  - `newTextArtifact(String name, String text, String description)`
  - `newTextArtifact(String name, String text)`
- `newDataArtifact(String name, Map<String, Object> data, String
description)`
  - `newDataArtifact(String name, Map<String, Object> data)`
- Each artifact automatically receives a unique `artifactId` via
`UUID.randomUUID()`.
- Uses `List.of()` for concise creation of single-part artifacts
(`TextPart`, `DataPart`).

### Tests

Added comprehensive unit tests in `ArtifactUtilsTest` to verify:
- UUID generation through mocking  
- Proper assignment of `name`, `description`, and `parts`  
- Correct creation of both text-based and data-based artifacts  
- Non-null and non-empty `artifactId` values  

### Example Usage

```java
Artifact textArtifact = ArtifactUtils.newTextArtifact("User Profile", "Basic user info");
Artifact dataArtifact = ArtifactUtils.newDataArtifact("Config", Map.of("version", "1.0"));

```
Fixes:a2aproject#303
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants