Get facebook posts attachments return NullPointerException - android

I'm trying to get page posts attachments. This is a JSON which I try to get:
{
"attachments": {
"data": [
{
"media": {
"image": {
"height": 720,
"src": "url",
"width": 720
}
}
}
]
},
"id": "post_id"
}
My #GET request:
#GET("{post_id}")
fun getPostsAttachments(#Path("post_id") postId : String?,
#Query("access_token") authToken: String?,
#Query("fields") media : String?)
:Observable<AttachmentsList>
Observable:
var getPostsAttachments: Observable<AttachmentsList> = facebook.getPostsAttachments(postId, "app_token", "attachments")
getPostsAttachments.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribeBy(
onNext = { result ->
imgUrl?.addAll(result.data)
Log.d("TAG_NEXT", "$result")
},
onError = { throwable -> Log.d("TAG_ERROR", throwable.toString()) },
onComplete = { Log.d("TAG_COMPLETE", "$imgUrl") }
)
And it return D/TAG_NEXT: AttachmentsList(data=null) and D/TAG_COMPLETE: null.
How can I fix it ? Maybe my link request is wrong ?

From what I can see on https://developers.facebook.com/docs/graph-api/reference/v2.11/attachment your path for the call is wrong. Try changing the Retrofit call annotation to #GET("{post_id}/attachments").

Related

Convert URL Request from Android to IOS

I need to make a request to get a token.
If my data isn't correct, i get right answer with "Wrong data"
But if my data is correct, I get an answer:
responseSerializationFailed(reason: Alamofire.AFError.ResponseSerializationFailureReason.inputDataNilOrZeroLength).
But it works on Android version.
Request Code for Android:
#FormUrlEncoded
#POST("api/v1/authorization/login")
fun login(
#Field("username") username: String,
#Field("password") password: String,
#Field("code") code: String
): Single<Response<Void>>
API:
{
"name": "Get token",
"request": {
"method": "POST",
"header": [],
"url": {
"raw": "http://192.168.9.41/api/v1/authorization/login?username=%2B79992402979&password=Qwerty%244&code=4242",
"protocol": "http",
"host": [
"192",
"168",
"9",
"41"
],
"path": [
"api",
"v1",
"authorization",
"login"
],
"query": [
{
"key": "username",
"value": "%2B79992402979"
},
{
"key": "password",
"value": "Qwerty%244"
},
{
"key": "code",
"value": "4242"
}
]
}
},
"response": []
}
My Swift code:
func confirmLoginWithCode(username: String, password: String, code: String, completion: #escaping () -> Void) {
let parameters: Parameters = [
"username": username,
"password": password,
"code": code
]
let url = "\(self.baseUrl)/api/v1/authorization/login"
let authRequest = AF.request(url,
method: .post,
parameters: parameters,
encoding: URLEncoding(destination: .queryString))
authRequest.responseString { (response) in
switch(response.result) {
case .success(let value):
let responseArr = value.components(separatedBy: "\u{0022}")
print(responseArr, "success")
case .failure(let error):
print("\(error) check error")
}
completion()
}
}
Now I realize that my question is pretty useless for the site, but here is my solution:
As it turned out, I just had to look in Header
func confirmLoginWithCode(username: String, password: String, code: String, completion: #escaping () -> Void) {
let parameters: Parameters = [
"username": username,
"password": password,
"code": code
]
let url = "\(self.baseUrl)/api/v1/authorization/login"
let authRequest = AF.request(url,
method: .post,
parameters: parameters,
encoding: URLEncoding(destination: .queryString))
authRequest.responseString { (response) in
switch(response.result) {
case .success(let value):
let responseArr = value.components(separatedBy: "\u{0022}")
if responseArr[31] == "Wrong verification code!" {
self.confirmLoginStatus = .incorrectCode
}
case .failure(let error):
if let receivedHeaders = response.response?.headers.value(for: "Authorization") {
let token = receivedHeaders.replacingOccurrences(of: "Bearer ", with: "")
self.userSettings.token = token
self.confirmLoginStatus = .success
} else {
self.confirmLoginStatus = .connectionError
}
self.errorStatus = error.errorDescription ?? "some error"
}
completion()
}
}

Apollo sends subscription as GET

my tech setup is as follows:
GraphQl via Apollo
AWS as backend
Native Android App written in Kotlin
GraphQl queries and mutations work without any problem.
I have the following Issue:
When creating a subscription the execution produces a GET instead of a POST. Resulting in a Backend Error saying:
<-- 400 Bad Request aws.url/graphql (152ms)
Content-Type: application/json;charset=UTF-8
Content-Length: 135
Connection: close
Date: Wed, 05 Jun 2019 12:40:04 GMT
x-amzn-RequestId: id
x-amzn-ErrorType: MalformedHttpRequestException
X-Cache: Error from cloudfront
Via: 1.1 cloudfront.url (CloudFront)
X-Amz-Cf-Pop: pop
X-Amz-Cf-Id: id
{
"errors" : [ {
"message" : "Invalid request, `query` can't be null.",
"errorType" : "MalformedHttpRequestException"
} ]
}
Here is my code:
Client creation:
val logger = HttpLoggingInterceptor()
logger.level = HttpLoggingInterceptor.Level.HEADERS
val blogger = HttpLoggingInterceptor()
blogger.level = HttpLoggingInterceptor.Level.BODY
val client = OkHttpClient.Builder()
.addInterceptor(blogger)
.authenticator(get())
.build()
ApolloClient.builder()
.serverUrl(BuildConfig.backendUrl)
.okHttpClient(client)
.subscriptionTransportFactory(WebSocketSubscriptionTransport.Factory(BuildConfig.backendUrl, client))
.build() as ApolloClient
Subscription
override suspend fun subscribeToVehiclePosition(vehicleId: String, listener: DataRegistration.Listener<SubscribeToVehicleSubscription.Data>): DataRegistration {
val registration = RemoteDataRegistration()
authenticatedClient.subscribe(SubscribeToVehicleSubscription.builder().id(vehicleId).build()).execute(object: ApolloSubscriptionCall.Callback<SubscribeToVehicleSubscription.Data> {
override fun onFailure(e: ApolloException) {
listener.onClose(e)
}
override fun onResponse(response: Response<SubscribeToVehicleSubscription.Data>) {
val data = response.data()
if (data != null) {
listener.onData(data)
}
}
override fun onTerminated() {
listener.onClose(IllegalStateException("Connection Terminated!!"))
}
override fun onCompleted() {
listener.onCompleted()
}
})
return registration
}
The graphql definition
subscription subscribeToVehicle($id: String!) {
subscribeToCreateVehiclePositionLog(vehicleId: $id) {
lat
lng
date
tripId
}
}
The schema
{
"kind": "OBJECT",
"name": "Subscription",
"description": null,
"fields": [
{
"name": "subscribeToCreateVehiclePositionLog",
"description": null,
"args": [
{
"name": "vehicleId",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "VehiclePositionLog",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
I´m a bit lost why the subscription is called as a GET call.
Can anybody help me / does know what´s wrong here?

Expected BEGIN_ARRAY but was BEGIN_OBJECT when using GSON in a JSON with no array name

I am consuming the Github API but I am facing the a problem "Expected BEGIN_ARRAY but was BEGIN_OBJECT when using GSON", I am using retrofit ato consume my API and kotlin for developing. I have create a model with the serialization, and a manager to handle API responses as you can see above.
This is the JSON I am consuming
[
{
"url": "https://api.github.com/gists/8ca806eb78108aa2fecef2ab33312444",
"forks_url": "https://api.github.com/gists/8ca806eb78108aa2fecef2ab33312444/forks",
"commits_url": "https://api.github.com/gists/8ca806eb78108aa2fecef2ab33312444/commits",
"id": "8ca806eb78108aa2fecef2ab33312444",
"node_id": "MDQ6R2lzdDhjYTgwNmViNzgxMDhhYTJmZWNlZjJhYjMzMzEyNDQ0",
"git_pull_url": "https://gist.github.com/8ca806eb78108aa2fecef2ab33312444.git",
"git_push_url": "https://gist.github.com/8ca806eb78108aa2fecef2ab33312444.git",
"html_url": "https://gist.github.com/8ca806eb78108aa2fecef2ab33312444",
"files": {
"ps-find-second-highest.js": {
"filename": "ps-find-second-highest.js",
"type": "application/javascript",
"language": "JavaScript",
"raw_url": "https://gist.githubusercontent.com/oyilmaztekin/8ca806eb78108aa2fecef2ab33312444/raw/dfb0d3e648ca39e9d21c55a77ca057d7175cf725/ps-find-second-highest.js",
"size": 478
}
},
"public": true,
"created_at": "2018-10-20T11:10:23Z",
"updated_at": "2018-10-20T11:10:23Z",
"description": "",
"comments": 0,
"user": null,
"comments_url": "https://api.github.com/gists/8ca806eb78108aa2fecef2ab33312444/comments",
"owner": {
"login": "oyilmaztekin",
"id": 863600,
"node_id": "MDQ6VXNlcjg2MzYwMA==",
"avatar_url": "https://avatars0.githubusercontent.com/u/863600?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/oyilmaztekin",
"html_url": "https://github.com/oyilmaztekin",
"followers_url": "https://api.github.com/users/oyilmaztekin/followers",
"following_url": "https://api.github.com/users/oyilmaztekin/following{/other_user}",
"gists_url": "https://api.github.com/users/oyilmaztekin/gists{/gist_id}",
"starred_url": "https://api.github.com/users/oyilmaztekin/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/oyilmaztekin/subscriptions",
"organizations_url": "https://api.github.com/users/oyilmaztekin/orgs",
"repos_url": "https://api.github.com/users/oyilmaztekin/repos",
"events_url": "https://api.github.com/users/oyilmaztekin/events{/privacy}",
"received_events_url": "https://api.github.com/users/oyilmaztekin/received_events",
"type": "User",
"site_admin": false
},
"truncated": false
}
This is my GistModel(with only the parts I want)
#Entity(tableName = "gists_table")
data class Gist(
#SerializedName("id") #PrimaryKey(autoGenerate = true) val gistId:Long?,
#SerializedName("description") val description:String?,
#SerializedName("comments_url") val commentsUrl:String?,
#SerializedName("owner") #Embedded val owner:Owner?)
data class Owner(
#SerializedName("avatar_url") var avatar:String,
#SerializedName("followers_url") var followers_url:String,
#SerializedName("following_url") var following_url:String)
This is my Guist manager
fun getGists(
gistsEndpoint: GitEndpoint,
page:Int,
itemsPerPage:Int,
onSuccess:(repos:List<Gist>) -> Unit,
onError: (error:String) -> Unit) {
gistsEndpoint.getGists(page,itemsPerPage).enqueue(
object : Callback<GistResponse> {
override fun onFailure(call: Call<GistResponse>?, t: Throwable?) {
Log.d(ContentValues.TAG, "fail to get data")
onError(t?.message ?: "unknown error")
}
override fun onResponse(
call: Call<GistResponse>?,
response: Response<GistResponse>
) {
Log.d(ContentValues.TAG, "got a response $response")
if (response.isSuccessful) {
val repos = response.body()?.data ?: emptyList()
onSuccess(repos)
} else {
onError(response.errorBody()?.string() ?: "Unknown error")
}
}
}
)
}
This is my GistResponse
/**
* It will represent the list of gists
*/
data class GistResponse(
#SerializedName("") val data: List<Gist>)
I also have a boundary call to handle recycler view scrollings and API request timings but I dont think thats relevant. I wanted to know if I am serializing well because I never had a JSON with an empty array name(normally starts with a data[{something}] and I call the "data" value in the List of the model like #SerializedName("data") var data:List. Can someone help with this problem?

How to parse dynamically named jsonarray using Gson and retrofit2?

I have a JSON response, which looks like this:
{
"equipment_layer": [
{
"id": 2,
"name": "Gateway",
"detail": "All gateways"
},
{
"id": 3,
"name": "Node",
"detail": "All Nodes"
},
{
"id": 1,
"name": "Miscellaneous",
"detail": "All miscellaneous assets"
},
{
"id": 4,
"name": "Sensors",
"detail": "All Sensors"
},
{
"id": 5,
"name": "IRM",
"detail": "Installation required material"
},
{
"id": 6,
"name": "Communication",
"detail": "All communication devices such as Cellular Router, ETU etc. which are purely communication"
}
],
"data": {
"1": [
{
"equipment_id": 353,
"item_quantity": 1,
"name": "DC Current Transformer (20mm) (Old)",
"shortcode": "SNS-DCI-CT20m-R1A",
"part_number": "718,804,805,",
"equipment_layer_id": 1,
"equipment_layer_name": "Miscellaneous"
},
{
"equipment_id": 357,
"item_quantity": 1,
"name": "Fuel Sensor - B4 (Old)",
"shortcode": "SNS-FUL-PSR-R1A",
"part_number": "718,810,811",
"equipment_layer_id": 1,
"equipment_layer_name": "Miscellaneous"
}
],
"2": [
{
"equipment_id": 345,
"item_quantity": 1,
"name": "RTU (Old)",
"shortcode": "RAN-RTU-PMCL-R1A",
"part_number": "787,788,789",
"equipment_layer_id": 2,
"equipment_layer_name": "Gateway"
}
],
"3": [
{
"equipment_id": 356,
"item_quantity": 1,
"name": "Battery Analyzer (Product) (Old)",
"shortcode": "RAN-BAM-PMCL-R1A",
"part_number": "787,808,809",
"equipment_layer_id": 3,
"equipment_layer_name": "Node"
}
],
"4": [
{
"equipment_id": 346,
"item_quantity": 1,
"name": "DC Current Transformer (30mm) (Old)",
"shortcode": "SNS-CT-DCI-R1A",
"part_number": "718,792,793",
"equipment_layer_id": 4,
"equipment_layer_name": "Sensors"
},
{
"equipment_id": 350,
"item_quantity": 1,
"name": "AC Block CT (Old)",
"shortcode": "SNS-ACI-BLK-R1A",
"part_number": "718,790,791",
"equipment_layer_id": 4,
"equipment_layer_name": "Sensors"
}
]
}
}
Now the part after the "data" label is dynamic, in a response I can have subarrays of "1", "2" but not of "3" or "4".The POJO of the data inside is same as you can see. So how can I parse this data? I'm using Rerofit2 with Gson.converterfactory. I've tried jsonchema2pojo as well but the data inside the "data" object is not showing up.
I've tried to follow this method:
Parsing Retrofit2 result using Gson with different JSON structures but I can't seem to trigger the UnrwapConverter.
This is my converterfactory implementation:
internal class UnwrappingGsonConverterFactory private constructor(private val gson: Gson) : Converter.Factory() {
override fun responseBodyConverter(type: Type, annotations: Array<Annotation>, retrofit: Retrofit): Converter<ResponseBody, *> ?{
if (!needsUnwrapping(annotations)) {
return super.responseBodyConverter(type, annotations, retrofit)
}
val typeAdapter = gson.getAdapter(TypeToken.get(type))
return UnwrappingResponseConverter(typeAdapter)
}
private class UnwrappingResponseConverter (private val typeAdapter: TypeAdapter<*>) : Converter<ResponseBody, Any> , AnkoLogger{
#Throws(IOException::class)
override fun convert(responseBody: ResponseBody): Any? {
responseBody.use { responseBody ->
JsonReader(responseBody.charStream()).use({ jsonReader ->
// Checking if the JSON document current value is null
val token = jsonReader.peek()
if (token === JsonToken.NULL) {
return null
}
// If it's an object, expect `{`
jsonReader.beginObject()
var value: Any? = null
// And iterate over all properties
while (jsonReader.hasNext()) {
val data = jsonReader.nextName()
debug("Unwrap Stuff: $data")
when (data) {
"1", "2", "3", "4", "5", "6" -> value = typeAdapter.read(jsonReader)
else ->jsonReader.skipValue()
}
}
// Consume the object end `}`
jsonReader.endObject()
return value
})
}
}
}
companion object {
fun create(gson: Gson): Converter.Factory {
return UnwrappingGsonConverterFactory(gson)
}
private fun needsUnwrapping(annotations: Array<Annotation>): Boolean {
for (annotation in annotations) {
if (annotation is Unwrap) {
return true
}
}
return false
}
}
}
And the interface:
#Retention(AnnotationRetention.RUNTIME)
#Target(AnnotationTarget.FUNCTION)
annotation class Unwrap
My data classes are these:
data class ActivityNodes(#SerializedName("equipment_layer") val equipmentLayer: List<EquipmentLayer>,
#SerializedName("data") val data: nodeData)
data class nodeData (#SerializedName("1") val nodeList: List<dataItem>) <-- this is where I need someway to tell SerializedName that the value can be anything from 1 to 6
data class dataItem(#SerializedName("equipment_id") val equipmentId: Int,
#SerializedName("item_quantity") val itemQuantity: Int,
#SerializedName("name") val name: String,
#SerializedName("shortcode") val shortCode: String,
#SerializedName("part_number") val partNumber: String,
#SerializedName("equipment_layer_id") val equipmentLayerId: Int,
#SerializedName("equipment_layer_name") val equipmentLayerName: String,
var isScanned: Boolean = false )
data class EquipmentLayer(#SerializedName("id") val id: Int,
#SerializedName("name") val name: String,
#SerializedName("detail") val details: String)
For the dynamic JSON, you have to parse the JSON string manually. To get JSON string from retrofit you have to use ScalarsConverterFactory instead of GsonConverterFactory.
Add this dependency:
compile 'com.squareup.retrofit2:converter-scalars:2.3.0'
Create Adapter like this:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://echo.jsontest.com")
.addConverterFactory(ScalarsConverterFactory.create())
.build()
Create request method with ResponseBody
public interface MyService {
#GET("/key/value/one/two")
Call<ResponseBody> getData();
}
You can get Json String like this:
MyService service = retrofit.create(MyService.class);
Call<ResponseBody> result = service.getData();
result.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Response<ResponseBody> response) {
try {
System.out.println(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(Throwable t) {
e.printStackTrace();
}
});
Now you have to parse the JSON string manually to get your data from JSON.
Hope it helps:)
use below for the part of "data" of json:
Type mapType = new TypeToken<Map<String, List<EqupmentDetail.class>>>() {}.getType(); // define generic type
Map<String, List<EqupmentDetail.class>> result= gson.fromJson(new InputStreamReader(source), mapType);
here define EqipmentDetails class same as your refence
this will definitely work

Insert mirror object using Parse Cloud

I have a scenario where I need to duplicate an object (after or before saving it to Parse) only changing one field.
The problem is, I can't figure out how to find the stop condition when saving this new object and the afterSave callback getting called again and again.
My object:
{
"createdAt": "2015-02-21T23:25:03.525Z",
"creator": {
"__type": "Pointer",
"className": "_User",
"objectId": "2k9OzzBrPr"
},
"date": {
"__type": "Date",
"iso": "2015-02-21T22:46:39.048Z"
},
"description": "Hdheha",
"from": {
"__type": "Pointer",
"className": "_User",
"objectId": "Sd9B1XyZVL"
},
"has_accepted": false,
"has_answered": false,
"objectId": "YQCWRo0j2V",
"status": 0,
"to": {
"__type": "Pointer",
"className": "_User",
"objectId": "2k9OzzBrPr"
},
"updatedAt": "2015-02-21T23:25:03.525Z",
"value": 2.3499999046325684
}
My (try) server code:
function saveMirrorDebit(request) {
var toUser = request.object.get("to");
var fromUser = request.object.get("from");
var invertedDebit = request.object;
var Debit = Parse.Object.extend("Debit");
var query = new Parse.Query(Debit);
query.equalTo("parent_debit", {
__type : "Pointer",
className : "Debit",
objectId : invertedDebit.id
});
query.find({
success : function (debit) {
if (debit) {
console.log('debito nao nulo');
} else {
console.log('debito nulo, criando o espelho invertido');
var newDebit = new Debit();
newDebit.set("creator", invertedDebit.get("creator"));
newDebit.set("from", toUser);
newDebit.set("to", fromUser);
newDebit.set("value", -invertedDebit.get("value"));
newDebit.set("parent_debit", {
__type : "Pointer",
className : "Debit",
objectId : invertedDebit.id
});
newDebit.save(null);
}
},
error : function (error) {
console.log(error);
}
});
}
Which is called on afterSave:
Parse.Cloud.afterSave("Debit", function (request) {
...
saveMirrorDebit(request);
...
}
How can I approach this ?
Thanks
First, I wouldn't recommend you duplicating any object... What are you trying to achieve?
Anyhow, in an afterSave, you can achieve what you want. Note that the beforeSave could save you one API call.
Parse.Cloud.afterSave("Debit", function (request) {
var debit = request.object;
if (!(debit.has("duplicated"))){
var Debit = Parse.Object.extend("Debit");
var duplicated_debit = new Debit();
duplicated_debit.set("creator", debit.get("creator"));
duplicated_debit.set("date", debit.get("date"));
duplicated_debit.set("from", debit.get("from"));
// repeat as many times as needed, include your "change logic here"
// that's where the magic happens
duplicated_debit.set("duplicated",true);
debit.set("duplicated",true);
Parse.Object.saveAll([duplicated_debit,debit]);
}
}

Categories

Resources