How can I mock two Uri components in one method - java

I have two url calls in one method that is in addnewMap() - one is the buildGetSubtenantsURL and the other is buildGetAssetsURL
public void addNewMap(MapDTO mapDTO) {
log.info("going to add the map data into db");
if (mapRepository.existsMapWithMapName(mapDTO.getMapName()))
throw new BadRequestException("Map with map name " + mapDTO.getMapName()
+ " already exists. Please provide a different map name.");
Map<String, String> subtenantInfoMap = new HashMap<>();
Maps mapEntity = new Maps();
String iottenant = mapDTO.getTenant();
String subtenantsURL = buildGetSubtenantsURL(null);
String subTenantsResponse = getSubtenants(subtenantsURL,iottenant);
JSONObject subTenant = getSubtenantName(subTenantsResponse);
checkForMultiplePagesSubtenants(subTenantsResponse, subtenantInfoMap,iottenant);
if(subtenantInfoMap.get(mapDTO.getSubtenantName()) != null) {
mapEntity = Maps.builder().subtenant(subtenantInfoMap.get(mapDTO.getSubtenantName()).toString()).build();
}
else {
throw new DataNotFoundException(SUBTENANT_DOESNT_EXIST);
}
String SubtenantId = subtenantInfoMap.get(mapDTO.getSubtenantName());
UriComponents assetsURL = buildGetAssetsURL(iottenant,SubtenantId);
String assetsResponse = getAssets(assetsURL, iottenant);
String mindsphereAssetId = getAssetId(assetsResponse);
if(mindsphereAssetId.isEmpty()) {
throw new DataNotFoundException(ASSET_ID_DOESNT_EXIST);
}
else {
mapEntity = Maps.builder().mindsphereAssetId(mindsphereAssetId).build();
}
mapEntity = Maps.builder().mapName(mapDTO.getMapName()).displayName(getDisplayName(mapDTO))
.description(Objects.nonNull(mapDTO.getDescription()) ? mapDTO.getDescription() : null)
.tenant(getTenantNameForMapDTO(mapDTO)).mindsphereAssetId(mindsphereAssetId).subtenant(subtenantInfoMap.get(mapDTO.getSubtenantName()).toString())
.mapLocation(mapDTO.getMapLocation()).operator(mapDTO.getOperator()).recipeName(mapDTO.getRecipeName()).subtenantName(mapDTO.getSubtenantName())
.createdBy(getUserEmail()).createdAt(new Timestamp(System.currentTimeMillis()))
.build();
Maps createdMap = mapRepository.saveAndFlush(mapEntity);
addStationsMappingforNewMap(createdMap);
}
I have written the test case for the above method as:
#Test
public void addNewMap() {
map = Maps.builder().mapId(1l).mapName("testMap").displayName("Map Test").mindsphereAssetId("a0609ebf2eb7400da8a5fd707e7f68b7").mapLocation("hyd").operator("operator").recipeName("recipe").subtenantName("NSTI").tenant("ctlbrdev").subtenant("9b04027dde5fbd047073805ab8c1c87c")
.tenant(Tenant).build();
maps = Arrays.asList(map);
mapDTO = MapDTO.builder().mapId(1l).mapName("testMap").displayName("Map Test").subtenantName("NSTI").mapLocation("hyd").recipeName("recipe").operator("operator").description("description")
.tenant("ctlbrdev").build();
ReflectionTestUtils.setField(mapService, "mindsphereBaseURL", MindsphereBaseURL);
ReflectionTestUtils.setField(mapService, "mindsphereSubtenantsURL", mindsphereSubtenantsURL);
ReflectionTestUtils.setField(mapService, "mindsphereAssetsURL", mindsphereAssetsURL);
when(restTemplate.exchange(ArgumentMatchers.anyString(), ArgumentMatchers.any(HttpMethod.class), ArgumentMatchers.any(HttpEntity.class),
ArgumentMatchers.<Class<String>>any()))
.thenReturn(new ResponseEntity<String>(entityDtoCreaters.getSubtenant(),HttpStatus.OK));
when(tokenCaching.retrieveHeadersContainingTechToken("ctblrdev")).thenReturn(new HttpHeaders());
when(mapRepository.existsMapWithMapName(any())).thenReturn(false);
//doReturn(Tenant).when(mapService).getTenantName();
doReturn(EMAIL).when(mapService).getUserEmail();
when(mapRepository.saveAndFlush(any())).thenReturn(map);
when(restTemplate.exchange(ArgumentMatchers.anyString(), ArgumentMatchers.any(HttpMethod.class), ArgumentMatchers.any(HttpEntity.class),
ArgumentMatchers.<Class<String>>any()))
.thenReturn(new ResponseEntity<String>(entityDtoCreaters.getSubtenant(),HttpStatus.OK));
Map<String, String> subtenantInfoMap = new HashMap<>();
subtenantInfoMap.get(mapDTO.getSubtenantName());
mapService.addNewMap(mapDTO);
It is not covering the getAssets() method, hence not covering the whole method. how can I achieve this?

Related

Why does my executorservice.submit returns null?

I want to call several external APIs concurrently with multithreading in java spring, then I use it like this:
#Service
public class ServiceImpl implements Service {
public static final Logger logger = LoggerFactory.getLogger("ServiceImpl");
#Autowired
SM sm;
#Transactional
#Override
public Map getReturnsSeq(Map params) throws Exception {
Map result1 = sm.callAPI1(params);
Map result2 = sm.callAPI2(params);
Map result3 = sm.callAPI3(params);
Map result4 = sm.callAPI4(params);
Map returnMap = new HashMap<>();
List comb1and2 = new ArrayList();
comb1and2.addAll((List) result1.get("data"));
comb1and2.addAll((List) result2.get("list_data"));
Comparator<Map<String, String>> mapComparator = (o1, o2) -> o2.get("date").compareTo(o1.get("date"));
comb1and2.sort(mapComparator);
returnMap.put("cmb12", comb1and2);
returnMap.put("api_3", result3);
returnMap.put("api_4", result4);
return returnMap
}
#Transactional
#Override
public Map getReturnsCon(Map params) throws Exception {
ExecutorService e1 = Executors.newFixedThreadPool(1);
ExecutorService e2 = Executors.newFixedThreadPool(1);
ExecutorService e3 = Executors.newFixedThreadPool(1);
ExecutorService e4 = Executors.newFixedThreadPool(1);
Map returnMap = new HashMap<>();
try {
Future<Map> result1 = e1.submit(new RecommendationAndHistory(params, 1));
Future<Map> result2 = e2.submit(new RecommendationAndHistory(params, 2));
Future<Map> result3 = e3.submit(new RecommendationAndHistory(params, 3));
Future<Map> result4 = e4.submit(new RecommendationAndHistory(params, 4));
List comb1and2 = new ArrayList();
comb1and2.addAll(result1.get().get("data"));
comb1and2.addAll(result2.get().get("list_data"));
Comparator<Map<String, String>> mapComparator = (o1, o2) -> o2.get("date").compareTo(o1.get("date"));
comb1and2.sort(mapComparator);
returnMap.put("cmb12", comb1and2);
returnMap.put("api_3", result3.get());
returnMap.put("api_4", result4.get());
} catch (Exception e){
logger.debug(e.getMessage());
} finally {
e1.shutdown();
e2.shutdown();
e3.shutdown();
e4.shutdown();
}
return returnMap;
}
class RecommendationAndHistory implements Callable<Map> {
private Map params;
private int code;
public RecommendationAndHistory(Map param, int cd) {
this.params = param;
this.code = cd;
}
#Override
public Map call() throws Exception {
if(code == 1) {
return sm.callAPI1(params);
} else if(code == 2) {
return sm.callAPI2(params);
} else if(code == 3) {
return sm.callAPI3(params);
} else if(code == 4) {
return sm.callAPI4(params);
}
return null;
}
}
}
Here is example of the callAPI1:
#Override
public function callAPI1(Map params) {
ExternalAPI e = externalAPIDao.getExternalAPI("API_1");
String url = e.getUrl() + "/get_history";
RestMiddlewareCommunicator rest = new RestMiddlewareCommunicator();
rest.setInputStream(true);
rest.setOutputStream(true);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
RestTemplate restTemplate = this.get_resttemplate();
HttpEntity entity = new HttpEntity(headers);
ResponseEntity<Map> response = restTemplate.exchange(url, HttpMethod.GET, entity, Map.class);
return response.getBody();
}
The externalAPIDao.getExternalAPI(String name) contains:
Session session = this.sessionFactory.getCurrentSession();
ExternalAPI e = session.createQuery("from EAPI where name = :name").setParameter("name", name).uniqueResult();
return e;
I can run the getReturnsSeq normally everytime, but takes very very long time. However, I got null everytime I run getReturnsCon. So, what is actually wrong from my code above and how to resolve it?
Edit
The results of the executors were null because of the HibernateException that can't get the currentSession for the currently running thread. Therefore I come back to the sequential process although it takes a long time, since currently I can't find the way out of this.
I am still wondering about how can I overcome this long execution time with multithreading without getting such exception from Hibernate. Any help is greatly appreciated!

how to fetch and validate csv header in open csv?

I want to fetch header from csv file . If I am not use this skipLines then I will get header at 0 index array . But I want to fetch header directly using HeaderColumnNameMappingStrategy but it will not work with my code.
I also want to validate header column list ( like csv had not allowed to contain extra column)
I had also check this How to validate the csv headers using opencsv but it was not helpful to me.
#SuppressWarnings({ "unchecked", "rawtypes" })
public Map<String, Object> handleStockFileUpload(MultipartFile file, Long customerId) {
Map<String, Object> responseMap = new HashMap<>();
responseMap.put("datamap", "");
responseMap.put("errormap", "");
responseMap.put("errorkeys", "");
List<Map<String, Integer>> list = new ArrayList<>();
List<StockCsvDTO> csvStockList = new ArrayList<>();
try {
String fileName = new SimpleDateFormat("yyyy_MM_dd_HHmmss").format(new Date()) + "_" + file.getOriginalFilename();
responseMap.put("filename", fileName);
File stockFile = new File(productsUploadFilePath + fileName);
stockFile.getParentFile().mkdirs();
FileOutputStream fos = new FileOutputStream(stockFile);
fos.write(file.getBytes());
fos.close();
CsvTransfer csvTransfer = new CsvTransfer();
ColumnPositionMappingStrategy ms = new ColumnPositionMappingStrategy();
ms.setType(StockCsv.class);
Reader reader = Files.newBufferedReader(Paths.get(productsUploadFilePath + fileName));
CSVReader csvReader = new CSVReader(reader);
CsvToBean cb = new CsvToBeanBuilder(reader)
.withType(StockCsv.class)
.withMappingStrategy(ms)
.withSkipLines(1)
.build();
csvTransfer.setCsvList(cb.parse());
reader.close();
csvStockList = csvTransfer.getCsvList();
} catch (Exception e) {
e.printStackTrace();
responseMap.put("status", "servererror");
}
responseMap.put("datamap", csvStockList);
return responseMap;
}
I found the following solution:
Use #CsvBindByName with HeaderColumnNameMappingStrategy,e.g. annotate your bean properties with #CsvBindByName:
public static class HollywoodActor {
private int id;
#CsvBindByName(column = "First Name")
private String firstName;
#CsvBindByName(column = "Last Name")
private String lastName;
// getter / setter
}
Add a method like this:
public class CsvParser {
public <T> ParseResult<T> parseByPropertyNames(Reader csvReader, Class<T> beanClass) throws IOException {
CSVReader reader = new CSVReaderBuilder(csvReader).withCSVParser(new
CSVParserBuilder().build()).build();
CsvToBean<T> bean = new CsvToBean();
HeaderColumnNameMappingStrategy<T> mappingStrategy = new HeaderColumnNameMappingStrategy();
mappingStrategy.setType(beanClass);
bean.setMappingStrategy(mappingStrategy);
bean.setCsvReader(reader);
List<T> beans = bean.parse();
return new CsvParseResult<>(mappingStrategy.generateHeader(), beans);
}
and also don't forget to add public class ParseResult
public class ParseResult <T> {
private final String[] headers;
private final List<T> lines;
// all-args constructor & getters
}
Use then use them in your code:
String csv = "Id,First Name,Last Name\n" + "1, \"Johnny\", \"Depp\"\n" + "2, \"Al\", \"Pacino\"";
CsvParseResult<HollywoodActor> parseResult = parser
.parseByPropertyNames(new InputStreamReader(new ByteArrayInputStream(csv.getBytes(StandardCharsets.UTF_8), HollywoodActor.class)));
From ParseResult.headers you can get actual headers from which were in your .csv file. Just compare them with what's expected.
Hope that helps!
Here I was comparing my csvHeader with originalHeader:
List<String> originalHeader = fileUploadUtility.getHeader(new StockCsv());
List<String> invalidHeader = csvHeader.stream().filter(o -> (originalHeader.stream().filter(f -> f.equalsIgnoreCase(o)).count()) < 1).collect(Collectors.toList());
if(null != invalidHeader && invalidHeader.size() > 0 && invalidHeader.toString().replaceAll("\\[\\]", "").length() > 0) {
msg = "Invalid column(s) : " + invalidHeader.toString().replace(", ]", "]") + ". Please remove invalid column(s) from file.";
resultMap.put(1, msg);
}
public List<String> getHeader(T pojo) {
// TODO Auto-generated method stub
final CustomMappingStrategy<T> mappingStrategy = new CustomMappingStrategy<>();
mappingStrategy.setType((Class<? extends T>) pojo.getClass());
String header[] = mappingStrategy.generateHeader();
List<String> strHeader = Arrays.asList(header);
return strHeader;
}
Here is an alternative to your present problem.First, define what you expect your headers to look like. For example:
public static final ArrayList<String> fileFormat = new ArrayList<> (Arrays.asList("Values1", "Values2", "Values3", "Values4"));
Now, write a method to return custom errors if any exist:
public String validateCsvFileDetails(MultipartFile file, Set<String> requiredHeadersArray) {
Set<String> errors = new HashSet<>();
try {
InputStream stream = file.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
String headerLine = reader.readLine();
if (Objects.isNull(headerLine))
return "The file has no headers, please ensure it has the correct upload format";
List<String> headersInFileList;
String[] headersInFileArray;
if (headerLine.contains(",")) {
headersInFileArray = StringUtils.split(headerLine, ",");
headersInFileList = Arrays.asList(headersInFileArray);
} else//the headerline has only one headerfield
{
headersInFileList = Collections.singletonList(headerLine);
}
for (String header : requiredHeadersArray) {
if (!headersInFileList.contains(header))
errors.add("The file has the wrong header format, please ensure " + header + " header is present");
}
//if there are errors, return it
if (!errors.isEmpty())
return sysUtils.getStringFromSet(errors);
//Ensure the csv file actually has values after the header, but don't read beyond the first line
String line;
int counter = 0;
while ((line = reader.readLine()) != null) {
counter++;
if (counter > 0)
break;
}
//if line is null return validation error
if (Objects.isNull(line))
return "Cannot upload empty file";
} catch (Exception e) {
logger.error(new Object() {
}.getClass().getEnclosingMethod().getName(), e);
return "System Error";
}
return null;
}
Now you can validate you file headers as follows:
String errors = validateCsvFileDetails(file, new HashSet<>(fileFormat));
if (errors != null)
return error
//proceed
Give this a try using captureHeader as a pre-filter:
...
private class CustomHeaderColumnNameMappingStrategy<T> extends HeaderColumnNameMappingStrategy {
private String[] expectedHeadersOrdered = {"Column1", "Column2", "Column3", "Column4", "Column5"};
#Override
public void captureHeader(CSVReader reader) throws IOException, CsvRequiredFieldEmptyException {
String[] actualCsvHeaders = reader.peek();
String actualHeader, expectedHeader;
if (expectedHeadersOrdered.length > actualCsvHeaders.length) {
throw new CsvRequiredFieldEmptyException("Missing header column.");
} else if (expectedHeadersOrdered.length < actualCsvHeaders.length) {
throw new IOException("Unexpected extra header column.");
}
// Enforce strict column ordering with index
// TODO: you might want to employ simple hashMap, List, set, etc. as needed
for (int i=0; i<actualCsvHeaders.length; i++) {
actualHeader = actualCsvHeaders[i];
expectedHeader = expectedHeadersOrdered[i];
if ( ! expectedHeader.equals(actualHeader) ) {
throw new IOException("Header columns mismatch in ordering.");
}
}
super.captureHeader(reader); // Back to default processing if the headers include ordering are as expected
}
}
CustomHeaderColumnNameMappingStrategy yourMappingStrategy = new CustomHeaderColumnNameMappingStrategy<YourPOJO>();
ourMappingStrategy.setType(YourPOJO.class);
try {
pojosFromCsv = new CsvToBeanBuilder<YourPOJO>(new FileReader(csvFile))
.withType(YourPOJO.class)
.withMappingStrategy(yourMappingStrategy)
.build();
pojosFromCsv.stream();
}
Inspired by Using captureHeader in OpenCSV

How to restrict the particular method to be executed once only, When factory and dataprovider annotation method is used?

I am automating database automation. I am using #factory and
#Dataprovider annotation in feed the inputs.
I want to restrict this method related alone runs once getCountOfPt1(poiLocId)
I tried setting boolean value also, but it fails, because I am using factory as well as dataprovider annotation.
The code Which I want to restrict and execute only once is
String pt1 = null;
if(!alreadyExecuted) {
Map<String, Integer> records = DbMr.getCountOfPt1(poiLocId);
pt1 = getMaxKey(records);
LOG.debug("Max key value is...." + pt1);
if (StringUtils.isBlank(pt11)) {
records.remove(null);
pt1 = getMaxKey(records);
alreadyExecuted = true;
}
}
Note: poiLocId which I passed in this method is from factory method
#Factory
public Object[] factoryMethod() {
Object[] poiLocIdData = null;
if (StringUtils.isNotBlank(cityName)) {
List<String> poiLocId = DbMr.getPoiLocId(cityName);
int size = poiLocId.size();
poiLocIdData = new Object[size];
for (int i = 0; i < size; i++) {
poiLocIdData[i] = new CollectsTest(poiLocId.get(i));
}
} else {
LOG.error("The parameter is required: Pass City Name");
Assert.fail("Problems with parameters");
}
return poiLocIdData;
}
public CollectTest(String locationId) {
poiLocId = locationId;
this.reportsPath = "reports_" + cityName;
this.extent = new ExtentReports();
}
#DataProvider(name = "pData")
public Object[][] getPData() {
List<PData> pList = DbMr.getCollectionPs(poiLocId);
Object[][] testData = new Object[pList.size()][];
for (int i = 0; i < poiList.size(); i++) {
testData[i] = new Object[] { pList.get(i) };
}
return testData;
}
#BeforeClass
private void setup() throws Exception {
ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(reportsPath + "/" +
cityName + "_extent.html");
htmlReporter.loadXMLConfig("src/test/resources/extent-config.xml");
extent.attachReporter(htmlReporter);
}
#Test(dataProvider = "pData")
public void verifyData(PData pData) throws Exception {
extentTest = extent.createTest(pData.toString());
String pt1 = null;
if(!alreadyExecuted) {
Map<String, Integer> records = DbMr.getCountOfPt1(poiLocId);
pt1 = getMaxKey(records);
LOG.debug("Max key value is...." + pt1);
if (StringUtils.isBlank(pt11)) {
records.remove(null);
pt1 = getMaxKey(records);
alreadyExecuted = true;
}
}
if (pt1.equalsIgnoreCase("xxxx")) {
Assert.assertEquals(pData.getpt1(), "xxxx");
}
Since #factory and #DataProvider work with the instance of the test class, so try to make the "alreadyExecuted" variable as a static variable.(since static variable is at class level")
The below code works fine and it runs once only, I have used map to execute only once.
// declare it as global variable
private static Map<String, String>LOC_ID_AND_PT1_COUNT_MAP = new HashMap();
//test method
#Test(dataProvider = "pData")
public void verifyData(PData pData) throws Exception {
extentTest = extent.createTest(pData.toString());
String pt1 = LOC_ID_AND_PT1_COUNT_MAP.get(LocId);
if (pt1 == null) {
Map<String, Integer> records =
DbMr.getCountOfPT1(LocId);
pT1 = getMaxKey(records);
LOG.debug("Max key value is...." + pt1);
if (StringUtils.isBlank(pt1)) {
records.remove(null);
pt1 = getMaxKey(records);
LOG.debug("Max key value is...." + pt1);
}
LOC_ID_AND_PT1_COUNT_MAP.put(locId, pt1);
}

How do pass multiple data types to HashMap

I am trying to pass string and int data (possibly other data types like time) to a HashMap to use in a doinbackground task in Android to amend a URL. The URL uses key value pairs to update a mysql database.
I've read about using an object to pass multiple variable types, but can't get it to work.
private void addChore(){
final String title2 = editTextTaskTitle.getText().toString().trim();
final String description2 = editTextDescription.getText().toString().trim();
final String person2 = itemPerson.toString().trim();
final int monday2 = cbMon;
class NewChore1 {
String title1;
String description1;
String person1;
int monday1;
NewChore1(String title1, String description1, String person1, int monday1){
this.title1 = title1;
this.description1 = description1;
this.person1 = person1;
this.monday1 = monday1;
}
}
class AddChoreM extends AsyncTask<Void,Void,String> {
ProgressDialog loading;
#Override
protected void onPreExecute() {
super.onPreExecute();
loading = ProgressDialog.show(AddChore.this,"Adding...","Wait...",false,false);
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
loading.dismiss();
Toast.makeText(AddChore.this,s,Toast.LENGTH_LONG).show();
}
#Override
protected String doInBackground(Void... v) {
HashMap<String, NewChore1> params1 = new HashMap<>();
params1.put(Config.KEY_CHORE_TASK_TITLE,?);
params1.put(Config.KEY_CHORE_DESCRIPTION,?);
params1.put(Config.KEY_CHORE_PERSON,?);
params1.put(Config.KEY_CHORE_MONDAY,?);
RequestHandler rh = new RequestHandler();
String res = rh.sendPostRequest(Config.URL_ADD, params1);
return res;
}
}
NewChore1 params = new NewChore1(title2, description2, person2, monday2);
AddChoreM addChoreM = new AddChoreM();
addChoreM.execute(params);
}
In RequestHandler, I have used the following.
private String getPostDataString(HashMap<String, Object> params) throws UnsupportedEncodingException {
StringBuilder result = new StringBuilder();
boolean first = true;
for (Map.Entry<String, Object> entry : params.entrySet()) {
if (first)
first = false;
else
result.append("&");
result.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
result.append("=");
result.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
}
return result.toString();
}
Edit: I was not quick enough so there are other answers already but the changes below should work. You can pass your NewChore1 object to your task and extract the parameters in doInBackground:
class AddChoreM extends AsyncTask<NewChore1,Void,String> {
And:
#Override
protected String doInBackground(NewChore1...chore) {
HashMap<String, String> params1 = new HashMap<>();
params1.put(Config.KEY_CHORE_TASK_TITLE, chore[0].title1);
params1.put(Config.KEY_CHORE_DESCRIPTION, chore[0].description1);
params1.put(Config.KEY_CHORE_PERSON,chore[0].person1);
params1.put(Config.KEY_CHORE_MONDAY,chore[0].monday1);
RequestHandler rh = new RequestHandler();
String res = rh.sendPostRequest(Config.URL_ADD, params1);
return res;
}
Finally:
NewChore1 params = new NewChore1(title2, description2, person2, monday2);
new addChoreM.execute(params);
Update:
Since sendPostRequest only accepts HashMap<String, String> you need to change to: HashMap<String, String> params1 = new HashMap<>();
And change your NewChore1 class to take only Strings.
If you use
Map<String, Object> params1 = new HashMap<>();
then you can store any type as value within the map.
Create a constructor for AddChoreM class and set your NewChore1 object through it. You can now easily extract the properties of NewChore1 in doInBackground.
class AddChoreM extends AsyncTask<Void,Void,String> {
ProgressDialog loading;
NewChore1 newChore1Obj;
public AddChoreM(NewChore1 newChore1Obj){
this.newChore1Obj = newChore1Obj;
}
#Override
protected String doInBackground(Void...v) {
HashMap<String, NewChore1> params1 = new HashMap<>();
String res = "";
if(newChore1Obj != null) {
params1.put(Config.KEY_CHORE_TASK_TITLE, newChore1Obj.title1);
params1.put(Config.KEY_CHORE_DESCRIPTION, newChore1Obj.description1);
params1.put(Config.KEY_CHORE_PERSON,newChore1Obj.person1);
params1.put(Config.KEY_CHORE_MONDAY,newChore1Obj.monday1);
RequestHandler rh = new RequestHandler();
res = rh.sendPostRequest(Config.URL_ADD, params1);
}
return res;
}
// Other methods of AsyncTask
//
}
Finally, create and execute AddChoreM like this.
NewChore1 params = new NewChore1(title2, description2, person2, monday2);
AddChoreM addChoreM = new AddChoreM(params);
addChoreM.execute();

how to remove a query parameter from a query string

I am using UriBuilder to remove a parameter from a URI:
public static URI removeParameterFromURI(URI uri, String param) {
UriBuilder uriBuilder = UriBuilder.fromUri(uri);
return uriBuilder.replaceQueryParam(param, "").build();
}
public static String removeParameterFromURIString(String uriString, String param) {
try {
URI uri = removeParameterFromURI(new URI(uriString), param);
return uri.toString();
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
The above sort of works and modifies:
http://a.b.c/d/e/f?foo=1&bar=2&zar=3
… into:
http://a.b.c/d/e/f?bar=&foo=1&zar=3
But it has the following issues:
It messes up the order of the parameters. I know that the order is not relevant but it still bothers me.
it doesn't fully remove the parameter, it just sets its value to the empty string. I would prefer is the parameter is completely removed from the query string.
Is there some standard or commonly used library that can achieve the above neatly without having to parse and hack the query string myself?
In Android, without import any library.
I write a util method inspired by this answerReplace query parameters in Uri.Builder in Android?(Replace query parameters in Uri.Builder in Android?)
Hope can help you. Code below:
public static Uri removeUriParameter(Uri uri, String key) {
final Set<String> params = uri.getQueryParameterNames();
final Uri.Builder newUri = uri.buildUpon().clearQuery();
for (String param : params) {
if (!param.equals(key)) {
newUri.appendQueryParameter(param, uri.getQueryParameter(param));
}
}
return newUri.build();
}
Using httpclient URIBuilder would be much cleaner if you can.
public String removeQueryParameter(String url, String parameterName) throws URISyntaxException {
URIBuilder uriBuilder = new URIBuilder(url);
List<NameValuePair> queryParameters = uriBuilder.getQueryParams();
for (Iterator<NameValuePair> queryParameterItr = queryParameters.iterator(); queryParameterItr.hasNext();) {
NameValuePair queryParameter = queryParameterItr.next();
if (queryParameter.getName().equals(parameterName)) {
queryParameterItr.remove();
}
}
uriBuilder.setParameters(queryParameters);
return uriBuilder.build().toString();
}
If you are on Android and want to remove all query parameters, you can use
Uri uriWithoutQuery = Uri.parse(urlWithQuery).buildUpon().clearQuery().build();
Using streams and URIBuilder from httpclient it would look like this
public String removeQueryParameter(String url, String parameterName) throws URISyntaxException {
URIBuilder uriBuilder = new URIBuilder(url);
List<NameValuePair> queryParameters = uriBuilder.getQueryParams()
.stream()
.filter(p -> !p.getName().equals(parameterName))
.collect(Collectors.toList());
if (queryParameters.isEmpty()) {
uriBuilder.removeQuery();
} else {
uriBuilder.setParameters(queryParameters);
}
return uriBuilder.build().toString();
}
To fully remove the parameter, you can use
public static URI removeParameterFromURI(URI uri, String param) {
UriBuilder uriBuilder = UriBuilder.fromUri(uri);
return uriBuilder.replaceQueryParam(param, (Object[]) null).build();
}
The following piece of code worked for me:
Code:
import java.util.Arrays;
import java.util.stream.Collectors;
public class RemoveURL {
public static void main(String[] args) {
final String remove = "password";
final String url = "http://testdomainxyz.com?username=john&password=cena&password1=cena";
System.out.println(url);
System.out.println(RemoveURL.removeParameterFromURL(url, remove));
}
public static String removeParameterFromURL(final String url, final String remove) {
final String[] urlArr = url.split("\\?");
final String params = Arrays.asList(urlArr[1].split("&")).stream()
.filter(item -> !item.split("=")[0].equalsIgnoreCase(remove)).collect(Collectors.joining("&"));
return String.join("?", urlArr[0], params);
}
}
Output
http://testdomainxyz.com?username=john&password=cena&password1=cena
http://testdomainxyz.com?username=john&password1=cena
Based on the suggestion by JB Nizzet, this is what I ended up doing (I added some extra logic to be able to assert whether I expect the parameter to be present, and if so, how many times):
public static URI removeParameterFromURI(URI uri, String parameter, boolean assertAtLeastOneIsFound, Integer assertHowManyAreExpected) {
Assert.assertFalse("it makes no sense to expect 0 or less", (assertHowManyAreExpected!=null) && (assertHowManyAreExpected<=0) );
Assert.assertFalse("it makes no sense to not assert that at least one is found and at the same time assert a definite expected number", (!assertAtLeastOneIsFound) && (assertHowManyAreExpected!=null) );
String queryString = uri.getQuery();
if (queryString==null)
return uri;
Map<String, List<String>> params = parseQuery(queryString);
Map<String, List<String>> paramsModified = new LinkedHashMap<>();
boolean found = false;
for (String key: params.keySet()) {
if (!key.equals(parameter))
Assert.assertNull(paramsModified.put(key, params.get(key)));
else {
found = true;
if (assertHowManyAreExpected!=null) {
Assert.assertEquals((long) assertHowManyAreExpected, params.get(key).size());
}
}
}
if (assertAtLeastOneIsFound)
Assert.assertTrue(found);
UriBuilder uriBuilder = UriBuilder.fromUri(uri)
.replaceQuery("");
for (String key: paramsModified.keySet()) {
List<String> values = paramsModified.get(key);
uriBuilder = uriBuilder.queryParam(key, (Object[]) values.toArray(new String[values.size()]));
}
return uriBuilder.build();
}
public static String removeParameterFromURI(String uri, String parameter, boolean assertAtLeastOneIsFound, Integer assertHowManyAreExpected) {
try {
return removeParameterFromURI(new URI(uri), parameter, assertAtLeastOneIsFound, assertHowManyAreExpected).toString();
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
private static Map<String, List<String>> parseQuery(String queryString) {
try {
final Map<String, List<String>> query_pairs = new LinkedHashMap<String, List<String>>();
final String[] pairs = queryString.split("&");
for (String pair : pairs) {
final int idx = pair.indexOf("=");
final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), StandardCharsets.UTF_8.name()) : pair;
if (!query_pairs.containsKey(key)) {
query_pairs.put(key, new ArrayList<String>());
}
final String value = idx > 0 && pair.length() > idx + 1 ? URLDecoder.decode(pair.substring(idx + 1), StandardCharsets.UTF_8.name()) : null;
query_pairs.get(key).add(value);
}
return query_pairs;
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
You can use simpler method from Collection based on #Flips solution:
public String removeQueryParameter(String url, String parameterName) throws URISyntaxException {
URIBuilder uriBuilder = new URIBuilder(url);
List<NameValuePair> queryParameters = uriBuilder.getQueryParams();
queryParameters.removeIf(param ->
param.getName().equals(parameterName));
uriBuilder.setParameters(queryParameters);
return uriBuilder.build().toString();
}
I am not sure if there is some library to help, but I would just split the string on "?" and take the second half and split it on "&". Then I would rebuild the string accordingly.
public static void main(String[] args) {
// TODO code application logic here
System.out.println("original: http://a.b.c/d/e/f?foo=1&bar=2&zar=3");
System.out.println("new : " + fixString("http://a.b.c/d/e/f?foo=1&bar=2&zar=3"));
}
static String fixString(String original)
{
String[] processing = original.split("\\?");
String[] processing2ndHalf = processing[1].split("&");
return processing[0] + "?" + processing2ndHalf[1] + "&" + processing2ndHalf[0] + "&" + processing2ndHalf[2];
}
Output:
To remove a paramater just remove it from the return string.
public static String removeQueryParameter(String url, List<String> removeNames) {
try {
Map<String, String> queryMap = new HashMap<>();
Uri uri = Uri.parse(url);
Set<String> queryParameterNames = uri.getQueryParameterNames();
for (String queryParameterName : queryParameterNames) {
if (TextUtils.isEmpty(queryParameterName)
||TextUtils.isEmpty(uri.getQueryParameter(queryParameterName))
|| removeNames.contains(queryParameterName)) {
continue;
}
queryMap.put(queryParameterName, uri.getQueryParameter(queryParameterName));
}
// remove all params
Uri.Builder uriBuilder = uri.buildUpon().clearQuery();
for (String name : queryMap.keySet()) {
uriBuilder.appendQueryParameter(name, queryMap.get(name));
}
return uriBuilder.build().toString();
} catch (Exception e) {
return url;
}
}
#TTKatrina's answer worked for me, but I need to remove query param from fragment too. So extended that for fragment and came up with this.
fun Uri.removeQueryParam(key: String): Uri {
//Create new Uri builder with no query params.
val builder = buildUpon().clearQuery()
//Add all query params excluding the key we don't want back to the new Uri.
queryParameterNames.filter { it != key }
.onEach { builder.appendQueryParameter(it, getQueryParameter(it)) }
//If query param is in fragment, remove from it.
val fragmentUri = fragment?.toUri()
if (fragmentUri != null) {
builder.encodedFragment(fragmentUri.removeQueryParam(key).toString())
}
//Now this Uri doesn't have the query param for [key]
return builder.build()
}

Categories

Resources