I don't understand the use of assert in #PactVerification. To me it seams more like a complicated way of saying 1 == 1. For example:
import static org.assertj.core.api.Assertions.assertThat;
public class PactConsumerDrivenContractUnitTest {
#Rule
public PactProviderRuleMk2 mockProvider
= new PactProviderRuleMk2("test_provider", "localhost", 8080, this);
#Pact(consumer = "test_consumer")
public RequestResponsePact createPact(PactDslWithProvider builder) {
return builder
.given("test GET ")
.uponReceiving("GET REQUEST")
.path("/")
.method("GET")
.willRespondWith()
.body("{\"condition\": true, \"name\": \"tom\"}")
}
#Test
#PactVerification()
public void givenGet_whenSendRequest_shouldReturn200WithProperHeaderAndBody() {
//when
ResponseEntity<String> response
= new RestTemplate().getForEntity(mockProvider.getUrl(), String.class);
//then
assertThat(response.getBody()).contains("condition", "true", "name", "tom");
}
}
So first in "createPact" we state
body("{\"condition\": true, \"name\": \"tom\"}")
Then in givenGet_whenSendRequest_shouldReturn200WithProperHeaderAndBody annotated #PactVerification we do this
assertThat(response.getBody()).contains("condition", "true", "name", "tom");
But why? We just said that! As far as I can see the assertion does not show up in the generated Pact file. It seams to fill no purpose?
In addition to that, I thought that the idea of contract testing was to reduce the need for integration test since they can break for example if test data changes. But here we still depend on test data. If there are no "Tom" in the Provider, then the test will fail. I primarily wanted to test if the contract is broken, not if the test data has changed.
The example given is a contrived one. In real life using Pact, you wouldn't do this. Your PactVerification would invoke a collaboration method/class/thing which is responsible for the external call to the service you are mocking.
So your assertions are then on what the collaborating function is doing.
Eg. A User Service might create an object with certain properties, that you know only are populated by that external call.
Testing assertions in your #PactVerification test method is not mandatory, yet still it might be very helpful. E.g. you may make a typo in your JSON body string and you wont be able to catch it in your test and it will break provider's pipeline. Assertions in this case have nothing to do with generated Pact file, they play role of a guard that checks in the end if the contract you have just defined (RequestResponsePact) matches all your expectations (assertions).
Also it is worth mentioning that your consumer contract tests should break only if provider tries to release a change that makes your expectations broken. And this is consumer's responsibility to write good contract tests. In your example you have defined following expectation:
#Pact(consumer = "test_consumer")
public RequestResponsePact createPact(PactDslWithProvider builder) {
return builder
.given("test GET ")
.uponReceiving("GET REQUEST")
.path("/")
.method("GET")
.willRespondWith()
.body("{\"condition\": true, \"name\": \"tom\"}")
}
This contract will be satisfied as long as condition == true and name == tom. This is over-specification of a response. You could define more flexible response with PactDslJsonBody DSL instead:
#Pact(consumer = "test_consumer")
public RequestResponsePact createPact(PactDslWithProvider builder) {
final DslPart body = new PactDslJsonBody()
.stringType("name", "tom")
.booleanType("condition", true);
return builder
.given("test GET ")
.uponReceiving("GET REQUEST")
.path("/")
.method("GET")
.willRespondWith()
.body(body)
.toPact();
}
This fragment will generate Pact file like:
{
"provider": {
"name": "providerA"
},
"consumer": {
"name": "test_consumer"
},
"interactions": [
{
"description": "GET REQUEST",
"request": {
"method": "GET",
"path": "/"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json; charset=UTF-8"
},
"body": {
"condition": true,
"name": "tom"
},
"matchingRules": {
"body": {
"$.name": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
},
"$.condition": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
}
}
}
},
"providerStates": [
{
"name": "test GET "
}
]
}
],
"metadata": {
"pact-specification": {
"version": "3.0.0"
},
"pact-jvm": {
"version": "3.5.10"
}
}
}
The main difference is that this Pact file uses matchingRules to test if:
type of condition field is boolean
type of name field is String
For strings you can also use PactDslJsonBody.stringMatcher(name, regex, value) method if needed. It allows you to define regular expression that will be tested using current field value.
Related
`Hi All,
After LinkedIn API versioning, it seems that below projection parameter is not supported with creatives API call now :
com.linkedin.ads.SponsoredVideoCreativeVariables
com.linkedin.ads.SponsoredUpdateCarouselCreativeVariables
com.linkedin.ads.SpotlightCreativeVariablesV2
com.linkedin.ads.SponsoredUpdateCreativeVariables
com.linkedin.ads.JobsCreativeVariablesV2
com.linkedin.ads.SponsoredInMailCreativeVariables
Due to this, we are not able to fetch response similar to Old API call with new API now. This seems to be big impact to us.
For example , We are not receiving any of fields like 'showMemberProfilePhoto', 'forumName' for SpotlightCreativeVariablesV2 with new API which we used to get previously like mentioned below.
We have 2 headers in request as mentioned in LinkedIn API migration document.
X-Restli-Protocol-Version : 2.0.0
LinkedIn-Version : 202210
Old API call : https://api.linkedin.com/v2/adCreativesV2?ids=List({$creative_id})&projection=(results(*(variables(*,data(*,com.linkedin.ads.SpotlightCreativeVariablesV2(*,share~(*)))))))
Old API Response :
{
"results": {
"46524875": {
"variables": {
"clickUri": "$clickUri",
"data": {
"com.linkedin.ads.SpotlightCreativeVariablesV2": {
"showMemberProfilePhoto": true,
"forumName": "$forumName",
"description": "$description",
"logo": "$logo",
"headline": "$headline",
"callToAction": "$callToAction"
}
}
}
}
}
}
New API Call : https://api.linkedin.com/rest/creatives?ids=List(urn%3Ali%3AsponsoredCreative%3A{$creative_id})
New API Response :
{
"results": {
"urn:li:sponsoredCreative:$creative_id": {
"servingHoldReasons": ["$servingHoldReasons"],
"lastModifiedAt": 12345678000,
"lastModifiedBy": "",
"content": {
"reference": "urn:li:ugcPost:$ugc_post"
},
"createdAt": 1234562340000,
"isTest": false,
"createdBy": "$createdBy",
"review": {
"status": "APPROVED"
},
"isServing": false,
"campaign": "urn:li:sponsoredCampaign:$campaign_id",
"id": "urn:li:sponsoredCreative:$creative_id",
"intendedStatus": "ACTIVE",
"account": "urn:li:sponsoredAccount:$account_id"
}
},
"statuses": {
"urn:li:sponsoredCreative:creative_id": 200
},
"errors": {}
}
Is there any way to get all those fields we used to get in old API call with new API as we need these fields for processing.
`
I am able to fetch the record from dynamo db and view the response successfully. I need to modify the fetched 'ACCOUNTNAME' attribute in the 'items' array and update the json and also update in dynamo db. Now when I try to update the fetched records I end up with the Invalid attribute value type exception.
I was trying to update it using the key with Array of Strings which is provided with code snippet also tried to update inside for loop using the individual string but both failed with same exception as
"statusCode": 400,
"body": {
"message": "Invalid attribute value type",
"error": {
"errorMessage": "ValidationException"
}
}
I tried to create params and update the call inside the for loop by setting the key as below,
Key: {
"UUID": {
"S": usersOfAccountFromDB.body.Items[key].UUID
}
,
"TYPE": {
"S": user
}
}
but also failed with the same exception.
Fetched Json from dynamo db
[
{
"DEFINITION": "914ba44a-8c26-4b60-af0f-96b6aa37efe6",
"UUID": "830a49cb-4ed3-41ae-b111-56714a71ab98",
"TYPE": "USER",
"RELATION": "01efd131-6a5d-4068-889e-9dba44262da5",
"ACCOUNTNAME": "Wolff LLC"
},
{
"DEFINITION": "1f60fded-323d-40e1-a7f8-e2d053b0bed0",
"UUID": "47db3bbe-53ac-4e58-a378-f42331141997",
"TYPE": "USER",
"RELATION": "01efd131-6a5d-4068-889e-9dba44262da5",
"ACCOUNTNAME": "Wolff LLC"
},
{
"DEFINITION": "05ddccba-2b6d-46bd-9db4-7b897ebe16ca",
"UUID": "e7290457-db77-48fc-bd1a-7056bfce8fab",
"TYPE": "USER",
"RELATION": "01efd131-6a5d-4068-889e-9dba44262da5",
"ACCOUNTNAME": "Wolff LLC"
},
.
.
.
.]
Now I tried to iterate the Json and setup UUID which is the key as the String array as below,
var userUUIDArray : string[] = [];
for (let key in usersOfAccountFromDB.body.Items) {
userUUIDArray.push(usersOfAccountFromDB.body.Items[key].UUID);
}
for (var uuid of userUUIDArray) {
console.log("UUID : " +uuid); // prints all the uuid
}
// Creating a parameter for the update dynamo db
var params = {
TableName: <tableName>,
Key: {
"UUID": {
"SS": userUUIDArray
}
,
"TYPE": {
"S": user
}
},
UpdateExpression: 'SET #ACCOUNTNAME = :val1',
ExpressionAttributeNames: {
'#ACCOUNTNAME': 'ACCOUNTNAME' //COLUMN NAME
},
ExpressionAttributeValues: {
':val1': newAccountName
},
ReturnValues: 'UPDATED_NEW',
};
//call the update of dynamodb
const result = await this.getDocClient().update(param).promise();
I get the error as below,
"body": {
"message": "Invalid attribute value type",
"error": {
"errorMessage": "ValidationException"
}
}
All the approaches failed with same above exception
The update operation which your code currently uses only allow a single item to be updated.
IIUC, you want to update multiple items with one API call. For this you need to use batchWrite operation. Keep in mind that you cannot update more than 25 items per invocation.
The origin of the error you are getting
Your code fails due to the use of "SS" in the UUID field. This field is of type string so you must use "S". Note however that since you're using the document client API you do not need to pass values using this notation. See this answer for further details.
I have resolved the issue now by running the update statement one by one using loop
for (let key in usersOfAccountFromDB.body.Items) {
var updateParam = {
TableName: process.env.AWS_DYNAMO_TABLE,
Key: {
UUID: usersOfAccountFromDB.body.Items[key].UUID,
TYPE: user
},
UpdateExpression: "SET #ACCOUNTNAME = :val1",
ExpressionAttributeNames: {
'#ACCOUNTNAME': 'ACCOUNTNAME'
},
ExpressionAttributeValues: {
":val1": newAccountName
},
ReturnValues: "UPDATED_NEW",
};
const result = await this.getDocClient().update(updateParam).promise();
}
I am checking a if a email is already registered or not
query=googleRef.orderByChild("email").equalTo(newEmail).addValueEventListener(object :ValueEventListener{
override fun onCancelled(p0: DatabaseError) {
println(p0.code)
}
override fun onDataChange(p0: DataSnapshot) {
if(p0.exists())
{
println("Yes user exists")
}
else if(!p0.exists())
{
println("Users dont exists")
}
}
Code from comments:
I had used a push for inserting:
googleRef.child("userID").push().setValue(userId)
googleRef.child("gname").push().setValue(userName)
googleRef.child("email").push().setValue(reEmail)
googleRef.child("photoUrl").push().setValue(userpicUrl)
If you add two users, the way you're adding code is going to result in a structure like this:
"googleRef": {
"userID": {
"-Ldfs32189eqdqA1": "userID1",
"-Ldfs32189eqdqA5": "userID2"
},
"gname": {
"-Ldfs32189eqdqA2": "gname1",
"-Ldfs32189eqdqA6": "gname2"
},
"email": {
"-Ldfs32189eqdqA3": "email1",
"-Ldfs32189eqdqA7": "email2"
},
"photoUrl": {
"-Ldfs32189eqdqA4": "photoUrl1",
"-Ldfs32189eqdqA8": "photoUrl2"
}
}
So you have a separate generated push ID (the keys starting with a -) for each property of each user, which is highly uncommon.
The more idiomatic form of storing user information is either this:
"googleRef": {
"-Ldfs32189eqdqA1": {
"userID": "userID1",
"gname": "gname1",
"email": "email1",
"photoUrl": "photoUrl1"
},
"-Ldfs32189eqdqA5": {
"userID": "userID2",
"gname": "gname2"
"email": "email2"
"photoUrl": "photoUrl2"
},
}
Or (even better) this:
"googleRef": {
"userID1": {
"gname": "gname1",
"email": "email1",
"photoUrl": "photoUrl1"
},
"userID2": {
"gname": "gname2"
"email": "email2"
"photoUrl": "photoUrl2"
},
}
The reasons these last two are more common is that they group the information for each user together, which makes it easier/possible to find information for each user. In both of these cases, you can find users with a specific email address with your query.
The reason the last one is best, is because the information for each user is stored under the user's ID, which is already guaranteed to be unique. This structure makes looking up the user's information by their UID possible without needing a query.
To write a structure like the last example, use:
Map<String, Object> values = new HashMap<>();
values.put("gname", userName)
values.put("email", reEmail)
values.put("photoUrl", userpicUrl)
googleRef.child(userId).setValue(values)
A final note: you can't return whether the node exists or node, since the data is loaded from Firebase asynchronously. To learn more about what that means, and the common workaround (which is to define a callback interface), see getContactsFromFirebase() method return an empty list
I have a java based service as the provider and a node JS app as the consumer.
I used a stub runner here https://github.com/spring-cloud-samples/stub-runner-boot for Node JS to run against the wiremock. But whether it's Node JS, browser or curl as client I get this "cursor" text in place of generated string from regex elements.
This is the contract:
request {
method GET()
url value(consumer(regex('/v2/accounts/[0-9]+')))
}
response {
status 200
headers {
contentType(applicationJson())
}
body (
"firstName": regex('[a-zA-Z]*'),
"lastName": regex('[a-zA-Z]*'),
"kycStatus": regex('FAILED|PASSED|PENDING|ERROR'),
"address": [
"streetAddress" : "3244 jackson street",
"city" : "City",
"state" : regex('[a-zA-Z]{2}'),
"zipcode": regex('^\\d{5}\$')
]
)
}
This is the actual response from wiremock:
Response:
HTTP/1.1 200
Content-Type: [application/json]
{
"firstName": {
"cursor": 9
},
"lastName": {
"cursor": 9
},
"kycStatus": {
"cursor": 27
},
"address": {
"streetAddress": "3244 jackson street",
"city": "City",
"state": {
"cursor": 11
},
"zipcode": {
"cursor": 7
}
}
}
I noticed that your cursor values are actually the number of characters in your regex. So that told me something was definitely wrong. I've never ran into this before.
I think you need to wrap your regex with value()
request {
method GET()
url value(consumer(regex('/v2/accounts/[0-9]+')))
}
response {
status 200
headers {
contentType(applicationJson())
}
body (
"firstName": value(producer(regex('[a-zA-Z]*'))),
"lastName": value(producer(regex('[a-zA-Z]*'))),
"kycStatus": value(producer(regex('FAILED|PASSED|PENDING|ERROR'))),
"address": [
"streetAddress" : "3244 jackson street",
"city" : "City",
"state" : value(producer(regex('[a-zA-Z]{2}'))),
"zipcode": value(producer(regex('^\\d{5}\$')))
]
)
}
I came across another example that affects request payloads in the same way, when generating wiremock stubs.
If I do not add at least one "field" to the request body, for example:
request{
// ...
body (
value(consumer(regex("[a-zA-Z0-9]+")), producer("derp"))
)
}
the request payload is required to be
{
"cursor" : 12
}
as seen by this wiremock stub generated in target/META-INF/.../mappings/myContract.json
"bodyPatterns" : [ {
"equalToJson" : "{\"cursor\":12}",
"ignoreArrayOrder" : false,
"ignoreExtraElements" : false
} ]
Solution
All I needed to do was add at least one field to the request body
body (
aMadeUpField: value(consumer(regex("[a-zA-Z0-9]+")), producer("derp"))
)
And the regex will now work for that field, as seen by my regenerated wiremock stub
"bodyPatterns" : [ {
"matchesJsonPath" : "$[?(#.['aMadeUpField'] =~ /[a-zA-Z0-9]+/)]"
} ]
This is probably why there is a line hidden in the documentation saying only JSON is supported for the request body.
Edit: Another thing to check is "anyBoolean()" which should be changed to "aBoolean()" and "anInteger()" which should be "anyInteger()" (SCC is i guess really inconsistent with naming...). I double check if they are right in IntelliJ by ctrl + hovering over the groovy methods and making sure they return DslProperty or ClientDslProperty
And, as the other poster said, be sure to wrap any regex() with value() and play around with consumer() and producer() if needed.
Following this tutorial as a guide (OData/EntityFramework/Asp.Net).
I'm able to execute a simple GET command on the root.
{
"#odata.context": "http://localhost:49624/$metadata",
"value": [
{
"name": "Appointments",
"kind": "EntitySet",
"url": "Appointments"
},
......
{
"name": "Clients",
"kind": "EntitySet",
"url": "Clients"
}
]
}
But anything more complex than that gives me an error message. (I'm using a null routePrefix.)
http://localhost:49624/Services
Gives me:
{
"Message": "No HTTP resource was found that matches the request URI 'http://localhost:49624/Services'.",
"MessageDetail": "No type was found that matches the controller named 'Services'."
}
Here's my super simple GET
[EnableQuery]
public IQueryable<Service> Get()
{
return db.Services;
}
If it matters I'm using Postman to test these commands. Although I imagine that is a non-factor.
I have a database & a DbSet for every table. I have no idea why I can't access any of this.
WebApiConfig:
config.MapHttpAttributeRoutes();
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Appointment>("Appointments");
builder.EntitySet<Service>("Services");
builder.EntitySet<Employee>("Employees");
builder.EntitySet<Client>("Clients");
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: null,
model: builder.GetEdmModel());
I'm sorry if this is a basic question but I'm really new to all this and have been at this wall too long already haha.
Jan Hommes above pointed out above that the controller class needs to be pluralized (In my case ServiceController -> ServicesController)