Giter VIP home page Giter VIP logo

pioneer'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

Watchers

 avatar  avatar  avatar  avatar

pioneer's Issues

Can we support connectionParams for GraphiQL?

I've noticed using GraphiQL that the request headers are not being sent for subscriptions. When I generate the Context I use the headers to get an access token, but the headers I defined within GraphiQL are not getting sent. The headers do work for queries and mutations.

Is there a better alternative or is this a bug?

Use operation specific Response to return an GraphQL Result when formatting thrown error by resolver

Describe the bug
Currently when a resolver thrown an error, it will use that operation's Response status code to send back a GraphQLResult with the thrown error, but leave out all other things set on it (i.e. headers, cookies, etc.).

It should not make the decision that an error meant the headers, cookies should not be set, and instead use the Response and all its values to encode the GraphQLResult with the error message.

Steps to reproduce

Have a resolver that throw an error after settings values in the response header.

extension Resolver {
    func doSomething(ctx: Context, _: NoArguments) throws -> String {
        ctx.response.headers.add(name: .init("X-Some-Header"), value: "something")
        throw Abort(.notImplemented, reason: "Expected error")
    }
}

Have the schema with this as mutation or query, so it can run on HTTP

Schema<Resolver, Context> {
    Query { ... }
    
    Mutation { 
        Field("doSomething", at: Resolver.doSomething)
    }
}

Run the server and make a request with the GraphQL query of:

mutation {
  doSomething
}

It should return a JSON of GraphQLResult with an empty data field and errors field with the thrown error (formatted as GraphQLError), but it will not send back any headers.

Adding the option to set HTTPBody​Stream​Strategy

Is your feature request related to a problem? Please describe.
Currently for POST request, the max body collection size is 16KB, which can be insufficient with large GraphQL queries

Describe the solution you'd like
Having the option to pass in HTTPBodyStreamStrategy either when constructing Pioneer or when apply to RouteBuilder.

Additional context
This should only be applied for POST request

Keep-alive messages were not sent at all

Description

The current usage of Timer seems to be invalid and thus not firing the callback properly. Hence, no keep-alive messages were sent. Technically, clients have features to reconnect by itself, but it's probably best to actually send the messages.

There should also be a configuration for turning this off when necessary

Contributing Guide

I have been the sole maintainer of the library for a while. I think I should start making things easier for new contributors to contribute to this library.

I have plans to add a contributing guide and some more details into the library in order so it's easy for someone new to make improvements to the codebase

Improvement: Use GraphQLSchema instead of Graphiti's Schema

The internal of Pioneer could be accepting lower level GraphQLSchema, which open up the options to use whatever GraphQL schema library that was built on the same base library.

On top of that keep a convenient initializer with Graphiti's Schema so the API wouldn't have to change too much.

Need further testing to figure out differences and caveats

Agnostic Server Library

Similar to apollo-server, it would make sense for Pioneer to be server library agnostic or at least support multiple server libraries.

This can be achieved by separating the core part of the library (GraphQL execution, Subscriptions Handler, GraphQL data structures) and the vapor part (HTTP Handlers, Websocket Handlers, Request and Response extensions), and later adding support for other libraries.

However, this can be quite a big undertaking. So to tackle this, I should keep maintaining the v0.* of the library with additional small feature updates and patches.

While under a separate branch, start working on separating the core and vapor part of the library.

If the separation is successful, I can continue by adding support in form of packages to other server libraries that are actively maintained and support Swift 5.5 properly.

I have no time estimate on how long this would take, and no deadline when this would start either.

Async Field Resolver for Protocol return types

Async/await resolvers for non-encodable return type like Protocols.

public extension Graphiti.Field {

    /// Async-await non-throwing GraphQL resolver function
    typealias AsyncAwaitResolve<ObjectType, Context, Arguments, FieldType> = (ObjectType) -> (Context, Arguments) async -> FieldType

    convenience init<ResolveType>(
        _ name: String,
        at function: @escaping AsyncAwaitResolve<ObjectType, Context, Arguments, ResolveType>,
        as: FieldType.Type,
        @ArgumentComponentBuilder<Arguments> _ argument: () -> [ArgumentComponent<Arguments>]
    ) {
        let resolve: AsyncResolve<ObjectType, Context, Arguments, FieldType> = { type in
            { context, arguments, eventLoopGroup in
                eventLoopGroup.task { await function(type)(context, arguments) }
            }
        }

        self.init(name, at: resolve, argument)
    }

    convenience init<ResolveType>(
        _ name: String,
        at function: @escaping AsyncAwaitResolve<ObjectType, Context, Arguments, ResolveType>,
        as: FieldType.Type,
    ) where Arguments == NoArguments {
        let resolve: AsyncResolve<ObjectType, Context, Arguments, FieldType> = { type in
            { context, arguments, eventLoopGroup in
                eventLoopGroup.task { await function(type)(context, arguments) }
            }
        }

        self.init(name, at: resolve, {})
    }


    /// Async-await throwing GraphQL resolver function
    typealias AsyncAwaitThrowingResolve<ObjectType, Context, Arguments, FieldType> = (ObjectType) -> (Context, Arguments) async throws -> FieldType

    convenience init<ResolveType>(
        _ name: String,
        at function: @escaping AsyncAwaitResolve<ObjectType, Context, Arguments, ResolveType>,
        as: FieldType.Type,
        @ArgumentComponentBuilder<Arguments> _ argument: () -> [ArgumentComponent<Arguments>]
    ) {
        let resolve: AsyncResolve<ObjectType, Context, Arguments, FieldType> = { type in
            { context, arguments, eventLoopGroup in
                eventLoopGroup.task { try await function(type)(context, arguments) }
            }
        }

        self.init(name, at: resolve, argument)
    }

    convenience init<ResolveType>(
        _ name: String,
        at function: @escaping AsyncAwaitResolve<ObjectType, Context, Arguments, ResolveType>,
        as: FieldType.Type,
    ) where Arguments == NoArguments {
        let resolve: AsyncResolve<ObjectType, Context, Arguments, FieldType> = { type in
            { context, arguments, eventLoopGroup in
                eventLoopGroup.task { try await function(type)(context, arguments) }
            }
        }

        self.init(name, at: resolve, {})
    }

    /// Async-await non-throwing  GraphQL resolver function
    typealias AsyncAwaitResolveWithEventLoop<ObjectType, Context, Arguments, FieldType> = (ObjectType) -> (Context, Arguments, EventLoopGroup) async -> FieldType

    convenience init<ResolveType>(
        _ name: String,
        at function: @escaping AsyncAwaitResolve<ObjectType, Context, Arguments, ResolveType>,
        as: FieldType.Type,
        @ArgumentComponentBuilder<Arguments> _ argument: () -> [ArgumentComponent<Arguments>]
    ) {
        let resolve: AsyncResolve<ObjectType, Context, Arguments, FieldType> = { type in
            { context, arguments, eventLoopGroup in
                eventLoopGroup.task {
                    await function(type)(context, arguments, eventLoopGroup)
                }
            }
        }
        self.init(name, at: resolve, argument)
    }

    convenience init<ResolveType>(
        _ name: String,
        at function: @escaping AsyncAwaitResolve<ObjectType, Context, Arguments, ResolveType>,
        as: FieldType.Type,
    ) where Arguments == NoArguments {
        let resolve: AsyncResolve<ObjectType, Context, Arguments, FieldType> = { type in
            { context, arguments, eventLoopGroup in
                eventLoopGroup.task {
                    await function(type)(context, arguments, eventLoopGroup)
                }
            }
        }
        self.init(name, at: resolve, {})
    }

    /// Async-await throwing  GraphQL resolver function
    typealias AsyncAwaitThrowingResolveWithEventLoop<ObjectType, Context, Arguments, FieldType> = (ObjectType) -> (Context, Arguments, EventLoopGroup) async throws -> FieldType

    convenience init<ResolveType>(
        _ name: String,
        at function: @escaping AsyncAwaitResolve<ObjectType, Context, Arguments, ResolveType>,
        as: FieldType.Type,
        @ArgumentComponentBuilder<Arguments> _ argument: () -> [ArgumentComponent<Arguments>]
    ) {
        let resolve: AsyncResolve<ObjectType, Context, Arguments, FieldType> = { type in
            { context, arguments, eventLoopGroup in
                eventLoopGroup.task {
                    try await function(type)(context, arguments, eventLoopGroup)
                }
            }
        }
        self.init(name, at: resolve, argument)
    }

    convenience init<ResolveType>(
        _ name: String,
        at function: @escaping AsyncAwaitResolve<ObjectType, Context, Arguments, ResolveType>,
        as: FieldType.Type,
    ) where Arguments == NoArguments {
        let resolve: AsyncResolve<ObjectType, Context, Arguments, FieldType> = { type in
            { context, arguments, eventLoopGroup in
                eventLoopGroup.task {
                    try await function(type)(context, arguments, eventLoopGroup)
                }
            }
        }
        self.init(name, at: resolve, {})
    }

}

Sendable ID

Immutable data structures aren't Sendable by default. ID is a built-in types that is immutable and should be safely copied across asynchronous code should conforms to Sendable

Better documentation

At the moment, all the documentation is cluttered in the README. I should create a documentation website and tidy things up

Adding advanced CSRF and XS-Search timing attacks prevention

Considering adding options to enable an optional CSRF and XS-Search timing attacks prevention, which can be done as another option for HTTPStrategy.

At the moment, the only measurement to prevent those attacks are setting HTTP Strategy to either .queryOnlyGet, .onlyPost, or splitQueryAndMutation, and ensure that only mutations can have side effects, which may be insufficient.

Pagination

Graphiti has Connection for adding pagination, can it be used somehow with QueryBuilder?

GraphQLJSONEncoder

GraphQLSwift/GraphQL has provided a custom JSONEncoder to maintain order of the JSON fields. Might want to look into this and see if can be applied.

Custom Directives

This is probably one of those features which needs adopting at both the Graphiti level and in Pioneer but it would be awesome if we could create our own directives.

The most obvious and helpful one would be some form of Auth directive documented here. This could feed into Vapor's authentication solution to allow us to only return certain types or fields if the user is authenticated to view them.

This is kind of possible loosely with the validation rules released in #72, however that has a couple flaws:

  1. The rules are not made fully transparent to the end user which could cause them to make requests they're not eligible unintentionally and cause confusion
  2. An error thrown in a validation rule will prevent any of the request from being made. It would be nice if, for example, the user requested 5 fields and only one of them was protected to still return the other 4.

Public structs have no public initialisers

Describe the bug
Pioneer exported many structs, but some have no explicit public initialisers

Expected behavior
Any exported struct must be able to initialise from any module without getting an error

Screenshots
Screenshot 2022-11-26 at 8 55 14 PM

GraphQL Tracing

I believe this may live partially here and partially in the dependencies this package relies on but it would be amazing for production use cases if we were able to trace the performance of queries, down to the individual resolvers and such.

Example: https://github.com/apollographql/apollo-tracing

Adding `EventLoopGroup` back in the async resolver.

There are many packages relying on swift-nio EventLoopGroup, like data loader and database orms. Given that Pioneer will pass that from the request. Async await variant for resolver should still provide the EventLoopGroup as the third parameter.

Alternatively, the async resolver should get its EventLoopGroup from the context given that it has access to the context.

Bringing it back

struct Resolver {
    func hello(ctx: (Request, Response), arg: NoArgument, group: EventLoopGroup) async -> String {}
}

Using context

struct Resolver {
    func hello(ctx: (Request, Response), arg: NoArgument) async -> String {
        let group = ctx.0.eventLoopGroup
    }
}

Field Resolve Middleware

Is your feature request related to a problem? Please describe.
It will be nice to have a middleware-like feature but are for individuals resolver basis and not the entirety of server level.

Describe the solution you'd like

  1. Extensions to Graphiti Field to allow middleware
Field("field1", at: Resolve.field1, use: [Middleware1, Middleware2, Middleware3]) {
    Argument("arg1", at: \.arg1)
}
  1. Wrapped for field resolvers to apply middleware
Field("field1", at: withMiddleware(Resolver.field1, use: Middleware1, Middleware2, Middleware3)) {
    Argument("arg1", at: \.arg1)
}

Describe alternatives you've considered
Implement this on Graphiti level

Field("field1", at: Resolve.field1) {
    Argument("arg1", at: \.arg1)
}
.middleware(use: Middleware1, Middleware2, Middleware3)

Swift 6 Safety Preparations

Description

I want to highlight that Swift 6 was already set to be a safer version for Swift 5 mainly focusing on erroring out unsafe concurrent code in regards to Sendable and actor isolation, as this is something to be aware of and probably worked on in the future but not necessary immediately.

Getting into that point is not easy as a lot of packages has yet fully added Sendable to data structures that may fit. However, periodical checks can be done to remove ones that can be refactored

Feature request: API Documentation

Feature requested

  • Improve current code documentation for public structs, classes, and functions.
  • Adding this code documentation to the online web documentation
  • Tidying README to utility existing web documentation

Motivation

  • Make it easier to use Pioneer without having to go back and forth to the documentation.
  • Allow user of libraries to infer solutions to problem or create new ones by identifying the behavior described in the code documentations
  • Allow better clarity for data structures, functions, methods, properties, constant, and behavior that might not be explained explicitly anywhere

Task

  • Improvement to code documentation with examples and detailed breakdown
  • API Reference documentation in documentation website
  • Cleaner README
    Use this repo as an example over-layer

Redis backed AsyncPubSub

It might make sense to consider adding something analogous to the following:

https://www.apollographql.com/blog/backend/subscriptions/graphql-subscriptions-with-redis-pub-sub/

While AsyncPubSub is great for many use cases, in production where we might have multiple server instances, it may be necessary to use something like Redis instead of an in memory pubsub to handle subscriptions. It would be cool if there could be an option to use Redis when configuring Pioneer and it would be even cooler if we could use the same API as AsyncPubSub.

So it would be a simple configuration change for the developer, and then we could switch between redis and the in memory AsyncPubSub without changing our EventStream code.

Do you think this is possible? I know this might be a challenge.

Error load fields

When I try to get the parent id, I get an error "Fatal error: Cannot access field before it is initialized or fetched: country_id".
I found that all fields are empty except id

extension AddressModel {
    func getCountry(context: Context, _: NoArguments, eventLoopGroup: EventLoopGroup) async throws -> CountryModel? {
        let id = self.$country.id
        return try await context.request.graphQLDataLoader
            .countryLoader.load(key: id, on: eventLoopGroup).get()
    }
}

Invalid websocket message type and message payload when subscription denied due to error

Expected behavior

If any subscription resolver throws an error, it would be converted to GraphQLError and capture its description. Then, server sent an Error / GQL_ERROR with that error.

[
  {
    "type": "error",
    "id": "...",
    "payload": [
       { "message": "Internal server error" }
    ]
  }
]

or sent a Next / GQL_DATA then with Complete / GQL_COMPLETE immediately.

[
  {
    "type": "next",
    "id": "...",
    "payload": {
      "data": null,
      "errors": [ { "message": "Internal server error" } ]
    }
  },
  {
    "type": "complete",
    "id": "...",
  }
]

Current behavior

If a subscription resolver throws an error. Its description will not be captured and the server sent a Next / GQL_DATA message with a generic error message, and not end the subscription.

[
  {
    "type": "next",
    "id": "...",
    "payload": {
      "data": null,
      "errors": [ { "message": "Internal server error" } ]
    }
  }
]

Possible Solution

  • Make a intermediately type to allow capturing errors when EventLoopFuture and put the error description into the GraphQL Error message
  • Change the message type from proto.next to proto.error or send a completion message afterwards

Steps to Reproduce

  1. Setup GraphQL API with a subscription resolver function that either can or always throw an error
  2. Enable websocket by setting websocketProtocol to graphqlWs
  3. Set the interval to any value beside nil
  4. Connect to the server through websocket

Context (Environment)

  • Machine: macOS v12 (Intel, SPM)
  • Web browser: Brave browser, Safari
  • GraphQL Schema Library: Graphiti
  • WebSocket Protocol: graphql-ws
  • HTTP Strategy: queryOnlyGet (default)
  • GraphQL IDE: GraphiQL
  • Port: 4000

AbortError headers are ignored

Describe the bug
When an AbortError is thrown at any point of the code the headers values are always ignored, and thus meant nothing.

To Reproduce
Steps to reproduce the behavior:

  1. Create any Schema
  2. Create a context builder that always throws an Abort or any AbortError with some headers
  3. Create and Run Pioneer GraphQL server
  4. Query any field so the context builder is executed
  5. See that no new headers are set in the response

Expected behavior
Expected that the headers is set to the response headers.

Desktop (please complete the following information):

  • OS: macOS v12 or up
  • Version 0.8.6
  • Any Pioneer configuration

Query Cost Analysis

For applications which seek the ability to charge for access to their GraphQL API, a cost analysis must be done to charge request based on their impact. As in, requesting a single model will cost less than multiple models with relationships and such.

It would be good if we could provide a way of intercepting requests before they're executed. This would allow us to throw an error (also helpful for auth), record usage, or even add support for a "dryRun" mode which simply returns the query cost without actually executing it.

Examples:

https://github.com/pa-bru/graphql-cost-analysis

https://github.com/slicknode/graphql-query-complexity

Async context builder

Considering an asynchronous version for contextBuilder. However, it might be overcomplicated and user of the library should instead opt for leaving a EventLoopFuture or Task properties instead

Invalid keep-alive message type for graphql-ws

Expected behavior

When connecting through websocket on an specified interval, a keep-alive message was supposed to be sent with the type of Ping and the form of

{
  "type": "ping"
}

Current behavior

Message of the type of ConnectionAck and this form was sent instead on that same interval

{
  "type": "connection_ack"
}

Possible Solution

At GraphQLWs.swift line 66, change the ConnectionAck constant to Ping

static var keepAliveMessage: String { GraphQLMessage(type: ConnectionAck).jsonString }

    static var keepAliveMessage: String { GraphQLMessage(type: Ping).jsonString }

Steps to Reproduce

  1. Setup GraphQL API normally
  2. Enable websocket by setting websocketProtocol to graphqlWs
  3. Set the interval to any value beside nil
  4. Connect to the server through websocket

Context (Environment)

  • Machine: macOS v12 (Intel, SPM)
  • Web browser: Brave browser, Safari
  • GraphQL Schema Library: Graphiti
  • WebSocket Protocol: graphql-ws
  • HTTP Strategy: queryOnlyGet (default)
  • GraphQL IDE: GraphiQL
  • Port: 4000

Incorrect resolver execution for operation type if given multiple types of operations

I've noticed in both GraphiQL and BananaCakePop that if include both a named subscription and named mutation operation, that the mutation always fails when it is run. I'm interested if anyone else has this issue. When I remove the subscription, the mutation works fine.

subscription NewMessage {
  newMessage {
    ...message
  }
}

mutation CreateMessage {
  createMessage(input: {chatId: "aaa", text: "Hello there!"}) {
    message {
       ...message
    }
  }

If I try to run CreateMessage it fails. If I remove the subscription, it succeeds. In both cases, the subscription runs fine.

The error I get is a 500 code error.
"{\"error\":true,\"reason\":\"The operation couldn’t be completed. (Pioneer.Pioneer<MyApp.MainAPI, MyApp.UserContext>.ResolveError error 0.)\"}"

I haven't add this problem with other servers, so I suspect this is a Pioneer specific bug with how Pioneer decodes the operations.

Documentation search is pointing to incorrect path

Describe the bug
Documentation search feature is able to find the proper documentation topic but points to an incorrect path which always goes to a 404 page.

To Reproduce
Steps to reproduce the behavior:

  1. Go to https://pioneer.dexclaimation.com
  2. Click on 'Search documentation'
  3. Scroll down to any option there (i.e. 'GraphQL IDE')
  4. Navigate to that page
  5. Should be redirected to a 404 page

Expected behavior
Search should points to an actual page, most of the documentation is under the /docs route.

Desktop (please complete the following information):

  • OS: macOS

Additional context
Seemed like the path is partially correct just missing the /docs route, must investigate why and how to fix it.

Improvement: Opted for using AsyncStream instead of Nozzle

By cancelling a task and forcing conversion to AsyncStream, the need for using both Nozzle and EventNozzle is minimal at best.

I should opt for using built-in features as it leave the optimization to the standard library maintainers and contributors.

I don't need to rip out all of the Desolate implementation, but it keep it as a place where I can delegate the work to other implementation that was already maintained.

Changes

Convert to AsyncStream and then AsyncEventStream

Convert into AsyncStream and Task with cancelation in mind

Better typing for payload

Is your feature request related to a problem? Please describe.
It's not a pleasant experience having to work with Map for the WebSocket payload, especially if the user already know what to expect.

Describe the solution you'd like
2 approach:

  • Allow user to specify what type they are expecting which might require a breaking change
  • Extends Map with functionalities to be parsed to other types

After pausing the websocket connection from Apollo iOS, it can't reconnect.

Describe the bug

So my team is using Apollo iOS on our frontend and Pioneer on the backend. I'm noticing that when we pause the websocket connection on the frontend (i.e., when backgrounding the app) that we are unable to reconnect the websocket later on. Perhaps this is an issue with Apollo iOS, but given that no one else is having this issue while using ApolloServer, I suspect this is a bug within Pioneer.

Front end code:

print(transport.isConnected()) // Prints true
transport.pauseWebSocketConnection() 
print(transport.isConnected()) // Prints false
transport.resumeWebSocketConnection(autoReconnect: true)
print(transport.isConnected()) // Prints false (should print true)

We're using Pioneer 1.0 with the defaults and the latest version of Apollo iOS.

GraphQL over WebSocket validation and timeout on init

Is your feature request related to a problem? Please describe.
Currently, there's no way to check if a WebSocket connection has a valid payload until an operation is executed. Also if a connection never send an init operation while connecting, while that connection wouldn't be able to make any request, there's no timeout for that connection.

Describe the solution you'd like

  • Adding a new argument when creating Pioneer that will be used to validate WebSocket connection during the init phase
  • Adding a timeout mechanism for non acknowledged connections

Improvement to Test

The current unit test is functional but it's at a pretty bad state where it's hard to work with and understand. It's important that this test cases are organised and structured better so that they don't just become useless code noises and actually mean something.

JSON Incorrectly Encoded

Describe the bug

As per the GraphQL specification the order of the returned data keys should match that of those provided in the query. Currently, it's randomised.

From looking into the code this appears to be caused by this line:

try res.content.encode(result)

On line 73 of the Pioneer+Http file. By default, this line will use the default JSONEncoder which is incorrect. We should be using the GraphQLJSONEncoder which is provided by the underlying GraphQL library.

It would also be nice if you gave us the ability to customise this encoder when we initialise the Pioneer struct.

Confirmed on v0.9.4

GraphiQL with Subscriptions

Given that GraphQL Playground has been retired and that GraphiQL already have support for adding subscription with both protocols.

GraphQL Playground should be removed now to GraphIQL instead.

GraphiQL Code with subscriptions
<!DOCTYPE html>
<html>
  <head>
    <style>
      body {
        height: 100%;
        margin: 0;
        width: 100%;
        overflow: hidden;
      }

      #graphiql {
        height: 100vh;
      }
    </style>

    <!--
      This GraphiQL example depends on Promise and fetch, which are available in
      modern browsers, but can be "polyfilled" for older browsers.
      GraphiQL itself depends on React DOM.
      If you do not want to rely on a CDN, you can host these files locally or
      include them directly in your favored resource bunder.
    -->
    <script
      crossorigin
      src="https://unpkg.com/react@16/umd/react.development.js"
    ></script>
    <script
      crossorigin
      src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"
    ></script>
    <!--
      These two files can be found in the npm module, however you may wish to
      copy them directly into your environment, or perhaps include them in your
      favored resource bundler.
     -->
    <link rel="stylesheet" href="https://unpkg.com/graphiql/graphiql.min.css" />
  </head>

  <body>
    <div id="graphiql">Loading...</div>
    <script src="https://unpkg.com/graphiql/graphiql.min.js" type="application/javascript"></script>
     <!--
      This line add subscriptions-transport-ws for better protocol options
     -->
    <script src="https://unpkg.com/subscriptions-transport-ws/browser/client.js" type="application/javascript"></script>
    <script>

      const url = 'http://localhost:4000/graphql';
      const subscriptionUrl = 'ws://localhost:4000/graphql/websocket';

      const subscriptionsClient = new window.SubscriptionsTransportWs.SubscriptionClient(subscriptionUrl, { reconnect: true });

      const fetcher = GraphiQL.createFetcher({
        url,
        subscriptionUrl,
      });
      
      ReactDOM.render(
        React.createElement(GraphiQL, {
          fetcher
        }),
        document.getElementById('graphiql'),
      );
    </script>
  </body>
</html>

Ambiguous use of async `init` on `Field` and `SubscriptionField`

Describe the bug
Given that Graphiti now supports async resolver. The current extensions given in Pioneer now produce a problem with ambiguity.

Solutions
Removing any async extensions for Graphiti altogether (excluding some which may come in handy).

Related PR

Should the context builder be allowed to throw an error?

One common example I could see is if the context requires verifying and decoding a JWT access token or if before the context is created a query needs to be made to a database and if the user doesn't exist it should throw an error.

To my knowledge this is not possible right now.

I think it's a relatively simple change in the handle function in Pioneer.swift. The context would just have to be created before executeGraphQL.

Improvement to optional spec-compliant

Is your feature request related to a problem? Please describe.
It would be great if Pioneer can pass some if not most of the optional spec requirement for GraphQL over HTTP.

Describe the solution you'd like
The changes shouldn't break any existing behaviour of Pioneer and shouldn't require anything done from the user side.

Describe alternatives you've considered
We are slightly at the same compliant as Apollo Server which is technically is enough, but if the optional requirements aren't causing any issues, it should be fine to implement it

Additional context

  • As of Nov 16
    Pioneer is currently passing 36 mandatory requirements, 1 optional requirements given in graphql-http spec
  • Update as of Dec 25
    There are 79 audits in total, Pioneer achieved ✅ 45 pass and ⚠️ 34 warnings (optional)

AWS subscriptions don't always publish

I'm having a weird bug happen on AWS where a subscription only publishes every other time that a certain mutation is performed. It does not do this when I run the server locally on localhost.

One thing I realized that may be causing this is that I'm using the in memory AsyncPubSub. It's possible that the server may be executing calls on 2 different instances. This may mean that the client is subscribing to one instance's subscriptions, but that the load balancer causes the mutations to switch off between different instances, so only half the time would the server with the open websocket be receiving the mutation and publishing it.

I will try to come up with a solution using Redis and the new PubSub protocol, but I wanted to get someone else's thoughts on this.

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.