I'm using Spring Cloud Contract Verifier
org.springframework.cloud:spring-cloud-starter-contract-verifier:jar:2.0.0.M8
org.springframework.cloud:spring-cloud-contract-maven-plugin:1.2.4.RELEASE
And I have the following contract:
request:
method: GET
url: /cars/list
response:
status: 200
headers:
Content-Type: application/json;charset=UTF-8
body:
cars:
[
{
"make": "Ford",
"model": "Fiesta",
"year": 2016,
"price": 16500.50
},
{
"make": "BMW",
"model": "X1",
"year": 2014,
"price": 22000.00
},
{
"make": "NISSAN",
"model": "Juke",
"year": 2017,
"price": 19300.00
}
]
Which is then converted in following java code:
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
assertThatJson(parsedJson).array("['cars']").contains("['model']").isEqualTo("Juke");
assertThatJson(parsedJson).array("['cars']").contains("['make']").isEqualTo("Ford");
assertThatJson(parsedJson).array("['cars']").contains("['year']").isEqualTo(2014);
assertThatJson(parsedJson).array("['cars']").contains("['year']").isEqualTo(2016);
assertThatJson(parsedJson).array("['cars']").contains("['model']").isEqualTo("Fiesta");
assertThatJson(parsedJson).array("['cars']").contains("['make']").isEqualTo("BMW");
assertThatJson(parsedJson).array("['cars']").contains("['year']").isEqualTo(2017);
assertThatJson(parsedJson).array("['cars']").contains("['make']").isEqualTo("NISSAN");
assertThatJson(parsedJson).array("['cars']").contains("['price']").isEqualTo(16500.5);
assertThatJson(parsedJson).array("['cars']").contains("['model']").isEqualTo("X1");
assertThatJson(parsedJson).array("['cars']").contains("['price']").isEqualTo(22000.0);
assertThatJson(parsedJson).array("['cars']").contains("['price']").isEqualTo(19300.0);
I have a few issues with this test:
1) It does not take order into account, meaning the following body would still be considered valid:
{
"cars": [
{
"make": "NISSAN",
"model": "Juke",
"year": 2017,
"price": 19300.00
},
{
"make": "BMW",
"model": "X1",
"year": 2014,
"price": 22000.00
},
{
"make": "Ford",
"model": "Fiesta",
"year": 2016,
"price": 16500.50
}
]
}
2) It just verifies that certain property is present anywhere in the response, meaning the following body would still be considered valid:
{
"cars": [
{
"make": "Ford",
"model": "Juke",
"year": 2016,
"price": 19300.00
},
{
"make": "BMW",
"model": "X1",
"year": 2017,
"price": 22000.00
},
{
"make": "NISSAN",
"model": "Fiesta",
"year": 2014,
"price": 16500.50
}
]
}
3) It ignores any extra values present in the response, meaning the following body would still be considered valid:
{
"cars": [
{
"make": "Ford",
"model": "Fiesta",
"year": 2017,
"price": 22000.00,
"state": "good"
},
{
"make": "NISSAN",
"model": "Juke",
"year": 2016,
"price": 19300.00,
"state": "ok"
},
{
"make": "BMW",
"model": "X1",
"year": 2014,
"price": 16500.50
},
{
"make": "Volkswagen",
"model": "Golf",
"year": 2018,
"price": 12500.00,
"state": "new"
}
]
}
I would like the generated test to be more strict and fail on any differences outlined in cases above.
Can it be achieved with Spring Cloud Contract Verifier?
To begin with you have a mismatch of versions
org.springframework.cloud:spring-cloud-starter-contract-verifier:jar:2.0.0.M8 org.springframework.cloud:spring-cloud-contract-maven-plugin:1.2.4.RELEASE
You are using a plugin in version different than the verifier. Those should match.
Now for your questions
1) It does not take order into account, meaning the following body would still be considered valid:
Yes, we don't test it out of the box. You can do it manually by providing your own JSON path matchers for the whole body (https://cloud.spring.io/spring-cloud-contract/single/spring-cloud-contract.html#contract-matchers)
2) It just verifies that certain property is present anywhere in the response, meaning the following body would still be considered valid:
Yeah, we assert that the structure is ok, that's the idea of contract tests. If you want more precise verifiactions use the matchers section (https://cloud.spring.io/spring-cloud-contract/single/spring-cloud-contract.html#contract-matchers)
3) It ignores any extra values present in the response, meaning the following body would still be considered valid:
And that's absolutely what you should be doing. Ignore unknown fields. You can read more about Postel's law here (https://en.wikipedia.org/wiki/Robustness_principle)
I would like the generated test to be more strict and fail on any differences outlined in cases above.
I think you're looking for a schema not a contract test or definition. You can use the Spring Rest Docs integration (https://cloud.spring.io/spring-cloud-contract/single/spring-cloud-contract.html#_generating_stubs_using_rest_docs) and over there enforce how exactly the stub must look like. But IMO (of course I don't know your domain) you're too restrictive.
Related
I'm integrating MPGS (Mastercard Payment Gateway Services). I want to use token to pay. But when I call the API Transaction "Pay", I get the wrong result. I want to know what went wrong. Thank you very much.
This is my payment process:
Use hosted checkout to complete the payment and save the sessionid of successful payment
Use the sessionid obtained in the first step to get the token and store the token
Reference API: POST /api/rest/version/62/merchant/{merchantId}/token
RequestBody:
{
"session": {
"id": "SESSION0002130457496K8245619M90"
}
Create session and obtain the sessionid and OrderID.
Reference API: Session: Create Checkout Session
Use Transaction: Pay to start payment
Reference API:Transaction: Pay
RequestBody:
{
"apiOperation": "PAY",
"order": {
"amount": 10.55,
"currency": "HKD"
},
"session": {
"id": "SESSION0002249161342J64341132I3"
},
"sourceOfFunds": {
"token": "5123456709720008",
"type": "SCHEME_TOKEN",
"provided": {
"card": {
"expiry": {
"month": "01",
"year": "39"
},
"storedOnFile": "TO_BE_STORED"
}
}
},
"transaction": {
"source": "INTERNET"
},
"agreement": {
"id": "m599944354",
"type": "UNSCHEDULED"
}
}
Response
{
"agreement": {
"id": "m599944354",
"type": "UNSCHEDULED"
},
"gatewayEntryPoint": "WEB_SERVICES_API",
"merchant": "myMerchantId",
"order": {
"amount": 10.55,
"authenticationStatus": "AUTHENTICATION_NOT_IN_EFFECT",
"chargeback": {
"amount": 0,
"currency": "HKD"
},
"creationTime": "2022-05-19T08:08:43.740Z",
"currency": "HKD",
"id": "2022051520752464800620225416016",
"lastUpdatedTime": "2022-05-19T08:08:43.754Z",
"merchantAmount": 10.55,
"merchantCategoryCode": "4812",
"merchantCurrency": "HKD",
"status": "FAILED",
"totalAuthorizedAmount": 0,
"totalCapturedAmount": 0,
"totalDisbursedAmount": 0,
"totalRefundedAmount": 0
},
"response": {
"gatewayCode": "BLOCKED"
},
"result": "FAILURE",
"risk": {
"response": {
"gatewayCode": "REJECTED",
"review": {
"decision": "NOT_REQUIRED"
},
"rule": [
{
"data": "NO_LIABILITY_SHIFT",
"name": "MSO_3D_SECURE",
"recommendation": "REJECT",
"type": "MSO_RULE"
},
{
"data": "512345",
"name": "MSO_BIN_RANGE",
"recommendation": "NO_ACTION",
"type": "MSO_RULE"
}
]
}
},
"sourceOfFunds": {
"provided": {
"card": {
"brand": "MASTERCARD",
"expiry": {
"month": "1",
"year": "39"
},
"fundingMethod": "CREDIT",
"number": "512345xxxxxx0008",
"scheme": "MASTERCARD",
"storedOnFile": "TO_BE_STORED"
}
},
"token": "5123456709720008",
"type": "CARD"
},
"timeOfLastUpdate": "2022-05-19T08:08:43.754Z",
"timeOfRecord": "2022-05-19T08:08:43.754Z",
"transaction": {
"acquirer": {
"id": "Myid",
"merchantId": "myMerchantId"
},
"amount": 10.55,
"authenticationStatus": "AUTHENTICATION_NOT_IN_EFFECT",
"currency": "HKD",
"id": "tran-14",
"source": "INTERNET",
"stan": "0",
"type": "PAYMENT"
},
"version": "62"
}
Problem points:
Is my method of obtaining token correct?
Whether the method of using "Transaction: Pay" is correct, and whether the parameters of request body are missing
Why is the order.authenticationStatus="AUTHENTICATION_NOT_IN_EFFECT" in the response in step 4? What "There is no authentication information associated with this transaction." means?
You need to update CARD Details using SESSION UPDATE and then make a payment using PAY.
Ensure the 3DS scheme is verified before attempting to PAY.
You don't need to provide card expiry details again in the API request body as it is already stored in the token.
Your transaction is getting BLOCKED because of Risk Rejection rule
"NO_LIABILITY_SHIFT"
"rule": [
{
"data": "**NO_LIABILITY_SHIFT**",
"name": "MSO_3D_SECURE",
"recommendation": "**REJECT**",
"type": "MSO_RULE"
}
]
The "NO_LIABILITY_SHIFT" rule is triggered when in the Merchant Admin Portal -> Transaction Filtering -> 3-D Secure Rules ->No Liability Shift -> "Reject" Screenshot and you are trying to perform Pay operation without 3DS authentication as otherwise in case of fraud or chargeback the liability will be on the merchant and acquirer side.
You need to perform EMV 3DS authentication before Pay operation. https://eu-gateway.mastercard.com/api/documentation/integrationGuidelines/supportedFeatures/pickAdditionalFunctionality/authentication/3DS/3DSecureAuthentication.html?locale=en_US
I am working on Magento2 [v2.4] Product integration use case.
I am creating a Product using REST API [postman] and observed that product price, color attributes are present in Request JSON but those are missing in Response received from Magento.
POST http://localhost/magento2/rest/V1/products
Request:
{
"product": {
"id": "2007",
"sku": "20210004",
"name": "Iphone 4",
"price": "400",
"status": 1,
"extension_attributes": {
"stock_item": {
"qty": 4,
"is_in_stock": "true"
}
},
"custom_attributes": [
{
"attribute_code": "color",
"value": 4
}
]
}
}
Response:
{
"id": 2007,
"sku": "20210004",
"name": "Iphone 4",
"attribute_set_id": 4,
"status": 1,
"visibility": 4,
"extension_attributes": {
"stock_item": {
"item_id": 18,
"product_id": 2007,
"stock_id": 1,
"qty": 4,
"is_in_stock": true,
"is_qty_decimal": false
}
},
"custom_attributes": [
{
"attribute_code": "options_container",
"value": "container2"
},
{
"attribute_code": "url_key",
"value": "iphone-4"
}
]
}
If you look at the request and response payload, you will see that Price and Color are there in request but somehow Magento did not honor those and got missed in response.
If I want to make this work, I had to re-send same payload again. Then I could see Price and Color in response.
Could you please suggest what is the issue here?
This is down to a bug in Magento (https://github.com/magento/magento2/issues/13639).
At the time of writing this should be fixed in 2.4.3. For versions 2.4.2 and lower you can add the following to your payload:
"type_id": "simple"
Trying to follow the example here: https://stripe.com/docs/issuing/cards/virtual
When I add params.AddExpand("number"), no number is returned, yet via the dashboard I was able to see the card numbers. Here's sample code and redacted info for the Req and Resp.
func (ac *appContext) CardRetrieve(id string) *stripe.IssuingCard {
stripe.Key = ac.Config.Stripe.SecretKey
params := stripe.IssuingCardParams{}
params.AddExpand("number")
params.AddExpand("cvc")
ic_num, _ := card.Get(id, ¶ms)
return ic_num
}
Returns:
{
"id": "ic_redacted",
"object": "issuing.card",
"brand": "Visa",
"cancellation_reason": null,
"cardholder": {
"id": "ich_redacted",
"object": "issuing.cardholder",
"billing": {
"address": {
"city": "A Beach",
"country": "US",
"line1": "404 Main St.",
"line2": "Suite #302",
"postal_code": "19001",
"state": "DE"
}
},
"company": null,
"created": 1613338532,
"email": "redacted#notreal.com",
"individual": {
"dob": {
"day": 20,
"month": 10,
"year": 1990
},
"first_name": "User",
"last_name": "Testing",
"verification": {
"document": {
"back": null,
"front": null
}
}
},
"livemode": false,
"metadata": {
},
"name": "User Testing",
"phone_number": "+15165551212",
"requirements": {
"disabled_reason": "under_review",
"past_due": [
]
},
"spending_controls": {
"allowed_categories": [
],
"blocked_categories": [
],
"spending_limits": [
{
"amount": 1,
"categories": [
],
"interval": "daily"
}
],
"spending_limits_currency": "usd"
},
"status": "active",
"type": "individual"
},
"created": 1613338532,
"currency": "usd",
"exp_month": 1,
"exp_year": 2024,
"last4": "0088",
"livemode": false,
"metadata": {
},
"replaced_by": null,
"replacement_for": null,
"replacement_reason": null,
"shipping": null,
"spending_controls": {
"allowed_categories": null,
"blocked_categories": null,
"spending_limits": [
{
"amount": 1,
"categories": [
],
"interval": "daily"
}
],
"spending_limits_currency": "usd"
},
"status": "inactive",
"type": "virtual"
}
What confuses me is the documentation found here:
https://stripe.com/docs/issuing/cards/virtual
It says: You can retrieve both the full unredacted card number and CVC from the API. For security reasons, these fields are only available for virtual cards and will be omitted unless you explicitly request them with the expand property. Additionally, they are only available through the Retrieve a card endpoint. That links to the issue card retrieval end point, but the params defined in the virtual cards example references the CardParams{} struct.
No of the examples show what imported module their aliasing for card to exec card.Get, but it stands to reason given the flow of the documentation that this should be IssuingCardParams{} and that the card alias is referencing: "github.com/stripe/stripe-go/issuing/card"
I also find it strange that we're defining params in the example but not passing it into the card.Get()
Edit:
I went digging through the module and it seems like to get the card details you have to call: details, _ := card.Details(id, params) but I get a 404 when trying to call that. The object returned is actually the right object and I see number and cvc, albeit nil.
I get the following error:
2021/02/15 00:33:06 Request error from Stripe (status 404): {"status":404,"message":"Unrecognized request URL (GET: /v1/issuing/cards/ic_redacted/details). Please see https://stripe.com/docs
So it seems you need to include a /v72 in the import:
"github.com/stripe/stripe-go/v72"
The documentation should be updated to show this and the virtual card example for go should also be updated.
I am trying to use the Trello service hook with Team Foundation Server. I have followed this tutorial and the connection worked without problems but, I can't get the informations I need from the Event JSON:
The Description I Tried
This is the Event JSON:
{
"id": "03c164c2-8912-4d5e-8009-3707d5f83734",
"eventType": "git.push",
"publisherId": "tfs",
"scope": 0,
"message": {
"text": "Jamal Hartnett pushed updates to Fabrikam-Fiber-Git:master.",
"html": "Jamal Hartnett pushed updates to Fabrikam-Fiber-Git:master.",
"markdown": "Jamal Hartnett pushed updates to `Fabrikam-Fiber-Git`:`master`."
},
"detailedMessage": {
"text": "Jamal Hartnett pushed a commit to Fabrikam-Fiber-Git:master.\n - Fixed bug in web.config file 33b55f7c",
"html": "Jamal Hartnett pushed a commit to Fabrikam-Fiber-Git:master.\n<ul>\n<li>Fixed bug in web.config file 33b55f7c\n</ul>",
"markdown": "Jamal Hartnett pushed a commit to [Fabrikam-Fiber-Git](https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_git/Fabrikam-Fiber-Git/):[master](https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_git/Fabrikam-Fiber-Git/#version=GBmaster).\n* Fixed bug in web.config file [33b55f7c](https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_git/Fabrikam-Fiber-Git/commit/33b55f7cb7e7e245323987634f960cf4a6e6bc74)"
},
"resource": {
"commits": [
{
"commitId": "33b55f7cb7e7e245323987634f960cf4a6e6bc74",
"author": {
"name": "Jamal Hartnett",
"email": "fabrikamfiber4#hotmail.com",
"date": "2015-02-25T19:01:00Z"
},
"committer": {
"name": "Jamal Hartnett",
"email": "fabrikamfiber4#hotmail.com",
"date": "2015-02-25T19:01:00Z"
},
"comment": "Fixed bug in web.config file",
"url": "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_git/Fabrikam-Fiber-Git/commit/33b55f7cb7e7e245323987634f960cf4a6e6bc74"
}
],
"refUpdates": [
{
"name": "refs/heads/master",
"oldObjectId": "aad331d8d3b131fa9ae03cf5e53965b51942618a",
"newObjectId": "33b55f7cb7e7e245323987634f960cf4a6e6bc74"
}
],
"repository": {
"id": "278d5cd2-584d-4b63-824a-2ba458937249",
"name": "Fabrikam-Fiber-Git",
"url": "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_apis/git/repositories/278d5cd2-584d-4b63-824a-2ba458937249",
"project": {
"id": "6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
"name": "Fabrikam-Fiber-Git",
"url": "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_apis/projects/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
"state": "wellFormed",
"visibility": "unchanged"
},
"defaultBranch": "refs/heads/master",
"remoteUrl": "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_git/Fabrikam-Fiber-Git"
},
"pushedBy": {
"id": "00067FFED5C7AF52#Live.com",
"displayName": "Jamal Hartnett",
"uniqueName": "Windows Live ID\\fabrikamfiber4#hotmail.com"
},
"pushId": 14,
"date": "2014-05-02T19:17:13.3309587Z",
"url": "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_apis/git/repositories/278d5cd2-584d-4b63-824a-2ba458937249/pushes/14"
},
"resourceVersion": "1.0",
"resourceContainers": {
"collection": {
"id": "c12d0eb8-e382-443b-9f9c-c52cba5014c2"
},
"account": {
"id": "f844ec47-a9db-4511-8281-8b63f4eaf94e"
},
"project": {
"id": "be9b3917-87e6-42a4-a549-2bc06a7a878f"
}
},
"createdDate": "2017-07-21T16:48:44.312Z"
}
This is the Request that was send to Trello:
Method: POST
URI: https://api.trello.com/1/cards?key=7d6630fd03ac2b6fc9fde2f2ef0c4096&token=********
HTTP Version: 1.1
Headers:
{
Content-Type: application/json; charset=utf-8
}
Content:
{
"name": "Test Nº ",
"desc": "Description: ",
"pos": "top",
"due": null,
"labels": "green",
"idList": "5935a0d45ff8e5a6c8f828b9"
}
The only field that I could get/read from the Event JSON was the "message".
What am I doing wrong?
If you want to get the push ID and comments, the description is:
{{push.pushId}}
{{push.commits[0].comment}}
Update
Eddie is right you should use push as the resource in this case. The docs are not so clear. In the end, it was something so simple. However the corresponding checkin is not work for Code checked in event.
The docs could definitely be more clear in how the resource are for each event which applied to help avoid this confusion.
The basic form of the placeholder is {{resource.field}} where
resource is the name of the resource raising the event (workitem, build, etc) and field is a field within the resource section of the
event, like id. So, if the subscription were for a completed build,
it might be something like:
Build {{build.id}} completed at {{build.finishTime}}
Seems the issue is you are using the wrong placeholder in this case. According to your send Request , resource.pushID & resource.commits.[0].comment is not replaced by values from the event that was raised.
I've looked through the documentation, but perhaps I've overlooked what I assume to be a straightforward task. Is it possible to provide a custom binding function so that, in an array of objects, each object corresponds to one cell, rather than each object corresponding to a full row? Would this binding maintain the reference to the original object so that the data would change after being modified in the spreadsheet?
For example, I'd want to create the following sheet:
With JSON in this structure:
[
{
"name": "USA",
"year": 2015,
"sales": 1,
},
{
"name": "USA",
"year": 2016,
"sales": 2,
},
{
"name": "USA",
"year": 2017,
"sales": 3,
},
{
"name": "Canada",
"year": 2015,
"sales": 4,
},
{
"name": "Canada",
"year": 2016,
"sales": 5,
},
{
"name": "Canada",
"year": 2017,
"sales": 6,
}
]
You should look at the columns definition. In there you can define the data source for each column such that it will iterate through your objects and set the values of each column given the id for that column. And yes, it uses references so if you edit them, your objects get edited as well.