Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 115 additions & 7 deletions instruqt/invite.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,44 @@ type inviteQuery struct {
TrackInvite `graphql:"trackInvite(inviteID: $inviteId)"`
}

type inviteTracksQuery struct {
TrackInvite trackInviteTracks `graphql:"trackInvite(inviteID: $inviteId)"`
}

// TrackInvite represents the data structure for an Instruqt track invite.
type TrackInvite struct {
Id string // The unique identifier for the invite.
PublicTitle string // The public title of the track invite.
RuntimeParameters struct { // The runtime parameters associated with the invite.
Id string // The unique identifier for the invite.
Title string // The internal title of the track invite.
PublicTitle string // The public title of the track invite.
PublicDescription string // The public description of the track invite.
AccessSetting string // The access setting for the invite.
InviteLimit int // The maximum number of claims allowed for the invite.
InviteCount int // The number of times the invite has been used.
ClaimCount int // The number of claims associated with the invite.
ExpiresAt time.Time // The timestamp when the invite expires.
StartsAt time.Time // The timestamp when the invite becomes available.
Created time.Time // The timestamp when the invite was created.
Last_Updated time.Time // The timestamp when the invite was last updated.
AllowAnonymous bool // Whether anonymous users can claim the invite.
AllowedEmailAddresses []string // The email addresses allowed to claim the invite.
AllowedEmailAddressesOnly bool // Whether only explicitly allowed email addresses can claim the invite.
CurrentUserAllowed bool // Whether the current API user can claim the invite.
CurrentUserClaimed bool // Whether the current API user has claimed the invite.
Type string // The invite type.
Status string // The invite status.
DaysUntil int // Number of days until the invite starts or expires.
CanClaim bool // Whether the invite can currently be claimed.
EmailOwnershipConfirmationRequired bool // Whether email ownership confirmation is required.
RuntimeParameters struct { // The runtime parameters associated with the invite.
EnvironmentVariables []variable // Environment variables used during the invite session.
}
Claims []TrackInviteClaim // A list of claims associated with the track invite.
Tracks []Track `graphql:"-"` // A list of tracks associated with the invite, only queried with WithTracks().
}

type trackInviteTracks struct {
Id string
Tracks []Track
}

// TrackInviteClaim represents a claim made by a user for a specific track invite.
Expand All @@ -54,48 +84,126 @@ type variable struct {
// GetInvite retrieves a track invite from Instruqt using its unique invite ID.
//
// Parameters:
//
// - inviteId: The unique identifier of the track invite to retrieve.
//
// - opts: Optional query modifiers, such as WithTracks.
//
// Returns:
// - TrackInvite: The track invite details if found.
// - error: Any error encountered while retrieving the invite.
func (c *Client) GetInvite(inviteId string) (i TrackInvite, err error) {
func (c *Client) GetInvite(inviteId string, opts ...Option) (i TrackInvite, err error) {
if inviteId == "" {
return i, nil
}

var q inviteQuery
variables := map[string]interface{}{
"inviteId": graphql.String(inviteId),
}

var q inviteQuery
if err := c.GraphQLClient.Query(c.Context, &q, variables); err != nil {
return i, err
}

options := &options{}
for _, opt := range opts {
opt(options)
}
if options.includeTracks {
tracks, err := c.GetInviteTracks(inviteId)
if err != nil {
return i, err
}
q.TrackInvite.Tracks = tracks
}

return q.TrackInvite, nil
}

// GetInviteTracks retrieves the tracks associated with a track invite.
//
// Parameters:
// - inviteId: The unique identifier of the track invite to retrieve tracks for.
//
// Returns:
// - []Track: The tracks associated with the invite.
// - error: Any error encountered while retrieving the invite tracks.
func (c *Client) GetInviteTracks(inviteId string) ([]Track, error) {
if inviteId == "" {
return nil, nil
}

var q inviteTracksQuery
variables := map[string]interface{}{
"inviteId": graphql.String(inviteId),
}
if err := c.GraphQLClient.Query(c.Context, &q, variables); err != nil {
return nil, err
}

return q.TrackInvite.Tracks, nil
}

// invitesQuery represents the GraphQL query structure for retrieving all track invites
// for a specific team.
type invitesQuery struct {
TrackInvites []TrackInvite `graphql:"trackInvites(teamSlug: $teamSlug)"`
}

type invitesTracksQuery struct {
TrackInvites []trackInviteTracks `graphql:"trackInvites(teamSlug: $teamSlug)"`
}

// GetInvites retrieves all track invites for the specified team slug from Instruqt.
//
// Returns:
// - []TrackInvite: A list of track invites for the team.
// - error: Any error encountered while retrieving the invites.
func (c *Client) GetInvites() (i []TrackInvite, err error) {
var q invitesQuery
func (c *Client) GetInvites(opts ...Option) (i []TrackInvite, err error) {
variables := map[string]interface{}{
"teamSlug": graphql.String(c.TeamSlug),
}

var q invitesQuery
if err := c.GraphQLClient.Query(c.Context, &q, variables); err != nil {
return i, err
}

options := &options{}
for _, opt := range opts {
opt(options)
}
if options.includeTracks {
tracksByInvite, err := c.GetInvitesTracks()
if err != nil {
return i, err
}
for idx := range q.TrackInvites {
q.TrackInvites[idx].Tracks = tracksByInvite[q.TrackInvites[idx].Id]
}
}

return q.TrackInvites, nil
}

// GetInvitesTracks retrieves track lists for all track invites in the team.
//
// Returns:
// - map[string][]Track: A map of invite ID to associated tracks.
// - error: Any error encountered while retrieving invite tracks.
func (c *Client) GetInvitesTracks() (map[string][]Track, error) {
var q invitesTracksQuery
variables := map[string]interface{}{
"teamSlug": graphql.String(c.TeamSlug),
}
if err := c.GraphQLClient.Query(c.Context, &q, variables); err != nil {
return nil, err
}

tracksByInvite := make(map[string][]Track, len(q.TrackInvites))
for _, invite := range q.TrackInvites {
tracksByInvite[invite.Id] = invite.Tracks
}
return tracksByInvite, nil
}
42 changes: 42 additions & 0 deletions instruqt/invite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,48 @@ func TestGetInvite(t *testing.T) {
mockClient.AssertExpectations(t)
}

func TestGetInviteWithTracks(t *testing.T) {
mockClient := new(MockGraphQLClient)
client := &Client{
GraphQLClient: mockClient,
}

inviteID := "invite-123"
expectedTracks := []Track{
{Id: "track-1", Slug: "cilium-getting-started", Title: "Getting Started with Cilium"},
{Id: "track-2", Slug: "tetragon-getting-started", Title: "Getting Started with Tetragon"},
}
inviteQueryResult := inviteQuery{
TrackInvite: TrackInvite{
Id: inviteID,
PublicTitle: "Test Invite",
},
}
tracksQueryResult := inviteTracksQuery{
TrackInvite: trackInviteTracks{
Id: inviteID,
Tracks: expectedTracks,
},
}

mockClient.On("Query", mock.Anything, &inviteQuery{}, mock.Anything).Run(func(args mock.Arguments) {
q := args.Get(1).(*inviteQuery)
*q = inviteQueryResult
}).Return(nil)
mockClient.On("Query", mock.Anything, &inviteTracksQuery{}, mock.Anything).Run(func(args mock.Arguments) {
q := args.Get(1).(*inviteTracksQuery)
*q = tracksQueryResult
}).Return(nil)

invite, err := client.GetInvite(inviteID, WithTracks())

assert.NoError(t, err)
assert.Equal(t, inviteID, invite.Id)
assert.Equal(t, "Test Invite", invite.PublicTitle)
assert.Equal(t, expectedTracks, invite.Tracks)
mockClient.AssertExpectations(t)
}

func TestGetInvites(t *testing.T) {
mockClient := new(MockGraphQLClient)
client := &Client{
Expand Down
11 changes: 11 additions & 0 deletions instruqt/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ type options struct {
includeChallenges bool
includeReviews bool

// Options for GetInvite*
includeTracks bool

// Options for GetChallenge*
includeAssignment bool
parseAssignmentVariables bool
Expand Down Expand Up @@ -79,6 +82,14 @@ func WithReviews() Option {
}
}

// WithTracks is a functional option to include tracks.
// Example usage: GetInvite("inviteID", WithTracks())
func WithTracks() Option {
return func(opts *options) {
opts.includeTracks = true
}
}

// WithTrackIDs sets the TrackIDs filter for methods that support it.
// Usage: GetPlays(from, to, take, skip, WithTrackIDs("track1", "track2"))
func WithTrackIDs(ids ...string) Option {
Expand Down
Loading