Create array from multiple responses - arrays

I have web APIs which can create and delete objects, however, to delete an object I need to use its Id which is generated when I create the object (I get the new object in JSON format as a response).
The URL of the delete method is .../delete/{id}.
My question is how can put this Id into an array (I know how to put this id into a variable using regEx) and then use the values in the array in the URL of the delete method so I could create multiple objects in a row and then delete them?

Let's say you have an extractor that extracts the id into id variable.
Add after it a JSR223 Post Processor with following code:
import java.util.List;
import java.util.ArrayList;
def id = vars["id"];
List<String> listIds = (List<String>) vars.getObject("listIds");
if (listIds == null) {
listIds = new ArrayList<String>();
vars.putObject("listIds", listIds);
}
listIds.add(id);
Then at the place where you want to do the call on array add:
Flow Control Action
Add to it as a child a JSR223 PreProcessor with following code:
import java.util.List;
List<String> listIds = (List<String>) vars.getObject("listIds");
vars.put("ids_matchNr", listIds.size());
listIds.eachWithIndex{it,index->
vars.put("ids_"+(index+1), Integer.toString(it));
}
After the Flow Control Action, add a ForEach Controller, with following configuration:

Related

Spring Data Mongodb Bulk Operation Example

Can some one please point me a complete example of Spring Data Mongodb DB bulk operation example.
I am trying to switch to bulk updates using spring data mongodb. Not able to find a good example.
Thank you.
BulkOprations in Spring data mongodb uses bulkWrite() from mongodb.
From mongoDB documentation ->
So When you want to update many entities with different updated in one query you can do that via this bulkOps.
Let us see an example eventhough it may not be an perfect one. Lets consider you have an Employee Collection with employees working in a company. Now After appraisal there will be change in salary for all the employees, and each employee salary change will be different and let's pretend there is no percentage wise hike involved and if you want to update the changes in one go you can use bulkOps.
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.BulkOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.util.Pair;
public class Example {
#Autowired
MongoTemplate mongoTemplate;
public int bulkUpdateEmployee(List<Pair<Query, Update>> updates){
return mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED,"employees",Employee.class).updateMulti(updates).execute().getModifiedCount();
}
}
--------------Here we can prepare the pair of query and update from -------
-------for each employee->
---"query" - id of employee is blabla
---"update"- set salary to xxx
Sharing the code for bulk operations which worked for me
BulkOperations bulkOps = mongoTemplate.bulkOps(BulkMode.UNORDERED, Person.class);
for(Person person : personList) {
Query query = new Query().addCriteria(new Criteria("id").is(person.getId()));
Update update = new Update().set("address", "new Address as per requirement");
bulkOps.updateOne(query, update);
}
BulkWriteResult results = bulkOps.execute();
I think following code is the simple example that anybody can uderstand
Note : Ensure that custom mongo repository is correctly configured.
#Autowired
MongoTemplate mongoTemplate;
public int bulkUpdate(String member)
{
Query query = new Query();
Criteria criteria=Criteria.where("column name").is(member);
query.addCriteria(criteria);
Update update = new Update();
update.set("column name",true);
return mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED, YourModelClass.class,"name of collection").updateMulti(query,update).execute().getModifiedCount();
}
There are some elegant ways to perform the bulkOperations in Spring data mongodb refer
An excerpt from the reference
Starting in version 2.6, MongoDB servers support bulk write commands for insert, update, and delete in a way that allows the driver to implement the correct semantics for BulkWriteResult and BulkWriteException.
There are two types of bulk operations, ordered and unordered bulk operations.
Ordered bulk operations execute all the operations in order and error out on the first write error.
Unordered bulk operations execute all the operations and report any errors. Unordered bulk operations do not guarantee the order of execution.
Sample bulk operation covering most of the features
import com.mongodb.BasicDBObject;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.BulkWriteOptions;
import com.mongodb.client.model.DeleteOneModel;
import com.mongodb.client.model.InsertOneModel;
import com.mongodb.client.model.ReplaceOneModel;
import com.mongodb.client.model.UpdateOneModel;
import com.mongodb.client.model.Updates;
import com.mongodb.client.model.WriteModel;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.aggregation.Fields;
import org.springframework.stereotype.Repository;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import static org.springframework.data.mongodb.core.aggregation.Fields.UNDERSCORE_ID;
#Repository
public class BulkUpdateDemoRepository {
#Autowired
private MongoOperations mongoOperations;
public void bulkUpdate() {
MongoCollection<Document> postsCollection = mongoOperations.getCollection("posts");
//If no top-level _id field is specified in the documents, the Java driver automatically
// adds the _id field to the inserted documents.
Document document = new Document("postId", 1)
.append("name", "id labore ex et quam laborum")
.append("email", "Eliseo#gardner.biz")
.append("secondary-email", "Eliseo#gardner.biz")
.append("body", "laudantium enim quasi est");
List<WriteModel<Document>> list = Arrays.asList(
//Inserting the documents
new InsertOneModel<>(document),
//Adding a field in document
new UpdateOneModel<>(new Document(UNDERSCORE_ID, 3),
new Document().append("$set",
new BasicDBObject("postDate", new Date()))),
//Removing field from document
new UpdateOneModel<>(new Document(Fields.UNDERSCORE_ID, 4),
Updates.unset("secondary-email")),
//Deleting document
new DeleteOneModel<>(new Document(Fields.UNDERSCORE_ID, 2)),
//Replacing document
new ReplaceOneModel<>(new Document(Fields.UNDERSCORE_ID, 3),
new Document(Fields.UNDERSCORE_ID, 3)
.append("secondary-email", "Eliseo-updated#gardner.biz")));
//By default bulk Write operations is ordered because the BulkWriteOptions'
// ordered flag is true by default, by disabling that flag we can perform
// the Unordered bulk operation
//ordered execution
postsCollection.bulkWrite(list);
//2. Unordered bulk operation - no guarantee of order of operation - disabling
// BulkWriteOptions' ordered flag to perform the Unordered bulk operation
postsCollection.bulkWrite(list, new BulkWriteOptions().ordered(false));
}
}

how to pass dataprovider to any test in testNG when dataset has data not specific to this testcase

I am trying to build a Selenium hybrid framework using TestNG wherein i am getting data from my excel datasheet. I am trying to use DataProvider of testNG, But problem is since my datasheet contains data which belongs to different test case (for eg. 2 rows for add user, 1 rows for modify user, some rows for searching user etc)
since my dataprovider will return all the data from datasheet and passing it to any particular testCase that will run for all row of dataprovider will cause problem (eg. create user will need 5 parameter but the data of edit user will not be sufficient to it).
how can we handle this problem?
Here's how you do this:
Within your .xls file, create a sheet which represents a particular functionality. (For e.g, login, compose, address-book etc., if I were to be taking the example of an emailing application)
Now each sheet would have test data for various test cases, that test out that particular functionality.
In your #Test method, you can create a new custom annotation (this would be a marker annotation), which would indicate the "sheet" name from which the data provider should be retrieving data from. If you are not keen on creating a new custom annotation, then you can make use of the "description" attribute of the #Test annotation to capture this information.
TestNG can natively inject a Method object to your #DataProvider annotated method. Here the Method object that was injected would represent the #Test method for which the data provider is about to be invoked. So now you can retrieve the sheet name, either from the new custom annotation (or) from the description attribute of the #Test annotation to figure out which sheet name to query for data.
That should solve your issue.
Here's a sample that demonstrates the overall idea. You would need to enrich the data provider, such that it uses the sheet name to query data from the excel spreadsheet. My sample just excludes all of that, for the sake of demonstration.
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
#Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
#Target({METHOD})
public #interface SheetName {
String value() default "";
}
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.lang.reflect.Method;
public class TestClass {
#Test(dataProvider = "dp")
#SheetName("one")
public void test1(String name) {
System.err.println("Name is " + name);
}
#Test(dataProvider = "dp")
#SheetName("two")
public void test2(int age) {
System.err.println("Age is " + age);
}
#DataProvider(name = "dp")
public Object[][] getData(Method method) {
String sheetName = getSheetName(method);
if (sheetName == null) {
// Handle the case, wherein our custom annotation is missing. That means the test perhaps
// expects
// either all of the data, or it could be a error case.
return new Object[][] {{}};
}
if ("one".equalsIgnoreCase(sheetName)) {
return new Object[][] {{"Cedric"}, {"Beust"}};
}
if ("two".equalsIgnoreCase(sheetName)) {
return new Object[][] {{1}, {2}};
}
// Handle the case, wherein we had a valid sheet name, but it represents a sheet that cant be
// found in our
// excel spreadsheet.
return new Object[][] {{}};
}
private String getSheetName(Method method) {
SheetName sheetName = method.getAnnotation(SheetName.class);
if (sheetName == null || sheetName.value().trim().isEmpty()) {
return null;
}
return sheetName.value();
}
}

Extract text value from an object to set the text in a listView

I'm trying set the text in my listView to the string value in my FavJokes class.
class FavJokes {
var index: Int? = null
var string: String? = null
}
When ever a user favorites a joke, I create a FavObject instance and assign it values for the index(as the Jokes are stored in an array) and the string value of the joke.
val newFav = FavJokes()
newFav.index = primaFreeze!!
newFav.string = ("${questionProvider.quesRegistry[primaFreeze!!]}... ${answerProvider.ansRegistry[primaFreeze!!]}")
favorites.add(newFav)
When I try to use this array of FavJoke objects as the datasource for my listView what I see is my project id and ".FavJokes#ee8e3e" show up on the listView itself.
I don't think I should populate the listView with just the string value for the joke because for my setOnItemClick{} method, the view I segue to is going to need the index of the joke in order to populate the textView with the correct joke.
I'm new to kotlin/Java started learning it about 3 days ago, but I do have some Swift experience. In Swift I could use an IndexPath object to get/set the values for my tableView. Is there such an object or a method that I can access and set the TextView.text values in Kotlin/Java?
In Android you set an adapter on the list view and then add a setOnItemClickListener, which when called will be provided with the index of the item in the list.
One the simplest adapters is an ArrayAdapter.
Here is a tutorial from a familiar source.
And here is a quick example from some existing code.
in OnCreateView I get my list view.
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
val rootView = inflater!!.inflate(R.layout.fragment_target_selection, container, false)
targetList = rootView.findViewById(R.id.names_list) as ListView
return rootView
}
then in my onResume I receive an array of objects which I then use to create my adapter.
disposables.add(telescopeInterface.namesListStream.subscribe {
println("here is the names list")
namesList = it
namesListAdapter = ArrayAdapter(this.context, android.R.layout.simple_list_item_1, namesList?.names)
println("setting up list view with the updated list")
setupListView()
})
then in the setupListView() I assign the adapter to the list view and add a listener.
private fun setupListView() {
namesListAdapter?.let { targetList.adapter = it }
targetList.setOnItemClickListener { adapterView, view, i, l ->
targetSelectionListenter?.targetSelected()
val entry = adapterView.getItemAtPosition(i)
println("item $entry")
telescopeInterface.sendSelectedTarget(entry as DSOCatalogNameEntry)
}
}
The full object remains in the list, the adapter is just used to select what is displayed in the list view, then you get the index of the selection, which you then use to extract the full object from your source list.

Getting Collection of particular Type with Hybris ModelService

Hiy!
I want all objects(rows in Test Type) with ModelService
So I could iterate through collection and update a Single row (object)'s attribute with new value
I see getModelService.create(TestModel.class) and getModelService.save()
but will they not create a new object/row rather than update a existing object?right
I don't want to create a new one rather selecting one of the existing matching my criteria and update one attribute of that
can somebody help with List<TestModel> testModels = getModelService.get(TestModel.class) will that return me all rows (collection) of Test Type/Table?
unfortunately I can't test it so need help
Actually I am in validateInterceptor ... and on the basis of this intercepted model changed attribute value I have to update another model attribute value...
thanks
ModelService.create(new TestModel.class) will create a single instance of the specified type and attach it to the modelservice's context.
But it will only be saved to the persistence store when you call modelService.save(newInstance)
ModelService.get() returns a model object but expects a Jalo object as input, (Jalo being the legacy persistence layer of hybris) so that won't work for you.
To retrieve objects you can either write your own queries using the FlexibleSearchService or you can have a look at the DefaultGenericDao which has a bunch of simple find() type of methods.
Typically you would inject the dao like e.g.:
private GenericDao<TestModel> dao;
[...]
public void myMethod()
{
List<TestModel> allTestModels = dao.find();
[...]
}
There are a lot more methods with which you can create WHERE type of statements to restrict your result.
Regarding ValidateInterceptor:
Have a look at the wiki page for the lifecycle of interceptors:
https://wiki.hybris.com/display/release5/Interceptors
It's not a good idea to modify 'all' objects of a type while being an interceptor of that type.
So if you're in an interceptor declared for the Test item type, then don't try to modify the items there.
If you happen to be in a different interceptor and want to modify items of a different type:
E.g. you have Type1 which has a list of Type2 objects in it and in the interceptor for Type1 you want to modify all Type2 objects.
For those scenarios you would have to add the instances of Type2 that you modify to the interceptor context so that those changes will be persisted.
That would be something like:
void onValidate(Test1 model, InterceptorContext ctx) throws InterceptorException
{
...
List<Type2> type2s = dao.find();
for (Type2 type2 : type2s)
{
// do something with it
// then make sure to persist that change
ctx.registerElementFor(type2, PersistenceOperation.SAVE);
[...]
}
}
First of all - i think it's not a good idea, to create/update models in any interceptor, especially in 'validation' one.
Regarding your question:
ModelService in most of the cases works with single model, and
designed for create/update/delete operations.
To retreive all models of certain type, you have to use FlexibleSearchService
Then to update each retrieved TestType model, you can use ModelService's save method.
A query to retreive all TestType models will look like:
SELECT PK FROM {TestType}
You could simply use the Flexible Search Service search by example method, and the model service to save them all. Here is an example using Groovy script, with all products :
import java.util.List
import de.hybris.platform.core.model.product.ProductModel
import de.hybris.platform.servicelayer.search.FlexibleSearchService
import de.hybris.platform.servicelayer.model.ModelService
FlexibleSearchService fsq = spring.getBean("flexibleSearchService")
ModelService ms = spring.getBean("modelService")
ProductModel prd = ms.create(ProductModel.class)
List<ProductModel> products = fsq.getModelsByExample(prd)
//Do Whatever you want with the objects in the List
ms.saveAll(products)

Spring Data MongoDB support bulk insert/save

I have been google for a while, not sure whether Spring Data MongoDB supports for bulk save.
I need to save a collection of documents into mongo as atomic, either all saved or none saved.
Can anyone share a link or some sample code for this?
When you do a save through MongoDB Java driver you can only pass a single document to MongoDB.
When you do an insert, you can pass a single element or you can pass an array of elements. The latter is what will result in a "bulk insert" (i.e. single insert command by client will result in multiple documents being inserted on the server).
However, since MongoDB does not support a notion of transaction, if one of the inserts fails there is no way to indicate that previously inserted documents should be deleted or rolled back.
For the purposes of atomicity, each document insert is a separate operation and there is no supported way to make MongoDB either insert all or none.
If this is something that your application requires there may be other ways to achieve it:
- change your schema so that these are subdocuments of a single parent document
(then there is technically only one "insert" of the parent document)
- write the transaction semantics into your application code
- use a database which natively supports two phase commit transactions.
We have used Spring Data and Mongo Driver to achieve copying data from one database server to another.
import com.mongodb.MongoBulkWriteException;
import com.mongodb.MongoClient;
import com.mongodb.MongoException;
import com.mongodb.bulk.BulkWriteResult;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.BulkWriteOptions;
import com.mongodb.client.model.InsertOneModel;
import com.mongodb.client.model.WriteModel;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Component;
#Component
public class DataCopy{
public void copyData(MongoTemplate sourceMongo,MongoTemplate destinationMongo ){
Class cls = EmployeeEntity.class;
String collectionName = sourceMongo.getCollectionName(cls).get();
MongoCollection<Document> collection = destinationMongo.getCollection(collectionName);
Query findQuery = new Query();
Criteria criteria = new Criteria();
criteria.andOperator(Criteria.where("firstName").is("someName"),
Criteria.where("lastName").is("surname"));
query.addCriteria(criteria);
Pageable pageable = PageRequest.of(0, 10000);
findQuery.with(pageable);
List<?> pagedResult = sourceMongo.find(findQuery, cls).get()
while (!pagedResult.isEmpty()) {
try {
BulkWriteResult result = collection.bulkWrite(
pagedResult.
stream().map(d -> mapWriteModel(d, destinationMongo)).collect(Collectors.toList()),
new BulkWriteOptions().ordered(false));
} catch (Exception e) {
log.error("failed to copy", e);
}
pageable = pageable.next();
findQuery.with(pageable);
pagedResult = sourceMongo.find(findQuery, cls).get();
}
}
}
private WriteModel<? extends Document> mapWriteModel(Object obj,
MongoTemplate mongoTemplate
) {
Document document = new Document();
mongoTemplate.getConverter().write(obj, document);
return new InsertOneModel<>(document);
}
// Code example to create mongo templates for source and target databases
MongoClient targetClient = new MongoClient("databaseUri")
MongoTemplate destinationMongo = new MongoTemplate(targetClient, "databaseName");
Hope this would be helpful to you.

Resources