{"id":15813848,"url":"https://github.com/ivankatliarchuk/sc-contract-car-rental","last_synced_at":"2025-10-12T00:32:33.166Z","repository":{"id":103302279,"uuid":"113551988","full_name":"ivankatliarchuk/sc-contract-car-rental","owner":"ivankatliarchuk","description":"consumer app","archived":false,"fork":false,"pushed_at":"2017-11-08T17:34:08.000Z","size":120,"stargazers_count":0,"open_issues_count":0,"forks_count":24,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-07T22:35:08.820Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Shell","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ivankatliarchuk.png","metadata":{"files":{"readme":"README.adoc","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"open_collective":null,"ko_fi":null,"github":["ivankatliarchuk"],"patreon":"cloudkats","otechie":"cloudkats","custom":"https://paypal.me/ivankatliarchuk","liberapay":"cloudkats","issuehunt":"ivankatliarchuk"}},"created_at":"2017-12-08T08:40:28.000Z","updated_at":"2022-11-16T13:55:28.000Z","dependencies_parsed_at":null,"dependency_job_id":"711bdc02-59d7-4703-9e97-db0d02f8574c","html_url":"https://github.com/ivankatliarchuk/sc-contract-car-rental","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ivankatliarchuk/sc-contract-car-rental","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivankatliarchuk%2Fsc-contract-car-rental","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivankatliarchuk%2Fsc-contract-car-rental/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivankatliarchuk%2Fsc-contract-car-rental/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivankatliarchuk%2Fsc-contract-car-rental/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ivankatliarchuk","download_url":"https://codeload.github.com/ivankatliarchuk/sc-contract-car-rental/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivankatliarchuk%2Fsc-contract-car-rental/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279009509,"owners_count":26084609,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-11T02:00:06.511Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-05T04:06:58.604Z","updated_at":"2025-10-12T00:32:33.144Z","avatar_url":"https://github.com/ivankatliarchuk.png","language":"Shell","funding_links":["https://github.com/sponsors/ivankatliarchuk","https://patreon.com/cloudkats","https://otechie.com/cloudkats","https://paypal.me/ivankatliarchuk","https://liberapay.com/cloudkats","https://issuehunt.io/r/ivankatliarchuk"],"categories":[],"sub_categories":[],"readme":"= Spring Cloud Contract Car Rental\n\n== car-rental (consumer)\n\nSends an HTTP request to fraud detection from a test\nGets a Stream message from fraud detection\n\n== fraud-detection (producer)\n\nProducer of the HTTP api and messages\n\n== Presentation steps\n\n=== Before\n\nStart Rabbit \u0026 Eureka\n\n[souce,bash]\n----\n$ docker-compose up -d\n$ spring cloud eureka\n----\n\n=== Start.spring.io\n\n=== car-rental\n\nweb, stubrunner, stream rabbit, wiremock, Eureka Discovery\n\nand `stream-test-support` for Contract \u0026 Stream\n\n```xml\n\t\t\u003cdependency\u003e\n\t\t\t\u003cgroupId\u003eorg.springframework.cloud\u003c/groupId\u003e\n\t\t\t\u003cartifactId\u003espring-cloud-stream-test-support\u003c/artifactId\u003e\n\t\t\t\u003cscope\u003etest\u003c/scope\u003e\n\t\t\u003c/dependency\u003e\n```\n\n=== fraud-detection\n\nweb, stream rabbit, verifier, REST Docs, wiremock, Eureka Discovery\n\nand `stream-test-support` for Contract \u0026 Stream\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eorg.springframework.cloud\u003c/groupId\u003e\n    \u003cartifactId\u003espring-cloud-stream-test-support\u003c/artifactId\u003e\n    \u003cscope\u003etest\u003c/scope\u003e\n\u003c/dependency\u003e\n```\n\nand `plugin` - REMEMBER ABOUT `EXTENSIONS`\n\n```xml\n\u003c!-- ADD A PLUGIN --\u003e\n\u003cplugin\u003e\n    \u003cgroupId\u003eorg.springframework.cloud\u003c/groupId\u003e\n    \u003cartifactId\u003espring-cloud-contract-maven-plugin\u003c/artifactId\u003e\n    \u003cversion\u003e${spring-cloud-contract.version}\u003c/version\u003e\n    \u003c!-- REMEMBER ABOUT THIS LINE --\u003e\n    \u003cextensions\u003etrue\u003c/extensions\u003e\n    \u003cconfiguration\u003e\n        \u003cbaseClassForTests\u003ecom.example.frauddetection.BaseClass\u003c/baseClassForTests\u003e\n    \u003c/configuration\u003e\n\u003c/plugin\u003e\n```\n\n\n=== Code\n\n==== HTTP\n\n*CONSUMER*\n\n- Generate consumer from start.spring.io\n- Add the missing dependencies\n- Start with a test on the consumer side\n- Write a not passing test to reach the `/fraud` endpoint of the producer\n- Add the WireMock stub to make the test pass (port `6543`)\n\n*PRODUCER*\n\n- Generate producer from start.spring.io\n- Add the missing dependencies\n- Write a contract for the `/frauds` endpoint (the typo is deliberate)\n- Configure the plugin with `baseClassForTests`\n- Run `./mvnw clean install` - show breaking tests\n- Write the controller\n- Write missing base class setup\n- Rerun the `./mvnw clean install` - things pass\n- Run the app at port `6544`\n\n*CONSUMER*\n\n- Write a new test that will reach the producer application directly (port `6544`)\n- The previous test with a stub passes, the new one fails (oops)\n- Write a new test with `StubRunnerRule` `com.example:fraud-detection` at (port `6545`)\n- The test fails cause first we'll shoot at `/fraud`\n- The test passes once we shoot at `/frauds`\n\n==== Messaging\n\n*CONSUMER*\n\n- Enable binding for Sink\n- In `application.properties`\n    * set the port to `9876`\n    * `spring.cloud.stream.bindings.input.destination=fraud`\n    * on the producer side it will be `frauds`\n- Add a `Fraud` pojo with a `name`\n- Add a `FraudListener` `@Component`\n    * with a method `fraud` that has `@StreamListener(Sink.class)`\n    * let it print a message out\n    * and store the name\n- Let's write a `FraudTests` class\n    * we want to see if our listener will work fine if we send it our\n    POJO\n    * let's write a test `should_store_info_about_fraud`\n    * make it a SpringBoot test\n    * `@Autowired FraudListener`\n    * `@Autowired Sink`\n    * given: a `new Fraud(\"marcin\")`\n    * when: `sink.input().send(MessageBuilder.withPayload(fraud).build());`\n    * then: `fraudListener.name == \"marcin\"`\n- The test passes - let's go to the producer\n\n*PRODUCER*\n\n- `EnableBinding(Source.class)`\n- Set properties\n    * `spring.cloud.stream.bindings.output.destination=frauds`\n    * Yup, that's a typo over there ^^\n    * `spring.cloud.stream.bindings.output.contentType=application/json`\n    * `server.port=6544`\n- Let's write a contract for messaging\n    * label `trigger_a_fraud`\n    * input method `triggerMethod()`\n    * output to destination `frauds`\n    * body `surname: \"Long\"`\n- Create a `Fraud` pojo in the `FraudController`\n- `FraudController` will need a `@PostMapping(\"/message\")` method called `message` that will use `Source`\nto send a message with `new Fraud(\"Long\")`\n- Let's run `./mvnw clean install` and generate tests\n    * they will fail cause we have a missing `triggerMethod()`\n- Let's create the `triggerMethod()` in the `BaseClass`\n    * Also we need to add the Spring context with `@AutoConfigureMessageVerifier`\n    * `@Autowired FraudController`\n    * call in the `triggerMethod()` the `fraudController.message()`\n\nLet's try to make both apps work! Let's run them together\n\n[source,bash]\n----\ncurl -X POST http://localhost:6544/message\n----\n\nNothing happens... Even though the tests passed. That's for 2 reasons\n\n- the destination is wrong. One is sending to `frauds` the other\nlistening to `fraud`\n- the POJO is wrong. Once expects `name` the other `surname`\n\nTime to fix the consumer\n\n*CONSUMER*\n\n- Let's use Stub Runner\n- `@AutoConfigureStubRunner(workOffline = true, ids = \"com.example:fraud-detection\")`\n- `@Autowired StubTrigger`\n- `stubTrigger.trigger(\"trigger_a_fraud\");`\n    * the test won't pass - let's update the destination\n- `spring.cloud.stream.bindings.input.destination=frauds`\n    * let's run again the tests - still they don't pass cause\n    the name is wrong\n- let's change `Fraud` to use `surname`\n    * now if we rerun the tests they pass\n\nLet's run both apps again, send the CURL - now they should work\n\n==== REST Docs\n\n*PRODUCER*\n\n- We need to write a test for the `message` endpoint\n- We'll write a test called `FraudControllerTests`\n- Use the test slices `AutoConfigureMockMvc` and `@AutoConfigureRestDocs(outputDir = \"target/snippets\")`\n+\n[source,java]\n----\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = FraudDetectionApplication.class)\n@AutoConfigureRestDocs(outputDir = \"target/snippets\")\n@AutoConfigureMockMvc\n----\n- Write a simple test to see if status OK happens when you send\na POST to `/message/`\n+\n[souce,java]\n----\n@Autowired private MockMvc mockMvc;\n\n\t@Test\n\tpublic void should_accept_a_post_message() throws Exception {\n\t\tmockMvc.perform(MockMvcRequestBuilders.post(\"/message\"))\n\t\t\t\t.andExpect(MockMvcResultMatchers.status().isOk())\n\t\t\t\t.andDo(MockMvcRestDocumentation.document(\"message\"));\n\t}\n----\n- Time to configure the build to package stuff properly\n- We want to disable the default Spring Cloud Contract packaging\napproach\n    * You can do it by setting `\u003cspring.cloud.contract.verifier.jar.skip\u003etrue\u003c/spring.cloud.contract.verifier.jar.skip\u003e`\n    property\n- Add the `src/assembly/stub.xml`\n+\n[source,xml]\n----\n\u003cassembly\n        xmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3\"\n        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n        xsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd\"\u003e\n    \u003cid\u003estubs\u003c/id\u003e\n    \u003cformats\u003e\n        \u003cformat\u003ejar\u003c/format\u003e\n    \u003c/formats\u003e\n    \u003cincludeBaseDirectory\u003efalse\u003c/includeBaseDirectory\u003e\n    \u003cfileSets\u003e\n        \u003cfileSet\u003e\n            \u003cdirectory\u003e${project.build.directory}/snippets/stubs\u003c/directory\u003e\n            \u003coutputDirectory\u003eMETA-INF/${project.groupId}/${project.artifactId}/${project.version}/mappings\u003c/outputDirectory\u003e\n            \u003cincludes\u003e\n                \u003cinclude\u003e**/*\u003c/include\u003e\n            \u003c/includes\u003e\n        \u003c/fileSet\u003e\n        \u003cfileSet\u003e\n            \u003cdirectory\u003e${project.build.directory}/stubs/META-INF/${project.groupId}/${project.artifactId}/${project.version}/mappings\u003c/directory\u003e\n            \u003coutputDirectory\u003eMETA-INF/${project.groupId}/${project.artifactId}/${project.version}/mappings\u003c/outputDirectory\u003e\n            \u003cincludes\u003e\n                \u003cinclude\u003e**/*\u003c/include\u003e\n            \u003c/includes\u003e\n        \u003c/fileSet\u003e\n        \u003cfileSet\u003e\n            \u003cdirectory\u003e${basedir}/src/test/resources/contracts\u003c/directory\u003e\n            \u003coutputDirectory\u003eMETA-INF/${project.groupId}/${project.artifactId}/${project.version}/contracts\u003c/outputDirectory\u003e\n            \u003cincludes\u003e\n                \u003cinclude\u003e**/*.groovy\u003c/include\u003e\n            \u003c/includes\u003e\n        \u003c/fileSet\u003e\n    \u003c/fileSets\u003e\n\u003c/assembly\u003e\n----\n- Add the assembly plugin setup\n+\n[source,xml]\n----\n\u003cplugin\u003e\n    \u003cgroupId\u003eorg.apache.maven.plugins\u003c/groupId\u003e\n    \u003cartifactId\u003emaven-assembly-plugin\u003c/artifactId\u003e\n    \u003cexecutions\u003e\n        \u003cexecution\u003e\n            \u003cid\u003estub\u003c/id\u003e\n            \u003cphase\u003eprepare-package\u003c/phase\u003e\n            \u003cgoals\u003e\n                \u003cgoal\u003esingle\u003c/goal\u003e\n            \u003c/goals\u003e\n            \u003cinherited\u003efalse\u003c/inherited\u003e\n            \u003cconfiguration\u003e\n                \u003cattach\u003etrue\u003c/attach\u003e\n                \u003cdescriptor\u003e${basedir}/src/assembly/stub.xml\u003c/descriptor\u003e\n            \u003c/configuration\u003e\n        \u003c/execution\u003e\n    \u003c/executions\u003e\n\u003c/plugin\u003e\n----\n- Run `./mvnw clean install`\n\n\n*CONSUMER*\n\n- Reuse the previously created setup and just send a `post`\n method to `/messages`. Expect status `200`\n\n==== Discovery\n\n*CONSUMER*\n\n- Add `@EnableDiscoveryClient`\n- Add `@Bean @LoadBalanced` for `RestTemplate`\n- Add `spring.application.name=car-rental`\n- In `FraudTests` we'll add another test (we need `@AutoConfigureStubRunner`)\n    * `@Autowired RestTemplate`\n    * Use the `RestTemplate` to call `\"http://fraud-detection/frauds\"`\n    * The test passes cause SC-Contract redirects the calls via artifact id\n\n==== Stub Runner Boot\n\n- Clone Stub Runner for Eureka \u0026 Rabbit from https://github.com/spring-cloud-samples/github-analytics-stub-runner-boot\n- Build it locally\n- `java -jar target/github-analytics-stub-runner-boot-0.0.1.M1.jar --stubrunner.workoffline=true --stubrunner.ids=com.example:fraud-detection`\n- Show http://localhost:8083/stubs\n- Show http://localhost:8083/triggers\n- Show http://localhost:8761 with registered apps\n- Run `car-rental`\n- `curl -X POST http://localhost:8083/triggers/trigger_a_fraud`\n- Check that in the logs we see that a message was received\n- Write a test that isn't annotated with `@AutoConfigureStubRunner`\nand inject `RestTemplate` - you can shoot a request to\n`fraud-detection` and a stub will respond\n    * or write a POST endpoint to `/rent` that will receive a\n    `{\"name\":\"marcin\"}` and then it will call `fraud-detection/frauds`\n    to retrieve a list of frauds. If that string contains the `name`\n    then set status code `406` and text `NO`. Otherwise `200` and `yes`\n    and run a curl `curl -X POST -d '{\"name\":\"marcin\"}' http://localhost:8765`\n\n\n==== Stub Runner Boot with a NodeJs app\n\n- Install `npm`\n- Install the `request` module `npm install request`\n- Ensure that Stub Runner Boot is not running\n- Go to `nodejs` folder and execute\n+\n```\n$ node app.js\n```\n- You will get sth like this\n+\n```\nERROR - status [404]\n```\n- Next run stub runner boot and show how easy it is to start a stub (this assumes that `fraud-detection` stubs were installed locally)\n+\n```\n$ git clone https://github.com/spring-cloud-samples/stub-runner-boot\n$ cd stub-runner-boot\n$ ./mvnw clean install -DskipTests\n$ java -jar target/stub-runner-boot-1.1.0.RELEASE.jar --stubrunner.workOffline=true --stubrunner.ids=\"com.example:fraud-detection:+:9876\"\n```\n\nor use Spring Cloud CLI\n\n```\n$ spring cloud stubrunner\n```\n\n- You will get sth like this\n+\n```javascript\n[\"marcin\",\"josh\"]\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivankatliarchuk%2Fsc-contract-car-rental","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fivankatliarchuk%2Fsc-contract-car-rental","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivankatliarchuk%2Fsc-contract-car-rental/lists"}