################################################################################
## Custom Directives
################################################################################

"""
USER_AUTH_CONDITIONS describes conditions that would prevent a given User to
execute any set of mutations or reserved queries.
"""
enum USER_AUTH_CONDITIONS {
  """
  MISSING_NAME is provided when the User does not have an associated username or
  display name.
  """
  MISSING_NAME

  """
  MISSING_EMAIL is provided when the User does not have an associated email
  address.
  """
  MISSING_EMAIL

  """
  BANNED is provided when the User is currently banned.
  """
  BANNED

  """
  SUSPENDED is provided when the User is currently under an active suspension.
  """
  SUSPENDED

  """
  PENDING_DELETION is provided when the User is scheduled for deletion and will
  remain after being deleted.
  """
  PENDING_DELETION
}

"""
auth is a directive that will enforce authorization rules on the schema
definition. It will restrict the viewer of the field based on roles or if the
`userIDField` is specified, it  will see if the current users ID equals the field
specified. This allows users that own a specific resource (like a comment, or a
flag) see their own content, but restrict it to everyone else. If the directive
is used without options, it simply requires a logged in user. `permit` can be
used to allow specific `USER_AUTH_CONDITIONS` that normally (if present) would
deny access to any edge associated with the `@auth` directive. If a User has
only some of the conditions listed, they will pass, but if they have at least
one more that isn't in the list, the request will be denied.
"""
directive @auth(
  roles: [USER_ROLE!]
  userIDField: String
  permit: [USER_AUTH_CONDITIONS!]
) on FIELD_DEFINITION

"""
constraint is a directive that will perform validation on input fields used as
arguments to parameters passed in to operations.
"""
directive @constraint(min: Int, max: Int) on ARGUMENT_DEFINITION

"""
rate enforces a rate limit on requests made by the user.
"""
directive @rate(max: Int = 1, seconds: Int!, key: String) on FIELD_DEFINITION

################################################################################
## Custom Scalar Types
################################################################################

"""
Time represented as an ISO8601 string.
"""
scalar Time

"""
Cursor represents a paginating cursor.
"""
scalar Cursor

"""
Locale represents a language code in the BCP 47 format.
"""
scalar Locale

################################################################################
## Actions
################################################################################

"""
COMMENT_FLAG_REPORTED_REASON is a reason that is reported by a User on a
Comment.
"""
enum COMMENT_FLAG_REPORTED_REASON {
  """
  COMMENT_REPORTED_OFFENSIVE is used when a User reported a Comment as being
  offensive.
  """
  COMMENT_REPORTED_OFFENSIVE

  """
  COMMENT_REPORTED_SPAM is used when a User reported a Comment as appearing like
  spam.
  """
  COMMENT_REPORTED_SPAM

  """
  COMMENT_REPORTED_OTHER is used when a User reported a Comment that doesn't
  fit into the other reported reasons.
  """
  COMMENT_REPORTED_OTHER
}

"""
COMMENT_FLAG_DETECTED_REASON is a reason that is detected by the system on a
Comment.
"""
enum COMMENT_FLAG_DETECTED_REASON {
  """
  COMMENT_DETECTED_TOXIC is used when the Comment was detected as being toxic by
  the system.
  """
  COMMENT_DETECTED_TOXIC

  """
  COMMENT_DETECTED_SPAM is used when the Comment was detected as having spam by
  the system.
  """
  COMMENT_DETECTED_SPAM

  """
  COMMENT_DETECTED_LINKS is used when the Comment was detected as containing
  links.
  """
  COMMENT_DETECTED_LINKS

  """
  COMMENT_DETECTED_BANNED_WORD is used when the Comment was detected as
  containing a banned word.
  """
  COMMENT_DETECTED_BANNED_WORD

  """
  COMMENT_DETECTED_SUSPECT_WORD is used when the Comment was detected as
  containing a suspect word.
  """
  COMMENT_DETECTED_SUSPECT_WORD

  """
  COMMENT_DETECTED_RECENT_HISTORY is used when a Comment author has exhibited a
  recent history of rejected comments.
  """
  COMMENT_DETECTED_RECENT_HISTORY

  """
  COMMENT_DETECTED_PREMOD_USER is used when a Comment author has been tagged as requiring premoderation
  """
  COMMENT_DETECTED_PREMOD_USER

  """
  COMMENT_DETECTED_REPEAT_POST is used when a Comment's text exactly matches a previous recent comment from the same author
  """
  COMMENT_DETECTED_REPEAT_POST
}

"""
COMMENT_FLAG_REASON is the union of the COMMENT_FLAG_REPORTED_REASON
and COMMENT_FLAG_DETECTED_REASON types.
"""
enum COMMENT_FLAG_REASON {
  COMMENT_REPORTED_OFFENSIVE
  COMMENT_REPORTED_SPAM
  COMMENT_REPORTED_OTHER
  COMMENT_DETECTED_TOXIC
  COMMENT_DETECTED_SPAM
  COMMENT_DETECTED_LINKS
  COMMENT_DETECTED_BANNED_WORD
  COMMENT_DETECTED_SUSPECT_WORD
  COMMENT_DETECTED_RECENT_HISTORY
  COMMENT_DETECTED_PREMOD_USER
  COMMENT_DETECTED_NEW_COMMENTER
  COMMENT_DETECTED_REPEAT_POST
}

"""
ReactionActionCounts stores all the counts for the counts for the reaction
action on a given item.
"""
type ReactionActionCounts {
  """
  total is the total number of reactions against a given item.
  """
  total: Int!
}

"""
DontAgreeActionCounts stores all the counts for the counts for the dontAgree
action on a given item.
"""
type DontAgreeActionCounts {
  """
  total is the total number of dontAgree actions against a given item.
  """
  total: Int!
}

type FlagReasonActionCounts {
  COMMENT_REPORTED_OFFENSIVE: Int!
  COMMENT_REPORTED_SPAM: Int!
  COMMENT_REPORTED_OTHER: Int!
  COMMENT_DETECTED_TOXIC: Int!
  COMMENT_DETECTED_SPAM: Int!
  COMMENT_DETECTED_LINKS: Int!
  COMMENT_DETECTED_BANNED_WORD: Int!
  COMMENT_DETECTED_SUSPECT_WORD: Int!
  COMMENT_DETECTED_RECENT_HISTORY: Int!
  COMMENT_DETECTED_PREMOD_USER: Int!
  COMMENT_DETECTED_NEW_COMMENTER: Int!
  COMMENT_DETECTED_REPEAT_POST: Int!
}

type Flag {
  """
  flagger is the User that created the Flag. If this is null, then the system
  created the Flag.
  """
  flagger: User

  """
  reason is the selected reason why the Flag is being created. If the reason is
  not defined, or existed from a previous version of Coral, it will return null
  to avoid errors.
  """
  reason: COMMENT_FLAG_REASON

  """
  additionalDetails stores information from the User as to why the Flag was
  created or is relevant.
  """
  additionalDetails: String
}

type FlagEdge {
  """
  node is the Flag for this edge.
  """
  node: Flag!

  """
  cursor is used in pagination.
  """
  cursor: Cursor!
}

type FlagsConnection {
  """
  edges are a subset of FlagEdge's.
  """
  edges: [FlagEdge!]!

  """
  nodes is a list of Flags.
  """
  nodes: [Flag!]!

  """
  pageInfo is information to aid in pagination.
  """
  pageInfo: PageInfo!
}

"""
FlagActionCounts stores all the counts for the counts for the flag action on a
given item and the reason counts.
"""
type FlagActionCounts {
  """
  total is the total number of flags against a given item.
  """
  total: Int!

  """
  reasons stores the counts for the various reasons that an item could be
  flagged for.
  """
  reasons: FlagReasonActionCounts!
}

"""
ActionCounts returns the counts of each action for an item.
"""
type ActionCounts {
  """
  reaction returns the counts for the reaction action on an item.
  """
  reaction: ReactionActionCounts!

  """
  dontAgree returns the counts for the dontAgree action on an item. This edge is
  restricted to administrators and moderators.
  """
  dontAgree: DontAgreeActionCounts! @auth(roles: [ADMIN, MODERATOR])

  """
  flag returns the counts for the flag action on an item. This edge is
  restricted to administrators and moderators.
  """
  flag: FlagActionCounts! @auth(roles: [ADMIN, MODERATOR])
}

"""
ActionPresence returns whether or not a given item has one of the following
actions on it. This is typically used to determine if a given user has left
one of the following actions.
"""
type ActionPresence {
  """
  reaction is true when a reaction action was left on an item.
  """
  reaction: Boolean!

  """
  dontAgree is true when a dontAgree action was left on an item.
  """
  dontAgree: Boolean!

  """
  flag is true when a flag action was left on an item.
  """
  flag: Boolean!
}

################################################################################
## Settings
################################################################################

enum FEATURE_FLAG {
  """
  DISABLE_WARN_USER_OF_TOXIC_COMMENT when enabled will turn off warnings for
  toxic comments.
  """
  DISABLE_WARN_USER_OF_TOXIC_COMMENT
}

# The moderation mode of the site.
enum MODERATION_MODE {
  """
  Comments posted while in `PRE` mode will be labeled with a `PREMOD`
  status and will require a moderator decision before being visible.
  """
  PRE

  """
  Comments posted while in `POST` will be visible immediately.
  """
  POST
}

"""
WordList describes all the available wordLists.
"""
type WordList {
  """
  banned words will by default reject the comment if it is found.
  """
  banned: [String!]!

  """
  suspect words will simply flag the comment.
  """
  suspect: [String!]!
}

################################################################################
## Moderation
################################################################################

"""
ModerationQueue returns the Comments associated with a Moderation Queue.
"""
type ModerationQueue {
  """
  id is a canonical identifier for this specific moderation queue.
  """
  id: ID!

  """
  count will return the number of Comments that are in this queue.
  """
  count: Int!

  """
  comments are the comments on the ModerationQueue.
  """
  comments(
    first: Int = 10 @constraint(max: 50)
    after: Cursor
  ): CommentsConnection!
}

"""
ModerationQueues are the list of ModerationQueue's that are supported inside
Coral that can be used to moderate Comments.
"""
type ModerationQueues {
  """
  unmoderated will return a ModerationQueue for all Comments that have not been
  moderated yet.
  """
  unmoderated: ModerationQueue!

  """
  reported will return a ModerationQueue for all Comments that have been
  published, have not been moderated by a human yet, and have been reported by
  a User via a flag.
  """
  reported: ModerationQueue!

  """
  pending will return a ModerationQueue for all Comments that were held back by
  the system and require moderation in order to be published.
  """
  pending: ModerationQueue!
}

################################################################################
## Auth
################################################################################

##########################
## AuthenticationTargetFilter
##########################

"""
AuthenticationTargetFilter when non-null, will specify the targets that a
specific authentication integration will be enabled on.
"""
type AuthenticationTargetFilter {
  admin: Boolean!
  stream: Boolean!
}

##########################
## LocalAuthIntegration
##########################

type LocalAuthIntegration {
  enabled: Boolean!

  """
  allowRegistration when true will allow users that have not signed up
  before with this authentication integration to sign up.
  """
  allowRegistration: Boolean!

  """
  targetFilter will restrict where the authentication integration should be
  displayed. If the value of targetFilter is null, then the authentication
  integration should be displayed in all targets.
  """
  targetFilter: AuthenticationTargetFilter!
}

##########################
## SSOAuthIntegration
##########################

"""
SSOAuthIntegration is an AuthIntegration that provides a secret to the admins
of a tenant, where they can sign a SSO payload with it to provide to the
embed to allow single sign on.
"""
type SSOAuthIntegration {
  enabled: Boolean!

  """
  allowRegistration when true will allow users that have not signed up
  before with this authentication integration to sign up.
  """
  allowRegistration: Boolean!

  """
  targetFilter will restrict where the authentication integration should be
  displayed. If the value of targetFilter is null, then the authentication
  integration should be displayed in all targets.
  """
  targetFilter: AuthenticationTargetFilter!

  """
  key is the secret that is used to sign tokens.
  """
  key: String @auth(roles: [ADMIN])

  """
  keyGeneratedAt is the Time that the key was effective from.
  """
  keyGeneratedAt: Time @auth(roles: [ADMIN])
}

##########################
## OIDCAuthIntegration
##########################

"""
DiscoveredOIDCConfiguration contains the discovered Provider Metadata as defined
in:

https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata

Discovery is not supported on all providers, and is described in the OpenID
Connect Discovery 1.0 incorporating errata set 1:

https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig
"""
type DiscoveredOIDCConfiguration {
  """
  issuer is defined as the `issuer` in:

  https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
  """
  issuer: String!

  """
  authorizationURL is defined as the `authorization_endpoint` in:

  https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
  """
  authorizationURL: String!

  """
  tokenURL is defined as the `token_endpoint` in:

  https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
  """
  tokenURL: String

  """
  jwksURI is defined as the `jwks_uri` in:

  https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
  """
  jwksURI: String!
}

"""
OIDCAuthIntegration provides a way to store Open ID Connect credentials. This
will be used in the admin to provide staff logins for users.
"""
type OIDCAuthIntegration {
  """
  enabled, when true, allows the integration to be enabled.
  """
  enabled: Boolean!

  """
  allowRegistration when true will allow users that have not signed up
  before with this authentication integration to sign up.
  """
  allowRegistration: Boolean!

  """
  targetFilter will restrict where the authentication integration should be
  displayed. If the value of targetFilter is null, then the authentication
  integration should be displayed in all targets.
  """
  targetFilter: AuthenticationTargetFilter!

  """
  name is the label assigned to reference the provider of the OIDC integration,
  and will be used in situations where the name of the provider needs to be
  displayed, like the login button.
  """
  name: String

  """
  callbackURL is the URL that the user should be redirected to in order to continue
  the authentication flow with the given integration. This field is not stored,
  and is instead computed from the Tenant.
  """
  callbackURL: String!

  """
  redirectURL is the URL that the user should be redirected to in order to start
  an authentication flow with the given integration. This field is not stored,
  and is instead computed from the Tenant.
  """
  redirectURL: String

  """
  clientID is the Client Identifier as defined in:

  https://tools.ietf.org/html/rfc6749#section-2.2
  """
  clientID: String @auth(roles: [ADMIN])

  """
  clientSecret is the Client Secret as defined in:

  https://tools.ietf.org/html/rfc6749#section-2.3.1
  """
  clientSecret: String @auth(roles: [ADMIN])

  """
  authorizationURL is defined as the `authorization_endpoint` in:

  https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
  """
  authorizationURL: String @auth(roles: [ADMIN])

  """
  tokenURL is defined as the `token_endpoint` in:

  https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
  """
  tokenURL: String @auth(roles: [ADMIN])

  """
  jwksURI is defined as the `jwks_uri` in:

  https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
  """
  jwksURI: String @auth(roles: [ADMIN])

  """
  issuer is defined as the `issuer` in:

  https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
  """
  issuer: String @auth(roles: [ADMIN])
}

##########################
## GoogleAuthIntegration
##########################

type GoogleAuthIntegration {
  """
  enabled, when true, will enable Google as an authentication integration.
  """
  enabled: Boolean!

  """
  allowRegistration when true will allow users that have not signed up
  before with this authentication integration to sign up.
  """
  allowRegistration: Boolean!

  """
  targetFilter will restrict where the authentication integration should be
  displayed. If the value of targetFilter is null, then the authentication
  integration should be displayed in all targets.
  """
  targetFilter: AuthenticationTargetFilter!

  """
  clientID is the Client Identifier as provided by the Google API Console.
  """
  clientID: String @auth(roles: [ADMIN])

  """
  clientSecret is the Client Secret as provided by the Google API Console.
  """
  clientSecret: String @auth(roles: [ADMIN])

  """
  callbackURL is the URL that the user should be redirected to in order to continue
  the authentication flow. This field is not stored, and is instead computed from
  the Tenant.
  """
  callbackURL: String!

  """
  redirectURL is the URL that the user should be redirected to in order to start
  an authentication flow with the given integration. This field is not stored,
  and is instead computed from the Tenant.
  """
  redirectURL: String!
}

##########################
## FacebookAuthIntegration
##########################

type FacebookAuthIntegration {
  """
  enabled, when true, will enable Facebook as an authentication integration.
  """
  enabled: Boolean!

  """
  allowRegistration when true will allow users that have not signed up
  before with this authentication integration to sign up.
  """
  allowRegistration: Boolean!

  """
  targetFilter will restrict where the authentication integration should be
  displayed. If the value of targetFilter is null, then the authentication
  integration should be displayed in all targets.
  """
  targetFilter: AuthenticationTargetFilter!

  """
  clientID is the Client Identifier as provided by the Facebook Developer
  Console.
  """
  clientID: String @auth(roles: [ADMIN])

  """
  clientSecret is the Client Secret as provided by the Facebook Developer
  Console.
  """
  clientSecret: String @auth(roles: [ADMIN])

  """
  callbackURL is the URL that the user should be redirected to in order to continue
  the authentication flow. This field is not stored, and is instead computed from
  the Tenant.
  """
  callbackURL: String!

  """
  redirectURL is the URL that the user should be redirected to in order to start
  an authentication flow with the given integration. This field is not stored,
  and is instead computed from the Tenant.
  """
  redirectURL: String!
}

##########################
## Auth
##########################

type AuthIntegrations {
  """
  local stores configuration related to email/password based logins.
  """
  local: LocalAuthIntegration!

  """
  sso stores configuration related to Single Sign On based logins.
  """
  sso: SSOAuthIntegration!

  """
  oidc stores configuration related to OpenID Connect based logins.
  """
  oidc: OIDCAuthIntegration!

  """
  google stores configuration related to Google based logins.
  """
  google: GoogleAuthIntegration!

  """
  facebook stores configuration related to Facebook based logins.
  """
  facebook: FacebookAuthIntegration!
}

"""
Auth contains all the settings related to authentication and
authorization.
"""
type Auth {
  """
  integrations are the set of configurations for the variations of
  authentication solutions.
  """
  integrations: AuthIntegrations!

  """
  sessionDuration determines the duration in seconds for which an access token is valid
  """
  sessionDuration: Int! @auth(roles: [ADMIN])
}

################################################################################
## ExternalIntegrations
################################################################################

type AkismetExternalIntegration {
  """
  enabled when True will enable the integration.
  """
  enabled: Boolean!

  """
  The key for the Akismet integration.
  """
  key: String

  """
  The site (blog) for the Akismet integration.
  """
  site: String
}

type PerspectiveExternalIntegration {
  """
  enabled when True will enable the integration.
  """
  enabled: Boolean!

  """
  The endpoint that Coral should use to communicate with the perspective API.
  """
  endpoint: String @auth(roles: [ADMIN])

  """
  The key for the Perspective API integration.
  """
  key: String @auth(roles: [ADMIN])

  """
  The threshold that given a specific toxic comment score, the comment will
  be marked by Coral as toxic.
  """
  threshold: Float

  """
  model is the Perspective model to use.
  """
  model: String @auth(roles: [ADMIN])

  """
  When True, comments sent will not be stored by the Google Perspective API.
  """
  doNotStore: Boolean @auth(roles: [ADMIN])

  """
  When True, comment moderation decisions will be sent to the Google
  Perspective API to help improve the comment analysis algorithms.
  """
  sendFeedback: Boolean @auth(roles: [ADMIN])
}

type ExternalIntegrations {
  """
  akismet provides integration with the Akismet Spam detection service.
  """
  akismet: AkismetExternalIntegration!

  """
  perspective provides integration with the Perspective API comment analysis
  platform.
  """
  perspective: PerspectiveExternalIntegration!
}

################################################################################
## RecentCommentHistory
################################################################################

"""
RecentCommentHistoryConfiguration controls the beheviour around when comments
from Users should be marked for pre-moderation automatically once they have
reached the trigger rate for rejected comments.
"""
type RecentCommentHistoryConfiguration {
  """
  enabled when true will pre-moderate users new comments once they have reached
  the trigger rejection rate.
  """
  enabled: Boolean!

  """
  timeFrame specifies the number of seconds that comments from a given User will
  be taken into account when computing their rejection rate.
  """
  timeFrame: Int!

  """
  triggerRejectionRate specifies the percentage of comments that a given User
  may have before their comments will then be placed into pre-moderation until
  the rejection rate decreases.
  """
  triggerRejectionRate: Float!
}

"""
RecentCommentHistory returns data associated with a User's recent commenting
history within the specified timeFrame configured.
"""
type RecentCommentHistory {
  """
  statuses stores the counts of all the statuses against Comments by a User
  within the timeFrame configured.
  """
  statuses: CommentStatusCounts!
}

################################################################################
## CharCount
################################################################################

type CharCount {
  """
  enabled when true, enables the character count moderation phase.
  """
  enabled: Boolean!

  """
  min is the smallest length of a Comment that may be posted.
  """
  min: Int

  """
  max is the largest length of a Comment that may be posted.
  """
  max: Int
}

################################################################################
## DisableCommenting
################################################################################

type DisableCommenting {
  """
  enabled when true will disable commenting site-wide.
  """
  enabled: Boolean!

  """
  message will be shown above the comment stream while
  commenting is disabled site-wide.
  """
  message: String!
}

################################################################################
## EmailConfiguration
################################################################################

type SMTP {
  secure: Boolean
  host: String
  port: Int
  authentication: Boolean
  username: String
  password: String
}

type EmailConfiguration {
  """
  enabled when true, will enable the emailing functionality in Coral.
  """
  enabled: Boolean!

  fromName: String
  fromEmail: String
  smtp: SMTP! @auth(roles: [ADMIN])
}

################################################################################
## Slack Configuration
################################################################################

type SlackConfiguration {
  """
  channels is the set of Slack channels configured to receive comments
  """
  channels: [SlackChannel!]!
}

type SlackChannelTriggers {
  """
  reportedComments is whether this channel will receive reported comments
  """
  reportedComments: Boolean!

  """
  pendingComments is whether this channel will receive pending comments
  """
  pendingComments: Boolean!

  """
  featuredComments is whether this channel will receive featured comments
  """
  featuredComments: Boolean!
}

type SlackChannel {
  """
  enabled is whether this Slack channel is enabled to send comments to its
  correlated web hook
  """
  enabled: Boolean!

  """
  name is the name assigned to the Slack channel
  """
  name: String

  """
  hookURL defines the URL that comments will be sent to
  """
  hookURL: String

  """
  triggers are the filters of types of comments that will be sent to
  the correlated channel
  """
  triggers: SlackChannelTriggers
}

################################################################################
## ReactionConfiguration
################################################################################

"""
ReactionConfiguration stores the configuration for reactions used across this
Tenant.
"""
type ReactionConfiguration {
  """
  icon is the string representing the icon to be used for the reactions.
  """
  icon: String!

  """
  iconActive is the string representing the icon that should be used when the
  icon should be considered active.
  """
  iconActive: String

  """
  label is the string placed beside the reaction icon to provide better context.
  """
  label: String!

  """
  labelActive is the string placed beside the reaction icon to provide better
  context when it has been selected.
  """
  labelActive: String!

  """
  sortLabel is the string placed inside of the sort menu to sort for comment
  with most reactions.
  """
  sortLabel: String!

  """
  color is the hex color code that can be used to change the color of the button.
  """
  color: String
}

################################################################################
## CommunityGuidelines
################################################################################

type CommunityGuidelines {
  """
  enable set to true will show the guidelines above the message box.
  """
  enabled: Boolean!

  """
  content of the guidelines.
  """
  content: String
}

"""
StoryMessageBox stores settings related to the Story Message Box.
"""
type StoryMessageBox {
  """
  enable when true will enable the Message Box on the comment stream.
  """
  enabled: Boolean!

  """
  icon when set will reference the string for the Message box used by the
  Message Box.
  """
  icon: String

  """
  content when set contains the actual markup for the Message Box.
  """
  content: String
}

################################################################################
## Settings
################################################################################

"""
CloseCommenting contains settings related to the automatic closing of commenting
on Stories.
"""
type CloseCommenting {
  """
  auto when true will configure the automatic close on each story as they are
  created based on the current configured timeout option.
  """
  auto: Boolean!

  """
  timeout is the amount of seconds from the `createdAt` timestamp that a given
  story will be considered closed.
  """
  timeout: Int!

  """
  message when provided will be the message that shows when the comment stream
  is closed for commenting.
  """
  message: String!
}

"""
Organization stores information about the organization behind this specific
instance of Coral.
"""
type Organization {
  """
  name is the name of the organization.
  """
  name: String!

  """
  contactEmail is the email of the organization.
  """
  contactEmail: String!

  """
  url is the URL to the organizations home page.
  """
  url: String!
}

"""
StoryScrapingConfiguration stores the configuration around story scraping.
"""
type StoryScrapingConfiguration {
  """
  enabled, when true, enables stories to be scraped. When disabled, stories will
  only be looked up instead, and must be created via the API directly.
  """
  enabled: Boolean!

  """
  proxyURL when specified, allows scraping requests to use the provided proxy.
  All requests will then be passed through the appropriote proxy as parsed by
  the [proxy-agent](https://www.npmjs.com/package/proxy-agent) package.
  """
  proxyURL: String

  """
  customUserAgent when specified will override the user agent used by fetch
  requests made during the scraping process.
  """
  customUserAgent: String
}

"""
StoryConfiguration stores the configuration for working with stories.
"""
type StoryConfiguration {
  """
  scraping stores configuration around story scraping.
  """
  scraping: StoryScrapingConfiguration!

  """
  disableLazy when true, will only allow lookups of stories created via the API.
  """
  disableLazy: Boolean!
}

"""
CommenterAccountFeatures stores the configuration for commenter account features.
"""
type CommenterAccountFeatures {
  """
  changeUsername when true, non-sso user may change username every 14 days
  """
  changeUsername: Boolean!

  """
  downloadComments when true, user may download their comment history
  """
  downloadComments: Boolean!

  """
  deleteAccount when true, non-sso user may permanently delete their account
  """
  deleteAccount: Boolean!
}

"""
StaffConfiguration specifies the configuration for the staff badges assigned to
users with any role above COMMENTER.
"""
type StaffConfiguration {
  """
  label is the string used when displaying the STAFF badge to users.
  """
  label: String!
}

"""
NewCommenterConfiguration specifies the features that apply to new commenters
"""
type NewCommentersConfiguration {
  """
  premodEnabled ensures that new commenters' comments are pre-moderated until they have
  enough approved comments
  """
  premodEnabled: Boolean!

  """
  approvedCommentsThreshold is the number of comments a user must have approved before their
  comments do not require premoderation
  """
  approvedCommentsThreshold: Int!
}

"""
Announcement is an organization-wide announcement displayed above the stream
for a set duration
"""
type Announcement {
  """
  id is a canonical identifier for this Annoucnement, used to dismiss on front-end.
  """
  id: ID!

  """
  createdAt is the creation date.
  """
  createdAt: Time!

  """
  disableAt is the computed date at which announcement will be invalid.
  """
  disableAt: Time!

  """
  duration determines how long the announcement will be valid for.
  """
  duration: Int!

  """
  content is the content displayed for the announcement.
  """
  content: String!
}

"""
Settings stores the global settings for a given Tenant.
"""
type Settings {
  """
  id is a canonical identifier for this Tenant.
  """
  id: ID!

  """
  domain is the domain that is associated with this Tenant.
  """
  domain: String! @auth(roles: [ADMIN])

  """
  staticURI if configured, is the static URI used to serve static files from.
  """
  staticURI: String @auth(roles: [ADMIN])

  """
  allowedDomains is the list of domains that stories can come from.
  """
  allowedDomains: [String!]! @auth(roles: [ADMIN])

  """
  locale is the specified locale for this Tenant.
  """
  locale: Locale!

  """
  live provides configuration options related to live updates for stories on
  this site.
  """
  live: LiveConfiguration!

  """
  moderation is the moderation mode for all Stories on the site.
  """
  moderation: MODERATION_MODE @auth(roles: [ADMIN])

  """
  communityGuidelines will be shown in the comments stream.
  """
  communityGuidelines: CommunityGuidelines!

  """
  premodLinksEnable will put all comments that contain links into premod.
  """
  premodLinksEnable: Boolean @auth(roles: [ADMIN])

  """
  closeCommenting contains settings related to the automatic closing of commenting on
  Stories.
  """
  closeCommenting: CloseCommenting!

  """
  customCSSURL is the URL of the custom CSS used to display on the frontend.
  """
  customCSSURL: String

  """
  disableCommenting will disable commenting site-wide.
  """
  disableCommenting: DisableCommenting!

  """
  editCommentWindowLength is the length of time (in seconds) after a comment is
  posted that it can still be edited by the author.
  """
  editCommentWindowLength: Int!

  """
  charCount stores the character count moderation settings.
  """
  charCount: CharCount!

  """
  organization stores information about the organization behind this specific
  instance of Coral.
  """
  organization: Organization!

  """
  email is the set of credentials and settings associated with the organization.
  """
  email: EmailConfiguration! @auth(roles: [ADMIN, MODERATOR])

  """
  slack is the configuration for slack communication
  """
  slack: SlackConfiguration! @auth(roles: [ADMIN, MODERATOR])

  """
  wordList will return a given list of words.
  """
  wordList: WordList! @auth(roles: [ADMIN, MODERATOR])

  """
  auth contains all the settings related to authentication and authorization.
  """
  auth: Auth!

  """
  integrations contains all the external integrations that can be enabled.
  """
  integrations: ExternalIntegrations! @auth(roles: [ADMIN, MODERATOR])

  """
  recentCommentHistory is the set of settings related to how automatic
  pre-moderation is controlled.
  """
  recentCommentHistory: RecentCommentHistoryConfiguration!
    @auth(roles: [ADMIN, MODERATOR])

  """
  reaction specifies the configuration for reactions.
  """
  reaction: ReactionConfiguration!

  """
  staff specifies the configuration for the staff badges assigned to users with
  any role above COMMENTER.
  """
  staff: StaffConfiguration!

  """
  accountFeatures stores the configuration for commenter account features.
  """
  accountFeatures: CommenterAccountFeatures!

  """
  stories stores the configuration around stories.
  """
  stories: StoryConfiguration! @auth(roles: [ADMIN])

  """
  featureFlags provides the enabled feature flags.
  """
  featureFlags: [FEATURE_FLAG!]! @auth(roles: [ADMIN])

  """
  createdAt is the time that the Settings was created at.
  """
  createdAt: Time! @auth(roles: [ADMIN])

  """
  newCommenters is the configuration for how new commenters comments are treated.
  """
  newCommenters: NewCommentersConfiguration! @auth(roles: [ADMIN])

  announcement: Announcement
}

################################################################################
## User
################################################################################

enum USER_ROLE {
  COMMENTER
  STAFF
  MODERATOR
  ADMIN
}

type LocalProfile {
  id: String!
}

type OIDCProfile {
  id: String!
  provider: String!
}

type SSOProfile {
  id: String!
}

type FacebookProfile {
  id: String!
}

type GoogleProfile {
  id: String!
}

"""
Profile is all the different profiles that a given User may have associated
with their account.
"""
union Profile =
    LocalProfile
  | OIDCProfile
  | SSOProfile
  | FacebookProfile
  | GoogleProfile

"""
Token facilitates accessing Coral externally with a token.
"""
type Token {
  id: ID!
  name: String!
  createdAt: Time!
}

"""
Invite represents a given User that is pending registration that has been
invited by an Administrator.
"""
type Invite {
  """
  id is the identifier for the Invite.
  """
  id: ID!

  """
  email is the email address that will be assigned and used for the
  invited User.
  """
  email: String!

  """
  role is the USER_ROLE that the User will be assigned upon
  account creation.
  """
  role: USER_ROLE!

  """
  createdBy is the User that created the Invite.
  """
  createdBy: User!

  """
  createdAt is the time that the Invite was created on.
  """
  createdAt: Time!
}

"""
BanStatusHistory is the list of all ban events against a specific User.
"""
type BanStatusHistory {
  """
  active when true, indicates that the given user is banned.
  """
  active: Boolean!

  """
  createdBy is the User that suspended the User. If `null`, the then the given
  User was banned by the system.
  """
  createdBy: User

  """
  createdAt is the time that the given User was banned.
  """
  createdAt: Time!

  """
  message is sent to banned user via email.
  """
  message: String!
}

"""
BanStatus contains information about a ban for a given User.
"""
type BanStatus {
  """
  active when true, indicates that the given user is banned.
  """
  active: Boolean!
    @auth(
      roles: [ADMIN, MODERATOR]
      userIDField: "userID"
      permit: [SUSPENDED, BANNED, PENDING_DELETION]
    )

  """
  history is the list of all ban events against a specific User.
  """
  history: [BanStatusHistory!]! @auth(roles: [ADMIN, MODERATOR])
}

"""
TimeRange represents a range of times.
"""
type TimeRange {
  """
  start is the time that the time range started on.
  """
  start: Time!

  """
  finish is the time that the time range finished at.
  """
  finish: Time!
}

"""
SuspensionStatusHistory is the list of all suspension events against a specific User.
"""
type SuspensionStatusHistory {
  """
  active is true when the given suspension status time range applies now.
  """
  active: Boolean!

  """
  from is the time range that the suspension is active for.
  """
  from: TimeRange!

  """
  createdBy is the User that suspended the User. If `null`, the then the given
  User was suspended by the system.
  """
  createdBy: User

  """
  createdAt is the time that the suspension was created at.
  """
  createdAt: Time!

  """
  modifiedBy is the User that cancelled/edited the suspension. If `null`, then
  the suspension has not been cancelled/edited, or has been edited by the
  system.
  """
  modifiedBy: User

  """
  modifiedAt is the time that the suspension was cancelled/edited. If `null`,
  then the suspension has not been cancelled/edited.
  """
  modifiedAt: Time

  """
  message is sent to suspended user via email.
  """
  message: String!
}

"""
SuspensionStatus stores the user suspension status as well as the history of
changes.
"""
type SuspensionStatus {
  """
  active when true, indicates that the given user is suspended.
  """
  active: Boolean!
    @auth(
      roles: [ADMIN, MODERATOR]
      userIDField: "userID"
      permit: [SUSPENDED, BANNED, PENDING_DELETION]
    )

  """
  until is the time that the current user suspension is over.
  """
  until: Time
    @auth(
      roles: [ADMIN, MODERATOR]
      userIDField: "userID"
      permit: [SUSPENDED, BANNED, PENDING_DELETION]
    )

  """
  history is the list of all suspension events against a specific User.
  """
  history: [SuspensionStatusHistory!]! @auth(roles: [ADMIN, MODERATOR])
}

type PremodStatusHistory {
  """
  active when true, indicates that the given user is premodded.
  """
  active: Boolean!

  """
  createdBy is the user that flagged the commenter as pre-mod
  """
  createdBy: User!

  """
  createdAt is the time the user was set to pre-mod
  """
  createdAt: Time!
}

type PremodStatus {
  """
  active when true, indicates that the given user is set to pre-mod.
  """
  active: Boolean!

  """
  history is the list of all suspension events against a specific User.
  """
  history: [PremodStatusHistory!]!
}

type UsernameHistory {
  """
  username is the username that was assigned
  """
  username: String!
    @auth(
      roles: [ADMIN, MODERATOR]
      userIDField: "userID"
      permit: [SUSPENDED, BANNED, PENDING_DELETION]
    )

  """
  createdBy is the user that created this username
  """
  createdBy: User! @auth(roles: [ADMIN, MODERATOR])

  """
  createdAt is the time the username was created
  """
  createdAt: Time!
    @auth(
      roles: [ADMIN, MODERATOR]
      userIDField: "userID"
      permit: [SUSPENDED, BANNED, PENDING_DELETION]
    )
}

type UsernameStatus {
  """
  history is the list of all usernames for this user
  """
  history: [UsernameHistory!]!
}

"""
UserStatus stores the user status information regarding moderation state.
"""
type UserStatus {
  """
  current represents the current statuses applied to the User.
  """
  current: [USER_STATUS!]!
    @auth(
      roles: [ADMIN, MODERATOR]
      userIDField: "userID"
      permit: [SUSPENDED, BANNED, PENDING_DELETION]
    )

  """
  username stores the history of username changes for this user
  """
  username: UsernameStatus!

  """
  banned stores the user banned status as well as the history of changes.
  """
  ban: BanStatus!

  """
  suspension stores the user suspension status as well as the history of
  changes.
  """
  suspension: SuspensionStatus!

  """
  premod stores the user premod status as well as the history of changes.
  """
  premod: PremodStatus! @auth(roles: [ADMIN, MODERATOR])
}

"""
USER_STATUS is used to describe the current state of a User. A User may exist in
multiple states.
"""
enum USER_STATUS {
  """
  ACTIVE is used when a User is not suspended or banned.
  """
  ACTIVE

  """
  BANNED is used when a User is banned.
  """
  BANNED

  """
  SUSPENDED is used when a User is currently suspended.
  """
  SUSPENDED

  """
  PREMOD is used when a User is currently set to require pre-moderation.
  """
  PREMOD
}

type ModeratorNote {
  """
  id is the identifier of the Note.
  """
  id: ID!

  """
  body is the content of the Note
  """
  body: String!

  """
  createdAt is the date in which the Note was created.
  """
  createdAt: Time!

  """
  createdBy is the Moderator that authored the Note.
  """
  createdBy: User!
}

enum DIGEST_FREQUENCY {
  """
  NONE will have the notifications send immediatly rather than bundling for digesting.
  """
  NONE

  """
  DAILY will queue up the notifications and send them daily.
  """
  DAILY

  """
  HOURLY will queue up the notifications and send them hourly.
  """
  HOURLY
}

"""
UserNotificationSettings stores the notification settings for a given User.
"""
type UserNotificationSettings {
  """
  onReply, when true, will enable notifications to be sent to users that have
  replies to their comments.
  """
  onReply: Boolean!

  """
  onFeatured, when true, will enable notifications to be sent to users that have
  their comment's featured.
  """
  onFeatured: Boolean!

  """
  onStaffReplies when true, will enable notifications to be sent to users that
  have a staff member reply to their comments. These notifications will
  supercede notifications that would have been sent for a basic reply
  notification.
  """
  onStaffReplies: Boolean!

  """
  onModeration when true, will enable notifications to be sent to users when a
  comment that they wrote that was previously unpublished, becomes published due
  to a moderator action.
  """
  onModeration: Boolean!

  """
  digestFrequency is the frequency to send notifications.
  """
  digestFrequency: DIGEST_FREQUENCY!
}

"""
User is someone that leaves Comments, and logs in.
"""
type User {
  """
  id is the identifier of the User.
  """
  id: ID!

  """
  username is the name of the User visible to other Users.
  """
  username: String

  """
  avatar is the url to the avatar for a specific User.
  """
  avatar: String

  """
  badges are user display badges
  """
  badges: [String!]

  """
  email is the current email address for the User.
  """
  email: String
    @auth(
      roles: [ADMIN, MODERATOR]
      userIDField: "id"
      permit: [MISSING_NAME, MISSING_EMAIL, SUSPENDED, BANNED, PENDING_DELETION]
    )

  """
  emailVerified when true indicates that the given email address has been
  verified.
  """
  emailVerified: Boolean
    @auth(
      roles: [ADMIN, MODERATOR]
      userIDField: "id"
      permit: [SUSPENDED, BANNED, PENDING_DELETION]
    )

  """
  profiles is the array of profiles assigned to the user.
  """
  profiles: [Profile!]!
    @auth(
      roles: [ADMIN, MODERATOR]
      userIDField: "id"
      permit: [MISSING_NAME, MISSING_EMAIL, SUSPENDED, BANNED, PENDING_DELETION]
    )

  """
  role is the current role of the User.
  """
  role: USER_ROLE!
    @auth(
      roles: [ADMIN, MODERATOR]
      userIDField: "id"
      permit: [SUSPENDED, BANNED, PENDING_DELETION]
    )

  """
  moderatorNotes are notes left by moderators about the User.
  """
  moderatorNotes: [ModeratorNote!]! @auth(roles: [ADMIN, MODERATOR])

  """
  ignoreable is a computed property based on the
  user's role. Typically, users with elevated privileges
  aren't allowed to be ignored.
  """
  ignoreable: Boolean!

  """
  comments are the comments written by the User.
  """
  comments(
    first: Int = 10 @constraint(max: 50)
    orderBy: COMMENT_SORT = CREATED_AT_DESC
    after: Cursor
  ): CommentsConnection!
    @auth(
      roles: [ADMIN, MODERATOR]
      userIDField: "id"
      permit: [SUSPENDED, BANNED, PENDING_DELETION]
    )

  """
  allComments are comments regardless of visibility status.
  """
  allComments(
    first: Int = 10 @constraint(max: 50)
    after: Cursor
  ): CommentsConnection! @auth(roles: [ADMIN, MODERATOR])

  """
  rejectedComments are comments that have been rejected.
  """
  rejectedComments(
    first: Int = 10 @constraint(max: 50)
    after: Cursor
  ): CommentsConnection! @auth(roles: [ADMIN, MODERATOR])

  """
  recentCommentHistory returns recent commenting history by the User.
  """
  recentCommentHistory: RecentCommentHistory! @auth(roles: [ADMIN, MODERATOR])

  """
  commentModerationActionHistory returns a CommentModerationActionConnection
  that this User has created.
  """
  commentModerationActionHistory(
    first: Int = 10 @constraint(max: 50)
    after: Cursor
  ): CommentModerationActionConnection! @auth(roles: [MODERATOR, ADMIN])

  """
  status stores the user status information regarding moderation state.
  """
  status: UserStatus!

  """
  tokens lists the access tokens associated with the account.
  """
  tokens: [Token!]!
    @auth(
      roles: [ADMIN]
      userIDField: "id"
      permit: [SUSPENDED, BANNED, PENDING_DELETION]
    )

  """
  ignoredUsers will return the list of ignored users.
  """
  ignoredUsers: [User!]!
    @auth(
      roles: [ADMIN, MODERATOR]
      userIDField: "id"
      permit: [SUSPENDED, BANNED, PENDING_DELETION]
    )

  """
  notifications stores the notification settings for the given User.
  """
  notifications: UserNotificationSettings!
    @auth(userIDField: "id", permit: [SUSPENDED, BANNED, PENDING_DELETION])

  """
  createdAt is the time that the User was created at.
  """
  createdAt: Time!

  """
  lastDownloadedAt the last time the user made a download request
  of their account data.
  """
  lastDownloadedAt: Time
    @auth(
      userIDField: "id"
      roles: [ADMIN, MODERATOR]
      permit: [SUSPENDED, BANNED, PENDING_DELETION]
    )

  """
  scheduledDeletionDate is the time when the User is
  scheduled to be deleted.
  """
  scheduledDeletionDate: Time
    @auth(
      userIDField: "id"
      roles: [ADMIN, MODERATOR]
      permit: [SUSPENDED, BANNED, PENDING_DELETION]
    )

  """
  deletedAt is the time when the User was deleted.
  """
  deletedAt: Time
    @auth(
      userIDField: "id"
      roles: [ADMIN, MODERATOR]
      permit: [SUSPENDED, BANNED, PENDING_DELETION]
    )
}

"""
UserEdge represents a unique User in a UsersConnection.
"""
type UserEdge {
  """
  node is the User for this edge.
  """
  node: User!

  """
  cursor is used in pagination.
  """
  cursor: Cursor!
}

"""
UsersConnection represents a subset of a stories list.
"""
type UsersConnection {
  """
  edges are a subset of UserEdge's.
  """
  edges: [UserEdge!]!

  """
  nodes is a list of User's.
  """
  nodes: [User!]!

  """
  pageInfo is information to aid in pagination.
  """
  pageInfo: PageInfo!
}

################################################################################
## Comment
################################################################################

type EditInfo {
  """
  edited will be True when the Comment has been edited in the past.
  """
  edited: Boolean!

  """
  editableUntil is the time that the comment is editable until.
  """
  editableUntil: Time
}

enum COMMENT_STATUS {
  """
  The comment is not PREMOD, but was not applied a moderation status by a
  moderator.
  """
  NONE

  """
  The comment has been approved by a moderator.
  """
  APPROVED

  """
  The comment has been rejected by a moderator.
  """
  REJECTED

  """
  The comment was created while the stories premoderation option was on, and
  new comments that haven't been moderated yet are referred to as
  "premoderated" or "premod" comments.
  """
  PREMOD

  """
  SYSTEM_WITHHELD represents a comment that was withheld by the system because
  it was flagged by an internal process for further review.
  """
  SYSTEM_WITHHELD
}

type CommentModerationAction {
  id: ID!

  """
  revision is the moderated CommentRevision.
  """
  revision: CommentRevision!

  """
  status represents the status that was assigned by the moderator.
  """
  status: COMMENT_STATUS!

  """
  moderator is the User that performed the Moderator action. If null, this means
  that the system has assigned the moderation status.
  """
  moderator: User

  """
  createdAt is the time that the CommentModerationAction was created.
  """
  createdAt: Time!
}

type CommentModerationActionEdge {
  """
  node is the CommentModerationAction for this edge.
  """
  node: CommentModerationAction!

  """
  cursor is used in pagination.
  """
  cursor: Cursor!
}

type CommentModerationActionConnection {
  """
  edges are a subset of CommentModerationActionEdge's.
  """
  edges: [CommentModerationActionEdge!]!

  """
  nodes is a list of CommentModerationAction's.
  """
  nodes: [CommentModerationAction!]!

  """
  pageInfo is information to aid in pagination.
  """
  pageInfo: PageInfo!
}

type CommentRevisionPerspectiveMetadata {
  """
  score is the value detected from the perspective API. This is returned as the
  percentage chance it would be considered toxic and can be compared to the
  defined threshold value.
  """
  score: Float!
}

type CommentRevisionMetadata {
  """
  perspective stores metadata associated with the pipeline analysis of this
  revision's body.
  """
  perspective: CommentRevisionPerspectiveMetadata
}

type CommentRevision {
  """
  id is the identifier of the CommentRevision.
  """
  id: ID!

  """
  comment is the reference to the original Comment associated with the current
  Comment.
  """
  comment: Comment!

  """
  actionCounts stores the counts of all the actions for the CommentRevision
  specifically.
  """
  actionCounts: ActionCounts! @auth(roles: [MODERATOR, ADMIN])

  """
  body is the content of the CommentRevision. If null, it indicates that the
  body text was deleted.
  """
  body: String

  """
  metadata stores details on a CommentRevision.
  """
  metadata: CommentRevisionMetadata! @auth(roles: [ADMIN, MODERATOR])

  """
  createdAt is the time that the CommentRevision was created.
  """
  createdAt: Time!
}

enum TAG {
  """
  STAFF is used when a Comment is written by a User that has the STAFF, ADMIN,
  or MODERATOR role.
  """
  STAFF

  """
  FEATURED is used when a Comment is marked as such by a staff member.
  """
  FEATURED
}

"""
Tag is a simple text message that can be displayed to segment tagable entities.
"""
type Tag {
  """
  code is the machine representation of the tag name.
  """
  code: TAG!

  """
  createdBy is the User that assigned the Tag. This is null when the Tag is
  assigned by the System.
  """
  createdBy: User @auth(roles: [MODERATOR, ADMIN])

  """
  createdAt is the time that the Tag was assigned on.
  """
  createdAt: Time! @auth(roles: [MODERATOR, ADMIN])
}

"""
CommentTagCounts provides count data for Tags made against Comment's.
"""
type CommentTagCounts {
  """
  FEATURED is the count of Comment's with the FEATURED tag.
  """
  FEATURED: Int!
}

"""
Comment is a comment left by a User on an Story or another Comment as a reply.
"""
type Comment {
  """
  id is the identifier of the Comment.
  """
  id: ID!

  """
  body is the content of the Comment, and is an alias to the body of the
  `currentRevision.body`.
  """
  body: String

  """
  revision is the current revision of the Comment's body.
  """
  revision: CommentRevision

  """
  revisionHistory stores the previous CommentRevision's, with the most recent
  edit last.
  """
  revisionHistory: [CommentRevision!]!
    @auth(
      roles: [MODERATOR, ADMIN]
      userIDField: "author_id"
      permit: [SUSPENDED, BANNED, PENDING_DELETION]
    )

  """
  createdAt is the date in which the Comment was created.
  """
  createdAt: Time!

  """
  author is the User that authored the Comment.
  """
  author: User

  """
  status represents the Comment's current status.
  """
  status: COMMENT_STATUS!

  """
  statusHistory returns a CommentModerationActionConnection that will list
  the history of moderator actions performed on the Comment, with the most
  recent last.
  """
  statusHistory(
    first: Int = 10 @constraint(max: 50)
    after: Cursor
  ): CommentModerationActionConnection! @auth(roles: [MODERATOR, ADMIN])

  """
  parentCount is the number of direct parents for this Comment. Currently this
  value is the same as depth.
  """
  parentCount: Int!

  """
  depth is the number of levels that a given comment is deep.
  """
  depth: Int!

  """
  replyCount is the number of replies. Only direct replies to this Comment
  are counted. Removed comments are included in this count.
  """
  replyCount: Int!

  """
  replies will return the replies to this Comment.
  """
  replies(
    first: Int = 10
    orderBy: COMMENT_SORT = CREATED_AT_DESC
    after: Cursor
  ): CommentsConnection!

  """
  parent is the immediate parent of a given comment.
  """
  parent: Comment

  """
  rootParent is the highest level parent Comment. This Comment would have been
  left on the Story itself.
  """
  rootParent: Comment

  """
  parents returns a CommentsConnection that allows accessing direct parents of
  the given Comment.
  """
  parents(last: Int = 1, before: Cursor): CommentsConnection!

  """
  editing returns details about the edit status of a Comment.
  """
  editing: EditInfo!

  """
  actionCounts stores the counts of all the actions for the Comment.
  """
  actionCounts: ActionCounts!

  """
  flags is the actual Flags that were left by the Users or the system.
  """
  flags(first: Int = 10 @constraint(max: 50), after: Cursor): FlagsConnection!
    @auth(roles: [ADMIN, MODERATOR])

  """
  viewerActionPresence stores the presence information for all the actions
  left by the current User on this Comment.
  """
  viewerActionPresence: ActionPresence

  """
  story is the Story that the Comment was written on.
  """
  story: Story!

  """
  The permalink for this comment.
  """
  permalink: String!

  """
  tags are the list of tags assigned to the Comment.
  """
  tags: [Tag!]!

  """
  deleted is whether the comment has been deleted.
  """
  deleted: Boolean
}

type PageInfo {
  """
  Indicates that there are more nodes after this subset.
  """
  hasNextPage: Boolean!

  """
  Included for legacy Relay reasons. Always set to false.
  """
  hasPreviousPage: Boolean!

  """
  Included for legacy Relay reasons. Always set to null.
  """
  startCursor: Cursor

  """
  Specifies the last node's cursor for forwards pagination.
  """
  endCursor: Cursor
}

"""
CommentEdge represents a unique Comment in a CommentsConnection.
"""
type CommentEdge {
  """
  node is the Comment for this edge.
  """
  node: Comment!

  """
  cursor is used in pagination.
  """
  cursor: Cursor!
}

"""
CommentsConnection represents a subset of a comment list.
"""
type CommentsConnection {
  """
  edges are a subset of CommentEdge's.
  """
  edges: [CommentEdge!]!

  """
  nodes is a list of Comment's.
  """
  nodes: [Comment!]!

  """
  pageInfo is information to aid in pagination.
  """
  pageInfo: PageInfo!
}

################################################################################
## CommentCounts
################################################################################

type CommentStatusCounts {
  """
  The comment is not PREMOD, but was not applied a moderation status by a
  moderator.
  """
  NONE: Int!

  """
  The comment has been approved by a moderator.
  """
  APPROVED: Int!

  """
  The comment has been rejected by a moderator.
  """
  REJECTED: Int!

  """
  The comment was created while the stories premoderation option was on, and
  new comments that haven't been moderated yet are referred to as
  "premoderated" or "premod" comments.
  """
  PREMOD: Int!

  """
  SYSTEM_WITHHELD represents a comment that was withheld by the system because
  it was flagged by an internal process for further review.
  """
  SYSTEM_WITHHELD: Int!
}

type CommentCounts {
  """
  totalPublished will return the count of all published Comments.
  """
  totalPublished: Int!

  """
  tags stores the counts of all the Tags against Comment's on this Story.
  """
  tags: CommentTagCounts!

  """
  statuses stores the counts of all the statuses against Comments on this Story.
  """
  statuses: CommentStatusCounts! @auth(roles: [ADMIN, MODERATOR])
}

################################################################################
## Story
################################################################################

enum COMMENT_SORT {
  CREATED_AT_DESC
  CREATED_AT_ASC
  REPLIES_DESC
  REACTION_DESC
}

"""
StoryMetadata stores all the metadata that is scraped using the scraping tools
inside Coral. Coral utilizes [metascraper](https://metascraper.js.org/) which uses
a variety of filters derived from [The Open Graph Protocol](https://ogp.me/) to
scan for metadata on the page.
"""
type StoryMetadata {
  """
  title stores the scraped title from the Story page.
  """
  title: String

  """
  author stores the scraped author from the Story page.
  """
  author: String

  """
  description stores the scraped description from the Story page.
  """
  description: String

  """
  image stores the scraped image from the Story page.
  """
  image: String

  """
  publishedAt stores the scraped publication date from the Story page.
  """
  publishedAt: Time

  """
  modifiedAt stores the scraped modified date from the Story page.
  """
  modifiedAt: Time

  """
  section stores the scraped section from the Story page.
  """
  section: String
}

"""
LiveConfiguration provides configuration options related to live updates.
"""
type LiveConfiguration {
  """
  configurable when false indicates that live updates cannot be modified.
  """
  configurable: Boolean!

  """
  enabled when true will allow live updates.
  """
  enabled: Boolean!
}

type StorySettings {
  """
  live provides configuration options related to live updates on this Story.
  """
  live: LiveConfiguration!

  """
  moderation determines whether or not this is a PRE or POST moderated story.
  """
  moderation: MODERATION_MODE!

  """
  premodLinksEnable will put all comments that contain links into premod.
  """
  premodLinksEnable: Boolean!

  """
  messageBox stores settings related to the Story Message Box.
  """
  messageBox: StoryMessageBox!
}

"""
STORY_STATUS represents filtering states that a Story can be in.
"""
enum STORY_STATUS {
  """
  OPEN represents when a given Story is open for commenting.
  """
  OPEN

  """
  CLOSED represents when a given Story is not open for commenting.
  """
  CLOSED
}

"""
Story is an Article or Page where Comments are written on by Users.
"""
type Story {
  """
  id is the identifier of the Story.
  """
  id: ID!

  """
  status is the status of the Story.
  """
  status: STORY_STATUS!

  """
  url is the url that the Story is located on.
  """
  url: String!

  """
  metadata stores the scraped metadata from the Story page.
  """
  metadata: StoryMetadata

  """
  scrapedAt is the Time that the Story had it's metadata scraped at. If the time
  is null, the Story has not been scraped yet.
  """
  scrapedAt: Time @auth(roles: [ADMIN, MODERATOR])

  """
  featuredComments are the Comments with the FEATURED tag on the Story.
  """
  featuredComments(
    first: Int = 10 @constraint(max: 50)
    orderBy: COMMENT_SORT = CREATED_AT_DESC
    after: Cursor
  ): CommentsConnection!

  """
  comments are the comments on the Story.
  """
  comments(
    first: Int = 20 @constraint(max: 50)
    orderBy: COMMENT_SORT = CREATED_AT_DESC
    after: Cursor
  ): CommentsConnection!

  """
  commentActionCounts stores the counts of all the actions against this Story and it's
  Comments.
  """
  commentActionCounts: ActionCounts! @auth(roles: [ADMIN, MODERATOR])

  """
  moderationQueues returns the set of ModerationQueues that are available for
  this Story.
  """
  moderationQueues: ModerationQueues! @auth(roles: [ADMIN, MODERATOR])

  """
  closedAt is the Time that the Story is closed for commenting. If null or in
  the future, the story is not yet closed.
  """
  closedAt: Time

  """
  isClosed returns true when the Story is currently closed for commenting.
  """
  isClosed: Boolean!

  """
  commentCounts stores all the counts of Comments that are left on the Comment.
  """
  commentCounts: CommentCounts!

  """
  createdAt is the date that the Story was created at.
  """
  createdAt: Time!

  """
  settings is the set of Settings on a Story that inherit from the global
  settings.
  """
  settings: StorySettings!

  """
  lastCommentedAt is the last time someone commented on this story.
  """
  lastCommentedAt: Time @auth(roles: [ADMIN])
}

"""
StoryEdge represents a unique Story in a StoriesConnection.
"""
type StoryEdge {
  """
  node is the Story for this edge.
  """
  node: Story!

  """
  cursor is used in pagination.
  """
  cursor: Cursor!
}

"""
StoriesConnection represents a subset of a stories list.
"""
type StoriesConnection {
  """
  edges are a subset of StoryEdge's.
  """
  edges: [StoryEdge!]!

  """
  nodes is a list of Story's.
  """
  nodes: [Story!]!

  """
  pageInfo is information to aid in pagination.
  """
  pageInfo: PageInfo!
}

################################################################################
## Query
################################################################################

type Query {
  """
  comment returns a specific comment.
  """
  comment(id: ID!): Comment

  """
  comments returns a filtered comments connection that can be paginated. This is
  a fairly expensive edge to filter against, moderation queues should utlilize
  the dedicated edges for more optimized responses.
  """
  comments(
    first: Int = 10 @constraint(max: 50)
    after: Cursor
    storyID: ID
    status: COMMENT_STATUS
    tag: TAG
    query: String
  ): CommentsConnection! @auth(roles: [ADMIN, MODERATOR])

  """
  story is a specific article that can be identified by either an ID or a URL.
  """
  story(id: ID, url: String): Story

  """
  stream will load a specific story that can be identified by either an ID or a
  URL and will create the story if that feature is enabled.
  """
  stream(id: ID, url: String): Story

  """
  stories returns filtered stories that can be paginated.
  """
  stories(
    first: Int = 10 @constraint(max: 50)
    after: Cursor
    status: STORY_STATUS
    query: String
  ): StoriesConnection! @auth(roles: [ADMIN, MODERATOR])

  """
  user will return the user referenced by their ID.

  TODO: evaluate adding a profile based lookup.
  """
  user(id: ID!): User @auth(roles: [ADMIN, MODERATOR])

  """
  users returns filtered users that can be paginated.

  TODO: evaluate adding status based filtering.
  """
  users(
    first: Int = 10 @constraint(max: 50)
    after: Cursor
    role: USER_ROLE
    query: String
    status: USER_STATUS
  ): UsersConnection! @auth(roles: [ADMIN, MODERATOR])

  """
  viewer is the current logged in User. If no user is currently logged in, it will
  return null. This is the only nullable field that can be returned that depends
  on the authentication state that will not throw an error.
  """
  viewer: User

  """
  settings is the Settings for a given Tenant.
  """
  settings: Settings!

  """
  discoverOIDCConfiguration will discover the OpenID Connect configuration based
  on the provided issuer. Discovery is not supported on all providers, and is
  described in the OpenID Connect Discovery 1.0 incorporating errata set 1:

  https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig

  If the provider does not support discovery, the response will be null.
  """
  discoverOIDCConfiguration(issuer: String!): DiscoveredOIDCConfiguration
    @auth(roles: [ADMIN])

  """
  debugScrapeStoryMetadata will return the information that Coral was able to
  scrape from the given URL. No data will be saved related to the Story based
  on this scrape.
  """
  debugScrapeStoryMetadata(url: String!): StoryMetadata @auth(roles: [ADMIN])

  """
  moderationQueues returns the set of ModerationQueues that are available for
  all stories or if given the story identified by the `storyID`.
  """
  moderationQueues(storyID: ID): ModerationQueues!
    @auth(roles: [ADMIN, MODERATOR])

  """
  activeStories is the list of most recently commented on stories identified
  by their `lastCommentedAt` field
  """
  activeStories(limit: Int = 10 @constraint(max: 25)): [Story!]!
    @auth(roles: [ADMIN])
    @rate(max: 2, seconds: 1)
}

################################################################################
## Mutations
################################################################################

##################
## updateNotificationSettings
##################

input UpdateNotificationSettingsInput {
  """
  onReply, when true, will enable notifications to be sent to users that have
  replies to their comments.
  """
  onReply: Boolean

  """
  onFeatured, when true, will enable notifications to be sent to users that have
  their comment's featured.
  """
  onFeatured: Boolean

  """
  onStaffReplies when true, will enable notifications to be sent to users that
  have a staff member reply to their comments. These notifications will
  supercede notifications that would have been sent for a basic reply
  notification.
  """
  onStaffReplies: Boolean

  """
  onModeration when true, will enable notifications to be sent to users when a
  comment that they wrote that was previously unpublished, becomes published due
  to a moderator action.
  """
  onModeration: Boolean

  """
  digestFrequency is the frequency to send notifications.
  """
  digestFrequency: DIGEST_FREQUENCY

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type UpdateNotificationSettingsPayload {
  """
  user is the possibly modified User.
  """
  user: User!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
## createComment
##################

"""
CreateCommentInput provides the input for the createComment Mutation.
"""
input CreateCommentInput {
  """
  nudge when true will instead return an error related to recoverable moderation
  faults such as a toxic comment or spam comment to provide user feedback to
  nudge the user to correct the comment.
  """
  nudge: Boolean = false

  """
  storyID is the ID of the Story where we are creating a comment on.
  """
  storyID: ID!

  """
  body is the Comment body, the content of the Comment.
  """
  body: String!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

"""
CreateCommentPayload contains the created Comment after the createComment
mutation.
"""
type CreateCommentPayload {
  """
  edge is the possibly created comment edge.
  """
  edge: CommentEdge!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
## createCommentReply
##################

"""
CreateCommentReplyInput provides the input for the createCommentReply Mutation.
"""
input CreateCommentReplyInput {
  """
  nudge when true will instead return an error related to recoverable moderation
  faults such as a toxic comment or spam comment to provide user feedback to
  nudge the user to correct the comment.
  """
  nudge: Boolean = false

  """
  storyID is the ID of the Story where we are creating a comment on.
  """
  storyID: ID!

  """
  parentID is the ID of the Comment that we are replying to.
  """
  parentID: ID!

  """
  parentRevisionID is the ID of the CommentRevision that we are replying to.
  """
  parentRevisionID: ID!

  """
  body is the Comment body, the content of the Comment.
  """
  body: String!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

"""
CreateCommentReplyPayload contains the created Comment after the createCommentReply
mutation.
"""
type CreateCommentReplyPayload {
  """
  edge is the possibly created comment edge.
  """
  edge: CommentEdge!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
## editComment
##################

"""
EditCommentInput provides the input for the editComment Mutation.
"""
input EditCommentInput {
  """
  commentID is the ID of the comment being edited.
  """
  commentID: ID!

  """
  body is the Comment body, the content of the Comment.
  """
  body: String!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

"""
EditCommentPayload contains the edited Comment after the editComment
mutation.
"""
type EditCommentPayload {
  """
  comment is the possibly edited comment.
  """
  comment: Comment!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
## updateSettings
##################

input SettingsOIDCAuthIntegrationInput {
  """
  enabled, when true, allows the integration to be enabled.
  """
  enabled: Boolean

  """
  allowRegistration when true will allow users that have not signed up
  before with this authentication integration to sign up.
  """
  allowRegistration: Boolean

  """
  targetFilter will restrict where the authentication integration should be
  displayed. If the value of targetFilter is null, then the authentication
  integration should be displayed in all targets.
  """
  targetFilter: SettingsAuthenticationTargetFilterInput

  """
  name is the label assigned to reference the provider of the OIDC integration,
  and will be used in situations where the name of the provider needs to be
  displayed, like the login button.
  """
  name: String

  """
  clientID is the Client Identifier as defined in:

  https://tools.ietf.org/html/rfc6749#section-2.2
  """
  clientID: String

  """
  clientSecret is the Client Secret as defined in:

  https://tools.ietf.org/html/rfc6749#section-2.3.1
  """
  clientSecret: String

  """
  authorizationURL is defined as the `authorization_endpoint` in:

  https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
  """
  authorizationURL: String

  """
  tokenURL is defined as the `token_endpoint` in:

  https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
  """
  tokenURL: String

  """
  jwksURI is defined as the `jwks_uri` in:

  https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
  """
  jwksURI: String

  """
  issuer is defined as the `issuer` in:

  https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
  """
  issuer: String
}

input SettingsSMTPInput {
  secure: Boolean
  host: String
  port: Int
  authentication: Boolean
  username: String
  password: String
}

input SettingsEmailConfigurationInput {
  """
  enabled when True, will enable the emailing functionality in Coral.
  """
  enabled: Boolean
  smtp: SettingsSMTPInput
  fromName: String
  fromEmail: String
}

input SettingsWordListInput {
  """
  banned words will by default reject the comment if it is found.
  """
  banned: [String!]

  """
  suspect words will simply flag the comment.
  """
  suspect: [String!]
}

input SettingsAuthenticationTargetFilterInput {
  admin: Boolean!
  stream: Boolean!
}

input SettingsLocalAuthIntegrationInput {
  enabled: Boolean

  """
  allowRegistration when true will allow users that have not signed up
  before with this authentication integration to sign up.
  """
  allowRegistration: Boolean

  """
  targetFilter will restrict where the authentication integration should be
  displayed. If the value of targetFilter is null, then the authentication
  integration should be displayed in all targets.
  """
  targetFilter: SettingsAuthenticationTargetFilterInput
}

input SettingsSSOAuthIntegrationInput {
  enabled: Boolean

  """
  allowRegistration when true will allow users that have not signed up
  before with this authentication integration to sign up.
  """
  allowRegistration: Boolean

  """
  targetFilter will restrict where the authentication integration should be
  displayed. If the value of targetFilter is null, then the authentication
  integration should be displayed in all targets.
  """
  targetFilter: SettingsAuthenticationTargetFilterInput
}

input SettingsGoogleAuthIntegrationInput {
  """
  enabled, when true, will enable Google as an authentication integration.
  """
  enabled: Boolean

  """
  allowRegistration when true will allow users that have not signed up
  before with this authentication integration to sign up.
  """
  allowRegistration: Boolean

  """
  targetFilter will restrict where the authentication integration should be
  displayed. If the value of targetFilter is null, then the authentication
  integration should be displayed in all targets.
  """
  targetFilter: SettingsAuthenticationTargetFilterInput

  """
  clientID is the Client Identifier as provided by the Google API Console.
  """
  clientID: String

  """
  clientSecret is the Client Secret as provided by the Google API Console.
  """
  clientSecret: String
}

input SettingsFacebookAuthIntegrationInput {
  """
  enabled, when true, will enable Facebook as an authentication integration.
  """
  enabled: Boolean

  """
  allowRegistration when true will allow users that have not signed up
  before with this authentication integration to sign up.
  """
  allowRegistration: Boolean

  """
  targetFilter will restrict where the authentication integration should be
  displayed. If the value of targetFilter is null, then the authentication
  integration should be displayed in all targets.
  """
  targetFilter: SettingsAuthenticationTargetFilterInput

  """
  clientID is the Client Identifier as provided by the Facebook Developer
  Console.
  """
  clientID: String

  """
  clientSecret is the Client Secret as provided by the Facebook Developer
  Console.
  """
  clientSecret: String
}

input SettingsAuthIntegrationsInput {
  """
  local stores configuration related to email/password based logins.
  """
  local: SettingsLocalAuthIntegrationInput

  """
  sso stores configuration related to Single Sign On based logins.
  """
  sso: SettingsSSOAuthIntegrationInput

  """
  oidc stores configuration related to OpenID Connect based logins.
  """
  oidc: SettingsOIDCAuthIntegrationInput

  """
  google stores configuration related to Google based logins.
  """
  google: SettingsGoogleAuthIntegrationInput

  """
  facebook stores configuration related to Facebook based logins.
  """
  facebook: SettingsFacebookAuthIntegrationInput
}

"""
Auth contains all the settings related to authentication and
authorization.
"""
input SettingsAuthInput {
  """
  integrations are the set of configurations for the variations of
  authentication solutions.
  """
  integrations: SettingsAuthIntegrationsInput
  """
  sessionDuration determines the duration in seconds for which an access token is valid
  """
  sessionDuration: Int!
}

input SettingsAkismetExternalIntegrationInput {
  """
  enabled when True will enable the integration.
  """
  enabled: Boolean

  """
  The key for the Akismet integration.
  """
  key: String

  """
  The site (blog) for the Akismet integration.
  """
  site: String
}

input SettingsPerspectiveExternalIntegrationInput {
  """
  enabled when True will enable the integration.
  """
  enabled: Boolean

  """
  The endpoint that Coral should use to communicate with the perspective API.
  """
  endpoint: String

  """
  The key for the Perspective API integration.
  """
  key: String

  """
  model is the name of the Perspective model to use.
  """
  model: String

  """
  The threshold that given a specific toxic comment score, the comment will
  be marked by Coral as toxic.
  """
  threshold: Float

  """
  When True, comments sent will not be stored by the Google Perspective API.
  """
  doNotStore: Boolean

  """
  When True, moderation actions will be sent to the Google Perspective API.
  """
  sendFeedback: Boolean
}

input SettingsExternalIntegrationsInput {
  """
  akismet provides integration with the Akismet Spam detection service.
  """
  akismet: SettingsAkismetExternalIntegrationInput

  """
  perspective provides integration with the Perspective API comment analysis
  platform.
  """
  perspective: SettingsPerspectiveExternalIntegrationInput
}

"""
RecentCommentHistoryConfigurationInput controls the beheviour around when comments from Users
should be marked for pre-moderation automatically once they have reached the
trigger rate for rejected comments.
"""
input RecentCommentHistoryConfigurationInput {
  """
  enabled when true will pre-moderate users new comments once they have reached
  the trigger rejection rate.
  """
  enabled: Boolean

  """
  timeFrame specifies the number of seconds that comments from a given User will
  be taken into account when computing their rejection rate.
  """
  timeFrame: Int

  """
  triggerRejectionRate specifies the percentage of comments that a given User
  may have before their comments will then be placed into pre-moderation until
  the rejection rate decreases.
  """
  triggerRejectionRate: Float
}

input CreateAnnouncementInput {
  """
  content of the announcement.
  """
  content: String!

  duration: Int!
  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

input DeleteAnnouncementInput {
  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type CreateAnnouncementPayload {
  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!

  settings: Settings!
}

type DeleteAnnouncementPayload {
  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!

  settings: Settings!
}

input SettingsCommunityGuidelinesInput {
  """
  enable set to true will show the guidelines above the message box.
  """
  enabled: Boolean

  """
  content of the guidelines.
  """
  content: String
}

input SettingsCharCountInput {
  """
  enabled when true, enables the character count moderation phase.
  """
  enabled: Boolean

  """
  min is the smallest length of a Comment that may be posted.
  """
  min: Int

  """
  max is the largest length of a Comment that may be posted.
  """
  max: Int
}

input SettingsDisableCommentingInput {
  """
  enabled when true will disable commenting site-wide.
  """
  enabled: Boolean

  """
  message will be shown above the comment stream while
  commenting is disabled site-wide.
  """
  message: String
}

input SettingsOrganizationInput {
  """
  name is the name of the organization.
  """
  name: String

  """
  contactEmail is the email of the organization.
  """
  contactEmail: String

  """
  url is the URL to the organizations home page.
  """
  url: String
}

input SettingsCloseCommentingInput {
  """
  auto when true will configure the automatic close on each story as they are
  created based on the current configured timeout option.
  """
  auto: Boolean

  """
  timeout is the amount of seconds from the `createdAt` timestamp that a given
  story will be considered closed.
  """
  timeout: Int

  """
  message when provided will be the message that shows when the comment stream
  is closed for commenting.
  """
  message: String
}

"""
StoryScrapingConfigurationInput stores the configuration around story scraping.
"""
input StoryScrapingConfigurationInput {
  """
  enabled, when true, enables stories to be scraped. When disabled, stories will
  only be looked up instead, and must be created via the API directly.
  """
  enabled: Boolean

  """
  proxyURL when specified, allows scraping requests to use the provided proxy.
  All requests will then be passed through the appropriote proxy as parsed by
  the [proxy-agent](https://www.npmjs.com/package/proxy-agent) package.
  """
  proxyURL: String

  """
  customUserAgent when specified will override the user agent used by fetch
  requests made during the scraping process.
  """
  customUserAgent: String
}

"""
StoryConfiguration stores the configuration for working with stories.
"""
input StoryConfigurationInput {
  """
  scraping stores configuration around story scraping.
  """
  scraping: StoryScrapingConfigurationInput

  """
  disableLazy when true, will only allow lookups of stories created via the API.
  """
  disableLazy: Boolean
}

"""
ReactionConfigurationInput stores the configuration for reactions used across
this Tenant.
"""
input ReactionConfigurationInput {
  """
  icon is the string representing the icon to be used for the reactions.
  """
  icon: String

  """
  iconActive is the string representing the icon that should be used when the
  icon should be considered active.
  """
  iconActive: String

  """
  label is the string placed beside the reaction icon to provide better context.
  """
  label: String

  """
  labelActive is the string placed beside the reaction icon to provide better
  context when it has been selected.
  """
  labelActive: String

  """
  sortLabel is the string placed inside of the sort menu to sort for comment
  with most reactions.
  """
  sortLabel: String

  """
  color is the hex color code that can be used to change the color of the button.
  """
  color: String
}

"""
StaffConfigurationInput specifies the configuration for the staff badges
assigned to users with any role above COMMENTER.
"""
input StaffConfigurationInput {
  """
  label is the string used when displaying the STAFF badge to users.
  """
  label: String
}

"""
CommenterAccountFeatures stores the configuration for commenter account features.
"""
input CommenterAccountFeaturesInput {
  """
  changeUsername when true, non-sso user may change username every 14 days
  """
  changeUsername: Boolean
  """
  downloadComments when true, user may download their comment history
  """
  downloadComments: Boolean
  """
  deleteAccount when true, non-sso user may permanently delete their account
  """
  deleteAccount: Boolean
}

input SlackTriggersConfigurationInput {
  """
  reportedComments is whether this channel will receive reported comments
  """
  reportedComments: Boolean

  """
  pendingComments is whether this channel will receive pending comments
  """
  pendingComments: Boolean

  """
  featuredComments is whether this channel will receive featured comments
  """
  featuredComments: Boolean
}

input SlackChannelConfigurationInput {
  """
  enabled is whether this Slack channel is enabled to send comments to its
  correlated web hook
  """
  enabled: Boolean

  """
  name is the name assigned to the Slack channel
  """
  name: String

  """
  hookURL defines the URL that comments will be sent to
  """
  hookURL: String

  """
  triggers are the filters of types of comments that will be sent to
  the correlated channel
  """
  triggers: SlackTriggersConfigurationInput
}

input SlackConfigurationInput {
  """
  channels are the channels configured for Slack integration
  """
  channels: [SlackChannelConfigurationInput!]
}

"""
NewCommenterConfigurationInput specifies the features that apply to new commenters
"""
input NewCommentersConfigurationInput {
  """
  premodEnabled ensures that new commenters' comments are pre-moderated until they have
  enough approved comments
  """
  premodEnabled: Boolean

  """
  approvedCommentsThreshold is the number of comments a user must have approved before their
  comments do not require premoderation
  """
  approvedCommentsThreshold: Int
}

"""
SettingsInput is the partial type of the Settings type for performing mutations.
"""
input SettingsInput {
  """
  live provides configuration options related to live updates for stories on
  this site.
  """
  live: LiveConfigurationInput

  """
  allowedDomains is the list of domains that stories can come from.
  """
  allowedDomains: [String!]

  """
  moderation is the moderation mode for all Stories on the site.
  """
  moderation: MODERATION_MODE

  """
  communityGuidelines will be shown in the comments stream.
  """
  communityGuidelines: SettingsCommunityGuidelinesInput

  """
  premodLinksEnable will put all comments that contain links into premod.
  """
  premodLinksEnable: Boolean

  """
  customCSSURL is the URL of the custom CSS used to display on the frontend.
  """
  customCSSURL: String

  """
  disableCommenting will disable commenting site-wide.
  """
  disableCommenting: SettingsDisableCommentingInput

  """
  editCommentWindowLength is the length of time (in seconds) after a comment is
  posted that it can still be edited by the author.
  """
  editCommentWindowLength: Int

  """
  organization stores information about the organization behind this specific
  instance of Coral.
  """
  organization: SettingsOrganizationInput

  """
  closeCommenting contains settings related to the automatic closing of commenting on
  Stories.
  """
  closeCommenting: SettingsCloseCommentingInput

  """
  wordList will return a given list of words.
  """
  wordList: SettingsWordListInput

  """
  email is the set of credentials and settings associated with the organization.
  """
  email: SettingsEmailConfigurationInput

  """
  auth contains all the settings related to authentication and authorization.
  """
  auth: SettingsAuthInput

  """
  integrations contains all the external integrations that can be enabled.
  """
  integrations: SettingsExternalIntegrationsInput

  """
  recentCommentHistory is the set of settings related to how automatic
  pre-moderation is controlled.
  """
  recentCommentHistory: RecentCommentHistoryConfigurationInput

  """
  charCount stores the character count moderation settings.
  """
  charCount: SettingsCharCountInput

  """
  stories stores the configuration around stories.
  """
  stories: StoryConfigurationInput

  """
  reaction specifies the configuration for reactions.
  """
  reaction: ReactionConfigurationInput

  """
  staff specifies the configuration for the staff badges assigned to users with
  any role above COMMENTER.
  """
  staff: StaffConfigurationInput

  """
  accountFeatures specifies the configuration for accounts.
  """
  accountFeatures: CommenterAccountFeaturesInput

  """
  slack specifies the configuration for Slack integration.
  """
  slack: SlackConfigurationInput

  """
  locale specifies the locale for this Tenant.
  """
  locale: Locale

  """
  newCommenters is the configuration for how new commenters comments are treated.
  """
  newCommenters: NewCommentersConfigurationInput
}

"""
UpdateSettingsInput provides the input for the updateSettings Mutation.
"""
input UpdateSettingsInput {
  """
  settings is the partial set of settings that will be used as a patch against
  the existing settings object.
  """
  settings: SettingsInput!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

"""
UpdateSettingsPayload contains the updated Settings after the updateSettings
mutation.
"""
type UpdateSettingsPayload {
  """
  settings is the updated Settings.
  """
  settings: Settings

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
## createCommentReaction
##################

input CreateCommentReactionInput {
  """
  commentID is the Comment's ID that we want to create a Reaction on.
  """
  commentID: ID!

  """
  commentRevisionID is the revision ID of the Comment that we're creating the
  Reaction on.
  """
  commentRevisionID: ID!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type CreateCommentReactionPayload {
  """
  comment is the Comment that the Reaction was created on.
  """
  comment: Comment

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
## removeCommentReaction
##################

input RemoveCommentReactionInput {
  """
  commentID is the Comment's ID that we want to remove a Reaction on.
  """
  commentID: ID!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type RemoveCommentReactionPayload {
  """
  comment is the Comment that the Reaction was removed on.
  """
  comment: Comment

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
## createCommentDontAgree
##################

input CreateCommentDontAgreeInput {
  """
  commentID is the Comment's ID that we want to create a DontAgree on.
  """
  commentID: ID!

  """
  commentRevisionID is the revision ID of the Comment that we're creating the
  DontAgree on.
  """
  commentRevisionID: ID!

  """
  additionalDetails stores information from the User as to why the Flag was
  created or is relevant.
  """
  additionalDetails: String

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type CreateCommentDontAgreePayload {
  """
  comment is the Comment that the DontAgree was created on.
  """
  comment: Comment

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
## removeCommentDontAgree
##################

input RemoveCommentDontAgreeInput {
  """
  commentID is the Comment's ID that we want to remove a DontAgree on.
  """
  commentID: ID!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type RemoveCommentDontAgreePayload {
  """
  comment is the Comment that the DontAgree was removed on.
  """
  comment: Comment

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
## createCommentFlag
##################

input CreateCommentFlagInput {
  """
  commentID is the Comment's ID that we want to create a Flag on.
  """
  commentID: ID!

  """
  commentRevisionID is the revision ID of the Comment that we're creating the
  Flag on.
  """
  commentRevisionID: ID!

  """
  reason is the selected reason why the Flag is being created.
  """
  reason: COMMENT_FLAG_REPORTED_REASON!

  """
  additionalDetails stores information from the User as to why the Flag was
  created or is relevant.
  """
  additionalDetails: String

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type CreateCommentFlagPayload {
  """
  comment is the Comment that the Flag was created on.
  """
  comment: Comment

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
## regenerateSSOKey
##################

input RegenerateSSOKeyInput {
  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type RegenerateSSOKeyPayload {
  """
  settings is the Settings that the SSO key was regenerated on.
  """
  settings: Settings

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
## createStory
##################

"""
StoryMetadataInput is the metadata for a given Story as provided via this API.
"""
input StoryMetadataInput {
  """
  title stores the title from the Story page.
  """
  title: String

  """
  author stores the author from the Story page.
  """
  author: String

  """
  description stores the description from the Story page.
  """
  description: String

  """
  image stores the image from the Story page.
  """
  image: String

  """
  publishedAt stores the publication date from the Story page.
  """
  publishedAt: Time

  """
  modifiedAt stores the modified date from the Story page.
  """
  modifiedAt: Time

  """
  section stores the section from the Story page.
  """
  section: String
}

"""
CreateStory is the input required to create a Story.
"""
input CreateStory {
  """
  id is the identifier of the Story.
  """
  id: ID!

  """
  url is the url that the Story is located on.
  """
  url: String!

  """
  metadata is the set of information relating to this Story that would normally
  be scraped, but can be provided here.
  """
  metadata: StoryMetadataInput

  """
  closedAt when provided specifies the date that the given story will be closed
  at. If not provided, the story close will use the settings to determine the
  automatic close date.
  """
  closedAt: Time
}

input CreateStoryInput {
  """
  story is the Story input needed to create a Story.
  """
  story: CreateStory!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type CreateStoryPayload {
  """
  story is the Story that was possibly created.
  """
  story: Story

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
## updateStory
##################

"""
UpdateStory is the input required to update a Story.
"""
input UpdateStory {
  """
  url is the url that the Story is located on.
  """
  url: String

  """
  metadata is the set of information relating to this Story that would normally
  be scraped, but can be provided here.
  """
  metadata: StoryMetadataInput
}

input UpdateStoryInput {
  """
  id is the identifier of the Story used either when the Story was created via
  the API or from Coral when it was lazily created.
  """
  id: ID!

  """
  story contains the fields that should be updated. Any fields not specified
  will not be changed.
  """
  story: UpdateStory!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type UpdateStoryPayload {
  """
  story is the Story that was possibly updated.
  """
  story: Story

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
## updateStorySettings
##################

"""
StoryMessageBoxInput stores settings related to the Story Message Box.
"""
input StoryMessageBoxInput {
  """
  enable when true will enable the Message Box on the comment stream.
  """
  enabled: Boolean

  """
  icon when set will reference the string for the Message box used by the
  Message Box.
  """
  icon: String

  """
  content when set contains the actual markup for the Message Box.
  """
  content: String
}

"""
LiveConfigurationInput provides configuration options related to live updates.
"""
input LiveConfigurationInput {
  """
  enabled when true will allow live updates.
  """
  enabled: Boolean
}

"""
UpdateStorySettings is the input required to update a Story's Settings.
"""
input UpdateStorySettings {
  """
  live provides configuration options related to live updates on this Story.
  """
  live: LiveConfigurationInput

  """
  moderation determines whether or not this is a PRE or POST moderated story.
  """
  moderation: MODERATION_MODE

  """
  premodLinksEnable will put all comments that contain links into premod.
  """
  premodLinksEnable: Boolean

  """
  messageBox stores settings related to the Story Message Box.
  """
  messageBox: StoryMessageBoxInput
}

input UpdateStorySettingsInput {
  """
  id is the identifier of the Story used either when the Story was created via
  the API or from Coral when it was lazily created.
  """
  id: ID!

  """
  settings contains the fields on the story settings that should be updated. Any
  fields not specified will not be changed.
  """
  settings: UpdateStorySettings!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type UpdateStorySettingsPayload {
  """
  story is the Story that was possibly updated.
  """
  story: Story

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
## closeStory
##################

input CloseStoryInput {
  """
  id is the identifier of the Story used either when the Story was created via
  the API or from Coral when it was lazily created.
  """
  id: ID!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type CloseStoryPayload {
  """
  story is the Story that was possibly updated.
  """
  story: Story

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
## openStory
##################

input OpenStoryInput {
  """
  id is the identifier of the Story used either when the Story was created via
  the API or from Coral when it was lazily created.
  """
  id: ID!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type OpenStoryPayload {
  """
  story is the Story that was possibly updated.
  """
  story: Story

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
## mergeStories
##################

input MergeStoriesInput {
  """
  sourceIDs are the list of Story ID's that should have their Comment's moved
  onto the Story indicated by `destinationID`. The Stories indicated by the
  `sourceIDs` field will be removed after the Comment's have been moved.
  """
  sourceIDs: [ID!]!

  """
  destinationID is the ID of the Story where all the other "source" Stories will
  have their Comment's moved onto.
  """
  destinationID: ID!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type MergeStoriesPayload {
  """
  story is the Story that all the source stories were merged into.
  """
  story: Story

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
## removeStory
##################

input RemoveStoryInput {
  """
  id is the identifier of the Story used either when the Story was created via
  the API or from Coral when it was lazily created.
  """
  id: ID!

  """
  includeComments when true will remove any Comment's that were left on the
  Story. This option should be used rarely, instead preferring to updating the
  Story URL and/or merging with the correct Story.
  """
  includeComments: Boolean = false

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type RemoveStoryPayload {
  """
  story is the Story that was possibly removed.
  """
  story: Story

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
## scrapeStory
##################

input ScrapeStoryInput {
  """
  id is the identifier of the Story used either when the Story was created via
  the API or from Coral when it was lazily created.
  """
  id: ID!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type ScrapeStoryPayload {
  """
  story is the Story that was possibly scraped.
  """
  story: Story

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
# approveComment
##################

input ApproveCommentInput {
  """
  commentID is the ID of the Comment that was approved.
  """
  commentID: ID!

  """
  commentRevisionID is the ID of the CommentRevision that is being approved.
  """
  commentRevisionID: ID!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type ApproveCommentPayload {
  """
  comment is the Comment that was approved.
  """
  comment: Comment

  """
  moderationQueues will return the selected moderation queues. If the `storyID`
  is provided, it will filter the moderation queues for only comments in that
  Story.
  """
  moderationQueues(storyID: ID): ModerationQueues

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
# rejectComment
##################

input RejectCommentInput {
  """
  commentID is the ID of the Comment that was rejected.
  """
  commentID: ID!

  """
  commentRevisionID is the ID of the CommentRevision that is being rejected.
  """
  commentRevisionID: ID!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type RejectCommentPayload {
  """
  comment is the Comment that was rejected.
  """
  comment: Comment

  """
  moderationQueues will return the selected moderation queues. If the `storyID`
  is provided, it will filter the moderation queues for only comments in that
  Story.
  """
  moderationQueues(storyID: ID): ModerationQueues

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
# featureComment
##################

input FeatureCommentInput {
  """
  commentID is the ID of the Comment that should be featured.
  """
  commentID: ID!

  """
  commentRevisionID is the ID of the CommentRevision that should be featured
  """
  commentRevisionID: ID!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type FeatureCommentPayload {
  """
  comment is the Comment that was featured.
  """
  comment: Comment

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!

  """
  moderationQueues will return the selected moderation queues. If the `storyID`
  is provided, it will filter the moderation queues for only comments in that
  Story.
  """
  moderationQueues(storyID: ID): ModerationQueues
}

##################
# unfeatureComment
##################

input UnfeatureCommentInput {
  """
  commentID is the ID of the featured Comment that should be unfeatured.
  """
  commentID: ID!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type UnfeatureCommentPayload {
  """
  comment is the Comment that was unfeatured.
  """
  comment: Comment

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
# setUsername
##################

input SetUsernameInput {
  """
  username is the desired username that should be set to the current User.
  """
  username: String!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type SetUsernamePayload {
  """
  user is the possibly modified User.
  """
  user: User!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
# updateUsername
##################

input UpdateUsernameInput {
  """
  username is the desired username that should be set to the current User.
  """
  username: String!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type UpdateUsernamePayload {
  """
  user is the possibly modified User.
  """
  user: User!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
# inviteUser
##################

input InviteUsersInput {
  """
  emails is the email addresses of the Users to be invited.
  """
  emails: [String!]!

  """
  role is the designated role of the User being invited.
  """
  role: USER_ROLE!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type InviteUsersPayload {
  """
  invites is the references to the invited Users.
  """
  invites: [Invite]!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

input CreateModeratorNoteInput {
  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!

  """
  body is the content of the Note
  """
  body: String!

  """
  userID the id of the User who is the subject of the note.
  """
  userID: ID!
}

input DeleteModeratorNoteInput {
  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
  """
  userID is the user who is the subject of the note
  """
  userID: ID!
  """
  id is the identifier of the note
  """
  id: ID!
}

type CreateModeratorNotePayload {
  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!

  """
  createdBy is the moderator who created the note
  user is the updated user.
  """
  user: User!
}

type DeleteModeratorNotePayload {
  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
  """
  user is the updated user.
  """
  user: User!
}

##################
# setEmail
##################

input SetEmailInput {
  """
  email is the email address to be associated with the User.
  """
  email: String!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type SetEmailPayload {
  """
  user is the possibly modified User.
  """
  user: User!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
# setPassword
##################

input SetPasswordInput {
  """
  password is the password that should be associated with the User.
  """
  password: String!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type SetPasswordPayload {
  """
  user is the possibly modified User.
  """
  user: User!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
# updatePassword
##################

input UpdatePasswordInput {
  """
  oldPassword is the old password that that should be compared against when
  trying to change the password before the change.
  """
  oldPassword: String!

  """
  newPassword is the new password that should be associated with the User.
  """
  newPassword: String!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type UpdatePasswordPayload {
  """
  user is the possibly modified User.
  """
  user: User!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
# requestAccountDeletion
##################

input RequestAccountDeletionInput {
  """
  password to verify for the current User.
  """
  password: String!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type RequestAccountDeletionPayload {
  """
  user is the possibly modified User.
  """
  user: User!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
# deleteUserAccount
##################

input DeleteUserAccountInput {
  """
  userID is the ID of the User being deleted.
  """
  userID: ID!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type DeleteUserAccountPayload {
  """
  user is the User that was deleted.
  """
  user: User

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
# cancelAccountDeletion
##################

input CancelAccountDeletionInput {
  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type CancelAccountDeletionPayload {
  """
  user is the possibly modified User.
  """
  user: User!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
# createToken
##################

input CreateTokenInput {
  """
  name is the desired name for the Token.
  """
  name: String!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type CreateTokenPayload {
  """
  user is the possibly modified User.
  """
  user: User!

  """
  token is the Token that was created.
  """
  token: Token!

  """
  signedToken is the signed Token associated with the account.
  """
  signedToken: String!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
# deactivateToken
##################

input DeactivateTokenInput {
  """
  id is the ID of the Token that should be deactivated.
  """
  id: ID!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type DeactivateTokenPayload {
  """
  user is the possibly modified User.
  """
  user: User!

  """
  token is the Token that was deleted.
  """
  token: Token

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
# updateUserUsername
##################

input UpdateUserUsernameInput {
  """
  userID is the ID of the User that should have their username updated.
  """
  userID: ID!

  """
  username is the desired username to set for the User.
  """
  username: String!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type UpdateUserUsernamePayload {
  """
  user is the possibly modified User.
  """
  user: User!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
# updateEmail
##################

input UpdateEmailInput {
  """
  email is the email address to set for the User.
  """
  email: String!
  """
  password is the users password.
  """
  password: String!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type UpdateEmailPayload {
  """
  user is the possibly modified User.
  """
  user: User!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
# updateUserEmail
##################

input UpdateUserEmailInput {
  """
  userID is the ID of the User that should have their email address updated.
  """
  userID: ID!

  """
  email is the email address to set for the User.
  """
  email: String!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type UpdateUserEmailPayload {
  """
  user is the possibly modified User.
  """
  user: User!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
# updateUserAvatar
##################

input UpdateUserAvatarInput {
  """
  userID is the ID of the User that should have their avatar updated.
  """
  userID: ID!

  """
  avatar is the URL to the avatar to set for the User. If set to `null` or not
  provided, it will be unset.
  """
  avatar: String

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type UpdateUserAvatarPayload {
  """
  user is the possibly modified User.
  """
  user: User!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
# updateUserRole
##################

input UpdateUserRoleInput {
  """
  userID is the ID of the User that should have their avatar updated.
  """
  userID: ID!

  """
  role is the `USER_ROLE` that the User should be set to.
  """
  role: USER_ROLE!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type UpdateUserRolePayload {
  """
  user is the possibly modified User.
  """
  user: User!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
# banUser
##################

input BanUserInput {
  """
  userID is the ID of the User that should have their account banned.
  """
  userID: ID!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!

  """
  message is sent to banned user via email.
  """
  message: String!
}

type BanUserPayload {
  """
  user is the possibly modified User.
  """
  user: User!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
# suspendUser
##################

input SuspendUserInput {
  """
  userID is the ID of the User that should be suspended.
  """
  userID: ID!

  """
  timeout is the length of time (in seconds) that a User should be suspended
  for.
  """
  timeout: Int!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!

  """
  message is sent to suspended user via email.
  """
  message: String!
}

type SuspendUserPayload {
  """
  user is the possibly modified User.
  """
  user: User!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
# removeUserBan
##################

input RemoveUserBanInput {
  """
  userID is the ID of the User that should have their account un-banned.
  """
  userID: ID!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type RemoveUserBanPayload {
  """
  user is the possibly modified User.
  """
  user: User!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
# removeUserSuspension
##################

input RemoveUserSuspensionInput {
  """
  userID is the ID of the User that should have their active suspensions
  removed.
  """
  userID: ID!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type RemoveUserSuspensionPayload {
  """
  user is the possibly modified User.
  """
  user: User!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
# premodUser
##################

input PremodUserInput {
  """
  userID is the ID of the User that should be premodded.
  """
  userID: ID!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type PremodUserPayload {
  """
  user is the possibly modified User.
  """
  user: User!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
# removePremod
##################

input RemovePremodUserInput {
  """
  userID is the ID of the User that should be premodded.
  """
  userID: ID!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type RemovePremodUserPayload {
  """
  user is the possibly modified User.
  """
  user: User!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
# ignoreUser
##################

input IgnoreUserInput {
  """
  userID is the ID of the User that should be ignored.
  """
  userID: ID!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type IgnoreUserPayload {
  """
  user is the User that we just ignored.
  """
  user: User!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

##################
# removeUserIgnore
##################

input RemoveUserIgnoreInput {
  """
  userID is the ID of the User that should have the ignore removed.
  """
  userID: ID!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type RemoveUserIgnorePayload {
  """
  user is the User that we removed the ignore from.
  """
  user: User!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

#########################
# requestCommentsDownload
#########################

input RequestCommentsDownloadInput {
  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type RequestCommentsDownloadPayload {
  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

#########################
# requestUserCommentsDownload
#########################

input RequestUserCommentsDownloadInput {
  """
  userID specifies user to download comments for
  """
  userID: ID!

  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!
}

type RequestUserCommentsDownloadPayload {
  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!

  """
  archiveURL is the archive url
  """
  archiveURL: String!
}

#########################
# enableFeatureFlag
#########################

input EnableFeatureFlagInput {
  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!

  """
  flag is the feature flag to create.
  """
  flag: FEATURE_FLAG!
}

type EnableFeatureFlagPayload {
  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!

  """
  flags is the current set of flags enabled.
  """
  flags: [FEATURE_FLAG!]!
}

#########################
# disableFeatureFlag
#########################

input DisableFeatureFlagInput {
  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!

  """
  flag is the feature flag to delete.
  """
  flag: FEATURE_FLAG!
}

type DisableFeatureFlagPayload {
  """
  clientMutationId is required for Relay support.
  """
  clientMutationId: String!

  """
  flags is the current set of flags enabled.
  """
  flags: [FEATURE_FLAG!]!
}

##################
## Mutation
##################

type Mutation {
  """
  createComment will create a Comment as the current logged in User.
  """
  createComment(input: CreateCommentInput!): CreateCommentPayload!
    @auth
    @rate(seconds: 3, key: "createComment")

  """
  createCommentReply will create a Comment as the current logged in User that is
  in reply to another Comment.
  """
  createCommentReply(
    input: CreateCommentReplyInput!
  ): CreateCommentReplyPayload! @auth @rate(seconds: 3, key: "createComment")

  """
  editComment will allow the author of a comment to change the body within the
  time allotment.
  """
  editComment(input: EditCommentInput!): EditCommentPayload! @auth

  """
  updateSettings will update the Settings for the given Tenant.
  """
  updateSettings(input: UpdateSettingsInput!): UpdateSettingsPayload!
    @auth(roles: [ADMIN])

  """
  regenerateSSOKey will regenerate the SSO key used to sign secrets. This will
  invalidate any existing user sessions.
  """
  regenerateSSOKey(input: RegenerateSSOKeyInput!): RegenerateSSOKeyPayload!
    @auth(roles: [ADMIN])

  """
  createCommentReaction will create a Reaction authored by the current logged in
  User on a Comment.
  """
  createCommentReaction(
    input: CreateCommentReactionInput!
  ): CreateCommentReactionPayload @auth @rate(max: 2, seconds: 1)

  """
  removeCommentReaction will remove a Reaction authored by the current logged in
  User on a Comment if it exists.
  """
  removeCommentReaction(
    input: RemoveCommentReactionInput!
  ): RemoveCommentReactionPayload @auth @rate(max: 2, seconds: 1)

  """
  createCommentDontAgree will create a DontAgree authored by the current logged in
  User on a Comment.
  """
  createCommentDontAgree(
    input: CreateCommentDontAgreeInput!
  ): CreateCommentDontAgreePayload @auth @rate(seconds: 3)

  """
  removeCommentDontAgree will remove a DontAgree authored by the current logged in
  User on a Comment if it exists.
  """
  removeCommentDontAgree(
    input: RemoveCommentDontAgreeInput!
  ): RemoveCommentDontAgreePayload @auth @rate(seconds: 3)

  """
  createCommentFlag will create a Flag authored by the current logged in User on
  a given Comment.
  """
  createCommentFlag(input: CreateCommentFlagInput!): CreateCommentFlagPayload!
    @auth
    @rate(seconds: 3)

  """
  featureComment will mark a given Comment as featured.
  """
  featureComment(input: FeatureCommentInput!): FeatureCommentPayload!
    @auth(roles: [ADMIN, MODERATOR])

  """
  unfeatureComment will remove the featured tag from a Comment that is featured.
  """
  unfeatureComment(input: UnfeatureCommentInput!): UnfeatureCommentPayload!
    @auth(roles: [ADMIN, MODERATOR])

  """
  createStory will create the provided Story.
  """
  createStory(input: CreateStoryInput!): CreateStoryPayload!
    @auth(roles: [ADMIN])

  """
  updateStory will update the given Story.
  """
  updateStory(input: UpdateStoryInput!): UpdateStoryPayload!
    @auth(roles: [ADMIN])

  """
  updateStory will update the given Story's settings.
  """
  updateStorySettings(
    input: UpdateStorySettingsInput!
  ): UpdateStorySettingsPayload! @auth(roles: [ADMIN, MODERATOR])

  """
  closeStory will close the given story for commenting.
  """
  closeStory(input: CloseStoryInput!): CloseStoryPayload!
    @auth(roles: [ADMIN, MODERATOR])

  """
  openStory will open the given story for commenting.
  """
  openStory(input: OpenStoryInput!): OpenStoryPayload!
    @auth(roles: [ADMIN, MODERATOR])

  """
  mergeStories will merge two stories together, merging their comment streams.
  This operation is irreversible.
  """
  mergeStories(input: MergeStoriesInput!): MergeStoriesPayload!
    @auth(roles: [ADMIN])

  """
  removeStory will remove the given Story.
  """
  removeStory(input: RemoveStoryInput!): RemoveStoryPayload!
    @auth(roles: [ADMIN])

  """
  scrapeStory will scrape the given Story and update the scraped metadata.
  """
  scrapeStory(input: ScrapeStoryInput!): ScrapeStoryPayload!
    @auth(roles: [ADMIN, MODERATOR])

  """
  approveComment will mark the Comment as APPROVED.
  """
  approveComment(input: ApproveCommentInput!): ApproveCommentPayload!
    @auth(roles: [MODERATOR, ADMIN])

  """
  rejectComment will mark the Comment as REJECTED.
  """
  rejectComment(input: RejectCommentInput!): RejectCommentPayload!
    @auth(roles: [MODERATOR, ADMIN])

  """
  inviteUsers will send emails to the users with a new account at the designated
  role.
  """
  inviteUsers(input: InviteUsersInput!): InviteUsersPayload!
    @auth(roles: [ADMIN])

  """
  setUsername will set the username on the current User if they have not set one
  before. This mutation will fail if the username is already set.
  """
  setUsername(input: SetUsernameInput!): SetUsernamePayload!
    @auth(
      permit: [MISSING_NAME, MISSING_EMAIL, SUSPENDED, BANNED, PENDING_DELETION]
    )

  """
  updateUsername will update the users username.
  """
  updateUsername(input: UpdateUsernameInput!): UpdateUsernamePayload!
    @auth(permit: [SUSPENDED, BANNED, PENDING_DELETION])
    @rate(seconds: 10)

  """
  setEmail will set the email address on the current User if they have not set
  one already. This mutation will fail if the email address is already set.
  """
  setEmail(input: SetEmailInput!): SetEmailPayload!
    @auth(
      permit: [MISSING_NAME, MISSING_EMAIL, SUSPENDED, BANNED, PENDING_DELETION]
    )

  """
  setPassword will set the password on the current User if they have not set
  one already. This mutation will fail if the password is already set.
  """
  setPassword(input: SetPasswordInput!): SetPasswordPayload! @auth

  """
  updatePassword allows the current logged in User to change their password if
  they already have one associated with them.
  """
  updatePassword(input: UpdatePasswordInput!): UpdatePasswordPayload!
    @auth
    @rate(seconds: 10)

  """
  requestAccountDeletion allows the current logged in User to request to
  delete their account.
  """
  requestAccountDeletion(
    input: RequestAccountDeletionInput!
  ): RequestAccountDeletionPayload! @auth @rate(seconds: 10)

  """
  deleteUserAccount will delete the target user now.
  """
  deleteUserAccount(input: DeleteUserAccountInput!): DeleteUserAccountPayload!
    @auth(roles: [ADMIN])

  """
  cancelAccountDeletion allows the current logged in User to cancel the
  request to delete their account
  """
  cancelAccountDeletion(
    input: CancelAccountDeletionInput!
  ): CancelAccountDeletionPayload!
    @auth(permit: [SUSPENDED, BANNED, PENDING_DELETION])

  """
  createToken allows an administrator to create a Token based on the current
  logged in User.
  """
  createToken(input: CreateTokenInput!): CreateTokenPayload!
    @auth(roles: [ADMIN])

  """
  deactivateToken will deactivate the current logged in User's Token based on
  the input.
  """
  deactivateToken(input: DeactivateTokenInput!): DeactivateTokenPayload!
    @auth(roles: [ADMIN])

  """
  updateUserUsername allows administrators to update a given User's username to
  the one provided.
  """
  updateUserUsername(
    input: UpdateUserUsernameInput!
  ): UpdateUserUsernamePayload! @auth(roles: [ADMIN])

  """
  updateEmail will update the current users email address.
  """
  updateEmail(input: UpdateEmailInput!): UpdateEmailPayload!
    @auth
    @rate(seconds: 10)

  """
  updateNotificationSettings can be used to update the notification settings for
  the current logged in user.
  """
  updateNotificationSettings(
    input: UpdateNotificationSettingsInput!
  ): UpdateNotificationSettingsPayload! @auth

  """
  updateUserEmail allows administrators to update a given User's email address
  to the one provided.
  """
  updateUserEmail(input: UpdateUserEmailInput!): UpdateUserEmailPayload!
    @auth(roles: [ADMIN])

  """
  updateUserAvatar allows administrators to update a given User's avatar to the
  one provided.
  """
  updateUserAvatar(input: UpdateUserAvatarInput!): UpdateUserAvatarPayload!
    @auth(roles: [ADMIN])

  """
  updateUserRole will update a given User's role.
  """
  updateUserRole(input: UpdateUserRoleInput!): UpdateUserRolePayload!
    @auth(roles: [ADMIN])

  """
  banUser will ban a specific User from interacting with Comments.
  """
  banUser(input: BanUserInput!): BanUserPayload!
    @auth(roles: [ADMIN, MODERATOR])

  """
  removeUserBan will remove an active ban from a User if they have one.
  """
  removeUserBan(input: RemoveUserBanInput!): RemoveUserBanPayload!
    @auth(roles: [ADMIN, MODERATOR])

  """
  suspendUser will suspend a specific User from interacting with Comments.
  """
  suspendUser(input: SuspendUserInput!): SuspendUserPayload!
    @auth(roles: [ADMIN, MODERATOR])

  """
  removeUserSuspension will remove an active suspension from a User if they have
  one.
  """
  removeUserSuspension(
    input: RemoveUserSuspensionInput!
  ): RemoveUserSuspensionPayload! @auth(roles: [ADMIN, MODERATOR])

  """
  ignoreUser will mark the given User as ignored by the current logged in User.
  """
  ignoreUser(input: IgnoreUserInput!): IgnoreUserPayload!
    @auth(permit: [SUSPENDED, BANNED, PENDING_DELETION])

  """
  removeUserIgnore will remove the given User from the ignored user list from
  the current logged in User.
  """
  removeUserIgnore(input: RemoveUserIgnoreInput!): RemoveUserIgnorePayload!
    @auth(permit: [SUSPENDED, BANNED, PENDING_DELETION])

  """
  requestCommentsDownload allows a user to request to download their comments.
  """
  requestCommentsDownload(
    input: RequestCommentsDownloadInput!
  ): RequestCommentsDownloadPayload!
    @auth(permit: [SUSPENDED, BANNED, PENDING_DELETION])

  """
  requestUserCommentsDownload allows a user to request to download their comments.
  """
  requestUserCommentsDownload(
    input: RequestUserCommentsDownloadInput!
  ): RequestUserCommentsDownloadPayload! @auth(roles: [ADMIN])

  """
  premodUser sets a user to mandatory premod
  """
  premodUser(input: PremodUserInput!): PremodUserPayload!
    @auth(roles: [ADMIN, MODERATOR])

  """
  removeUserPremod removes a user from mandatory premod
  """
  removeUserPremod(input: RemovePremodUserInput!): RemovePremodUserPayload!
    @auth(roles: [ADMIN, MODERATOR])

  """
  createModeratorNote creates a note on a user account.
  """
  createModeratorNote(
    input: CreateModeratorNoteInput!
  ): CreateModeratorNotePayload! @auth(roles: [ADMIN, MODERATOR])

  """
  deleteModeratorNote deletes a note on a user account.
  """
  deleteModeratorNote(
    input: DeleteModeratorNoteInput!
  ): DeleteModeratorNotePayload! @auth(roles: [ADMIN, MODERATOR])

  """
  enableFeatureFlag will enable a given FEATURE_FLAG.
  """
  enableFeatureFlag(input: EnableFeatureFlagInput!): EnableFeatureFlagPayload!
    @auth(roles: [ADMIN])

  """
  disableFeatureFlag will disable a given FEATURE_FLAG
  """
  disableFeatureFlag(
    input: DisableFeatureFlagInput!
  ): DisableFeatureFlagPayload! @auth(roles: [ADMIN])

  createAnnouncement(
    input: CreateAnnouncementInput!
  ): CreateAnnouncementPayload! @auth(roles: [ADMIN])

  deleteAnnouncement(
    input: DeleteAnnouncementInput!
  ): DeleteAnnouncementPayload! @auth(roles: [ADMIN])
}

##################
## Subscriptions
##################

"""
CommentStatusUpdatedPayload is returned when a Comment has it's status updated
after it was created.
"""
type CommentStatusUpdatedPayload {
  """
  newStatus  is the new status assigned to the Comment. This status may not
  match the status provided by `comment.status` due to race conditions in the
  data loaders.
  """
  newStatus: COMMENT_STATUS!

  """
  oldStatus is the old status that was previously assigned to the Comment.
  """
  oldStatus: COMMENT_STATUS!

  """
  moderator is the User that updated the Comment's status. If null, then the
  system assigned the new Comment status (for example, when a comment is edited
  by the author, and now contains a banned word).
  """
  moderator: User

  """
  comment is the updated Comment after the status has been updated.
  """
  comment: Comment!
}

"""
MODERATION_QUEUE references the specific ModerationQueue that a given Comment
can be associated with.
"""
enum MODERATION_QUEUE {
  """
  UNMODERATED refers to the ModerationQueue for all Comments that have not been
  moderated yet.
  """
  UNMODERATED

  """
  REPORTED refers to the ModerationQueue for all Comments that have been
  published, have not been moderated by a human yet, and have been reported by
  a User via a flag.
  """
  REPORTED

  """
  PENDING refers to the ModerationQueue for all Comments that were held back by
  the system and require moderation in order to be published.
  """
  PENDING
}

"""
CommentEnteredModerationQueuePayload is returned when a Comment enters a
specific ModerationQueue.
"""
type CommentEnteredModerationQueuePayload {
  """
  queue refers to the specific ModerationQueue that a given Comment entered.
  """
  queue: MODERATION_QUEUE!

  """
  comment is the Comment that entered the ModerationQueue.
  """
  comment: Comment!
}

"""
CommentLeftModerationQueuePayload is returned when a Comment leaves a specific
ModerationQueue.
"""
type CommentLeftModerationQueuePayload {
  """
  queue refers to the specific ModerationQueue that a given Comment left.
  """
  queue: MODERATION_QUEUE!

  """
  comment is the Comment that left the ModerationQueue.
  """
  comment: Comment!
}

"""
CommentCreatedPayload is returned when a new top level Comment is created on a
Story.
"""
type CommentCreatedPayload {
  """
  comment is the new top level Comment that was created on the Story.
  """
  comment: Comment!
}

"""
CommentReplyCreatedPayload is returned when a Comment is created as a reply to
another Comment where the selected ancestor Comment is in the ancestor chain.
"""
type CommentReplyCreatedPayload {
  """
  comment is the new reply Comment that was created.
  """
  comment: Comment!
}

"""
CommentFeaturedPayload is returned when a Comment is featured.
"""
type CommentFeaturedPayload {
  """
  comment is the Comment that was featured.
  """
  comment: Comment!
}

"""
CommentReleasedPayload is returned when a new top level Comment is approved from premod stream
"""
type CommentReleasedPayload {
  """
  comment is the new top level Comment that was approved on the Story.
  """
  comment: Comment!
}

type Subscription {
  """
  commentEnteredModerationQueue returns when a Comment enters a ModerationQueue.
  Note that a Comment may enter multiple moderation queues.
  """
  commentEnteredModerationQueue(
    storyID: ID
    queue: MODERATION_QUEUE
  ): CommentEnteredModerationQueuePayload! @auth(roles: [MODERATOR, ADMIN])

  """
  commentLeftModerationQueue returns when a Comment leaves a ModerationQueue.
  Note that a Comment may leave multiple moderation queues.
  """
  commentLeftModerationQueue(
    storyID: ID
    queue: MODERATION_QUEUE
  ): CommentLeftModerationQueuePayload! @auth(roles: [MODERATOR, ADMIN])

  """
  commentStatusUpdated returns when a Comment has it's status changed after
  being created.
  """
  commentStatusUpdated(id: ID): CommentStatusUpdatedPayload!
    @auth(roles: [MODERATOR, ADMIN])

  """
  commentCreated returns when a Comment is created on the top level of a Story
  that is visible.
  """
  commentCreated(storyID: ID!): CommentCreatedPayload!

  """
  commentReleased returns when a Comment on a premoderated stream is approved
  """
  commentReleased(storyID: ID!): CommentReleasedPayload!

  """
  commentReplyCreated returns when a Comment is posted in the ancestor chain of
  comments.
  """
  commentReplyCreated(ancestorID: ID!): CommentReplyCreatedPayload!

  """
  commentFeatured returns when a Comment is featured.
  """
  commentFeatured(storyID: ID!): CommentFeaturedPayload!
    @auth(roles: [MODERATOR, ADMIN])
}
