lists.sr.ht/api/graph/schema.graphqls

768 lines
20 KiB
GraphQL

# This schema definition is available in the public domain, or under the terms
# of CC-0, at your choice.
"String of the format %Y-%m-%dT%H:%M:%SZ"
scalar Time
"Opaque string"
scalar Cursor
"""
URL from which some secondary data may be retrieved. You must provide the
same Authentication header to this address as you did to the GraphQL resolver
which provided it. The URL is not guaranteed to be consistent for an extended
length of time; applications should submit a new GraphQL query each time they
wish to access the data at the provided URL.
"""
scalar URL
"Used to provide a human-friendly description of an access scope"
directive @scopehelp(details: String!) on ENUM_VALUE
"""
This is used to decorate fields which are only accessible with a personal
access token, and are not available to clients using OAuth 2.0 access tokens.
"""
directive @private on FIELD_DEFINITION
"""
This is used to decorate fields which are for internal use, and are not
available to normal API users.
"""
directive @internal on FIELD_DEFINITION
enum AccessScope {
ACLS @scopehelp(details: "access control lists")
EMAILS @scopehelp(details: "emails")
LISTS @scopehelp(details: "mailing lists")
PATCHES @scopehelp(details: "patches")
PROFILE @scopehelp(details: "profile information")
SUBSCRIPTIONS @scopehelp(details: "tracker & ticket subscriptions")
}
enum AccessKind {
RO @scopehelp(details: "read")
RW @scopehelp(details: "read and write")
}
"""
Decorates fields for which access requires a particular OAuth 2.0 scope with
read or write access.
"""
directive @access(scope: AccessScope!, kind: AccessKind!) on FIELD_DEFINITION
# https://semver.org
type Version {
major: Int!
minor: Int!
patch: Int!
"""
If this API version is scheduled for deprecation, this is the date on which
it will stop working; or null if this API version is not scheduled for
deprecation.
"""
deprecationDate: Time
}
interface Entity {
canonicalName: String!
}
"A registered user"
type User implements Entity {
id: Int!
created: Time!
updated: Time!
canonicalName: String!
username: String!
email: String!
url: String
location: String
bio: String
lists(cursor: Cursor): MailingListCursor @access(scope: LISTS, kind: RO)
emails(cursor: Cursor): EmailCursor @access(scope: EMAILS, kind: RO)
threads(cursor: Cursor): ThreadCursor @access(scope: EMAILS, kind: RO)
patches(cursor: Cursor): PatchsetCursor @access(scope: PATCHES, kind: RO)
}
"A mailbox not associated with a registered user"
type Mailbox implements Entity {
canonicalName: String!
name: String!
address: String!
}
enum Visibility {
PUBLIC
UNLISTED
PRIVATE
}
type MailingList {
id: Int!
created: Time!
updated: Time!
name: String!
owner: Entity! @access(scope: PROFILE, kind: RO)
# Markdown
description: String
visibility: Visibility!
"""
List of globs for permitted or rejected mimetypes on this list
e.g. text/*
"""
permitMime: [String!]!
rejectMime: [String!]!
"List of threads on this list in order of most recently bumped"
threads(cursor: Cursor): ThreadCursor! @access(scope: EMAILS, kind: RO)
"List of emails received on this list in reverse chronological order"
emails(cursor: Cursor): EmailCursor! @access(scope: EMAILS, kind: RO)
"List of patches received on this list in order of most recently bumped"
patches(cursor: Cursor): PatchsetCursor! @access(scope: PATCHES, kind: RO)
"True if an import operation is underway for this list"
importing: Boolean!
"The access that applies to this user for this list"
access: ACL! @access(scope: ACLS, kind: RO)
"The user's subscription for this list, if any"
subscription: MailingListSubscription @access(scope: SUBSCRIPTIONS, kind: RO)
"URLs to application/mbox archives for this mailing list"
archive: URL!
last30days: URL!
#
# The following resolvers are only available to the list owner:
"Access control list entries for this mailing list"
acl(cursor: Cursor): MailingListACLCursor! @access(scope: ACLS, kind: RO)
defaultACL: GeneralACL!
"""
Returns a list of mailing list webhook subscriptions. For clients
authenticated with a personal access token, this returns all webhooks
configured by all GraphQL clients for your account. For clients
authenticated with an OAuth 2.0 access token, this returns only webhooks
registered for your client.
"""
webhooks(cursor: Cursor): WebhookSubscriptionCursor!
"Returns details of a mailing list webhook subscription by its ID."
webhook(id: Int!): WebhookSubscription
}
type OAuthClient {
uuid: String!
}
enum WebhookEvent {
LIST_CREATED @access(scope: LISTS, kind: RO)
LIST_UPDATED @access(scope: LISTS, kind: RO)
LIST_DELETED @access(scope: LISTS, kind: RO)
EMAIL_RECEIVED @access(scope: EMAILS, kind: RO)
PATCHSET_RECEIVED @access(scope: PATCHES, kind: RO)
}
interface WebhookSubscription {
id: Int!
events: [WebhookEvent!]!
query: String!
url: String!
"""
If this webhook was registered by an authorized OAuth 2.0 client, this
field is non-null.
"""
client: OAuthClient @private
"All deliveries which have been sent to this webhook."
deliveries(cursor: Cursor): WebhookDeliveryCursor!
"Returns a sample payload for this subscription, for testing purposes"
sample(event: WebhookEvent!): String!
}
type UserWebhookSubscription implements WebhookSubscription {
id: Int!
events: [WebhookEvent!]!
query: String!
url: String!
client: OAuthClient @private
deliveries(cursor: Cursor): WebhookDeliveryCursor!
sample(event: WebhookEvent!): String!
}
type MailingListWebhookSubscription implements WebhookSubscription {
id: Int!
events: [WebhookEvent!]!
query: String!
url: String!
client: OAuthClient @private
deliveries(cursor: Cursor): WebhookDeliveryCursor!
sample(event: WebhookEvent!): String!
list: MailingList!
}
type WebhookDelivery {
uuid: String!
date: Time!
event: WebhookEvent!
subscription: WebhookSubscription!
requestBody: String!
"""
These details are provided only after a response is received from the
remote server. If a response is sent whose Content-Type is not text/*, or
cannot be decoded as UTF-8, the response body will be null. It will be
truncated after 64 KiB.
"""
responseBody: String
responseHeaders: String
responseStatus: Int
}
interface WebhookPayload {
uuid: String!
event: WebhookEvent!
date: Time!
}
type MailingListEvent implements WebhookPayload {
uuid: String!
event: WebhookEvent!
date: Time!
list: MailingList!
}
type EmailEvent implements WebhookPayload {
uuid: String!
event: WebhookEvent!
date: Time!
email: Email!
}
type PatchsetEvent implements WebhookPayload {
uuid: String!
event: WebhookEvent!
date: Time!
patchset: Patchset!
}
interface ACL {
"Permission to browse or subscribe to emails"
browse: Boolean!
"Permission to reply to existing threads"
reply: Boolean!
"Permission to start new threads"
post: Boolean!
"Permission to moderate the list"
moderate: Boolean!
}
"""
These ACLs are configured for specific entities, and may be used to expand or
constrain the rights of a participant.
"""
type MailingListACL implements ACL {
id: Int!
created: Time!
list: MailingList! @access(scope: LISTS, kind: RO)
entity: Entity! @access(scope: PROFILE, kind: RO)
browse: Boolean!
reply: Boolean!
post: Boolean!
moderate: Boolean!
}
"""
An ACL entry that applies "generally", for example the rights which apply to
all subscribers to a list.
"""
type GeneralACL implements ACL {
browse: Boolean!
reply: Boolean!
post: Boolean!
moderate: Boolean!
}
type Thread {
created: Time!
updated: Time!
subject: String!
replies: Int!
participants: Int!
sender: Entity!
root: Email!
list: MailingList! @access(scope: LISTS, kind: RO)
"Replies to this thread, in chronological order"
descendants(cursor: Cursor): EmailCursor!
"A mailto: URI for replying to the latest message in this thread"
mailto: String!
"URL to an application/mbox archive of this thread"
mbox: URL!
"""
Thread parsed as a tree.
The returned list is never empty. The first item is guaranteed to be the root
message. The blocks are sorted in topological order.
"""
blocks: [ThreadBlock!]!
}
"""
A block of text in an email thread.
Blocks are parts of a message's body that aren't quotes of the parent message.
A block can be a reply to a parent block, in which case the parentStart and
parentEnd fields indicate which part of the parent message is replied to. A
block can have replies, each of which will be represented by a block in the
children field.
"""
type ThreadBlock {
"Unique identifier for this block."
key: String!
"The block's plain-text content."
body: String!
"Index of the parent block (if any) in Thread.blocks."
parent: Int
"""
Replies to this block.
The list items are indexes into Thread.blocks.
"""
children: [Int!]!
"The email this block comes from."
source: Email!
"The range of this block in the source email body."
sourceRange: ByteRange!
"""
If this block is a reply to a particular chunk of the parent block, this
field indicates the range of that chunk in the parent's email body.
"""
parentRange: ByteRange
}
"""
A byte range.
"""
type ByteRange {
"Inclusive start byte offset."
start: Int!
"Exclusive end byte offset."
end: Int!
}
type Email {
id: Int!
"""
The entity which sent this email. Will be a User if it can be associated
with an account, or a Mailbox otherwise.
"""
sender: Entity!
"Time we received this email (non-forgable)."
received: Time!
"Time given by Date header (forgable)."
date: Time
"The Subject header."
subject: String!
"The Message-ID header, without angle brackets."
messageID: String!
"The In-Reply-To header, if present, without angle brackets."
inReplyTo: String
"""
Provides the value (or values) of a specific header from this email. Note
that the returned value is coerced to UTF-8 and may be lossy under certain
circumstances.
"""
header(want: String!): [String!]!
"Retrieves the value of an address list header, such as To or Cc."
addressList(want: String!): [Mailbox!]!
"The decoded text/plain message part of the email, i.e. email body."
body: String!
"A URL from which the full raw message envelope may be downloaded."
envelope: URL!
thread: Thread!
parent: Email
patch: Patch
patchset: Patchset @access(scope: PATCHES, kind: RO)
list: MailingList! @access(scope: LISTS, kind: RO)
}
"""
Information parsed from the subject line of a patch, such that the following:
[PATCH myproject v2 3/4] Add foo to bar
Will produce:
index: 3
count: 4
version: 2
prefix: "myproject"
subject: "Add foo to bar"
"""
type Patch {
index: Int
count: Int
version: Int
prefix: String
subject: String
}
enum PatchsetStatus {
UNKNOWN
PROPOSED
NEEDS_REVISION
SUPERSEDED
APPROVED
REJECTED
APPLIED
}
type Patchset {
id: Int!
created: Time!
updated: Time!
subject: String!
version: Int!
prefix: String
status: PatchsetStatus!
submitter: Entity!
coverLetter: Email @access(scope: EMAILS, kind: RO)
thread: Thread! @access(scope: EMAILS, kind: RO)
supersededBy: Patchset
list: MailingList! @access(scope: LISTS, kind: RO)
patches(cursor: Cursor): EmailCursor! @access(scope: EMAILS, kind: RO)
tools: [PatchsetTool!]!
"URL to an application/mbox archive of only the patches in this thread"
mbox: URL!
}
enum ToolIcon {
PENDING
WAITING
SUCCESS
FAILED
CANCELLED
}
"""
Used to add some kind of indicator for a third-party process associated with
a patchset, such as a CI service validating the change.
"""
type PatchsetTool {
id: Int!
created: Time!
updated: Time!
icon: ToolIcon!
details: String!
patchset: Patchset!
}
interface ActivitySubscription {
id: Int!
created: Time!
}
type MailingListSubscription implements ActivitySubscription {
id: Int!
created: Time!
list: MailingList! @access(scope: LISTS, kind: RO)
}
"""
A cursor for enumerating ACL entries
If there are additional results available, the cursor object may be passed
back into the same endpoint to retrieve another page. If the cursor is null,
there are no remaining results to return.
"""
type MailingListACLCursor {
results: [MailingListACL!]!
cursor: Cursor
}
"""
A cursor for enumerating mailing lists
If there are additional results available, the cursor object may be passed
back into the same endpoint to retrieve another page. If the cursor is null,
there are no remaining results to return.
"""
type MailingListCursor {
results: [MailingList!]!
cursor: Cursor
}
"""
A cursor for enumerating threads
If there are additional results available, the cursor object may be passed
back into the same endpoint to retrieve another page. If the cursor is null,
there are no remaining results to return.
"""
type ThreadCursor {
results: [Thread!]!
cursor: Cursor
}
"""
A cursor for enumerating emails
If there are additional results available, the cursor object may be passed
back into the same endpoint to retrieve another page. If the cursor is null,
there are no remaining results to return.
"""
type EmailCursor {
results: [Email!]!
cursor: Cursor
}
"""
A cursor for enumerating patchsets
If there are additional results available, the cursor object may be passed
back into the same endpoint to retrieve another page. If the cursor is null,
there are no remaining results to return.
"""
type PatchsetCursor {
results: [Patchset!]!
cursor: Cursor
}
"""
A cursor for enumerating subscriptions
If there are additional results available, the cursor object may be passed
back into the same endpoint to retrieve another page. If the cursor is null,
there are no remaining results to return.
"""
type ActivitySubscriptionCursor {
results: [ActivitySubscription!]!
cursor: Cursor
}
"""
A cursor for enumerating a list of webhook deliveries
If there are additional results available, the cursor object may be passed
back into the same endpoint to retrieve another page. If the cursor is null,
there are no remaining results to return.
"""
type WebhookDeliveryCursor {
results: [WebhookDelivery!]!
cursor: Cursor
}
"""
A cursor for enumerating a list of webhook subscriptions
If there are additional results available, the cursor object may be passed
back into the same endpoint to retrieve another page. If the cursor is null,
there are no remaining results to return.
"""
type WebhookSubscriptionCursor {
results: [WebhookSubscription!]!
cursor: Cursor
}
type Query {
"Returns API version information"
version: Version!
"Returns the authenticated user"
me: User! @access(scope: PROFILE, kind: RO)
"Looks up a specific user"
user(id: Int!): User @access(scope: PROFILE, kind: RO)
userByName(username: String!): User @access(scope: PROFILE, kind: RO)
"Looks up a specific mailing list"
mailingList(id: Int!): MailingList @access(scope: LISTS, kind: RO)
mailingListByName(name: String!): MailingList @access(scope: LISTS, kind: RO)
mailingListByOwner(ownerName: String!, listName: String!): MailingList @access(scope: LISTS, kind: RO)
"Looks up a specific email by its ID"
email(id: Int!): Email @access(scope: EMAILS, kind: RO)
"""
Looks up a specific email by its Message-ID header, including the angle
brackets ('<' and '>').
"""
message(messageID: String!): Email @access(scope: EMAILS, kind: RO)
"Looks up a patchset by ID"
patchset(id: Int!): Patchset @access(scope: EMAILS, kind: RO)
"List of mailing lists that the authenticated user has ownership of"
mailingLists(cursor: Cursor): MailingListCursor! @access(scope: LISTS, kind: RO)
"List of subscriptions of the authenticated user"
subscriptions(cursor: Cursor): ActivitySubscriptionCursor @access(scope: SUBSCRIPTIONS, kind: RO)
"""
Returns a list of user webhook subscriptions. For clients
authenticated with a personal access token, this returns all webhooks
configured by all GraphQL clients for your account. For clients
authenticated with an OAuth 2.0 access token, this returns only webhooks
registered for your client.
"""
userWebhooks(cursor: Cursor): WebhookSubscriptionCursor!
"Returns details of a user webhook subscription by its ID."
userWebhook(id: Int!): WebhookSubscription
"""
Returns information about the webhook currently being processed. This is
not valid during normal queries over HTTP, and will return an error if used
outside of a webhook context.
"""
webhook: WebhookPayload!
}
# You may omit any fields to leave them unchanged.
# TODO: Allow users to change the name of a mailing list
input MailingListInput {
description: String
visibility: Visibility
"""
List of globs for permitted or rejected mimetypes on this list
e.g. text/*
"""
permitMime: [String!]
rejectMime: [String!]
}
# All fields are required
input ACLInput {
browse: Boolean!
reply: Boolean!
post: Boolean!
moderate: Boolean!
}
input UserWebhookInput {
url: String!
events: [WebhookEvent!]!
query: String!
}
input MailingListWebhookInput {
url: String!
events: [WebhookEvent!]!
query: String!
}
type Mutation {
"Creates a new mailing list"
createMailingList(
name: String!,
description: String,
visibility: Visibility!): MailingList! @access(scope: LISTS, kind: RW)
"Updates a mailing list."
updateMailingList(
id: Int!,
input: MailingListInput!): MailingList @access(scope: LISTS, kind: RW)
"Deletes a mailing list"
deleteMailingList(id: Int!): MailingList @access(scope: LISTS, kind: RW)
"Adds or updates the ACL for a user on a mailing list"
updateUserACL(
listID: Int!,
userID: Int!,
input: ACLInput!): MailingListACL @access(scope: ACLS, kind: RW)
"Adds or updates the ACL for an email address on a mailing list"
updateSenderACL(
listID: Int!,
address: String!,
input: ACLInput!): MailingListACL @access(scope: ACLS, kind: RW)
"""
Updates the default ACL for a mailing list, which applies to users and
senders for whom a more specific ACL does not exist.
"""
updateMailingListACL(
listID: Int!,
input: ACLInput!): MailingList @access(scope: ACLS, kind: RW)
"""
Removes a mailing list ACL. Following this, the default mailing list ACL will
apply to this user.
"""
deleteACL(id: Int!): MailingListACL @access(scope: ACLS, kind: RW)
"Updates the status of a patchset"
updatePatchset(id: Int!, status: PatchsetStatus!): Patchset @access(scope: PATCHES, kind: RW)
"Create a new patchset tool"
createTool(patchsetID: Int!, details: String!, icon: ToolIcon!): PatchsetTool @access(scope: PATCHES, kind: RW)
"Updates the status of a patchset tool by its ID"
updateTool(id: Int!, details: String, icon: ToolIcon): PatchsetTool @access(scope: PATCHES, kind: RW)
"Creates a mailing list subscription"
mailingListSubscribe(listID: Int!): MailingListSubscription @access(scope: SUBSCRIPTIONS, kind: RW)
"Deletes a mailing list subscription"
mailingListUnsubscribe(listID: Int!): MailingListSubscription @access(scope: SUBSCRIPTIONS, kind: RW)
"""
Creates a new user webhook subscription. When an event from the
provided list of events occurs, the 'query' parameter (a GraphQL query)
will be evaluated and the results will be sent to the provided URL as the
body of an HTTP POST request. The list of events must include at least one
event, and no duplicates.
This query is evaluated in the webhook context, such that query { webhook }
may be used to access details of the event which trigged the webhook. The
query may not make any mutations.
"""
createUserWebhook(config: UserWebhookInput!): WebhookSubscription!
"""
Deletes a user webhook. Any events already queued may still be
delivered after this request completes. Clients authenticated with a
personal access token may delete any webhook registered for their account,
but authorized OAuth 2.0 clients may only delete their own webhooks.
Manually deleting a webhook configured by a third-party client may cause
unexpected behavior with the third-party integration.
"""
deleteUserWebhook(id: Int!): WebhookSubscription!
"Creates a new mailing list webhook."
createMailingListWebhook(listId: Int!, config: MailingListWebhookInput!): WebhookSubscription!
"Deletes a mailing list webhook."
deleteMailingListWebhook(id: Int!): WebhookSubscription!
triggerUserEmailWebhooks(emailId: Int!): Email! @internal
triggerListEmailWebhooks(listId: Int!, emailId: Int!): Email! @internal
}