Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I've just ran into the problem at the end of part 4 #39

Closed
Sebosek opened this issue Jan 15, 2021 · 3 comments
Closed

I've just ran into the problem at the end of part 4 #39

Sebosek opened this issue Jan 15, 2021 · 3 comments

Comments

@Sebosek
Copy link

Sebosek commented Jan 15, 2021

Hi,

I've just run into the problem at the end of part 4. I'm getting the following error:

query GetSpeaker {
  speaker(id: 1) {
    name
  }
}

>>

{
  "errors": [
    {
      "message": "The ID `1` has an invalid format."
    }
  ]
}

My version of app is available here https://github.com/Sebosek/ConferencePlanner, probably I missing something.

The SpeakerQueries contains ID attribute with type int,

namespace ConferencePlanner.GraphQl.Speakers
{
    [ExtendObjectType(Name = Consts.QUERY)]
    public class SpeakerQueries
    {
        [UseApplicationDbContext]
        public Task<List<Speaker>> GetSpeakersAsync([ScopedService] ApplicationDbContext context) =>
            context.Speakers.ToListAsync();

        public Task<Speaker> GetSpeakerAsync(
            [ID(nameof(Speaker))] int id, 
            SpeakerByIdDataLoader dataLoader,
            CancellationToken cancellationToken) => dataLoader.LoadAsync(id, cancellationToken);
        
        public async Task<IReadOnlyCollection<Speaker>> GetSpeakersAsync(
            [ID(nameof(Speaker))] int[] ids, 
            SpeakerByIdDataLoader dataLoader,
            CancellationToken cancellationToken) => 
            await dataLoader.LoadAsync(ids, cancellationToken).ConfigureAwait(false);
    }
}

The SpeakerType contains ImplementationNode,

namespace ConferencePlanner.GraphQl.Types
{
    public class SpeakerType : ObjectType<Speaker>
    {
        protected override void Configure(IObjectTypeDescriptor<Speaker> descriptor)
        {
            descriptor
                .ImplementsNode()
                .IdField(p => p.Id)
                .ResolveNode(WithDataLoader);
            
            descriptor
                .Field(f => f.SessionSpeakers)
                .ResolveWith<SpeakerResolvers>(t => t.GetSessionsAsync(default!, default!, default!, default))
                .UseDbContext<ApplicationDbContext>()
                .Name("sessions");
        }

        private static Task<Speaker> WithDataLoader(IResolverContext context, int id) =>
            context.DataLoader<SpeakerByIdDataLoader>().LoadAsync(id, context.RequestAborted);
        
        private class SpeakerResolvers
        {
            public async Task<IReadOnlyCollection<Session>> GetSessionsAsync(
                Speaker speaker,
                [ScopedService] ApplicationDbContext dbContext,
                SessionByIdDataLoader sessionById,
                CancellationToken cancellationToken)
            {
                var ids = await dbContext.Speakers
                    .Where(w => w.Id == speaker.Id)
                    .Include(i => i.SessionSpeakers)
                    .SelectMany(s => s.SessionSpeakers.Select(e => e.SessionId))
                    .ToListAsync(cancellationToken)
                    .ConfigureAwait(false);

                return await sessionById.LoadAsync(ids, cancellationToken);
            }
        }
    }
}

And in ConfigureServices Relay is enabled

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton(CreateAutomapper());
    services.AddPooledDbContextFactory<ApplicationDbContext>(options => 
        options.UseSqlite(CONNECTION_STRING).UseLoggerFactory(ApplicationDbContext.DbContextLoggerFactory));
    services
        .AddGraphQLServer()
        .AddQueryType(d => d.Name(Consts.QUERY))
            .AddTypeExtension<SpeakerQueries>()
            .AddTypeExtension<SessionQueries>()
            .AddTypeExtension<TrackQueries>()
        .AddMutationType(d => d.Name(Consts.MUTATION))
            .AddTypeExtension<SpeakerMutations>()
            .AddTypeExtension<SessionMutations>()
            .AddTypeExtension<TrackMutations>()
        .AddType<AttendeeType>()
        .AddType<SessionType>()
        .AddType<SpeakerType>()
        .AddType<TrackType>()
        .EnableRelaySupport()
        .AddDataLoader<SpeakerByIdDataLoader>()
        .AddDataLoader<SessionByIdDataLoader>();
}

Any idea, what should I check?

@Sebosek
Copy link
Author

Sebosek commented Jan 16, 2021

It turns out, once I went through step no. 3 in Enable Relay support, you're no longer using integers as ID.
The description of scalar type ID doesn't help, I would say, it makes more confusion than helping, but yeah... Just stop using numbers as ID, there is apparently some magic behind Hot Chocolate.

The ID scalar type represents a unique identifier, often used to refetch an object or as key for a cache. 
The ID type appears in a JSON response as a String; however, it is not intended to be human-readable.
When expected as an input type, any string (such as "4") or integer (such as 4) input value will be accepted as an ID.

@Sebosek Sebosek closed this as completed Jan 16, 2021
@davorinjurkovic007
Copy link

Hi @Sebosek
Can you please more explain this problem and give some code how should look like that part.

thanks

@bpolojan
Copy link

bpolojan commented Jul 13, 2023

At Step 3 when you create a speaker the Id is not encrypted. And that's why the query GetSpeakerById works only for step 3.
At step 4 encryption is added - so you need to create the speakers again with mutation.

mutation AddSpeaker{ addSpeaker(input: { name:"Speaker Name" bio:"Speaker Bio" webSite:"https://speaker.com" }) { speaker{ id, name } } }

Then you will receive a result with an encrypted Id:
{ "data": { "addSpeaker": { "speaker": { "id": "U3BlYWtlcgppMTE=", "name": "Speaker Name" } } } }

In the end you need to query using the encrypted Id :

query GetSpeakerById { speaker1: speaker(id: "U3BlYWtlcgppMTE=") { name id } }
and you will get the expected result:

{ "data": { "speaker1": { "name": "Speaker Name", "id": "U3BlYWtlcgppMTE=" } } }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants