Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use strong types for additional data in problem reports #28964

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

donat
Copy link
Member

@donat donat commented Apr 25, 2024

One feature we want to support for the Problems API is to be attach domain-specific additional data to problem reports. There's a(n internal) solution that uses a string map, which is quite rudimentary and we don't want to make it part of the actual API.

The ultimate goal is to use template to declare additional data for problems.

interface Problem<S> {
    S getAdditionalData();
}

This PR implements the first step to get there: it replaces the string map in the private API with on object reference. The main change is new signature for the InternalProblemSpec.additionalData() method:

<U extends AdditionalDataSpec> InternalProblemSpec additionalData(Class<? extends U> specType, Action<? super U> config);

It allows producers to specify an action that configures additional data. The spec types, and their corresponding custom additional data types are predefined, with the (functionally sealed) AdditionalData and AdditonalDataSpec interfaces. The PR add support for three data types: generic data (ie the old string map), type validation data, and deprecation data.

We PR does not expose these new types on the Tooling API events, although it establishes the pattern on how to do it, should the use-case arise. For now, all additional data types are exposed with the original string map format with a clear documentation that the content of the map is subject of change between the releases.

@gradle gradle deleted a comment from donat Apr 25, 2024
@donat donat force-pushed the donat/problems/strongly-typed-additional-data-2 branch from 88e9456 to d46e542 Compare April 25, 2024 12:13
@gradle gradle deleted a comment from donat Apr 25, 2024
@donat donat force-pushed the donat/problems/strongly-typed-additional-data-2 branch 2 times, most recently from 23c1a4c to 8629122 Compare April 26, 2024 13:39
@donat donat changed the title Donat/problems/strongly typed additional data 2 Introduce strong t Apr 26, 2024
@donat donat changed the title Introduce strong t Introduce strong types for InternalProblemSpec.additionalData() Apr 26, 2024
@gradle gradle deleted a comment from donat Apr 26, 2024
@donat donat force-pushed the donat/problems/strongly-typed-additional-data-2 branch from 8629122 to 8c46ac0 Compare May 2, 2024 07:31
@gradle gradle deleted a comment from donat May 2, 2024
@donat donat changed the title Introduce strong types for InternalProblemSpec.additionalData() Use strong types for additional data in problem reports May 2, 2024
@donat donat changed the title Use strong types for additional data in problem reports Use strong types for additional data in problem reports May 2, 2024
@donat donat changed the title Use strong types for additional data in problem reports Use strong types for additional data in problem reports May 2, 2024
@donat donat force-pushed the donat/problems/strongly-typed-additional-data-2 branch from 8c46ac0 to 6f51fd3 Compare May 2, 2024 08:04
@gradle gradle deleted a comment from donat May 2, 2024
@donat donat self-assigned this May 7, 2024
@donat donat force-pushed the donat/problems/strongly-typed-additional-data-2 branch from 6f51fd3 to de41444 Compare May 9, 2024 14:50
@gradle gradle deleted a comment from donat May 9, 2024
@gradle gradle deleted a comment from donat May 9, 2024
@gradle gradle deleted a comment from donat May 9, 2024
@gradle gradle deleted a comment from donat May 10, 2024
@gradle gradle deleted a comment from donat May 10, 2024
@@ -25,8 +25,6 @@
public interface TypeAwareProblemBuilder extends InternalProblemSpec {
TypeAwareProblemBuilder withAnnotationType(@Nullable Class<?> classWithAnnotationAttached);

TypeAwareProblemBuilder typeIsIrrelevantInErrorMessage();
Copy link
Member Author

Choose a reason for hiding this comment

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

We can identify which type validation problems should not have the type in the error message based on the problem ID; we no longer need to have a separate field for that.

@donat donat force-pushed the donat/problems/strongly-typed-additional-data-2 branch from 5e482f1 to 6ff6f30 Compare May 10, 2024 15:40
@donat donat marked this pull request as ready for review May 10, 2024 15:40
@donat donat requested review from a team as code owners May 10, 2024 15:40
@donat donat requested review from ghale, bamboo and mlopatkin and removed request for a team, ghale, bamboo and mlopatkin May 10, 2024 15:40
@@ -78,6 +87,7 @@ public static GsonBuilder createGsonBuilder() {
gsonBuilder.registerTypeHierarchyAdapter(DocLink.class, new DocLinkAdapter());
gsonBuilder.registerTypeHierarchyAdapter(ProblemLocation.class, new LocationAdapter());
gsonBuilder.registerTypeHierarchyAdapter(ProblemCategory.class, new ProblemCategoryAdapter());
gsonBuilder.registerTypeHierarchyAdapter(AdditionalData.class, new AdditionalDataAdapter());
Copy link
Member

Choose a reason for hiding this comment

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

🤔 I didn't see this code before. Why are we using JSON for ValidationProblemSerialization while using Kryo for most other things?

Copy link
Member Author

Choose a reason for hiding this comment

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

Thank you for the review!
I am not sure. The JSON-based serialisation was already there when we got started with the Problems API integration.

Copy link
Member

Choose a reason for hiding this comment

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

It might be interesting to find out what is going on there.

Copy link
Contributor

Choose a reason for hiding this comment

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

I introduced the JSON part, it used to be just a file that we would read on the other end.
with no structured information.

Copy link
Member Author

Choose a reason for hiding this comment

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

public class DefaultProblemBuilder implements InternalProblemBuilder {
public class DefaultProblemBuilder implements InternalProblemBuilder {

private static List<Class<?>> supportedAdditionalDataTypes = ImmutableList.<Class<?>>of(
Copy link
Member

Choose a reason for hiding this comment

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

🤔 Do you already have plans how to handle extensibility here?

Copy link
Member Author

Choose a reason for hiding this comment

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

For now, I want to keep the AdditionalData as sealed class. Gradle should control what are the allowed types and disallow any extensibility from third parties.
If we have a reasonable use-case for third parties, only then I'd proceed with extensibility. The idea is to do what we do around model building: producers register what types of data they want to expose. Consumers would declare what kind of data they expect and the problems API would only forward the known items. I'd only consider this for spec/implementation if we have a very compelling use-case.


public class DefaultProblemBuilder implements InternalProblemBuilder {
public class DefaultProblemBuilder implements InternalProblemBuilder {
Copy link
Member

Choose a reason for hiding this comment

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

❓ Is there a spec for this change to help understand the reasoning?

Copy link
Member Author

Choose a reason for hiding this comment

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

I considered this as an internal refactoring, so I don't have any spec for this. At the same time, I'll add some description very soon to this PR.

Copy link
Member Author

Choose a reason for hiding this comment

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

I've updated the PR's description to reflect to your question.

@@ -86,7 +85,7 @@ public RuntimeException getException() {
}

@Override
public Map<String, Object> getAdditionalData() {
public AdditionalData<?> getAdditionalData() {
Copy link
Member

Choose a reason for hiding this comment

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

🤔 I was expecting something like this:

  • Problem gets a type parameter describing what kind of data it uses.
  • ProblemDefinition should also have the type parameter, though it probably doesn't need it in the first place
  • This getter returns the type parameter.

I understand that you need the type of the builder when adding the additional data. Though as soon as it has been created, you don't need the builder type any more, right? I'll leave a comment to the method in InternalProblemSpec.

Copy link
Member Author

Choose a reason for hiding this comment

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

That was essentially my first attempt, but the change got so big that I decided to step back and deal with the removal of the Map type for additionalData. Which is this PR.

*/
InternalProblemSpec additionalData(String key, Object value);
<T extends AdditionalData<U>, U> InternalProblemSpec additionalData(Class<? extends AdditionalData<U>> type, Action<? super U> config);
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
<T extends AdditionalData<U>, U> InternalProblemSpec additionalData(Class<? extends AdditionalData<U>> type, Action<? super U> config);
<T extends AdditionalData, U extends Builder<T>> InternalProblemSpec additionalData(Class<U> builderType, Action<? super U> config);

❓ Would something like this work? So you'd need to specify the builder type when adding additional data instead of specifying the builder with the additional data itself. That way consumers don't need to worry about the builder type.

Copy link
Member Author

Choose a reason for hiding this comment

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

That's fair, I'll take a stab at it.

Copy link
Member Author

Choose a reason for hiding this comment

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

The nice thing about the current implementation is the ease of use of the API. For example, when you write something like

 .additionalData(DeprecationData.class,

The type system will only allow you to pass an object of type Action<DeprecationDataSpec> as a second parameter.

That way consumers don't need to worry about the builder type.

It's not exactly a builder, but a spec type; a companion interface declaring what are the allowed configuration options for particular additional data type. Is this something that you'd disallow seeing on the consumer-side?

Copy link
Member

Choose a reason for hiding this comment

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

I think what you'd pass in the API is the spec type, and then the spec type should know the additional data it can build. The consumer should only be interested in the actual data, not what was used to construct it, right?

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed; ptal

*/
@Incubating
@NonNullApi
public interface TypeValidationData extends AdditionalData {
Copy link
Member

Choose a reason for hiding this comment

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

❌ This is missing a type parameter.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is a Tooling API event type; it does not have the same type parameters you have it on the producer side (ie in org.gradle.api.problems.internal.TypeValidationData).

Copy link
Member

Choose a reason for hiding this comment

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

Why do we expose the TypeValidationDatavia the TAPI? Is this something we want to have for Android Studio?

Copy link
Member Author

Choose a reason for hiding this comment

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

I wanted to expose something to showcase how we can expose custom additional data types. It doesn't have to be TypeValidationData, but now you mentioned it, we probably should not expose anything new on the Tooling API without a clear use-case.

Copy link
Member Author

Choose a reason for hiding this comment

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

I've removed the tapi event subtype representing type validation problems.

@donat donat force-pushed the donat/problems/strongly-typed-additional-data-2 branch from af6e537 to 0cbcad5 Compare May 15, 2024 07:01
@donat donat force-pushed the donat/problems/strongly-typed-additional-data-2 branch 3 times, most recently from 4c7e2ae to e59995c Compare May 16, 2024 14:10
@gradle gradle deleted a comment from donat May 16, 2024
@donat donat force-pushed the donat/problems/strongly-typed-additional-data-2 branch from e59995c to cb6a9ca Compare May 16, 2024 14:24
@donat
Copy link
Member Author

donat commented May 16, 2024

@bot-gradle test this

@gradle gradle deleted a comment from donat May 16, 2024
@bot-gradle
Copy link
Collaborator

I've triggered the following builds for you. Click here to see all build failures.

The internal Problems API allows clients to attach additional data.
The data is currently stored in a map object. This change introduces
strong, domain-specific types for the same daa.
@donat donat force-pushed the donat/problems/strongly-typed-additional-data-2 branch from cb6a9ca to 0d02c1c Compare May 16, 2024 15:05
@donat
Copy link
Member Author

donat commented May 16, 2024

@bot-gradle test this

@gradle gradle deleted a comment from donat May 16, 2024
@bot-gradle
Copy link
Collaborator

I've triggered the following builds for you. Click here to see all build failures.

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.

None yet

4 participants