navidk0 / simplegraphql-for-unity Goto Github PK
View Code? Open in Web Editor NEWA simple graphQL client that allows one to use .graphql files (or code) for queries, mutations, and subscriptions with Unity.
License: MIT License
A simple graphQL client that allows one to use .graphql files (or code) for queries, mutations, and subscriptions with Unity.
License: MIT License
Using NativeWebSocket would also allow us to support subscriptions on WebGL/HTML5.
git url didn't work for me. This one did: https://github.com/LastAbyss/SimpleGraphQL-For-Unity.git
I guess I was a bit quick with one of those PRs as it doesn't seem to work on WebGL. I get errors like this in the console
JsonSerializationException: Unable to find a constructor to use for type SimpleGraphQL.Response`1[<>f__AnonymousType4`1[<>f__AnonymousType2`1[System.String]]]. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path 'data', line 1, position 8.
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject (Newtonsoft.Json.JsonReader reader, Newtonsoft.Json.Serialization.JsonObjectContract objectContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, Newtonsoft.Json.Serialization.JsonProperty containerProperty, System.String id, System.Boolean& createdFromNonDefaultCreator) [0x00000] in <00000000000000000000000000000000>:0
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x00000] in <00000000000000000000000000000000>:0
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x00000] in <00000000000000000000000000000000>:0
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x00000] in <00000000000000000000000000000000>:0
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x00000] in <00000000000000000000000000000000>:0
I think it's related to code stripping. Although elegant, perhaps the lambda resolver method doesn't quite work with il2cpp builds.
Looking into workarounds.
Or maybe I just need to add an empty constructor somewhere and put some attribute on it. Will investigate further tomorrow.
Hi, this is a really cool project! I was considering doing something similar myself/forking the "graphQL-client-unity" project because I didn't like how it hides too much behind its gui in scriptable objects, and I also wanted something that was a Unity package. This project seems like a really good start.
Now, I mainly develop for WebGL, though. So it would be good to clarify exactly what works, and what doesn't.
This should work with all platforms (Mono/IL2CPP) except for WebGL, since Unity WebGL has issues with threading. If you are using WebGL, this package may be hit-or-miss for you at the present time. It makes use of UnityWebRequest where possible, but the WebSockets are the main issue, so subscriptions may not properly work. If you do not need subscriptions, WebGL may work just fine.
Specifically, the "may or may not" without WebSockets part may be good to clarify.
I think some parts of the Task API may be problematic on WebGL, but I don't know enough about it to say whether the parts used here are safe for WebGL or not, but in any case most of it should be possible to fix by using UniTask instead, in any case that's what I normally use myself to be on the safe side.
It would also be cool to check if the solution proposed here is enough to be able to support subscriptions on WebGL as well.
I'll do some prototyping, just wanted to say hi and hear your thoughts on:
Task
on WebGLAdd error handling for sockets.
Hi,
I get this exception from GraphQLParser/ParserContext.cs:147
GraphQLSyntaxErrorException: Syntax Error GraphQL (1:25) Expected $, found [
1: query Games($gameIds: ID[]!) {
^
I'm guessing there is currently no support for this graphql syntax: (although it is valid syntax and it works against my running and validated schema):
query Games($gameIds: [ID!]!) {
games(gameIds: $gameIds) {
_id
...
}
}
Is the Parser a port of an existing GraphQL parser implementation?
In the readme it says that the library is usable with coroutines with callback, would you mind adding an example on how to achieve this for those amongst us with less in depth C# knowledge?
Also, is there a advantage of using coroutines over async/await? I read in the Unity manual that using async/await is discouraged, but don't know how or if this applies to this situation?
I have a bunch of graphql files I'd like to use, but I cant drag them into the Files field in the GraphQLConfig.
When looking at the code, those files are actually ScriptableObjects which I can't create as they lack the appropriate menu item for it. How do I proceed?
Documentation could use some improvement to showcase how to use the Request API vs the Query API (aka GraphQL from files). Some small examples would probably be enough.
From #15, which was merged, but my last post still stands. It could be a solution that doesn't require intervention if the GUID changes within the package.
I see, hmm. Using the GUID feels quite fragile, I must admit. I wonder if there's a better way just using a relative path directly to the link.xml file and then providing the full path from there? We could just stick it inside our project, copy it to their project in Plugins/SimpleGraphQL before build using a IPreprocessBuildWithReport.OnPreprocessBuild? (We could also do it on install when assets are being imported.)
This will require having our own mock server created during testing.
Hi,
I was encountering issues when using multiple subscriptions in my application (using graphql-transport-ws subProtocol).
SimpleGraphQLClient is (by design) using a single Websocket instance for all running subscriptions.
However, when a running subscription is unsubscribed (the graphql server will handle this request with a stop/complete payload), the WebSocketUpdate breaks (https://github.com/LastAbyss/SimpleGraphQL-For-Unity/blob/master/Runtime/SimpleGraphQL/HttpUtils.cs#L368), and no further payloads are being processed anymore, so other subscriptions don't receive any updates.
I suggest to just continue the loop, when a subscription completes/ends. Or would this harm performance?
Another suggestion is to track the running subscriptions, and disconnect (dispose) the single web socket as soon as all subscriptions where unsubscribed again. When opening a new subscription afterwards, this would reopen a new connection, and restart the update cycle.
What do you think?
var client = new GraphQLClient("xxx/api/graphql", null, headers, null);
var request = new Request
{
Query = "query FindAccount($userId: Long!) { findAccount(userId: $userId) { "
+"... on Account { id, type, userId, email, expirationDate, lastSubscriptionReminderDate, isDeveloper }"
+"... on NotFound { notFound }"
+"... on BadRequestError { type, message }"
+"... on AppError { type, message} } }",
Variables = new
{
userId= this.userId().Value()
}
};
var responseType = new { findAccount = new { type ="" , message =""} } ;
var response = await client.Send( () => responseType, request);
How do I properly work on query that can return multiple response Types?
Thanks in advance
Fer
You have to dispose the UnityWebRequest and its UploadHandler and Downloadhandler when its not in use any more.
The reason why you are not seeing this error (but will still experience leaking and memory build up) is because UnityWebRequest is using NativeArray, but the warnings associated with it can just be turned on (and off again) if you install the Burst package into your project. So its a good tip to install that, then turn on the debugging, and uninstall it again. That way you will always see this error when you forget to Dispose.
A Native Collection has not been disposed, resulting in a memory leak. Allocated from:
Unity.Collections.NativeArray`1:.ctor(Byte[], Allocator)
UnityEngine.Networking.UploadHandlerRaw:.ctor(Byte[])
SimpleGraphQL.<PostRequestAsync>d__5:MoveNext() (at D:\statespacelabs\Packages\SimpleGraphQL-For-Unity\Runtime\SimpleGraphQL\HttpUtils.cs:53)
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1:Start(<PostRequestAsync>d__5&)
SimpleGraphQL.HttpUtils:PostRequestAsync(String, Request, Dictionary`2, String, String)
SimpleGraphQL.<Send>d__6:MoveNext() (at D:\statespacelabs\Packages\SimpleGraphQL-For-Unity\Runtime\SimpleGraphQL\GraphQLClient.cs:76)
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1:Start(<Send>d__6&)
SimpleGraphQL.GraphQLClient:Send(Request, Dictionary`2, String, String)
SimpleGraphQL.<Send>d__7`1:MoveNext() (at D:\statespacelabs\Packages\SimpleGraphQL-For-Unity\Runtime\SimpleGraphQL\GraphQLClient.cs:94)
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1:Start(<Send>d__7`1&)
SimpleGraphQL.GraphQLClient:Send(Request, Dictionary`2, String, String)
AimLab.GraphQL.<QueryInternal>d__15`1:MoveNext() (at Assets\Runtime\GraphQLUtility.cs:162)
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1:Start(<QueryInternal>d__15`1&)
AimLab.GraphQL.GraphQLUtility:QueryInternal(GraphQLConfig, AccessTokenResponse, String, OperationType, Object, Int32)
AimLab.GraphQL.<Query>d__14`1:MoveNext() (at Assets\Runtime\GraphQLUtility.cs:147)
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1:Start(<Query>d__14`1&)
AimLab.GraphQL.GraphQLUtility:Query(AccessTokenResponse, String, OperationType, Object)
Hi,
I've been trying out the library and I've had mixed successes.
Queries without any variables work, including the ones that require JWT authorisation, which is awesome!
I can't get mutations to work however. Keep getting backend errors that the variables are not provided.
Graphql document:
mutation loginWithPassword($email:String! $password: String!)
{
loginWithPassword(email: $email, password: $password) {
tokens {
accessToken
},
user {
profile {
fullName
}
}
}
}
Code to send the mutation:
public async void PostLogin(string email, string password)
{
var graphQL = new GraphQLClient(Config);
Query loginMutation = graphQL.FindQuery("Login");
string results = await graphQL.SendAsync(
loginMutation,
"jwt",
null,
new Dictionary<string, string>
{
{"$email", email},
{"$password", password}
}
);
Debug.Log(results);
}
Stacktrace:
{"errors":[{"message":"Variable \"$email\" of required type \"String!\" was not provided.","locations":[{"line":1,"column":28}],"extensions":{"code":"INTERNAL_SERVER_ERROR","exception":{"stacktrace":["GraphQLError: Variable \"$email\" of required type \"String!\" was not provided."," at _loop (/app/node_modules/graphql/execution/values.js:94:17)"," at coerceVariableValues (/app/node_modules/graphql/execution/values.js:121:16)"," at getVariableValues (/app/node_modules/graphql/execution/values.js:50:19)"," at buildExecutionContext (/app/node_modules/graphql/execution/execute.js:206:61)"," at executeImpl (/app/node_modules/graphql/execution/execute.js:104:20)"," at Object.execute (/app/node_modules/graphql/execution/execute.js:63:35)"," at /app/node_modules/apollo-server-core/src/requestPipeline.ts:545:22"," at Generator.next (<anonymous>)"," at /app/node_modules/apollo-server-core/dist/requestPipeline.js:8:71"," at new Promise (<anonymous>)"]}}},{"message":"Variable \"$password\" of required type \"String!\" was not provided.","locations":[{"line":1,"column":43}],"extensions":{"code":"INTERNAL_SERVER_ERROR","exception":{"stacktrace":["GraphQLError: Variable \"$password\" of required type \"String!\" was not provided."," at _loop (/app/node_modules/graphql/execution/values.js:94:17)"," at coerceVariableValues (/app/node_modules/graphql/execution/values.js:121:16)"," at getVariableValues (/app/node_modules/graphql/execution/values.js:50:19)"," at buildExecutionContext (/app/node_modules/graphql/execution/execute.js:206:61)"," at executeImpl (/app/node_modules/graphql/execution/execute.js:104:20)"," at Object.execute (/app/node_modules/graphql/execution/execute.js:63:35)"," at /app/node_modules/apollo-server-core/src/requestPipeline.ts:545:22"," at Generator.next (<anonymous>)"," at /app/node_modules/apollo-server-core/dist/requestPipeline.js:8:71"," at new Promise (<anonymous>)"]}}}]}
I've verified the mutation in graphql playground and it's definitely correctly formed. Also tried putting in hardcoded variables and writing the variable keys without $.
Am I missing something? I'm pretty new to C#, so might just be that I'm making a mistake somewhere....
I have multiple subscription open within a mobile game I am developing. I was wandering If there is any way to unsubscribe from all active subscriptions and unregister all their listeners in order to safely reload my game.
Subscription queries need to be placed in a separate .graphql file, so they can be be referenced by only using the filename when using FindQuery (since other parameters don't seem to be working for subscriptions).
This is an easy workaround, but still can give some headaches when following documentation.
@NavidK0 Just wondering how you prefer things to be done?
Things like:
Might make sense to put this in a contributing.md
.
Hi, I have an app that uses this simple graphql package. The package seems to be working perfectly on Mono on both unity editor and android device but when i switch to scripting backend il2cpp I can only see the data on the editor but not on the android device.
Hello,
Nice work on the graphql queries and mutations.
Everything seems very easy to understand
I noticed when I add a header dictionary to the field to Subscribe like so:
bool success = await graphQL.Subscribe(
query.ToRequest(),
new Dictionary<string, string>
{
{"header", "headerSerializedCorrectly"},
{"payload", "e30="},
}
);
The response from the server is the header and payload fields are missing in the header.
I followed the structure of AppSync to the Tee: https://docs.aws.amazon.com/appsync/latest/devguide/real-time-websocket-client.html
Hello! I'm trying to use this package to make graphql requests but when I try to follow the example code to send a request, it throws an error about it can't convert a query to a request. I would appreciate some guidance on this, thank you!
After upgrading, my project stopped working due to this change:
The code that stopped building
var result = await apiClient.Send(
() => new { result = resultTypeResolver() },
request,
null,
token,
scheme);
var result = await apiClient.Send(
() => new { result = resultTypeResolver() },
request,
null, // <-- needed to add this argument after upgrading to latest version
null,
token,
scheme);
We should probably either bump major version or restore an overload with the old api, or move the settings arg so it's the last overload.
Send API takes a Request object now. Subscribe API should be updated to do the same.
Complains about ScriptedImporter is missing.
Sorry, upgraded the repo to 2020.3 (where it builds fine) already, so I don't have the full error, just thought I'd flag it.
I submitted a fix for a webgl stripping issue earlier, but it turns out it wasn't enough.
There are no errors. The deserialized responses that are returned have no errors, but the Response.Data
field is also null
even though I can see that there is data in the response body in the browser inspector. The code that accesses stuff inside the Data field, i.e. Response.Data.gameSession.id
also happily converts to null
, so I didn't really get any errors for these things, which is why I didn't notice earlier.
I managed to make it work by adding a link.xml
file to disable stripping
<linker>
<assembly fullname="LastAbyss.SimpleGraphQL.Runtime" preserve="all" />
<assembly fullname="AssemblyCSharp" preserve="all" />
</linker>
However, it also increased build size quite badly... I'll do some more tests and see if I can be more specific.
I guess we should either remove the anonymous api again or add a big fat warning to the readme?
Thank you for the wonderful library.
Unity 2021.2.0b13 (macOS)
error CS0539: 'LinkXmlInjector.OnBeforeRun(BuildReport, UnityLinkerBuildPipelineData)' in explicit interface declaration is not found among members of the interface that can be implemented
/Editor/LinkXmlInjector.cs
void IUnityLinkerProcessor.OnBeforeRun(BuildReport report, UnityLinkerBuildPipelineData data)
{
}
void IUnityLinkerProcessor.OnAfterRun(BuildReport report, UnityLinkerBuildPipelineData data)
{
}
https://unity3d.com/jp/unity/whats-new/2021.1.0
Hi !
I'm trying to make a simple graphql query, supposed to return an array of integers. I've replaced my query and the value in the example. Nevertheless i'm getting the following error when trying to access response.Result
:
Response<<anonymous type: <anonymous type: string tokenId>[] tokens>>' does not contain a definition for 'Result' and no accessible extension method 'Result' accepting a first argument of type 'Response<<anonymous type: <anonymous type: string tokenId>[] tokens>>' could be found (are you missing a using directive or an assembly reference?)
The return from the query is { owner: { tokens: Array<{ tokenID }> } }
The query is supposed to return an array of integers, is it giving me that error because of the return type?
Thanks a lot <3
Hi, I have been using SimpleGraphQL package for one of the games I am developing and recently when I update to the latest version I noticed a bit of a lag when making requests. Not sure where the problem is but the lag is noticeable. The package version I was using before is 1.1.3 and I have updated to version 1.2.0.
It's currently hard to do proper error handling with this library.
Normal GraphQL error responses from the server are more or less fine, the response object will have an Errors
field, which can be checked.
However, if something else fails when calling GraphQLClient.Send
that is not a GraphQL error response, the method will simply return null
or an empty string.
This is because HttpUtils.PostRequestAsync
returns webRequest.downloadHandler.text
without checking for or passing on webRequest.error
or webRequest.result
.
This means the following logic can't currently be implemented:
if (connection error)
wait and try again
if (http 401 unauthorized)
log in and try again
if (http 404 etc)
give up
One quite simple solution might be to throw exceptions that wrap the UnityWebRequest
.
Personally, I would prefer not throwing and instead return something like rust's Result
types, so it's obvious that something can fail and that you'd have to handle errors. However it looks like we'd have to wait for C# 9 enum class support in Unity to implement this in a clean way.
@NavidK0 Perhaps it's better to go with a simple stupid approach and add a UnityWebRequestException
class to the API? Then we could change it later?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.