How to transform PageRequest object to query string? - rest

I'm writing an RESTful API which consumes another RESTful Data API, and I'm using Spring Data.
The client side send the page request using query params like:
http://api.mysite.com/products?page=1&size=20&sort=id,asc&sort=name,desc
And I transform the params to PageRequest object and transfer it to the Service Layer.
In the service, I would like to use TestTemplate to interact with the Data API which use a URL, and How can I transform the PageRequest object to a query string like
page=1&size=20&sort=id,asc&sort=name,desc
then I can request data like:
restTemplate.getForEntity("http://data.mysite.com/products?page=1&size=20&sort=id,asc&sort=name,desc",String.class)

I know I am a bit late on this answer, but I too was not able to found an already implemented way to do this. I ended up developing my own routine for doing this:
public static String encodeURLComponent(String component){
try {
return URLEncoder.encode(component, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("You are dumm enough that you cannot spell UTF-8, are you?");
}
}
public static String queryStringFromPageable(Pageable p){
StringBuilder ans = new StringBuilder();
ans.append("page=");
ans.append(encodeURLComponent(p.getPageNumber() + ""));
// No sorting
if (p.getSort() == null)
return ans.toString();
// Sorting is specified
for(Sort.Order o : p.getSort()){
ans.append("&sort=");
ans.append(encodeURLComponent(o.getProperty()));
ans.append(",");
ans.append(encodeURLComponent(o.getDirection().name()));
}
return ans.toString();
}
It is not complete, there probably are a couple of details that I am missing, but for my user case (and I think for most of them) this works.

you can pass as new :
new PageRequest(int page,int size)
And in repository layer you can write as:
Page<User> findByName(String name,Pageable page)
in place of Pageable page you have to pass new PageRequest(int page,int size)
For ascending order you can refer this:
List<Todo> findByTitleOrderByTitleAsc(String title);

I think you just need to iterate through the request parameters from your other API request, and then pass all the parameters values into the new Url for a new API request.
sudo code could be:
//all query params can be found in Request.QueryString
var queryParams = Request.QueryString;
private string ParseIntoQuery(NameValueCollection values)
{
//parse the identified parameters into a query string format
// (i.e. return "?paramName=paramValue&paramName2=paramValue2" etc.)
}
in your code, you will then do this:
restTemplate.getForEntity(urlAuthority + ParseIntoQuery(Request.QueryString));
simple as that. Hope that answers?

UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl("http://data.mysite.com/products");
addRequestParam(uriComponentsBuilder, "page", pageable.getPageNumber());
addRequestParam(uriComponentsBuilder, "size", pageable.getPageSize());
String uri = uriComponentsBuilder.toUriString() + sortToString(pageable.getSort());
addRequestParam method:
private static void addRequestParam(UriComponentsBuilder uriBuilder, String parameterName, Object parameter) {
if (parameter != null) {
uriBuilder.queryParam(parameterName, parameter);
}
}
implement sortToString(Sort sort) method as #Shalen. You have to obtain something like this: &sort=name,asc&sort=name2,desc. Return "" if Sort is null;

Related

Parse Server SDK - Include Object method doesn't work for fetching the whole object in flutter

I was using parse server sdk in my app for database.
I have three class in my Back4App Dashboard which are "_User", "Office", "Office_Members".
In Office_Members class it has following columns,
user_id (Pointer to _User)
office_id (Pointer to Office)
count
To fetch the data including Pointer to _User as well from Office_Members, I am using following code,
QueryBuilder<ParseObject> parseQuery = QueryBuilder<ParseObject>(ParseObject("Office_Members"))
..whereEqualTo("office_id", ParseResponse_OfficeObject)
..includeObject(["user_id "]);
ParseResponse apiResponse = await parseQuery.query();
Output :
Payload : [{"className":"Office_Members","objectId":"twpDY51PUK","createdAt":"2020-08-14T09:58:59.775Z","updatedAt":"2020-08-14T09:58:59.775Z","office_id":{"__type":"Pointer","className":"Office","objectId":"4dkfSMrwBI"},"user_id":{"__type":"Pointer","className":"_User","objectId":"Hx5xJ5ABxG"},"count":1}]
In my payload response i am not getting whole user_id pointer response.
So can anybody help me that what i might be doing wrong?
Thanks.
The data should be included.
The logging function simply does not print the data of pointers.
The data should be included. The print function not print the data of pointers.
You can print it out directly for testing purposes, E.g.
response.results[0].get('user_id').get('name')
Evaluation Expression E.g.
In your model u can access at same way, E.g
Call Model
if(response.success){
return response.results.map((p) => Example.fromParse(p)).toList();
} else {
throw ParseErrors.getDescription(response.error.code);
}
Model
import 'package:parse_server_sdk/parse_server_sdk.dart';
class Example {
Example({this.id, this.name});
Example.fromParse(ParseObject parseObject) :
id = parseObject.objectId,
name = parseObject.get('user_id').get('name');
final String id;
final String name ;
#override
String toString() {
return 'Example{id: $id, name: $name}';
}
}
Why not simply use cloud code ? I'm not to familiar with flutter but I can suggest you this alternative solution.
Write a function like this.
Parse.Cloud.define("fetchMemberAndUser", async (request) => {
//Pass in ParseResponse_OfficeObject ID as parameter
var objectId = request.params.id;
//Now do a simple get query
var query = new Parse.Query(Parse.Object.extend("Office_Members"));
//Using .includes to get the user profile object
query.include("user_id");
//This will return Office_Memebers Object along with user profile
return query.get(objectId,{useMasterKey:true});
}

Callback function issues in FB.API for Unity

I am using Unity 5.5.2f1 pro and facebook's SDK v 7.9.4
I have a script which after login (managed in a previous scene) sends an API request to FB asking for friends, name and email and sends that info as a POST to a php website.
code:
[Serializable]
public struct FBData {
public string first_name;
public string email;
public string friends;
public string id;}
public class UserManagement : MonoBehaviour {
string urlSaveUserData="some php website";
public Text testTxt;
FBData parsedData;
// Use this for initialization
void Start () {
//Check if it's the first time the user is opening the app.
if (UserInfo.FIRST_TIME) {
//update text (only used for testing, should be removed in production.)
testTxt.text = "Your user id is: " + UserInfo.ID;
//Perform FB.API call to get User Data.
getUserData ();
//Save in SQL table. (won't get here if line in getUserData() is active)
StartCoroutine ("saveUserData");
} else {
//do something else.
}
note: Since this is meant for iOS I have to test it on a device so I'm using text in the screen to display info (think of it as a badly implemented print statement).
The problem: In my callback function for FB.API I write in the text Gameobject (aka testTxt) the parsed information from the response which is saved in the Custom UserInfo clss. It display's correctly but the code gets stuck there. It doesn't continue to the next function. HOWEVER, if I delete/comment that line and don't display anything in the text field. The codes does continue to the POST function BUT the information from the API call is not passed, i.e my custom class is empty (leading me to believe the callback function is not called at all).
public void getUserData(){
string query = "me?fields=first_name,email,friends";
FB.API (query, HttpMethod.GET, Apicallback, new Dictionary<string, string> ());
}
private void Apicallback(IGraphResult result){
//Parse Graph response into a specific class created for this result.
parsedData = JsonUtility.FromJson<FBData>(result.RawResult);
//Pass each field into UserInfo class.
UserInfo.EMAIL = parsedData.email;
UserInfo.FRIENDS = parsedData.friends;
UserInfo.NAME = parsedData.first_name;
UserInfo.FACEBOOKID = parsedData.id;
/*problem area, if I comment line below, then previous information is apparently not stored. If left as is then testTxt displays correct information but code gets stuck there. */
testTxt.text = "This is the info from USerInfoInside the APICallback: " + UserInfo.EMAIL + UserInfo.FRIENDS + UserInfo.FACEBOOKID;
}
The function below is to send info to php website, is there for illustrative purposes:
code:
public IEnumerator saveUserData() {
//get user info (this information is EMPTY if line in getUserData() is commented.
parsedData.id = UserInfo.FACEBOOKID;
parsedData.friends = UserInfo.FRIENDS;
parsedData.first_name = UserInfo.NAME;
parsedData.email = UserInfo.EMAIL;
//translate data into json
string JsonBodyData = JsonUtility.ToJson (parsedData);
//Custom web request (POST method doesnt seem to work very well, documentation example sends empty form)
var w = new UnityWebRequest(urlSaveUserData, "POST");
byte[] bodyRaw = new System.Text.UTF8Encoding().GetBytes(JsonBodyData);
w.uploadHandler = (UploadHandler) new UploadHandlerRaw(bodyRaw);
w.downloadHandler = (DownloadHandler) new DownloadHandlerBuffer();
w.SetRequestHeader("Content-Type", "application/json");
yield return w.Send();
//work with received data...}
Im stuck here any help is appreciated. Thanks!
Be sure to use EscapeURL when using strings directly for JSON or HTTP POST and GET methods. The lack of this treatment tends to screw things over, particulary in iOS platforms.
From what I can see, this code
string query = "me?fields=first_name,email,friends";
should instead be escaped as
string query = WWW.EscapeURL("me?fields=first_name,email,friends");
so characters like "?" won't get encoded as an URL symbol.
I'm assuming you don't need to do that for your illustrative example, because UnityWebRequest already escapes your POST request strings internally, but I can't fully confirm that.

How to add content header to Flurl

I would like to know how to add a content header to a flurl-statement.
The onedrive implementation requires me to add a content-type header to the content, and tried every possible solution with no luck.
I'm forced to use the regular httpclient with the following code.
Public Async Function UploadFile(folder As String, filepath As String) As Task(Of Boolean) Implements ICloud.UploadFile
Dim data As Byte() = File.ReadAllBytes(filepath)
Dim uploadurl As String = "drive/items/" + folder + ":/" + Path.GetFileName(filepath) + ":/" + "content?access_token=" + Token.access_token
Using client As New HttpClient()
client.BaseAddress = New Uri(ApiUrl)
Dim request As HttpRequestMessage = New HttpRequestMessage(HttpMethod.Put, uploadurl)
request.Content = New ByteArrayContent(data)
request.Content.Headers.Add("Content-Type", "application/octet-stream")
request.Content.Headers.Add("Content-Length", data.Length)
Dim response = Await client.SendAsync(request)
Return response.IsSuccessStatusCode
End Using
End Function
I already tried the regular PutJsonAsync method of Flurl, but with no luck.
It's the only non-flurl piece remaining in my code.
Thanx in advance.
The real issue here is that there's currently no out-of-the-box support for sending streams or byte arrays in Flurl. I plan to add some soon, but with the implementation details you already have it's easy to add this yourself with an extension method. (Forgive the C#, hopefully you can translate to VB.)
public static Task<HttpResponseMessage> PutFileAsync(this FlurlClient client, string filepath)
{
var data = File.ReadAllBytes(filepath);
var content = new ByteArrayContent(data);
content.Headers.Add("Content-Type", "application/octet-stream");
content.Headers.Add("Content-Length", data.Length);
return client.SendAsync(HttpMethod.Put, content: content);
}
The above works if you already have a FlurlClient, but as the docs describe it's a good idea to have corresponding string and Url extensions, which can just delegate to the above method:
public static Task<HttpResponseMessage> PutFileAsync(this Url url, string filepath)
{
return new FlurlClient(url).PutFileAsync(filepath);
}
public static Task<HttpResponseMessage> PutFileAsync(this string url, string filepath)
{
return new FlurlClient(url).PutFileAsync(filepath);
}
Tuck those away in a static helper class and they should work seamlessly with Flurl:
await uploadurl.PutFileAsync(filepath)

Update records using Salesforce REST API - Patch method not working

I'm trying to update a record residing in salesforce table. Im using the Java HttpClient REST API to do the same. Getting an error when we use PATCH to update a record in Salesforce.
PostMethod post = new PostMethod(
instanceUrl + "/services/data/v20.0/sobjects/" +
objectName + "/" + Id + "?_HttpMethod=PATCH"
);
[{"message":"HTTP Method 'PATCH' not allowed. Allowed are HEAD,GET,POST","errorCode":"METHOD_NOT_ALLOWED"}]
Also tried doing the following:
PostMethod post = new PostMethod(
instanceUrl + "/services/data/v20.0/sobjects/" + objectName + "/" + Id)
{
public String getName() { return "PATCH";
}
};
This also returns the same error. We are using apache tomcat with commons-httpclient-3.1.jar library. Please advise on how this can be done.
Please check if you you're using the right implementation of PATCH method, see: Insert or Update (Upsert) a Record Using an External ID.
Also check if your REST URL is correct, probably your objectId is not passed in correctly from Javascript.
The ObjectName is the Name of an Salesforce table i.e. 'Contact'. And Id is the Id of a specific record you want to update in the table.
Similar:
Can I update without an ID?
for Drupal: How to deal with SalesforceException: HTTP Method 'PATCH' not allowed. Allowed are HEAD,GET,POST
As I think you're aware, commons httpclient 3.1 does not have the PATCH method, and the library has been end-of-lifed. In your code above, you're trying to add the HTTP method as a query parameter, which doesn't really make sense.
As seen on SalesForce Developer Board, you can do something like this instead:
HttpClient httpclient = new HttpClient();
PostMethod patch = new PostMethod(url) {
#Override
public String getName() {
return "PATCH";
}
};
ObjectMapper mapper = new ObjectMapper();
StringRequestEntity sre = new StringRequestEntity(mapper.writeValueAsString(data), "application/json", "UTF-8");
patch.setRequestEntity(sre);
httpclient.executeMethod(patch);
This allows you to PATCH without switching out your httpclient library.
I created this method to send the patch request via the Java HttpClient class.
I am using JDK V.13
private static VarHandle Modifiers; // This method and var handler are for patch method
private static void allowMethods(){
// This is the setup for patch method
System.out.println("Ignore following warnings, they showed up cause we are changing some basic variables.");
try {
var lookUp = MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup());
Modifiers = lookUp.findVarHandle(Field.class, "modifiers", int.class);
} catch (IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
try {
Field methodField = HttpURLConnection.class.getDeclaredField("methods");
methodField.setAccessible(true);
int mods = methodField.getModifiers();
if (Modifier.isFinal(mods)) {
Modifiers.set(methodField, mods & ~Modifier.FINAL);
}
String[] oldMethods = (String[])methodField.get(null);
Set<String> methodsSet = new LinkedHashSet<String>(Arrays.asList(oldMethods));
methodsSet.addAll(Collections.singletonList("PATCH"));
String[] newMethods = methodsSet.toArray(new String[0]);
methodField.set(null, newMethods);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}

Debugging JSON in GWT (Cross Server)

How do I pass the jsonObj from the javascript code in getJson to the java code handleJsonResponse. If my syntax is correct, what do I do with a JavaScriptObject?
I know that the jsonObj contains valid data because alert(jsonObj.ResultSet.totalResultsAvailable) returns a large number :) --- but some how it's not getting passed correctly back into Java.
EDIT: I solved it... by passing in jsonObj.ResultSet.Result to the java function and working on it using a JSONArray.
Thanks.
public native static void getJson(int requestId, String url, MyClass handler) /*-{
alert(url);
var callback = "callback" + requestId;
var script = document.createElement("script");
script.setAttribute("src", url+callback);
script.setAttribute("type", "text/javascript");
window[callback] = function(jsonObj) { // jsonObj DOES contain data
handler.#com.michael.random.client.MyClass::handleJsonResponse(Lcom/google/gwt/core/client/JavaScriptObject;)(jsonObj);
window[callback + "done"] = true;
}
document.body.appendChild(script);
}-*/;
public void handleJsonResponse(JavaScriptObject jso) { // How to utilize JSO
if (jso == null) { // Now the code gets past here
Window.alert("Couldn't retrieve JSON");
return;
}
Window.alert(jso.toSource()); // Alerts 'null'
JSONArray array = new JSONArray(jso);
//Window.alert(""+array.size());
}
}
Not exactly sure how to fix this problem that I had, but I found a workaround. The javascript jsonObj is is multidimensional, and I didn't know how to manipulate the types in the java function. So instead, I passed jsonObj.ResultSet.Result to my function handler, and from there I was able to use get("string") on the JSONArray.
What is toSource() supposed to do? (The documentation I see for it just says "calls toSource".) What about toString()?
If your call to alert(jsonObj.ResultSet.totalResultsAvailable) yields a valid result, that tells me jsonObj is a JavaScript Object, not an Array. Looks to me like the constructor for JSONArray only takes a JS Array (e.g., ["item1", "item2", {"key":"value"}, ...] )