Spring Cloud Contract consumer side test returns 404 - wiremock

While running Spring Contract test on consumer side using stubs. I got following response first for endpoint creation then for request that was sent to it and then for the response.
Both type1 and type2 are enum fields, the other fields are string.
127.0.0.1 - POST /mappings
Connection: [keep-alive]
User-Agent: [Apache-HttpClient/4.5.12 (Java/11.0.5)]
Host: [localhost:11291]
Content-Length: [834]
Content-Type: [text/plain; charset=UTF-8]
{
"id" : "7d0b6496-be88-43c0-891d-bdb8ef8ae033",
"request" : {
"url" : "/testEndpoint",
"method" : "PUT",
"headers" : {
"Content-Type" : {
"matches" : "application/json;charset=UTF-8.*"
}
},
"bodyPatterns" : [ {
"matchesJsonPath" : "$[?(#.['name'] == 'Test')]"
}, {
"matchesJsonPath" : "$[?(#.['type1'] == 'NONE')]"
}, {
"matchesJsonPath" : "$[?(#.['type2'] == 'NONE')]"
}, {
"matchesJsonPath" : "$[?(#.['description'] == null)]"
}, {
"matchesJsonPath" : "$[?(#.['comment1'] == null)]"
}, {
"matchesJsonPath" : "$[?(#.['comment2'] == null)]"
} ]
},
"response" : {
"status" : 201,
"transformers" : [ "response-template" ]
},
"uuid" : "7d0b6496-be88-43c0-891d-bdb8ef8ae033"
}
127.0.0.1 - PUT /testEndpoint
Connection: [keep-alive]
User-Agent: [Apache-HttpClient/4.5.12 (Java/11.0.5)]
Host: [localhost:11291]
Accept-Encoding: [gzip,deflate]
Content-Length: [117]
Content-Type: [application/json; charset=UTF-8]
{"name":"Test","type1":"NONE","type2":"NONE","description":null,"comment1":null,"comment2":null}
The response I got was:
Matched response definition:
(no response definition configured)
Response:
HTTP/1.1 404
(no headers)

Actually I see that the problem is that the regex for json content type is "application/json;charset=UTF-8.*" and the actual is "application/json; charset=UTF-8" (notice the space). You should change your contract to support a regex for that space (e.g. application/json.*)

Related

Swagger 2.0 uploading a file to SP Online that doesn't send the extra content through

I'm hoping someone can point out my mistake here.
I have the following swagger definition which I use on swaggerhub that will upload a file to Sharepoint document library via the rest api
{
"swagger" : "2.0",
"info" : {
"description" : "defaultDescription",
"version" : "2",
"title" : "defaultTitle"
},
"host" : "someSite.sharepoint.com",
"schemes" : [ "https" ],
"paths" : {
"/sites/ms/_api/Web/GetFolderByServerRelativeUrl('doc/test/tt')/Files/Add(url='{filename}',overwrite=true)" : {
"post" : {
"consumes" : [ "multipart/form-data" ],
"produces" : [ "application/json" ],
"parameters" : [ {
"in" : "formData",
"name" : "upfile",
"type" : "file",
"required" : true,
"description" : "The file to upload."
},
{
"in" : "path",
"name" : "filename",
"type" : "string",
"required" : true
} ],
"responses" : {
"200" : {
"schema" : {
"type" : "string"
},
"description" : "Definition generated from Swagger Inspector"
}
}
}
}
}
}
Problem is I can't open any files on SP because they're broken, and I believe I found the reason when I tested with a txt file.
I'll send a text file only containing Sample text bu when I open it on SP doc library it contains all the following as well
-------------------------------28947758029299
Content-Disposition: form-data; name="upfile"; filename="myt.txt"
Content-Type: text/plain
Sample text
-------------------------------28947758029299--
Is the issue with my content type or should I use the parameter differently, I tried researching this but what I found just matched the original guid I found
https://swagger.io/docs/specification/2-0/file-upload/
We came across a similar issue in our project.
The root cause is that Swagger 2.0 won't let you specify a different Content-Type for type file. You have to use multipart/form-data. See details here: https://swagger.io/docs/specification/2-0/file-upload/
To resolve this, you have to change the type from file to generic object. For example:
paths:
/sharepoint_upload/:
post:
description: "This will uploads a document to SharePoint."
operationId: "uploadDocuments"
consumes:
- "application/octet-stream"
produces:
- "application/json"
parameters:
- name: "documentBody"
in: "body"
description: "The actual document"
required: true
schema:
type: "object"
responses:
200:
description: "OK - Your request was successfully completed."
400:

Spring-Data #RepositoryRestResource deleteByName uses wrong HTTP-Method when deleting a resource

I'm faced with a scenario where the custom #RepositoryRestResource interface-method is involved by the wrong HTTP-Method. For example:
#RepositoryRestResource(path = "matches", collectionResourceRel = "matches")
public interface MatchRepo extends Neo4jRepository<Match, Long> {
Collection<Match> findAllByCodeName(#Param("codeName") String codeName);
#Transactional
Long deleteAllByCodeName(#Param("codeName") String codeName);
}
Request:
curl -i -X GET 'http://localhost:8003/spring-data/api/v1/matches/search/findAllByCodeName?codeName=Test-CodeName-1'
Note the above GET HTTP-Method. This is expected, & i'm happy with the Response:
HTTP/1.1 200
Content-Type: application/hal+json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 20 Nov 2018 15:32:49 GMT
{
"_embedded" : {
"matches" : [ {
"id" : "1",
"codeName" : "Test-CodeName-1",
"round" : 1,
"me" : "ROCK",
"pc" : "ROCK",
"result" : "D",
"timestamp" : "Nov 20, 2018, 05:32:27 AM",
"lastUpdated" : "Nov 20, 2018, 05:32:27 AM",
"created" : "Nov 20, 2018, 05:32:27 AM",
"_links" : {
"self" : {
"href" : "http://localhost:8003/spring-data/api/v1/matches/22"
},
"match" : {
"href" : "http://localhost:8003/spring-data/api/v1/matches/22"
}
}
} ]
},
"_links" : {
"self" : {
"href" : "http://localhost:8003/spring-data/api/v1/matches/search/findAllByCodeName?codeName=Test-CodeName-1"
}
}
}%
This is what appears on the Intelli-J Console-Mappings:
http://localhost:8003/spring-data/api/v1/{repository}/search
& I implemented the request as indicated in the mappings, as shown below. But the problem becomes evident when I am deleting a resource with a GET HTTP-Method as shown below:
Request:
curl -i -X GET 'http://localhost:8003/spring-data/api/v1/matches/search/deleteAllByCodeName?codeName=Test-CodeName-1'
Response:
HTTP/1.1 200
Content-Type: application/hal+json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 20 Nov 2018 15:51:33 GMT
{
"10":
}
I need to find a way to make my custom deleteAllByCodeName(#Param) interface-method from the MatchRepo class to execute with the correct HTTP-Method. Must use DELETE HTTP-Method & not the GET HTTP-Method and adhere to the REST-API Design Principles.
The manual notes that search resources only support GET requests.
https://docs.spring.io/spring-data/rest/docs/3.1.2.RELEASE/reference/html/#repository-resources.search-resource
You can prevent this repo method from being exported:
#RestResource(exported = false)
Long deleteAllByCodeName(#Param("codeName") String codeName);
and create a normal Spring MVC controller that handles the delete request.

Spring Cloud Contract provider return same as request

I'm working with two microservices using Spring Cloud Contract. One providing its contract, and the other one consuming it. In one scenario the provider response is the same that the request.
So the provider contract is like this:
Contract.make {
request {
method 'POST'
url '/provider/foo'
body(
"foo": $(regex("[a-zA-Z0-9]{20}"))
)
}
response {
status 200
body(
"fooResponse": fromRequest().body("\$.foo")
)
}
And the generated wiremock mapping:
{
"id" : "a80c0871-f4c0-49e3-8cc1-94de39899669",
"request" : {
"url" : "/provider/foo",
"method" : "POST",
"bodyPatterns" : [ {
"matchesJsonPath" : "$[?(#.['foo'] =~ /[a-zA-Z0-9]{20}/)]"
} ]
},
"response" : {
"status" : 200,
"body" : "{\"fooResponse\":\"{{{jsonpath this '$.foo'}}}\"}",
"transformers" : [ "response-template" ]
},
"uuid" : "a80c0871-f4c0-49e3-8cc1-94de39899669",
"scenarioName" : "scenarioReturnSameAsRequest",
"requiredScenarioState" : "Started"
}
But when my code calls to the provider, with foo as any text, the wiremock returns:
{
"fooResponse" : "{{{jsonpath this '$.foo'}}}"
}
How can I build a contract that responses the same parameters as the request body?
Edit
I tried with a fixed value on the response and works fine:
Contract.make {
request {
method 'POST'
url '/provider/foo'
body(
"foo": $(regex("[a-zA-Z0-9]{20}"))
)
}
response {
status 200
body(
"fooResponse": "fooValue"
)
}
Now wiremock return:
{
"fooResponse" : "fooValue"
}
Maybe is not supported getting from request a regex value?
I think the mapping should contain request.body instead of this. Also I wonder if you need to use 3 times a { or just 2 times. Or do you need to escape these?
Possible mapping:
"response" : {
"status" : 200,
"body" : "{\"fooResponse\":\"{{jsonpath request.body '$.foo'}}\"}",
"transformers" : [ "response-template" ]
},
See also the chapter JSONPath helper on http://wiremock.org/docs/response-templating
I had the same problem once. You can try to use value() like this:
"fooResponse": value(fromRequest().body('$.foo'))

Spring Boot Rest search 404

I noticed periodically that /search doesn't seem to work on my #RepositoryRestResource:
Spring Boot Output
2014-08-25 13:37:51.526 INFO 10645 --- [ main] o.s.d.r.w.RepositoryRestHandlerMapping : Mapped "{[/{repository}/search],methods=[HEAD],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.http.HttpEntity<?> org.springframework.data.rest.webmvc.RepositorySearchController.headForSearches(org.springframework.data.rest.webmvc.RootResourceInformation)
2014-08-25 13:37:51.526 INFO 10645 --- [ main] o.s.d.r.w.RepositoryRestHandlerMapping : Mapped "{[/{repository}/search],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.hateoas.ResourceSupport org.springframework.data.rest.webmvc.RepositorySearchController.listSearches(org.springframework.data.rest.webmvc.RootResourceInformation)
2014-08-25 13:37:51.526 INFO 10645 --- [ main] o.s.d.r.w.RepositoryRestHandlerMapping : Mapped "{[/{repository}/search/{search}],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.http.ResponseEntity<java.lang.Object> org.springframework.data.rest.webmvc.RepositorySearchController.executeSearch(org.springframework.data.rest.webmvc.RootResourceInformation,org.springframework.web.context.request.WebRequest,java.lang.String,org.springframework.data.domain.Pageable,org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler)
2014-08-25 13:37:51.526 INFO 10645 --- [ main] o.s.d.r.w.RepositoryRestHandlerMapping : Mapped "{[/{repository}/search/{search}],methods=[GET],params=[],headers=[],consumes=[],produces=[application/x-spring-data-compact+json],custom=[]}" onto public org.springframework.hateoas.ResourceSupport org.springframework.data.rest.webmvc.RepositorySearchController.executeSearchCompact(org.springframework.data.rest.webmvc.RootResourceInformation,org.springframework.web.context.request.WebRequest,java.lang.String,java.lang.String,org.springframework.data.domain.Pageable,org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler)
2014-08-25 13:37:51.526 INFO 10645 --- [ main] o.s.d.r.w.RepositoryRestHandlerMapping : Mapped "{[/{repository}/search/{search}],methods=[HEAD],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.http.ResponseEntity<java.lang.Object> org.springframework.data.rest.webmvc.RepositorySearchController.headForSearch(org.springframework.data.rest.webmvc.RootResourceInformation,java.lang.String)
curl on /persons
curl http://localhost:8080/persons
{
"_links" : {
"self" : {
"href" : "http://localhost:8080/persons{?page,size,sort}",
"templated" : true
}
},
"_embedded" : {
"persons" : [ {
"firstName" : "Jimmy",
"lastName" : "Neutron",
"_links" : {
"self" : {
"href" : "http://localhost:8080/persons/2"
}
}
}, {
"firstName" : "Jimmy",
"lastName" : "Page",
"_links" : {
"self" : {
"href" : "http://localhost:8080/persons/3"
}
}
}, {
"firstName" : "Jimmy",
"lastName" : "Johns",
"_links" : {
"self" : {
"href" : "http://localhost:8080/persons/4"
}
}
} ]
},
"page" : {
"size" : 20,
"totalElements" : 3,
"totalPages" : 1,
"number" : 0
}
cURL on /search
curl -v http://localhost:8080/persons/search
* Adding handle: conn: 0x7f9903004400
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 0 (0x7f9903004400) send_pipe: 1, recv_pipe: 0
* About to connect() to localhost port 8080 (#0)
* Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> GET /persons/search HTTP/1.1
> User-Agent: curl/7.30.0
> Host: localhost:8080
> Accept: */*
>
< HTTP/1.1 404 Not Found
* Server Apache-Coyote/1.1 is not blacklisted
< Server: Apache-Coyote/1.1
< Content-Length: 0
< Date: Mon, 25 Aug 2014 18:55:27 GMT
<
* Connection #0 to host localhost left intact
Usually I can restart the Application and it randomly appears with 0 changes:
curl http://localhost:8080/persons
{
"_links" : {
"self" : {
"href" : "http://localhost:8080/persons{?page,size,sort}",
"templated" : true
},
"search" : {
"href" : "http://localhost:8080/persons/search"
}
},
"page" : {
"size" : 20,
"totalElements" : 0,
"totalPages" : 0,
"number" : 0
}
Is there something needed to enable search functionality 100% of the time?
I had the same problem and found out that causes it.
Spring loads only one repository for your domain type to find search resource mappings. If you have multiple repositories for your domain type, spring somehow randomly selects one and uses that one. If that one does not contain your custom search method, it does not load.
I don't quite buy the randomness argument, but if you can really reproduce it, feel free to open a ticket in our tracker.
The general rule is that the search resource is only exposed if there's at least a single query method exposed (i.e. a query method that's not actively configured to not be exported) on the repository in question.
Generally speaking, you shouldn't really let clients construct URIs on the fly but rather inspect the response for links and follow them. Thus, I'd argue that if you get the first response, performing a call to /search is invalid as the client has no reason to assume the resource to even exist. If you start with the second response, you can discover a search link advertising a search resource being available and follow it.
Getting random "Not Found" error can happen in case you have multiple repositories defined for the same domain object. It is a known Spring Data Rest bug which happens due to undeterministic order in which those repositories are discovered/registered.
Please vote at https://jira.spring.io/browse/DATAREST-923 to get it fixed.

Can I send neo4j browser a cypher query via get/post?

How can I send (programatically) a cypher query to neo4j browser (via get/post) in order to display the resulted graph?
e.g., something like: http://localhost:7474/browser/query='match n return n'
Yes, you can.
Example request
POST http://localhost:7474/db/data/cypher
Accept: application/json; charset=UTF-8
Content-Type: application/json
{
"query" : "MATCH (x {name: 'I'})-[r]->(n) RETURN type(r), n.name, n.age",
"params" : {
}
}
Example response
200: OK
Content-Type: application/json; charset=UTF-8
{
"columns" : [ "type(r)", "n.name", "n.age" ],
"data" : [ [ "know", "him", 25 ], [ "know", "you", null ] ]
}