Skip to content

Add builder for OpenAPIContract. #103

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

Merged
merged 16 commits into from
Jul 2, 2025

Conversation

lukasjelonek
Copy link
Contributor

Will allow to add new fields to the contract setup without requiring to change or to add new factory methods.

Motivation:

Split the pull request #101 into two simpler request: one for the builder part, one for the additional media types. This pull request covers the first part.

Conformance:

You should have signed the Eclipse Contributor Agreement: Done
Please also make sure you adhere to the code style guidelines: https://github.com/vert-x3/wiki/wiki/Vert.x-code-style-guidelines: May require some reformatting.

@@ -46,7 +43,7 @@ public interface OpenAPIContract {
* @return A succeeded {@link Future} holding an {@link OpenAPIContract} instance, otherwise a failed {@link Future}.
*/
static Future<OpenAPIContract> from(Vertx vertx, String unresolvedContractPath) {
return readYamlOrJson(vertx, unresolvedContractPath).compose(json -> from(vertx, json));
return builder(vertx).setContract(unresolvedContractPath).build();
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we explicitly mention, that we also support YAML files?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In the naming of the methods? Or in the javadoc?

Copy link
Contributor

Choose a reason for hiding this comment

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

In the javadoc

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 added it to the javadoc of the builder in 8fe103e

* @return The builder, for a fluent interface
*/
public OpenAPIContractBuilder setContract(String contractPath) {
this.contractFile = contractPath;
Copy link
Contributor

Choose a reason for hiding this comment

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

What is it now, Path or File?

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 will use path at every place

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 replaced all occurences of file and replaced it with path

@@ -57,7 +54,11 @@ static Future<OpenAPIContract> from(Vertx vertx, String unresolvedContractPath)
* @return A succeeded {@link Future} holding an {@link OpenAPIContract} instance, otherwise a failed {@link Future}.
*/
static Future<OpenAPIContract> from(Vertx vertx, JsonObject unresolvedContract) {
return from(vertx, unresolvedContract, emptyMap());
if (unresolvedContract == null)
return Future.failedFuture(OpenAPIContractException.createInvalidContract("Spec must not be null"));
Copy link
Contributor

Choose a reason for hiding this comment

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

I saw this check is now made in the Builder and also throws a BuilderException which makes more sense. We should not throw it here.

Of course this is kind of breaking, so we should mention it in the release notes. But it is allowed, we are still in Technical Preview and the impact is very low.

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 left it here to not break existing tests. If it is no problem to remove it, I will remove it and the corresponding test(s).

Comment on lines 159 to 166
var resolvedContracts = Future
.succeededFuture(additionalContracts)
.compose(x -> readContractFiles()
.map(r -> {
var all = new HashMap<>(x);
all.putAll(r);
return all;
}));
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
var resolvedContracts = Future
.succeededFuture(additionalContracts)
.compose(x -> readContractFiles()
.map(r -> {
var all = new HashMap<>(x);
all.putAll(r);
return all;
}));
var allAdditionalContracts = readContractFiles()
.map(resolvedFiles -> {
var all = new HashMap<>(additionalContracts);
all.putAll(resolvedFiles);
return all;
});

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point to reduce unnecessary code. Is corrected in 32fe992

});
}

private Future<OpenAPIContract> buildOpenAPIContract(JsonObject unresolvedContract,
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
private Future<OpenAPIContract> buildOpenAPIContract(JsonObject unresolvedContract,
private Future<OpenAPIContract> buildOpenAPIContract(JsonObject resolvedContract,

Now it is resolved, isn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is corrected in 2dd5ae0

Comment on lines 179 to 180
return failedFuture(createInvalidContract("Spec must not be null"));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this branch is not possible, it is also not tested. Please add a test case for this branch, or remove it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is removed in 494d5fe

}

private Future<OpenAPIContract> buildOpenAPIContract(JsonObject unresolvedContract,
Map<String, JsonObject> additionalContractFiles) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Map<String, JsonObject> additionalContractFiles) {
Map<String, JsonObject> additionalContracts) {

This are no longer contract files, they are resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is corrected in 2dd5ae0

Comment on lines 190 to 203
List<Future<?>> validationFutures = new ArrayList<>(additionalContractFiles.size());
for (String ref : additionalContractFiles.keySet()) {
// Todo: As soon a more modern Java version is used the validate part could be extracted in a private static
// method and reused below.
JsonObject file = additionalContractFiles.get(ref);
Future<?> validationFuture = version.validateAdditionalContractFile(vertx, repository, file)
.compose(v -> vertx.executeBlocking(() -> repository.dereference(ref, JsonSchema.of(ref, file))));

validationFutures.add(validationFuture);
}
return Future.all(validationFutures).map(repository);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
List<Future<?>> validationFutures = new ArrayList<>(additionalContractFiles.size());
for (String ref : additionalContractFiles.keySet()) {
// Todo: As soon a more modern Java version is used the validate part could be extracted in a private static
// method and reused below.
JsonObject file = additionalContractFiles.get(ref);
Future<?> validationFuture = version.validateAdditionalContractFile(vertx, repository, file)
.compose(v -> vertx.executeBlocking(() -> repository.dereference(ref, JsonSchema.of(ref, file))));
validationFutures.add(validationFuture);
}
return Future.all(validationFutures).map(repository);
List<Future<?>> validationFutures = new ArrayList<>(additionalContractFiles.size());
additionalContractFiles.forEach((ref, additionalContract) -> {
Future<?> validationFuture = version.validateAdditionalContractFile(vertx, repository, additionalContract)
.compose(v -> vertx.executeBlocking(() -> repository.dereference(ref, JsonSchema.of(ref, additionalContract))));
validationFutures.add(validationFuture);
});
return Future.all(validationFutures).map(repository);

Copy link
Contributor

Choose a reason for hiding this comment

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

Of course variables need to be renamed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Here the contract file naming is used in another class. I corrected the naming in 663d164 and implemented your foreach suggestion in a8bd99a

*/
@ExtendWith(VertxExtension.class)
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
public class OpenAPIContractBuilderTest {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please have a look into class ResourceHelper which is also used in OpenAPIContractTest. There are some useful helper methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Refactored strings to constants in 710040a

* @param additionalContractFiles The additional contract files
* @param vertx The related Vert.x instance.
* @param contract The unresolved contract.
* @param additionalContractPartPaths The additional contract part paths
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* @param additionalContractPartPaths The additional contract part paths
* @param additionalContractParts The additional contract parts

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 01ba794

/**
* Builder for OpenAPIContracts.<br>
*
* In the simplest case (you only have one contract) you must either provide a path to your openapi-contract in json
Copy link
Contributor

Choose a reason for hiding this comment

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

I thought we mentioned it in the JavaDoc of the methods which allows to specify a contract path or additional contract part paths

}

/**
* Puts a contract that is referenced by the main contract. This method can be
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* Puts a contract that is referenced by the main contract. This method can be
* Puts an additional contract part that is referenced by the main contract. This method can be

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 01ba794


/**
* Puts a contract that is referenced by the main contract. This method can be
* called multiple times to add multiple referenced contracts. Overrides a previously
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* called multiple times to add multiple referenced contracts. Overrides a previously
* called multiple times to add multiple referenced additional contract parts. Overrides a previously

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 01ba794

/**
* Puts a contract that is referenced by the main contract. This method can be
* called multiple times to add multiple referenced contracts. Overrides a previously
* added contract when the same key is used.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* added contract when the same key is used.
* added additional contract part when the same key is used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 01ba794

* called multiple times to add multiple referenced contracts.
*
* @param key The unique key for the contract.
* @param contractPart The contract object.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* @param contractPart The contract object.
* @param contractPart The additional contract part.

Copy link
Contributor Author

@lukasjelonek lukasjelonek Jul 1, 2025

Choose a reason for hiding this comment

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

Fixed in 01ba794

}

/**
* Uses the contracts from the provided map to resolve referenced contracts.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* Uses the contracts from the provided map to resolve referenced contracts.
* Uses the additional contract parts from the provided map to resolve referenced additional contract parts.

Copy link
Contributor Author

@lukasjelonek lukasjelonek Jul 1, 2025

Choose a reason for hiding this comment

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

Fixed in 01ba794


/**
* Uses the contracts from the provided map to resolve referenced contracts.
* Replaces all previously put contracts by {@link #putAdditionalContractPart(String, JsonObject)}.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* Replaces all previously put contracts by {@link #putAdditionalContractPart(String, JsonObject)}.
* Replaces all previously put additional contract parts by {@link #putAdditionalContractPart(String, JsonObject)}.

Copy link
Contributor Author

@lukasjelonek lukasjelonek Jul 1, 2025

Choose a reason for hiding this comment

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

Fixed in 01ba794

/**
* Uses the contracts from the provided map to resolve referenced contracts.
* Replaces all previously put contracts by {@link #putAdditionalContractPart(String, JsonObject)}.
* If the same key is used also replaces the contracts set by {@link #putAdditionalContractPartPath(String, String)}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* If the same key is used also replaces the contracts set by {@link #putAdditionalContractPartPath(String, String)}
* If the same key is used also replaces the additional contract part paths set by {@link #putAdditionalContractPartPath(String, String)}

Copy link
Contributor Author

@lukasjelonek lukasjelonek Jul 1, 2025

Choose a reason for hiding this comment

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

Fixed in 01ba794

import io.vertx.json.schema.JsonSchemaValidationException;
import io.vertx.json.schema.OutputUnit;
import io.vertx.json.schema.SchemaRepository;
import io.vertx.json.schema.*;
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in c8858f8

@pk-work pk-work merged commit 0087153 into eclipse-vertx:main Jul 2, 2025
5 checks passed
@pk-work pk-work deleted the feature/builder branch July 2, 2025 15:03
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