1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-07-31 09:24:31 +03:00

Have better output types on mutations

This commit is contained in:
Quentin Gliech
2023-04-25 16:12:20 +02:00
parent 3b0f2ea1b0
commit ed5c367df6
8 changed files with 647 additions and 173 deletions

View File

@ -36,17 +36,17 @@ mod state;
pub use self::{
model::{CreationEvent, Node},
mutations::RootMutations,
query::RootQuery,
mutations::Mutation,
query::Query,
state::{BoxState, State},
};
pub type Schema = async_graphql::Schema<RootQuery, RootMutations, EmptySubscription>;
pub type SchemaBuilder = async_graphql::SchemaBuilder<RootQuery, RootMutations, EmptySubscription>;
pub type Schema = async_graphql::Schema<Query, Mutation, EmptySubscription>;
pub type SchemaBuilder = async_graphql::SchemaBuilder<Query, Mutation, EmptySubscription>;
#[must_use]
pub fn schema_builder() -> SchemaBuilder {
async_graphql::Schema::build(RootQuery::new(), RootMutations::new(), EmptySubscription)
async_graphql::Schema::build(Query::new(), Mutation::new(), EmptySubscription)
.register_output_type::<Node>()
.register_output_type::<CreationEvent>()
}

View File

@ -18,9 +18,9 @@ use async_graphql::MergedObject;
/// The mutations root of the GraphQL interface.
#[derive(Default, MergedObject)]
pub struct RootMutations(user_email::UserEmailMutations);
pub struct Mutation(user_email::UserEmailMutations);
impl RootMutations {
impl Mutation {
#[must_use]
pub fn new() -> Self {
Self::default()

View File

@ -13,11 +13,15 @@
// limitations under the License.
use anyhow::Context as _;
use async_graphql::{Context, InputObject, Object, ID};
use mas_storage::job::{JobRepositoryExt, ProvisionUserJob, VerifyEmailJob};
use async_graphql::{Context, Description, Enum, InputObject, Object, ID};
use mas_storage::{
job::{JobRepositoryExt, ProvisionUserJob, VerifyEmailJob},
user::UserRepository,
RepositoryAccess,
};
use crate::{
model::{NodeType, UserEmail},
model::{NodeType, User, UserEmail},
state::ContextExt,
};
@ -35,6 +39,60 @@ struct AddEmailInput {
user_id: ID,
}
/// The status of the `addEmail` mutation
#[derive(Enum, Copy, Clone, Eq, PartialEq)]
pub enum AddEmailStatus {
/// The email address was added
Added,
/// The email address already exists
Exists,
}
/// The payload of the `addEmail` mutation
#[derive(Description)]
enum AddEmailPayload {
Added(mas_data_model::UserEmail),
Exists(mas_data_model::UserEmail),
}
#[Object(use_type_description)]
impl AddEmailPayload {
/// Status of the operation
async fn status(&self) -> AddEmailStatus {
match self {
AddEmailPayload::Added(_) => AddEmailStatus::Added,
AddEmailPayload::Exists(_) => AddEmailStatus::Exists,
}
}
/// The email address that was added
async fn email(&self) -> UserEmail {
match self {
AddEmailPayload::Added(email) | AddEmailPayload::Exists(email) => {
UserEmail(email.clone())
}
}
}
/// The user to whom the email address was added
async fn user(&self, ctx: &Context<'_>) -> Result<User, async_graphql::Error> {
let state = ctx.state();
let mut repo = state.repository().await?;
let user_id = match self {
AddEmailPayload::Added(email) | AddEmailPayload::Exists(email) => email.user_id,
};
let user = repo
.user()
.lookup(user_id)
.await?
.context("User not found")?;
Ok(User(user))
}
}
/// The input for the `sendVerificationEmail` mutation
#[derive(InputObject)]
struct SendVerificationEmailInput {
@ -42,6 +100,62 @@ struct SendVerificationEmailInput {
user_email_id: ID,
}
/// The status of the `sendVerificationEmail` mutation
#[derive(Enum, Copy, Clone, Eq, PartialEq)]
enum SendVerificationEmailStatus {
/// The verification email was sent
Sent,
/// The email address is already verified
AlreadyVerified,
}
/// The payload of the `sendVerificationEmail` mutation
#[derive(Description)]
enum SendVerificationEmailPayload {
Sent(mas_data_model::UserEmail),
AlreadyVerified(mas_data_model::UserEmail),
}
#[Object(use_type_description)]
impl SendVerificationEmailPayload {
/// Status of the operation
async fn status(&self) -> SendVerificationEmailStatus {
match self {
SendVerificationEmailPayload::Sent(_) => SendVerificationEmailStatus::Sent,
SendVerificationEmailPayload::AlreadyVerified(_) => {
SendVerificationEmailStatus::AlreadyVerified
}
}
}
/// The email address to which the verification email was sent
async fn email(&self) -> UserEmail {
match self {
SendVerificationEmailPayload::Sent(email)
| SendVerificationEmailPayload::AlreadyVerified(email) => UserEmail(email.clone()),
}
}
/// The user to whom the email address belongs
async fn user(&self, ctx: &Context<'_>) -> Result<User, async_graphql::Error> {
let state = ctx.state();
let mut repo = state.repository().await?;
let user_id = match self {
SendVerificationEmailPayload::Sent(email)
| SendVerificationEmailPayload::AlreadyVerified(email) => email.user_id,
};
let user = repo
.user()
.lookup(user_id)
.await?
.context("User not found")?;
Ok(User(user))
}
}
/// The input for the `verifyEmail` mutation
#[derive(InputObject)]
struct VerifyEmailInput {
@ -51,6 +165,68 @@ struct VerifyEmailInput {
code: String,
}
/// The status of the `verifyEmail` mutation
#[derive(Enum, Copy, Clone, Eq, PartialEq)]
enum VerifyEmailStatus {
/// The email address was just verified
Verified,
/// The email address was already verified before
AlreadyVerified,
/// The verification code is invalid
InvalidCode,
}
/// The payload of the `verifyEmail` mutation
#[derive(Description)]
enum VerifyEmailPayload {
Verified(mas_data_model::UserEmail),
AlreadyVerified(mas_data_model::UserEmail),
InvalidCode,
}
#[Object(use_type_description)]
impl VerifyEmailPayload {
/// Status of the operation
async fn status(&self) -> VerifyEmailStatus {
match self {
VerifyEmailPayload::Verified(_) => VerifyEmailStatus::Verified,
VerifyEmailPayload::AlreadyVerified(_) => VerifyEmailStatus::AlreadyVerified,
VerifyEmailPayload::InvalidCode => VerifyEmailStatus::InvalidCode,
}
}
/// The email address that was verified
async fn email(&self) -> Option<UserEmail> {
match self {
VerifyEmailPayload::Verified(email) | VerifyEmailPayload::AlreadyVerified(email) => {
Some(UserEmail(email.clone()))
}
VerifyEmailPayload::InvalidCode => None,
}
}
/// The user to whom the email address belongs
async fn user(&self, ctx: &Context<'_>) -> Result<Option<User>, async_graphql::Error> {
let state = ctx.state();
let mut repo = state.repository().await?;
let user_id = match self {
VerifyEmailPayload::Verified(email) | VerifyEmailPayload::AlreadyVerified(email) => {
email.user_id
}
VerifyEmailPayload::InvalidCode => return Ok(None),
};
let user = repo
.user()
.lookup(user_id)
.await?
.context("User not found")?;
Ok(Some(User(user)))
}
}
#[Object]
impl UserEmailMutations {
/// Add an email address to the specified user
@ -58,7 +234,7 @@ impl UserEmailMutations {
&self,
ctx: &Context<'_>,
input: AddEmailInput,
) -> Result<UserEmail, async_graphql::Error> {
) -> Result<AddEmailPayload, async_graphql::Error> {
let state = ctx.state();
let id = NodeType::User.extract_ulid(&input.user_id)?;
let requester = ctx.requester();
@ -75,15 +251,18 @@ impl UserEmailMutations {
// duplicated in mas_handlers
// Find an existing email address
let existing_user_email = repo.user_email().find(user, &input.email).await?;
let user_email = if let Some(user_email) = existing_user_email {
user_email
let (added, user_email) = if let Some(user_email) = existing_user_email {
(false, user_email)
} else {
let clock = state.clock();
let mut rng = state.rng();
repo.user_email()
let user_email = repo
.user_email()
.add(&mut rng, &clock, user, input.email)
.await?
.await?;
(true, user_email)
};
// Schedule a job to verify the email address if needed
@ -95,7 +274,12 @@ impl UserEmailMutations {
repo.save().await?;
Ok(UserEmail(user_email))
let payload = if added {
AddEmailPayload::Added(user_email)
} else {
AddEmailPayload::Exists(user_email)
};
Ok(payload)
}
/// Send a verification code for an email address
@ -103,7 +287,7 @@ impl UserEmailMutations {
&self,
ctx: &Context<'_>,
input: SendVerificationEmailInput,
) -> Result<UserEmail, async_graphql::Error> {
) -> Result<SendVerificationEmailPayload, async_graphql::Error> {
let state = ctx.state();
let user_email_id = NodeType::UserEmail.extract_ulid(&input.user_email_id)?;
let requester = ctx.requester();
@ -122,7 +306,8 @@ impl UserEmailMutations {
}
// Schedule a job to verify the email address if needed
if user_email.confirmed_at.is_none() {
let needs_verification = user_email.confirmed_at.is_none();
if needs_verification {
repo.job()
.schedule_job(VerifyEmailJob::new(&user_email))
.await?;
@ -130,7 +315,12 @@ impl UserEmailMutations {
repo.save().await?;
Ok(UserEmail(user_email))
let payload = if needs_verification {
SendVerificationEmailPayload::Sent(user_email)
} else {
SendVerificationEmailPayload::AlreadyVerified(user_email)
};
Ok(payload)
}
/// Submit a verification code for an email address
@ -138,7 +328,7 @@ impl UserEmailMutations {
&self,
ctx: &Context<'_>,
input: VerifyEmailInput,
) -> Result<UserEmail, async_graphql::Error> {
) -> Result<VerifyEmailPayload, async_graphql::Error> {
let state = ctx.state();
let user_email_id = NodeType::UserEmail.extract_ulid(&input.user_email_id)?;
let requester = ctx.requester();
@ -161,7 +351,7 @@ impl UserEmailMutations {
if user_email.confirmed_at.is_some() {
// Just return the email address if it's already verified
// XXX: should we return an error instead?
return Ok(UserEmail(user_email));
return Ok(VerifyEmailPayload::AlreadyVerified(user_email));
}
// XXX: this logic should be extracted somewhere else, since most of it is
@ -172,13 +362,12 @@ impl UserEmailMutations {
.user_email()
.find_verification_code(&clock, &user_email, &input.code)
.await?
.context("Invalid verification code")?;
.filter(|v| v.is_valid());
if verification.is_valid() {
return Err(async_graphql::Error::new("Invalid verification code"));
}
let Some(verification) = verification else {
return Ok(VerifyEmailPayload::InvalidCode);
};
// TODO: display nice errors if the code was already consumed or expired
repo.user_email()
.consume_verification_code(&clock, verification)
.await?;
@ -197,6 +386,6 @@ impl UserEmailMutations {
repo.save().await?;
Ok(UserEmail(user_email))
Ok(VerifyEmailPayload::Verified(user_email))
}
}

View File

@ -26,9 +26,9 @@ use self::{upstream_oauth::UpstreamOAuthQuery, viewer::ViewerQuery};
/// The query root of the GraphQL interface.
#[derive(Default, MergedObject)]
pub struct RootQuery(BaseQuery, UpstreamOAuthQuery, ViewerQuery);
pub struct Query(BaseQuery, UpstreamOAuthQuery, ViewerQuery);
impl RootQuery {
impl Query {
#[must_use]
pub fn new() -> Self {
Self::default()

View File

@ -51,6 +51,10 @@ module.exports = {
"plugin:prettier/recommended",
],
rules: {
"@graphql-eslint/input-name": [
"error",
{ checkInputType: true, caseSensitiveInputType: false },
],
"@graphql-eslint/relay-edge-types": [
"error",
{
@ -64,9 +68,9 @@ module.exports = {
"error",
{
exceptions: {
// The '*Connection', '*Edge' and 'PageInfo' types don't have IDs
// The '*Connection', '*Edge', '*Payload' and 'PageInfo' types don't have IDs
types: ["PageInfo"],
suffixes: ["Connection", "Edge"],
suffixes: ["Connection", "Edge", "Payload"],
},
},
],

View File

@ -12,6 +12,38 @@ input AddEmailInput {
userId: ID!
}
"""
The payload of the `addEmail` mutation
"""
type AddEmailPayload {
"""
Status of the operation
"""
status: AddEmailStatus!
"""
The email address that was added
"""
email: UserEmail!
"""
The user to whom the email address was added
"""
user: User!
}
"""
The status of the `addEmail` mutation
"""
enum AddEmailStatus {
"""
The email address was added
"""
ADDED
"""
The email address already exists
"""
EXISTS
}
type Anonymous implements Node {
id: ID!
}
@ -187,6 +219,26 @@ The input/output is a string in RFC3339 format.
"""
scalar DateTime
"""
The mutations root of the GraphQL interface.
"""
type Mutation {
"""
Add an email address to the specified user
"""
addEmail(input: AddEmailInput!): AddEmailPayload!
"""
Send a verification code for an email address
"""
sendVerificationEmail(
input: SendVerificationEmailInput!
): SendVerificationEmailPayload!
"""
Submit a verification code for an email address
"""
verifyEmail(input: VerifyEmailInput!): VerifyEmailPayload!
}
"""
An object with an ID.
"""
@ -309,28 +361,10 @@ type PageInfo {
endCursor: String
}
"""
The mutations root of the GraphQL interface.
"""
type RootMutations {
"""
Add an email address to the specified user
"""
addEmail(input: AddEmailInput!): UserEmail!
"""
Send a verification code for an email address
"""
sendVerificationEmail(input: SendVerificationEmailInput!): UserEmail!
"""
Submit a verification code for an email address
"""
verifyEmail(input: VerifyEmailInput!): UserEmail!
}
"""
The query root of the GraphQL interface.
"""
type RootQuery {
type Query {
"""
Get the current logged in browser session
"""
@ -397,6 +431,38 @@ input SendVerificationEmailInput {
userEmailId: ID!
}
"""
The payload of the `sendVerificationEmail` mutation
"""
type SendVerificationEmailPayload {
"""
Status of the operation
"""
status: SendVerificationEmailStatus!
"""
The email address to which the verification email was sent
"""
email: UserEmail!
"""
The user to whom the email address belongs
"""
user: User!
}
"""
The status of the `sendVerificationEmail` mutation
"""
enum SendVerificationEmailStatus {
"""
The verification email was sent
"""
SENT
"""
The email address is already verified
"""
ALREADY_VERIFIED
}
type UpstreamOAuth2Link implements Node & CreationEvent {
"""
ID of the object.
@ -635,6 +701,42 @@ input VerifyEmailInput {
code: String!
}
"""
The payload of the `verifyEmail` mutation
"""
type VerifyEmailPayload {
"""
Status of the operation
"""
status: VerifyEmailStatus!
"""
The email address that was verified
"""
email: UserEmail
"""
The user to whom the email address belongs
"""
user: User
}
"""
The status of the `verifyEmail` mutation
"""
enum VerifyEmailStatus {
"""
The email address was just verified
"""
VERIFIED
"""
The email address was already verified before
"""
ALREADY_VERIFIED
"""
The verification code is invalid
"""
INVALID_CODE
}
"""
Represents the current viewer
"""
@ -646,6 +748,6 @@ Represents the current viewer's session
union ViewerSession = BrowserSession | Anonymous
schema {
query: RootQuery
mutation: RootMutations
query: Query
mutation: Mutation
}

View File

@ -30,6 +30,25 @@ export type AddEmailInput = {
userId: Scalars['ID'];
};
/** The payload of the `addEmail` mutation */
export type AddEmailPayload = {
__typename?: 'AddEmailPayload';
/** The email address that was added */
email: UserEmail;
/** Status of the operation */
status: AddEmailStatus;
/** The user to whom the email address was added */
user: User;
};
/** The status of the `addEmail` mutation */
export enum AddEmailStatus {
/** The email address was added */
Added = 'ADDED',
/** The email address already exists */
Exists = 'EXISTS'
}
export type Anonymous = Node & {
__typename?: 'Anonymous';
id: Scalars['ID'];
@ -145,6 +164,35 @@ export type CreationEvent = {
createdAt: Scalars['DateTime'];
};
/** The mutations root of the GraphQL interface. */
export type Mutation = {
__typename?: 'Mutation';
/** Add an email address to the specified user */
addEmail: AddEmailPayload;
/** Send a verification code for an email address */
sendVerificationEmail: SendVerificationEmailPayload;
/** Submit a verification code for an email address */
verifyEmail: VerifyEmailPayload;
};
/** The mutations root of the GraphQL interface. */
export type MutationAddEmailArgs = {
input: AddEmailInput;
};
/** The mutations root of the GraphQL interface. */
export type MutationSendVerificationEmailArgs = {
input: SendVerificationEmailInput;
};
/** The mutations root of the GraphQL interface. */
export type MutationVerifyEmailArgs = {
input: VerifyEmailInput;
};
/** An object with an ID. */
export type Node = {
/** ID of the object. */
@ -220,38 +268,9 @@ export type PageInfo = {
startCursor?: Maybe<Scalars['String']>;
};
/** The mutations root of the GraphQL interface. */
export type RootMutations = {
__typename?: 'RootMutations';
/** Add an email address to the specified user */
addEmail: UserEmail;
/** Send a verification code for an email address */
sendVerificationEmail: UserEmail;
/** Submit a verification code for an email address */
verifyEmail: UserEmail;
};
/** The mutations root of the GraphQL interface. */
export type RootMutationsAddEmailArgs = {
input: AddEmailInput;
};
/** The mutations root of the GraphQL interface. */
export type RootMutationsSendVerificationEmailArgs = {
input: SendVerificationEmailInput;
};
/** The mutations root of the GraphQL interface. */
export type RootMutationsVerifyEmailArgs = {
input: VerifyEmailInput;
};
/** The query root of the GraphQL interface. */
export type RootQuery = {
__typename?: 'RootQuery';
export type Query = {
__typename?: 'Query';
/** Fetch a browser session by its ID. */
browserSession?: Maybe<BrowserSession>;
/**
@ -286,37 +305,37 @@ export type RootQuery = {
/** The query root of the GraphQL interface. */
export type RootQueryBrowserSessionArgs = {
export type QueryBrowserSessionArgs = {
id: Scalars['ID'];
};
/** The query root of the GraphQL interface. */
export type RootQueryNodeArgs = {
export type QueryNodeArgs = {
id: Scalars['ID'];
};
/** The query root of the GraphQL interface. */
export type RootQueryOauth2ClientArgs = {
export type QueryOauth2ClientArgs = {
id: Scalars['ID'];
};
/** The query root of the GraphQL interface. */
export type RootQueryUpstreamOauth2LinkArgs = {
export type QueryUpstreamOauth2LinkArgs = {
id: Scalars['ID'];
};
/** The query root of the GraphQL interface. */
export type RootQueryUpstreamOauth2ProviderArgs = {
export type QueryUpstreamOauth2ProviderArgs = {
id: Scalars['ID'];
};
/** The query root of the GraphQL interface. */
export type RootQueryUpstreamOauth2ProvidersArgs = {
export type QueryUpstreamOauth2ProvidersArgs = {
after?: InputMaybe<Scalars['String']>;
before?: InputMaybe<Scalars['String']>;
first?: InputMaybe<Scalars['Int']>;
@ -325,13 +344,13 @@ export type RootQueryUpstreamOauth2ProvidersArgs = {
/** The query root of the GraphQL interface. */
export type RootQueryUserArgs = {
export type QueryUserArgs = {
id: Scalars['ID'];
};
/** The query root of the GraphQL interface. */
export type RootQueryUserEmailArgs = {
export type QueryUserEmailArgs = {
id: Scalars['ID'];
};
@ -341,6 +360,25 @@ export type SendVerificationEmailInput = {
userEmailId: Scalars['ID'];
};
/** The payload of the `sendVerificationEmail` mutation */
export type SendVerificationEmailPayload = {
__typename?: 'SendVerificationEmailPayload';
/** The email address to which the verification email was sent */
email: UserEmail;
/** Status of the operation */
status: SendVerificationEmailStatus;
/** The user to whom the email address belongs */
user: User;
};
/** The status of the `sendVerificationEmail` mutation */
export enum SendVerificationEmailStatus {
/** The email address is already verified */
AlreadyVerified = 'ALREADY_VERIFIED',
/** The verification email was sent */
Sent = 'SENT'
}
export type UpstreamOAuth2Link = CreationEvent & Node & {
__typename?: 'UpstreamOAuth2Link';
/** When the object was created. */
@ -516,6 +554,27 @@ export type VerifyEmailInput = {
userEmailId: Scalars['ID'];
};
/** The payload of the `verifyEmail` mutation */
export type VerifyEmailPayload = {
__typename?: 'VerifyEmailPayload';
/** The email address that was verified */
email?: Maybe<UserEmail>;
/** Status of the operation */
status: VerifyEmailStatus;
/** The user to whom the email address belongs */
user?: Maybe<User>;
};
/** The status of the `verifyEmail` mutation */
export enum VerifyEmailStatus {
/** The email address was already verified before */
AlreadyVerified = 'ALREADY_VERIFIED',
/** The verification code is invalid */
InvalidCode = 'INVALID_CODE',
/** The email address was just verified */
Verified = 'VERIFIED'
}
/** Represents the current viewer */
export type Viewer = Anonymous | User;
@ -548,7 +607,7 @@ export type BrowserSessionQueryQueryVariables = Exact<{
}>;
export type BrowserSessionQueryQuery = { __typename?: 'RootQuery', browserSession?: { __typename?: 'BrowserSession', id: string, createdAt: any, lastAuthentication?: { __typename?: 'Authentication', id: string, createdAt: any } | null, user: { __typename?: 'User', id: string, username: string } } | null };
export type BrowserSessionQueryQuery = { __typename?: 'Query', browserSession?: { __typename?: 'BrowserSession', id: string, createdAt: any, lastAuthentication?: { __typename?: 'Authentication', id: string, createdAt: any } | null, user: { __typename?: 'User', id: string, username: string } } | null };
export type HomeQueryQueryVariables = Exact<{
count: Scalars['Int'];
@ -556,7 +615,7 @@ export type HomeQueryQueryVariables = Exact<{
}>;
export type HomeQueryQuery = { __typename?: 'RootQuery', currentBrowserSession?: { __typename?: 'BrowserSession', id: string, user: (
export type HomeQueryQuery = { __typename?: 'Query', currentBrowserSession?: { __typename?: 'BrowserSession', id: string, user: (
{ __typename?: 'User', id: string, username: string }
& { ' $fragmentRefs'?: { 'CompatSsoLoginList_UserFragment': CompatSsoLoginList_UserFragment;'BrowserSessionList_UserFragment': BrowserSessionList_UserFragment;'OAuth2SessionList_UserFragment': OAuth2SessionList_UserFragment } }
) } | null };
@ -566,7 +625,7 @@ export type OAuth2ClientQueryQueryVariables = Exact<{
}>;
export type OAuth2ClientQueryQuery = { __typename?: 'RootQuery', oauth2Client?: { __typename?: 'Oauth2Client', id: string, clientId: string, clientName?: string | null, clientUri?: any | null, tosUri?: any | null, policyUri?: any | null, redirectUris: Array<any> } | null };
export type OAuth2ClientQueryQuery = { __typename?: 'Query', oauth2Client?: { __typename?: 'Oauth2Client', id: string, clientId: string, clientName?: string | null, clientUri?: any | null, tosUri?: any | null, policyUri?: any | null, redirectUris: Array<any> } | null };
export const BrowserSession_SessionFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BrowserSession_session"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"BrowserSession"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"lastAuthentication"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]} as unknown as DocumentNode<BrowserSession_SessionFragment, unknown>;
export const BrowserSessionList_UserFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BrowserSessionList_user"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"browserSessions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"Variable","name":{"kind":"Name","value":"count"}}},{"kind":"Argument","name":{"kind":"Name","value":"after"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"BrowserSession_session"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BrowserSession_session"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"BrowserSession"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"lastAuthentication"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]} as unknown as DocumentNode<BrowserSessionList_UserFragment, unknown>;

View File

@ -2,13 +2,55 @@ import { IntrospectionQuery } from "graphql";
export default {
__schema: {
queryType: {
name: "RootQuery",
name: "Query",
},
mutationType: {
name: "RootMutations",
name: "Mutation",
},
subscriptionType: null,
types: [
{
kind: "OBJECT",
name: "AddEmailPayload",
fields: [
{
name: "email",
type: {
kind: "NON_NULL",
ofType: {
kind: "OBJECT",
name: "UserEmail",
ofType: null,
},
},
args: [],
},
{
name: "status",
type: {
kind: "NON_NULL",
ofType: {
kind: "SCALAR",
name: "Any",
},
},
args: [],
},
{
name: "user",
type: {
kind: "NON_NULL",
ofType: {
kind: "OBJECT",
name: "User",
ofType: null,
},
},
args: [],
},
],
interfaces: [],
},
{
kind: "OBJECT",
name: "Anonymous",
@ -482,6 +524,82 @@ export default {
},
],
},
{
kind: "OBJECT",
name: "Mutation",
fields: [
{
name: "addEmail",
type: {
kind: "NON_NULL",
ofType: {
kind: "OBJECT",
name: "AddEmailPayload",
ofType: null,
},
},
args: [
{
name: "input",
type: {
kind: "NON_NULL",
ofType: {
kind: "SCALAR",
name: "Any",
},
},
},
],
},
{
name: "sendVerificationEmail",
type: {
kind: "NON_NULL",
ofType: {
kind: "OBJECT",
name: "SendVerificationEmailPayload",
ofType: null,
},
},
args: [
{
name: "input",
type: {
kind: "NON_NULL",
ofType: {
kind: "SCALAR",
name: "Any",
},
},
},
],
},
{
name: "verifyEmail",
type: {
kind: "NON_NULL",
ofType: {
kind: "OBJECT",
name: "VerifyEmailPayload",
ofType: null,
},
},
args: [
{
name: "input",
type: {
kind: "NON_NULL",
ofType: {
kind: "SCALAR",
name: "Any",
},
},
},
],
},
],
interfaces: [],
},
{
kind: "INTERFACE",
name: "Node",
@ -831,83 +949,7 @@ export default {
},
{
kind: "OBJECT",
name: "RootMutations",
fields: [
{
name: "addEmail",
type: {
kind: "NON_NULL",
ofType: {
kind: "OBJECT",
name: "UserEmail",
ofType: null,
},
},
args: [
{
name: "input",
type: {
kind: "NON_NULL",
ofType: {
kind: "SCALAR",
name: "Any",
},
},
},
],
},
{
name: "sendVerificationEmail",
type: {
kind: "NON_NULL",
ofType: {
kind: "OBJECT",
name: "UserEmail",
ofType: null,
},
},
args: [
{
name: "input",
type: {
kind: "NON_NULL",
ofType: {
kind: "SCALAR",
name: "Any",
},
},
},
],
},
{
name: "verifyEmail",
type: {
kind: "NON_NULL",
ofType: {
kind: "OBJECT",
name: "UserEmail",
ofType: null,
},
},
args: [
{
name: "input",
type: {
kind: "NON_NULL",
ofType: {
kind: "SCALAR",
name: "Any",
},
},
},
],
},
],
interfaces: [],
},
{
kind: "OBJECT",
name: "RootQuery",
name: "Query",
fields: [
{
name: "browserSession",
@ -1135,6 +1177,48 @@ export default {
],
interfaces: [],
},
{
kind: "OBJECT",
name: "SendVerificationEmailPayload",
fields: [
{
name: "email",
type: {
kind: "NON_NULL",
ofType: {
kind: "OBJECT",
name: "UserEmail",
ofType: null,
},
},
args: [],
},
{
name: "status",
type: {
kind: "NON_NULL",
ofType: {
kind: "SCALAR",
name: "Any",
},
},
args: [],
},
{
name: "user",
type: {
kind: "NON_NULL",
ofType: {
kind: "OBJECT",
name: "User",
ofType: null,
},
},
args: [],
},
],
interfaces: [],
},
{
kind: "OBJECT",
name: "UpstreamOAuth2Link",
@ -1836,6 +1920,42 @@ export default {
],
interfaces: [],
},
{
kind: "OBJECT",
name: "VerifyEmailPayload",
fields: [
{
name: "email",
type: {
kind: "OBJECT",
name: "UserEmail",
ofType: null,
},
args: [],
},
{
name: "status",
type: {
kind: "NON_NULL",
ofType: {
kind: "SCALAR",
name: "Any",
},
},
args: [],
},
{
name: "user",
type: {
kind: "OBJECT",
name: "User",
ofType: null,
},
args: [],
},
],
interfaces: [],
},
{
kind: "UNION",
name: "Viewer",