Documentation
¶
Overview ¶
Package discussion implements a sync mechanism to mirror GitHub discussions state into a storage.DB. All the functionality is provided by the Client, created by New.
This package stores the following key schemas in the database:
["discussion.SyncProject", Project] => JSON of [projectSync] structure ["discussion.Event", Project, Discussion, API, ID] => [DBTime, Raw(JSON)] ["discussion.EventByTime", DBTime, Project, Discussion, API, ID] => []
To reconstruct the history of a given discussion, scan for keys from ["discussion.Event", Project, Discussion] to ["discussion.Event", Project, Discussion, ordered.Inf].
The API field is "/discussions", or "/discussions/comments", so the first key-value pair is the discussion with its body text and metadata.
The IDs are GitHub's and appear to be ordered by creation time within an API, so that the comments are time-ordered and the discussions are time-ordered, but comments and discussions are not ordered with respect to each other. To order them fully, fetch all the events and sort by the time in the JSON.
The JSON is the raw JSON served from GitHub describing the event. Storing the raw JSON avoids having to re-download everything if we decide another field is of interest to us.
EventByTime is an index of Events by DBTime, which is the time when the record was added to the database, which is not necessarily related to the time the event occurred. Code that processes new events can record which DBTime it has most recently processed and then scan forward in the index to learn about new events.
Index ¶
- Constants
- type Client
- func (c *Client) Add(project string) error
- func (c *Client) DocWatcher() *timed.Watcher[*Event]
- func (c *Client) EventWatcher(name string) *timed.Watcher[*Event]
- func (c *Client) Events(project string, discMin, discMax int64) iter.Seq[*Event]
- func (c *Client) EventsAfter(t timed.DBTime, project string) iter.Seq[*Event]
- func (c *Client) Sync(ctx context.Context) error
- func (c *Client) SyncProject(ctx context.Context, project string) error
- func (c *Client) Testing() *TestingClient
- func (*Client) ToDocs(e *Event) (iter.Seq[*docs.Doc], bool)
- type Comment
- type Discussion
- type Event
- type TestingClient
Constants ¶
const ( DiscussionAPI string = "/discussions" CommentAPI string = "/discussions/comments" // both comments and replies )
The recognized event kinds. The events are fetched from the GrapQL API, which uses queries instead of API endpoints, so these "endpoints" are merely for identification purposes. We use the term API for consistency with the github.Event.API field.
const DocWatcherID = "discussiondocs"
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a client for making requests to the GitHub GraphQL API and syncing discussion state with a storage.DB.
func New ¶
New creates a new client for making requests to the GitHub GraphQL API.
The secret database is expected to have a secret named "api.github.com" of the form "user:pass" where user is a user-name (ignored by GitHub) and pass is an API token ("ghp_...").
func (*Client) Add ¶
Add adds a GitHub project of the form "owner/repo" (for example "golang/go") to the database. It only adds the project sync metadata. The initial data fetch does not happen until [Sync] or [SyncProject] is called. If the project is already present, Add does nothing and returns nil.
func (*Client) DocWatcher ¶
DocWatcher returns the page watcher with name "discussiondocs". Implements docs.Source.DocWatcher.
func (*Client) EventWatcher ¶
EventWatcher returns a new timed.Watcher with the given name. It picks up where any previous Watcher of the same name left off.
func (*Client) Events ¶
Events returns an iterator over discussion events for the given project, limited to discussions in the range discMin ≤ discussion ≤ discMax. If discMax < 0, there is no upper limit. The events are iterated over in (Project, Discussion, Kind, ID) order, so "/discussions" events come first, then "/discussions/comments" events. Within an event kind, the events are ordered by increasing ID, which corresponds to increasing event time on GitHub.
func (*Client) EventsAfter ¶
EventsAfter returns an iterator over discussion events in the given project after DBTime t, which should be e.DBTime from the most recent processed event. The events are iterated over in DBTime order, so the DBTime of the last successfully processed event can be used in a future call to EventsAfter. If project is the empty string, then events from all projects are returned.
func (*Client) SyncProject ¶
SyncProject syncs a single project.
func (*Client) Testing ¶
func (c *Client) Testing() *TestingClient
Testing returns a TestingClient, which provides access to Client functionality intended for testing. Testing only returns a non-nil TestingClient in testing mode, which is active if the current program is a test binary (that is, testing.Testing returns true) or if [Client.EnableTesting] has been called. Otherwise, Testing returns nil.
Each Client has only one TestingClient associated with it. Every call to Testing returns the same TestingClient.
type Comment ¶
type Comment struct {
// URL of this comment.
URL string `json:"url"`
// URL of the discussion this is a comment on
DiscussionURL string `json:"discussion_url"`
// URL of the comment this is a reply to, if applicable
ReplyToURL string `json:"reply_to_url,omitempty"`
Author github.User `json:"author"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
Body string `json:"body"`
}
Comment represents a GitHub discussion comment and its metadata.
type Discussion ¶
type Discussion struct {
URL string `json:"url"`
Number int64 `json:"number"`
Author github.User `json:"author"`
Title string `json:"title"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
LastEditedAt string `json:"last_edited_at,omitempty"`
ClosedAt string `json:"closed_at"`
Body string `json:"body"`
UpvoteCount int `json:"upvote_count"`
Locked bool `json:"locked"`
ActiveLockReason string `json:"active_lock_reason,omitempty"`
Labels []github.Label `json:"labels"`
}
Discussion represents a GitHub discussion and its metadata.
type Event ¶
type Event struct {
DBTime timed.DBTime // when event was last written
Project string // project (e.g. "golang/go")
Discussion int64 // discussion number
API string // the event kind ("API" for consistency with the [github.Event.API] field)
ID int64 // ID of event; each API has a different ID space. (Project, Discussion, API, ID) is assumed unique
JSON []byte // JSON for the event data
Typed any // Typed unmarshaling of the event data, of type [*Discussion], [*Comment]
Updated time.Time // when the event was last updated (according to GitHub)
}
An Event is a single GitHub discussion event stored in the database.
func (*Event) LastWritten ¶
LastWritten implements docs.Entry.LastWritten.
type TestingClient ¶
type TestingClient struct {
// contains filtered or unexported fields
}
A TestingClient provides access to Client functionality intended for testing.
See Client.Testing for a description of testing mode.
func (*TestingClient) AddComment ¶
func (tc *TestingClient) AddComment(project string, disc int64, comment *Comment) int64
AddIssueComment adds the given issue comment to the identified project issue, assigning it a new comment ID starting at 10¹⁰. AddIssueComment creates a new entry in the associated Client's underlying database, so other Client's using the same database will see the issue comment too.
NOTE: Only one TestingClient should be adding issues, since they do not coordinate in the database about ID assignment. Perhaps they should, but normally there is just one Client.
func (*TestingClient) AddDiscussion ¶
func (tc *TestingClient) AddDiscussion(project string, d *Discussion) int64
AddDiscussion adds the given discussion to the identified project, assigning it a new issue number starting at 10⁹. AddDiscussion creates a new entry in the associated Client's underlying database, so other Client's using the same database will see the issue too.
NOTE: Only one TestingClient should be adding issues, since they do not coordinate in the database about ID assignment. Perhaps they should, but normally there is just one Client.