I have a JSON schema file that looks something like this
{
"data": {
"__schema": {
"queryType": { "name": "Query" },
"mutationType": { "name": "Mutation" },
"subscriptionType": null,
"types": [
{
"kind": "OBJECT",
"name": "Query",
"description": "",
"fields": [
{
"name": "planningSearch",
"description": "",
"args": [
{
"name": "search",
"description": "",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "PlanningSearchInput",
"ofType": null
}
However this won't work in my Android studio project, I'm following the Apollo Kotlin GraphQL tutorial and the schema they are using and tell you to add to your project looks like this:
type Query {
launches("The number of results to show. Must be >= 1. Default = 20" pageSize: Int, "If you add a cursor here, it will only return results _after_ this cursor" after: String): LaunchConnection!
launch(id: ID!): Launch
me: User
totalTripsBooked: Int
}
type LaunchConnection {
cursor: String!
hasMore: Boolean!
launches: [Launch]!
}
type Launch {
id: ID!
site: String
mission: Mission
rocket: Rocket
isBooked: Boolean!
}
type Mission {
name: String
missionPatch(size: PatchSize): String
}
type User {
id: ID!
email: String!
profileImage: String
trips: [Launch]!
token: String
}
type Mutation {
bookTrips(launchIds: [ID]!): TripUpdateResponse!
cancelTrip(launchId: ID!): TripUpdateResponse!
login(email: String): User
}
type TripUpdateResponse {
success: Boolean!
message: String
launches: [Launch]
}
schema {
query: Query
mutation: Mutation
subscription: Subscription
}
What can I do to convert the JSON schema to the expected one?
What you are expected is called GraphQL SDL schema format, and there's some npm tools like graphql-introspection-json-to-sdl, graphql-json-to-sdl or some online web site - json2sdl can easily to do this.
Related
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()
}
}
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?
I want to send a post request with loopback "invokeStaticMethod".
Please help me how to do it.
I want to send a POST API request to below url:
localhost:3000/api/user/id/unblock With parameter {"userId", "blockId"}
Please let me know how can I send a POST request with Loopback
You could create a remote method like this:
User.unblock = function(id, userId, blockId, callback) {
var result;
// TODO
callback(null, result);
};
Then, the remote method definition in the json file could look like this:
"unblock": {
"accepts": [
{
"arg": "id",
"type": "string",
"required": true,
"description": "",
"http": {
"source": "path"
}
},
{
"arg": "userId",
"type": "string",
"required": false,
"description": "",
"http": {
"source": "form"
}
},
{
"arg": "blockId",
"type": "string",
"required": false,
"description": "",
"http": {
"source": "form"
}
}
],
"returns": [
{
"arg": "result",
"type": "object",
"root": false,
"description": ""
}
],
"description": "",
"http": [
{
"path": "/:id/unblock",
"verb": "post"
}
]
}
Then your remote method would look like this:
You could play around with function arguments and use one body argument instead of 2 form arguments and read the data from there, although I believe that if there are only 2 additional parameters it's better to put them separately. But it depends on your approach.
I believe this is what you are looking for...
https://loopback.io/doc/en/lb3/Adding-remote-methods-to-built-in-models.html
In your case, it should look something like this...
module.exports = function(app) {
const User = app.models.User;
User.unblock = function(userId, blockId, cb) {
... <Your logic goes here> ...
cb(null, result);
};
User.remoteMethod('unblock', {
accepts: [{arg: 'userId', type: 'string'}, {arg: 'blockId', type: 'string'}],
returns: {arg: 'result', type: 'string'}
});
I am used loopback's framework to generate my APIs. Now, I am trying to write a custom "RemoteMethod" that will require a long number (timestamp in unix format such as 1466598625506) and return an array of Sync Objects (I am using retrofit to comunicate with the endpoints). In my android app when I call the end point "getRecodsAfterTimestamp" it should return the records with equal or bigger timeStamp value than the provided one in the request. What It returns is all of the records (3 at this time).
This is how my model (sync.json) called Sync looks like:
{
"name": "Sync",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"uuid": {
"type": "string"
},
"table": {
"type": "string"
},
"action": {
"type": "string"
},
"timeChanged": {
"type": "number"
}
},
"validations": [],
"relations": {},
"acls": [],
"methods": {}
}
And this is my sync.js with the remote method looks like:
module.exports = function(Sync) {
Sync.getRecodsAfterTimestamp = function(timestamp, cb){
// var response;
Sync.find({where:{ or: [
{timeChanged:timestamp},
{timeChanged: {gt:timestamp } }
] }}, function(err, sync) {
cb(null,sync);
// response.push(sync);
});
// cb(null, response);
}
Sync.remoteMethod (
'getRecodsAfterTimestamp',
{
http: {path: '/getRecodsAfterTimestamp', verb: 'get'},
accepts: {arg: 'timeChanged', type: 'number', http: { source: 'query' } },
returns: {
arg: 'data',
type: 'array',
root: true
}
}
);
};
I dont know if it matters but this is my retrofit method declaration:
#GET("Syncs")
Call<List<Sync>> getAllSyncsAfterThisTimeStamp(#Query(("getRecodsAfterTimestamp?timeChanged=")) long timeChanged);
And here I am calling it like that:
Long timeStamp = 1466598625506L;
Log.e(TAG, "Job Service task is running...");
getAllSyncsCall = espcService.getAllSyncsAfterThisTimeStamp(timeStamp);
getAllSyncsCall.enqueue(EspcJobSheculerService.this);
This code returns
This is not the result I want. It should have returned all of the records after 1466598625506 which is two records only.
Your query is correct.
Check in find callback you get right output or not
I am attempting to query some hierarchical data in firebase. I'm having a little difficulty in figuring out how to query the following data structure:
{
"orgs": {
"-KBFXBBEyvgtfqMvU4pi": {
"name": "ACME 123",
"owner": "-K9IPqIUIuEFzLS0f_Pe",
"users": {
"-KBF_GhwTmXfR6Jce30t": {
"email": "userA#company.com",
"permission": "editor",
"userKey": "K99LV9cTjh1ovW1D5j2"
},
"-KBF_M533zzbUilGvAAW": {
"email": "userB#company.com",
"permission": "editor"
}
}
},
"-KBFaKlJ8tfqjBQjAZgq": {
"name": "ACME Alt LLC",
"owner": "-K9IPqIUIuEFzLS0f_ZZ",
"users": {
"-KBFbD4trt9nyeHPUQbn": {
"email": "userX#co.com",
"permission": "editor"
}
}
}
}
}
Specifically, I would like to find out if the email address "userB#company.com" exists. But this is a little confusing for me since I need to search through 2 levels (orgs and users).
After reading some more documentation it seems I really shouldn't be nesting my data like this. I'll be honest, it seems kind of contrary to do this when working with a JSON schema that that is hierarchical. Anyway, this is what I'm looking to do now:
{
"orgs": {
"-KBFXBBEyvgtfqMvU4pi": {
"name": "ACME 123",
"owner": "-K9IPqIUIuEFzLS0f_Pe"
}
},
"-KBFaKlJ8tfqjBQjAZgq": {
"name": "ACME Alt LLC",
"owner": "-K9IPqIUIuEFzLS0f_ZZ"
}
}
},
"orgMembership": {
"-KBFXBBEyvgtfq7h381h": {
"org": "-KBFXBBEyvgtfqMvU4pi",
"email": "userA#company.com",
"permission": "editor"
}
}
Then I can use the following query:
orgMRef.orderByChild("email").equalTo("userA#company.com").once("child_added", function(snapshot) {
console.log("found: " + snapshot.key());
});