GithubHelp home page GithubHelp logo

rivantsov / ngraphql Goto Github PK

View Code? Open in Web Editor NEW
42.0 42.0 5.0 902 KB

GraphQL .NET Server and Client

License: MIT License

C# 99.86% Batchfile 0.14%
graphql graphql-api graphql-server net net-core

ngraphql's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

ngraphql's Issues

[Bug] Failed to register with generic class

image

================= GraphQL Model Errors Detected =========================
GraphQL type SearchOutput`1 already registered; module: BookingGraphQLModule.
================= End GraphQL Model Errors ==============================

Object type as Input type

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.

Empty json input causing error

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:

empty_json

Enum type in string

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 }

[Bug] Mapping failed when subclass from the abstract class

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 ==============================

DateTime is not returned in UTC

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.
image

The response from NGraphQL is without the UTC:
image

Thanks in advance!

[Question] Is multiple resolver possible?

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.

Enum return capital case

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"
    }
  }
}

Query type field

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.

Error format in NGraphQL

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.".

Empty schema

@rivantsov ,

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.

Resolver method name

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();
    }

Should the model mapping based on GraphQLName?

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.

Question about mapping

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:

  1. Will these 3 mapping being evaluated even the user doesn't select any of the attributes?
  2. Will all of these mapping being evaluated if only one of the attribute is selected?
  3. In my LoginResult entity, there is already an attribute LastLoggedInOn, but I would like this being overridden as shown in the code above, this is possible right?
  4. I would like these mapping to be evaluated only if selected by user, possible?

Altair schema doesn't support for generic type

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>>();

image

Subscription

See there is subscription method in the example, is it ready already? It would be interesting to try that out. Thanks.

Error with Altair

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.

image

Mutation return void

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.

Subscription example

May I know is there any example for subscription, even is integrated with other packages? Thanks.

GraphQL vs REST

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.

[Question] How to hide fields from server side?

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.

[Question] Null annotation

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!

No longer run after upgraded to ver 1.7

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.

Mutation with IFormFile field

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.

DateTime with Timezone, inconsistent between Query and Mutation

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; }

The value stored in DB
image

Is this a bug?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.