spring-cloud-contract-samples icon indicating copy to clipboard operation
spring-cloud-contract-samples copied to clipboard

Example with external contracts and contractsMode CLASSPATH

Open robeatoz opened this issue 5 years ago • 6 comments

Preamble

My setup contains the following modules:

Module name Corresponding module in the samples Description
my-contracts beer_contracts Module with contracts.
my-producer producer_with_external_contracts Producer of the API.
my-consumer - Consumer of the API.

I wanted to execute the Spring Cloud Contract tests during the build with contractsMode CLASSPATH. For that reason, I created an aggregator containing these three modules und executed mvn clean test.

In the samples, the beer_contracts module with external contracts does not provide the artifact with the classifier stubs. I choose another approach for my project. The module my-contracts does provide the artifact with the classifier stubs. The modules my-consumer and my-producer do not know each other but the API and the module my-contracts.

Issue

To demonstrate how Spring Cloud Contract works with external contracts, the samples contain already the modules beer_contracts and producer_with_external_contracts. This works with contractsMode LOCAL but not with contractsMode CLASSPATH.

In my project this has a couple of reasons:

After setting contractsMode to CLASSPATH in my-producer the following problems occur:

  1. No stubs were found on classpath for [com.example:beer-contracts]

    I had to add the dependency of beer-contracts to the plugin dependencies in producer_with_external_contracts like this:

    <plugin>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-contract-maven-plugin</artifactId>
      <version>${spring-cloud-contract.version}</version>
      <extensions>true</extensions>
      <configuration>
          ...
        <contractDependency>
          <groupId>de.demo</groupId>
          <artifactId>my-contracts</artifactId>
          <classifier>stubs</classifier>
        </contractDependency>
        <contractsMode>CLASSPATH</contractsMode>
          ...
      </configuration>
    
      <dependencies>
        <dependency>
          <groupId>de.demo</groupId>
          <artifactId>my-contracts</artifactId>
          <version>0.0.1.BUILD-SNAPSHOT</version>
          <classifier>stubs</classifier>
        </dependency>
      </dependencies>
    </plugin>
    

    The next error message is the following:

  2. [ERROR] Unresolveable build extension: Plugin org.springframework.cloud:spring-cloud-contract-maven-plugin:2.1.3.BUILD-SNAPSHOT or one of its dependencies could not be resolved

    Normally Maven will resolve the dependencies of modules and plugins during the build. The problem here is the <extensions>true</extensions> tag. I guess that dependencies of extensions have to be available at the time the build starts and can not be resolved during the build. To fix it, I just had to include the spring-cloud-contract-maven-plugin to the extensions and remove the <extensions>true</extensions> tag (or set it to false):

    <build>
      <extensions>
        <extension>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-contract-maven-plugin</artifactId>
          <version>${spring-cloud-contract.version}</version>
        </extension>
      </extensions>
    
      <plugins>
          ...
        <plugin>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-contract-maven-plugin</artifactId>
          <version>${spring-cloud-contract.version}</version>
          <extensions>false</extensions>
          <configuration>
              ...
            <contractsMode>CLASSPATH</contractsMode>
          </configuration>
          <dependencies>
            <dependency>
              <groupId>de.demo</groupId>
              <artifactId>my-contracts</artifactId>
              <version>0.0.1.BUILD-SNAPSHOT</version>
              <classifier>stubs</classifier>
            </dependency>
          </dependencies>
        </plugin>
      </plugins>
    </build>
    

After these fixes, the build and all Spring Cloud Contract tests run properly. Now I have the following questions:

  • Is my approach with my-contracts and artifact with classifier stubs used in consumer and producer by Spring Cloud Contracts intended?

  • How would a setup look with given modules beer_contracts and producer_with_external_contracts with contractsMode CLASSPATH? Just adapting my fixes does not work.

robeatoz avatar Aug 06 '19 07:08 robeatoz

Hi!

Is my approach with my-contracts and artifact with classifier stubs used in consumer and producer by Spring Cloud Contracts intended?

I don't think we have ever assumed that someone would use the classpath mode with external contracts.

How would a setup look with given modules beer_contracts and producer_with_external_contracts with contractsMode CLASSPATH?

For sure you need to add the dependency to the classpath.

[ERROR] Unresolveable build extension: Plugin org.springframework.cloud:spring-cloud-contract-maven-plugin:2.1.3.BUILD-SNAPSHOT or one of its dependencies could not be resolved

This seems to mean that the snapshot dependency of the plugin couldn't have been found. I don't think you need to disable the extensions to retrieve it. Since you've already downloaded the plugin, can you rollback to point number 1 and try again.

I'm actually surprised that the CLASSPATH thing works anyways ;) But it's good to see that it does

marcingrzejszczak avatar Aug 06 '19 07:08 marcingrzejszczak

Thanks for the fast feedback!

This seems to mean that the snapshot dependency of the plugin couldn't have been found.

I have the same error message with version 2.1.2.RELEASE and <extensions>true</extensions>:

Unresolveable build extension: Plugin org.springframework.cloud:spring-cloud-contract-maven-plugin:2.1.2.RELEASE or one of its dependencies could not be resolved

But this is only relevant for contractsMode CLASSPATH.

Are you interested to provide an example for classpath mode with external contracts or is this scenario too specific? In that case, I will close the issue :)

robeatoz avatar Aug 06 '19 08:08 robeatoz

I have the same error message with version 2.1.2.RELEASE and true:

wow, I'm surprised!

Are you interested to provide an example for classpath mode with external contracts or is this scenario too specific?

I think that it's specific, but if you're interested, feel free to provide a PR to this repo with a working sample (Maven and Gradle). That way, if anyone is interested, we'll show that there's an option to make this work :)

marcingrzejszczak avatar Aug 06 '19 08:08 marcingrzejszczak

I created a draft for a PR in #108. To reproduce the error message

[ERROR] Unresolveable build extension: Plugin org.springframework.cloud:spring-cloud-contract-maven-plugin:2.1.3.BUILD-SNAPSHOT or one of its dependencies could not be resolved

you just have to edit the module producer_with_external_contracts_classpath:

Remove

<extensions>
  <extension>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-contract-maven-plugin</artifactId>
    <version>${spring-cloud-contract.version}</version>
  </extension>
</extensions>

Change

<extensions>false</extensions>

to

<extensions>true</extensions>

robeatoz avatar Aug 06 '19 13:08 robeatoz

Bumping this. Same situation, I have a multi-module project with one module containing the actual REST provider, and a separate module containing the contracts (as well as an OpenAPI specification), to be deployed as a separate artifact. This layout seems logical to me, not all that specific. It would be nice if the Classpath mode supported multi-module projects.

mplain avatar Apr 13 '20 10:04 mplain

+1 to this issue I have 'producer' and 'producer-api' modules. I import 'producer-api' both to 'producer' and 'consumer' modules, so it seems natural to store 'producer' contracts in 'producer-api' module

awelless avatar Oct 25 '21 18:10 awelless