ctu-fee-group / Christofel

Modular Discord bot for unofficial CTU FEE server. Handles authentication, user interaction etc. Allows for attaching plugins during runtime using AssemblyLoadContext.
MIT License
5 stars 0 forks source link

Design GraphQL API #6

Closed Rutherther closed 3 years ago

Rutherther commented 3 years ago

API will be used from the frontend to support various features. The basic ones are to support logging in and auth flow.

adamijak commented 3 years ago

Api should support Admin actions, which are now invoked via discord messages. It should replace f.e. send/edit embed message, send/edit message. Discord login (token) should be sufficient for permission check .

Rutherther commented 3 years ago

Library https://github.com/ChilliCream/hotchocolate could be used. Code-first approach will probably be the best option https://chillicream.com/docs/hotchocolate/v10/code-first

We can also use graphql-doctor (https://github.com/cap-collectif/graphql-doctor) in Github Actions for checking evolution in schema.

Rutherther commented 3 years ago
schema {
  query: Query
  mutation: Mutation
}

"The node interface is implemented by entities that have a global unique identifier."
interface Node {
  id: ID!
}

type Mutation {
  "Register using discord.\nThis should be first step of registration.\nSecond one is to register using CTU (registerCtu)."
  registerDiscord("Input of the mutation" input: RegisterDiscordInput!): RegisterDiscordPayload!
  "Register using CTU.\nThis should be second and last step of registration.\nThe first step is to register using Discord (registerDiscord)."
  registerCtu("Input of the mutation" input: RegisterCtuInput!): RegisterCtuPayload!
  "Verify specified registration code to know what stage\nof registration should be used (registerDiscord or registerCtu)"
  verifyRegistrationCode(input: VerifyRegistrationCodeInput!): VerifyRegistrationCodePayload!
}

type Query {
  node(id: ID!): Node
}

type RegisterCtuPayload {
  user: User
  "Validation errors in case that there are any"
  errors: [UserError!]!
}

"Result of registerDiscord mutation"
type RegisterDiscordPayload {
  "Information about the user.\nOnly userId, discordId and registrationCode are expected to be filled at this point"
  user: User
  "Registration code used for the second step of the registration (registerCtu)"
  registrationCode: String!
  "Validation errors in case that there are any"
  errors: [UserError!]!
}

"Database table that holds authenticated users\nor users in auth process."
type User implements Node {
  id: ID!
  "Id of the user on Discord"
  discordId: UnsignedLong
  createdAt: DateTime!
  updatedAt: DateTime
  "Last date of authentication"
  authenticatedAt: DateTime
  "CTU account username"
  ctuUsername: String
  "When this user is a duplicity (DuplicitUser is not null)\nthen set this to true if this user is allowed to finish the auth process"
  duplicityApproved: Boolean!
  "Code used for registration purposes"
  registrationCode: String
}

"Validation error"
type UserError {
  message: String!
}

"Result of verifyRegistration mutation"
type VerifyRegistrationCodePayload {
  "What step of the registration should be used"
  verificationStage: RegistrationCodeVerification!
  "Validation errors in case that there are any"
  errors: [UserError!]!
}

"Input for registerCtu mutation.\n            \nOauthCode is the code obtained from oauth2.\nRedirect uri is the one passed to oauth2.\nRegistration code is obtained from the first step of the registration (registerDiscord)."
input RegisterCtuInput {
  oauthCode: String!
  redirectUri: String!
  registrationCode: String!
}

"Input for registerDiscord mutation.\n            \nOauthCode is the code obtained from oauth2.\nRedirect uri is the one passed to oauth2."
input RegisterDiscordInput {
  oauthCode: String!
  redirectUri: String!
}

"Input of verifyRegistration mutation"
input VerifyRegistrationCodeInput {
  registrationCode: String!
}

enum RegistrationCodeVerification {
  "Code was not found, use registerDiscord"
  NOT_VALID
  "Code was found and only discord was registered, use registerCtu"
  DISCORD_AUTHORIZED
  "Code was found and both discord and ctu were linked.\nThe process was not finalized, maybe because of a duplicity.\nUse registerCtu"
  CTU_AUTHORIZED
  "This code was already used for registration and the user was successfully authenticated.\nThis typically should not be returned as codes are removed after authentication is done"
  DONE
}

"The `@defer` directive may be provided for fragment spreads and inline fragments to inform the executor to delay the execution of the current fragment to indicate deprioritization of the current fragment. A query with `@defer` directive will cause the request to potentially return multiple responses, where non-deferred data is delivered in the initial response and data deferred is delivered in a subsequent response. `@include` and `@skip` take precedence over `@defer`."
directive @defer("If this argument label has a value other than null, it will be passed on to the result of this defer directive. This label is intended to give client applications a way to identify to which fragment a deferred result belongs to." label: String "Deferred when true." if: Boolean) on FRAGMENT_SPREAD | INLINE_FRAGMENT
"The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions."
directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR
"The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`."
directive @stream("If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String "The initial elements that shall be send down to the consumer." initialCount: Int! "Streamed when true." if: Boolean!) on FIELD
"The `DateTime` scalar represents an ISO-8601 compliant date time type."
scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time")
"The UnsignedLong scalar type represents a unsigned 64-bit numeric non-fractional value greater than or equal to 0."
scalar UnsignedLong

Current schema, may be changed a little bit till v1.0.0