rivantsov / ngraphql Goto Github PK
View Code? Open in Web Editor NEWGraphQL .NET Server and Client
License: MIT License
GraphQL .NET Server and Client
License: MIT License
Hi @rivantsov ,
Can I have object type as input type?
I mean, suppose my input object type is exactly the same as my output object type, do I still need to declare 2 separate type for it?
If not, then which should I register into, as an object type or input type?
Thanks.
Hi @rivantsov ,
My input object's attributes are nullable, when I pass in empty json as input, it hit query error, is this normal?
My input object:
public class SearchUserParamsType
{
[Null] public string Id { get; set; }
[Null] public string Name { get; set; }
[Null] public string Contact { get; set; }
[Null] public string Email { get; set; }
}
Reproduction video:
The input type will complaint if a string has been passed to a Enum type.
Is it possible to make it read from string as well?
mutation MyMutation { login ( request: {type :"Form",loginId:"jasonlaw", password:"12345"} ) { status authenticationToken failedMessage loginId flags user { id name email picture { key url } phoneNumber hasMemberRole hasVendorRole hasMemberRole officer { name password community { id name description address city state postcode country useLogoAsBackgroundImage logo { key url } } } member { address adminLevel community { id name description address city state postcode country useLogoAsBackgroundImage logo { key url } } } } } }
{ "errors": [ { "message": "Invalid value '\"Form\"', expected Enum value.", "locations": [ { "line": 2, "column": 27 } ], "path": [ "login", "request" ], "extensions": { "code": "INPUT_ERROR" } } ], "data": null }
Here is my model (simplify):
public interface IAccount
{
string Name {get; set;}
ILogin Login {get; set;}
}
public abstract class Account_
{
public string Name;
public string Username; // It works if move this attribute down to Account class
}
public class Account : Account_
{
}
My Mapping in GraphQLModule:
MapEntity<IAccount>().To(x => new Account { Username = x.Login.Username });
Error:
================= GraphQL Model Errors Detected =========================
Field 'Account.username' (module BookingGraphQLModule) has no associated resolver or mapped entity field.
================= End GraphQL Model Errors ==============================
Hi @rivantsov ,
I have an issue that related to UTC datetime again. :(
My service is running in hosting server with UTC timezone.
My entity has all datetime annotated with UTC.
public interface IMembership
{
[PrimaryKey, CascadeDelete] ICompanyIdentity CompanyIdentity { get; set; }
[NoUpdate, CascadeDelete] ICompany Company { get; set; }
[NoUpdate] IUser User { get; set; }
int Points { get; }
IList<IVoucher> Vouchers { get; }
IList<IPointTrans> PointTrans { get; }
int VisitCounter { get; set; }
[Utc, Nullable] DateTime? FirstVisit { get; set; } // Stored in UTC DateTime
[Utc, Nullable] DateTime? LastVisit { get; set; } // Stored in UTC DateTime
[Utc, Nullable] DateTime? LastRedeemedVoucher { get; set; } // Stored in UTC DateTime
bool LastVisitRating { get; set; }
[Utc, Auto(AutoType.CreatedOn)] DateTime CreatedOn { get; }
[Utc, Auto(AutoType.UpdatedOn)] DateTime UpdatedOn { get; }
}
The values stored in database are correct as expected.
The response from NGraphQL is without the UTC:
Thanks in advance!
See StarWars full example, with HTTP Server and Graphiql UI here:
https://github.com/rivantsov/starwars
Hi @rivantsov ,
Can we have multiple resolvers, eg, separated by query and mutation?
If yes, what happen if both resolvers are triggered during the run? Will both also going through the BeginRequest and EndRequest?
Will that be any implication of that?
Thanks in advance.
Hi @rivantsov ,
The enum type is returned as full capital case, is this a bug?
// My enum
public enum LoginStatus
{
Success,
Failed,
OneTimePasswordUsed,
Disabled,
}
// Enum register in graphql module
EnumTypes.Add(typeof(LoginStatus));
// Result from REST API
// The status has value exactly as per defined
{
"status": "Success",
"loginId": "01F61K04NHSFKB20WVK90W392P",
"username": "[email protected]",
"name": "Admin",
"flags": 0,
"lastLoggedInOn": "2021-05-23T04:45:21.773Z",
"refreshToken": "Osh08jdsxzbmB2LM5pGOferjo7oU2klYX+xjUbMWtMUYQ+7dugJyUizx4JFK3YR9s8PKdUoBdFix0bzDfCbvIlv3si8fymInyc31Mb4MXQn4oqRdHDPulBVP23Fb/zKQX//15cowhfMA5Q5WC5JaQ9JK+sljdrNy2qDRkhKnAuMos1/KN9Fgb5yhWpsVCCRSw029Croj/tDkfAwXqhXd2BL10+D7EYaM93JWzer6Id7AV8FJe76Ei5vAznKPJL5fjvozjyBV0gdCkNbPUMNF32pNdPztVSMA4eKEqhxHDR61VM1SjrThtJnd0PR9s22xA/VP9J4rZV8b9HvaKNRIzJNvqGX4A7gtqxOmM3ph5k4/hVo3fcgb3uuzr9/DvGsx2IOM+kKWkZeO+IV3AG44JzSb+ijIAYm1ALloNTKZRrEFo5+42xRsH9It+pznTF+BlbDGm1aGjAUyCvjx3/Sdn29kqrDaMX+zkLoLlbhh/j8="
}
//Result from GraphQL
// The status has value of full capital case SUCCESS
{
"data": {
"login": {
"loginId": "01F61K04NHSFKB20WVK90W392P",
"status": "SUCCESS",
"name": "Admin"
}
}
}
Hi @rivantsov ,
When reading this, it suggests that for each mutation should have a dedicated payload, and the payload should return a field of Query type.
I am wondering how do we achieve this with NGraphQL?
Thanks in advance.
This is a continuation/follow up of the discussion originated in 'namespaces' issue in graphql-spec repo. The problem of strongly typed languages was out main line there, so I moved it here. The original post of the branch:
Hi @rivantsov ,
I have a Entity validation logic as below.
[Entity]
[Validate(typeof(AuthorizationModule), nameof(AuthorizationModule.ValidateLogin))]
[EntitySavingEvent(typeof(AuthorizationModule), nameof(AuthorizationModule.SavingLogin))]
[Display("{LoginId}/{Username}")]
public interface ILogin
{
...
}
public static void ValidateLogin(ILogin login)
{
var record = EntityHelper.GetRecord(login);
if (record.Status == EntityStatus.New)
{
var session = record.Session;
var existing = session.FindLogin(login.Username, login.Tenant);
session.ValidateEntity(login, existing == null, "", "", null, $"Login with username {login.Username} already exists.");
}
}
When calling from REST, I have the error formatted as below.
[
{
"code": "",
"message": "Login with username [email protected] already exists.",
"tag": "",
"path": "ILogin/ILogin/01F6PX8W98JTDD05XNDCQC0YN8",
"parameters": {}
}
]
However, when calling from NGraphQL, I have the following error format:
{
"errors": [
{
"message": "Client faults detected.",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"register"
],
"extensions": {
"code": "RESOLVER_ERROR",
"Details": "Vita.Entities.ClientFaultException: Client faults detected.\r\n at Vita.Entities.OperationContext.ThrowValidation()\r\n at Vita.Entities.Runtime.EntitySession.SaveChanges()\r\n at Aether.Booking.BookingModule.RegisterCustomer(OperationContext context, RegisterCustomerInput input) in E:\\JSL\\Aether\\aetherall\\Aether.Booking\\BookingModule_Register.cs:line 73\r\n at Aether.Booking.Api.GraphQL.BookingResolvers.Register(IFieldContext context, RegisterCustomerInput input) in E:\\JSL\\Aether\\aetherall\\Aether.Booking.Api\\GraphQL\\BookingResolvers_Mutation_Register.cs:line 12\r\nFaults:\r\nLogin with username [email protected] already exists.\r\n"
}
},
{
"message": "Client faults detected.",
"locations": [],
"path": [],
"extensions": {
"code": "SERVER_ERROR",
"Details": "Vita.Entities.ClientFaultException: Client faults detected.\r\n at Vita.Entities.OperationContext.ThrowValidation()\r\n at Vita.Entities.Runtime.EntitySession.SaveChanges()\r\n at Aether.Booking.Api.GraphQL.BookingResolvers.EndRequest(IRequestContext request) in E:\\JSL\\Aether\\aetherall\\Aether.Booking.Api\\GraphQL\\BookingResolvers.cs:line 25\r\n at NGraphQL.Server.Execution.OperationFieldExecuter.ExecuteOperationFieldAsync()\r\n at NGraphQL.Server.Execution.RequestHandler.ExecuteAllNonParallel(IList`1 executers)\r\n at NGraphQL.Server.Execution.RequestHandler.ExecuteOperationAsync(GraphQLOperation op, OutputObjectScope topScope)\r\n at NGraphQL.Server.Execution.RequestHandler.ExecuteAsync()\r\n at NGraphQL.Server.GraphQLServer.ExecuteRequestAsync(RequestContext context)\r\nFaults:\r\nLogin with username [email protected] already exists.\r\n Login with username [email protected] already exists.\r\n"
}
}
],
"data": {}
}
Do you think it is possible to make both error format in a more consistent way, at least the message in GraphQL should be "Login with username [email protected] already exists.".
May I know what should I check if the schema cannot be shown in the GraphiQL or Altair?
There is no error and I can't figure out what is going wrong.
Thanks.
When I name the method with GetUsers, it will hit the error.
Since there is type specified, I expect it will not get confused, bug?
// Error when name the method GetUsers:
// Resolver method CommunityQueryResolvers.GetUsers: parameter count mismatch with field arguments, expected 3, with added IFieldContext and possibly Parent object parameter.
[ResolvesField("users", typeof(ICommunityQuery))]
public IList<IUser> GetUsersByQuery(IFieldContext context)
{
return _session.EntitySet<IUser>().ToList();
}
// Error when name the method GetUsers:
// Resolver method CommunityQueryResolvers.GetUsers: parameter count mismatch with field arguments, expected 1, with added IFieldContext and possibly Parent object parameter.
[ResolvesField("users", typeof(Community_))]
public IList<IUser> GetUsersByCommunity(IFieldContext context, ICommunity community, string type)
{
var members = type == null || type == "Member" ? _session.EntitySet<IMemberInCommunity>()
.Where(x => x.Community == community)
.Select(x => x.User).ToList() : new List<IUser>();
var officers = type == null || type == "Officer" ? _session.EntitySet<IUser>()
.Where(x => x.OfficerCommunity == community)
.ToList() : new List<IUser>();
return (IList<IUser>)members.Union(officers).Distinct();
}
public class Community_
{
[GraphQLName("users")]
public List<User_> GetUsers([Null] string type) { return default; }
}
}
public interface ICommunityQuery
{
[GraphQLName("users")]
IList<User_> GetUsers();
}
Hi @rivantsov ,
I hit the error when have GraphQLName defined in the model.
================= GraphQL Model Errors Detected =========================
Field 'unloading' has no associated resolver or mapped entity field. Field: 'Berth.unloading', mapping from (entity) type 'Quintiq.Scheduler.Entity.IBerth', (module 'SchedulerGraphQLModule').
Field 'unloading' has no associated resolver or mapped entity field. Field: 'Berth.unloading', mapping from (entity) type 'Quintiq.Scheduler.GraphQL.Model.Berth', (module 'SchedulerGraphQLModule').
================= End GraphQL Model Errors ==============================
I suppose the GraphQLName is for external reference only, and the mapping should be based on the actual field name, is this not the case?
Entity schema:
[Entity]
public interface IBerth
{
[PersistOrderIn("SeqOfBerth")]
IList<IUnloading> Unloadings { get; set; }
}
GraphQL Model:
public class Berth
{
// Error if uncomment the GraphQLName
//[GraphQLName("unloading")]
public IList<Unloading> Unloadings;
}
Thank you.
Hi @rivantsov ,
I have some questions that require your clarification.
Base on the following mapping:
MapEntity<LoginResult>().To(x => new LoginResponse
{
Flags = x.Login.FlagsInt(),
LastLoggedInOn = x.Login == null ? null : x.Login.LastSuccessOnClientTime,
RefreshToken = x.Status == LoginStatus.Success
? AuthorizationModule.CreateRefreshToken(EntityHelper.GetSession(x).Context, true) : null
});
Questions:
Hi,
With the following, GraphiQL is working fine, but not Altair, is this due to Altair is too strict or something the framework doesn't handle properly?
public class SearchOutput<T>
{
[Null] public PageInfo PageInfo { get; set; }
public IList<T> Results { get; set; }
}
ObjectTypes.Add( typeof(SearchOutput<Category>));
MapEntity<SearchOutput<ICategory>>().To<SearchOutput<Category>>();
See there is subscription method in the example, is it ready already? It would be interesting to try that out. Thanks.
Hi @rivantsov ,
I have tried to switch to Altair from the GraphiQL, but hit the following errors.
Is this framework issue or Altair issue?
Notes: it is working fine with GraphiQL.
many improvements, added Client package. Enjoy it
explaining design decisions, goals, and comparison with other solutions
Hi @rivantsov ,
I have one quick question, can we return void from mutation?
For eg, I have a logout api, and it has nothing to return, accept the http status 200 or exception.
In this case, what is the best practice for graphql?
Thanks in advance.
May I know is there any example for subscription, even is integrated with other packages? Thanks.
Hi @rivantsov ,
Could you please share with me what is your biggest motivation (or anyone) to move from REST to GraphQL?
I have tried GraphQL for the POC and I don't feel like I am fully utilize its power, since all stuff can be done by the conventional REST with a more simpler way.
With GraphQL, we need to specific each single fields for retrieval, also for the input variables we even need to specific the datatype in the query. For eg, I need to specifically define LoginRequest as my input variable type, while in REST all I need to do is just a JSON with correct fields.
I think the GraphQL is powerful but I am not sure it is a good fit for me. I would like to seek for your opinion before going any further.
Thank you very much.
Eg:
// GraphQL Type
public class Community_
{
[GraphQLName("user")]
public User_ GetUser(string type) { return default; }
}
// GRaphQL Query
[GraphQLName("user") ]
User_ GetUser(string type);
Hi @rivantsov ,
Suppose we want to hide some fields base on certain condition from the server, how can we do that?
Eg,
user query the following:
SomeEntity { field1 field2 field3 }
while we have our direct mapping as following:
Mapping().To();
Due to some reason, we want to always return null to field3 even those there is value.
The condition logic could be complex and not feasible in mapping expression.
Thanks in advance.
Hi @rivantsov ,
May I know is the Null annotation only useful for input type?
For output fields, I don't see any different with or without it, I could be wrong.
Thanks!
Hi @rivantsov ,
After upgraded to ver 1.7, the graphql seems not working anymore. I see you had made some changes on the startup strategy, not sure if I need to make any changes on my side here.
Btw, I see there is also builder.AddGraphQLServer extensions, could you please also add for services (IServiceCollection)?
Thanks.
Hi @rivantsov ,
My I know how do we handle for mutation case with IFormFile argument ?
In REST, I use multipart form data to solve this problem, wondering how this is handled in ngraphql.
Thanks.
Hi @rivantsov ,
I found the DateTime return from query and mutation seems inconsistent, however I can't be sure.
Here is what I found, a Company object type with the following mapping:
.MapEntity<ICompany>()
.To(x => new Company
{
Id = x.Tenant.Id,
CurrentPlanId = x.CurrentPlan == null ? null : x.CurrentPlan.Id,
ExpiryDate = x.CurrentPlan == null ? null : x.CurrentPlan.ExpiryDate,
});
Result return from mutation:
{
"data": {
"buyPlan": {
"expiryDate": "2024-05-26T00:00:00+08:00"
}
}
}
Result return from query:
{
"data": {
"companies": [
{
"expiryDate": "2024-05-26T00:00:00"
}
]
}
}
The result from mutation in UTC format but not in query.
Here is how I define the ExpiryDate in entity:
[DateOnly, NoUpdate] DateTime ExpiryDate { get; set; }
Is this a bug?
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.