Documentation
¶
Overview ¶
contract.go: Wires the organization plugin into the forge-dashboard contract surface via plugin.ContractContributor.
Package organization provides an opt-in organization management plugin for AuthSome.
When registered, this plugin contributes organization CRUD, member management, invitation workflows, team management, dashboard pages, and admin routes. Without this plugin, no organization functionality exists.
Index ¶
- Constants
- Variables
- type AcceptInvitationRequest
- type AddMemberRequest
- type AdminListOrgsRequest
- type CheckSlugRequest
- type Config
- type CreateInvitationRequest
- type CreateOrgRequest
- type CreateTeamRequest
- type DeclineInvitationRequest
- type DeleteOrgRequest
- type DeleteTeamRequest
- type GetInvitationRequest
- type GetOrgRequest
- type GetTeamRequest
- type InvitationListResponse
- type InvitationPreview
- type ListInvitationsRequest
- type ListMembersRequest
- type ListOrgsRequest
- type ListTeamsRequest
- type MemberListResponse
- type OrgListResponse
- type Plugin
- func (p *Plugin) AcceptInvitation(ctx context.Context, token string) (*organization.Member, error)
- func (p *Plugin) AcceptInvitationAs(ctx context.Context, token string, userID id.UserID) (*organization.Member, error)
- func (p *Plugin) AddMember(ctx context.Context, m *organization.Member) error
- func (p *Plugin) AdminListOrganizations(ctx context.Context, appID id.AppID) ([]*organization.Organization, error)
- func (p *Plugin) CreateInvitation(ctx context.Context, inv *organization.Invitation) error
- func (p *Plugin) CreateOrganization(ctx context.Context, o *organization.Organization) error
- func (p *Plugin) CreateTeam(ctx context.Context, t *organization.Team) error
- func (p *Plugin) DeclineInvitation(ctx context.Context, token string) error
- func (p *Plugin) DeclineInvitationAs(ctx context.Context, token string, userID id.UserID) error
- func (p *Plugin) DeleteOrganization(ctx context.Context, orgID id.OrgID) error
- func (p *Plugin) DeleteTeam(ctx context.Context, teamID id.TeamID) error
- func (p *Plugin) ExportUserData(ctx context.Context, userID id.UserID) (label string, data any, err error)
- func (p *Plugin) GetInvitation(ctx context.Context, orgID id.OrgID, invID id.InvitationID) (*organization.Invitation, error)
- func (p *Plugin) GetOrganization(ctx context.Context, orgID id.OrgID) (*organization.Organization, error)
- func (p *Plugin) GetTeam(ctx context.Context, teamID id.TeamID) (*organization.Team, error)
- func (p *Plugin) IsOrgSlugAvailable(ctx context.Context, appID id.AppID, slug string) (bool, error)
- func (p *Plugin) ListInvitations(ctx context.Context, orgID id.OrgID) ([]*organization.Invitation, error)
- func (p *Plugin) ListInvitationsPage(ctx context.Context, orgID id.OrgID, opts page.Opts) (page.Page[*organization.Invitation], error)
- func (p *Plugin) ListMembers(ctx context.Context, orgID id.OrgID) ([]*organization.Member, error)
- func (p *Plugin) ListMembersPage(ctx context.Context, orgID id.OrgID, opts page.Opts) (page.Page[*organization.Member], error)
- func (p *Plugin) ListTeams(ctx context.Context, orgID id.OrgID) ([]*organization.Team, error)
- func (p *Plugin) ListTeamsPage(ctx context.Context, orgID id.OrgID, opts page.Opts) (page.Page[*organization.Team], error)
- func (p *Plugin) ListUserOrganizations(ctx context.Context, userID id.UserID) ([]*organization.Organization, error)
- func (p *Plugin) ListUserOrganizationsPage(ctx context.Context, userID id.UserID, opts page.Opts) (page.Page[*organization.Organization], error)
- func (p *Plugin) Name() string
- func (p *Plugin) OnInit(_ context.Context, engine plugin.Engine) error
- func (p *Plugin) RegisterContract(d *dispatcher.Dispatcher, reg contract.Registry, wreg contract.WardenRegistry, ...) error
- func (p *Plugin) RegisterRoutes(router forge.Router) error
- func (p *Plugin) RemoveMember(ctx context.Context, memberID id.MemberID) error
- func (p *Plugin) ResendInvitation(ctx context.Context, orgID id.OrgID, invID id.InvitationID) (*organization.Invitation, error)
- func (p *Plugin) RevokeInvitation(ctx context.Context, orgID id.OrgID, invID id.InvitationID) (*organization.Invitation, error)
- func (p *Plugin) SetStore(s store.Store)
- func (p *Plugin) UpdateMemberRole(ctx context.Context, memberID id.MemberID, role organization.MemberRole) (*organization.Member, error)
- func (p *Plugin) UpdateOrganization(ctx context.Context, o *organization.Organization) error
- func (p *Plugin) UpdateTeam(ctx context.Context, t *organization.Team) error
- type RemoveMemberRequest
- type ResendInvitationRequest
- type RevokeInvitationRequest
- type SlugAvailableResponse
- type StatusResponse
- type TeamListResponse
- type UpdateMemberRequest
- type UpdateOrgRequest
- type UpdateTeamRequest
Constants ¶
const DefaultInvitationTTL = 7 * 24 * time.Hour
DefaultInvitationTTL is how long an invitation stays acceptable when Config.InvitationTTL is left at zero.
const ResendInvitationInterval = time.Minute
ResendInvitationInterval is how long an invitation must wait between sends.
Variables ¶
var ErrInvitationExpired = errors.New("organization: invitation has expired")
ErrInvitationExpired is returned when an invitation is accepted after its ExpiresAt. The HTTP layer answers it with 410 Gone.
var ErrInvitationNotActionable = errors.New("organization: invitation can no longer be changed")
ErrInvitationNotActionable is returned by ResendInvitation and RevokeInvitation when the invitation has already been accepted, declined or revoked. The HTTP layer answers it with 409 Conflict.
var ErrInvitationNotPending = errors.New("organization: invitation is not pending")
ErrInvitationNotPending is returned when an invitation is accepted or declined after it was already accepted, declined or revoked. The HTTP handlers answer it with 409.
var ErrInvitationResendTooSoon = errors.New("organization: invitation was sent a moment ago")
ErrInvitationResendTooSoon is returned by ResendInvitation inside ResendInvitationInterval of the last send. The HTTP layer answers it with 429 Too Many Requests.
var ErrSlugTaken = errors.New("organization: slug already taken")
ErrSlugTaken is returned when an organization is created with a slug that another organization in the same app already holds. The HTTP layer answers it with 409 Conflict.
Functions ¶
This section is empty.
Types ¶
type AcceptInvitationRequest ¶ added in v0.0.7
type AcceptInvitationRequest struct {
Token string `json:"token" description:"Invitation token"`
}
AcceptInvitationRequest binds the body for POST /orgs/invitations/accept.
type AddMemberRequest ¶ added in v1.9.0
type AddMemberRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
UserID string `json:"user_id" description:"User identifier to add"`
Role string `json:"role,omitempty" description:"Member role (owner, admin, member)"`
}
AddMemberRequest binds path + body for POST /orgs/:orgId/members.
type AdminListOrgsRequest ¶ added in v1.9.0
type AdminListOrgsRequest struct {
AppID string `query:"app_id" description:"Application ID"`
}
AdminListOrgsRequest binds query params for GET /admin/orgs.
type CheckSlugRequest ¶ added in v1.9.0
type CheckSlugRequest struct {
AppID string `query:"app_id" description:"Application ID"`
Slug string `query:"slug" description:"Slug to check"`
}
CheckSlugRequest binds query params for GET /orgs/check-slug.
type Config ¶
type Config struct {
// PathPrefix is the HTTP path prefix for organization routes.
// Defaults to the engine's BasePath.
PathPrefix string
// InvitationTTL is how long an invitation can be accepted after it is
// created. Zero means DefaultInvitationTTL (seven days). A negative value
// turns expiry off, so invitations never lapse, which was the behaviour
// before this option existed.
InvitationTTL time.Duration
// InvitationURLTemplate is the link an invitation email carries, with
// {token} standing in for the invitation token, for example
// "https://app.example.com/accept-invite?token={token}". Empty (the
// default) means the plugin sends no invitation email, which was the
// behaviour before this option existed. Mail goes through the plugin
// registered as "notification"; without one nothing is sent either.
InvitationURLTemplate string
// RoleRanks gives role names other than owner, admin and member a rank
// for the checks that compare roles: who may read or change an
// organization, grant a role, or remove a member. Keys are lower case
// names; values are on the built-in scale, member 1, admin 2, owner 3,
// so {"builder": 1} lets a builder do what a member can and nothing
// more. A name not listed ranks 0, below member, which is how every
// custom role was treated before this option existed. The built-in names
// cannot be changed here. Be careful with 2 or 3: a role ranked as admin
// passes every admin-level check in this plugin.
RoleRanks map[string]int
}
Config configures the organization plugin.
type CreateInvitationRequest ¶ added in v1.9.0
type CreateInvitationRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
Email string `json:"email" description:"Email address to invite"`
Role string `json:"role,omitempty" description:"Member role (owner, admin, member)"`
}
CreateInvitationRequest binds path + body for POST /orgs/:orgId/invitations.
type CreateOrgRequest ¶ added in v1.9.0
type CreateOrgRequest struct {
AppID string `json:"app_id,omitempty" description:"Application ID (optional, uses default)"`
Name string `json:"name" description:"Organization name"`
Slug string `json:"slug" description:"URL-safe slug"`
Logo string `json:"logo,omitempty" description:"Organization logo URL"`
}
CreateOrgRequest binds the body for POST /orgs.
type CreateTeamRequest ¶
type CreateTeamRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
Name string `json:"name" description:"Team name"`
Slug string `json:"slug" description:"URL-safe team slug"`
}
CreateTeamRequest binds path + body for POST /orgs/:orgId/teams.
type DeclineInvitationRequest ¶ added in v0.0.7
type DeclineInvitationRequest struct {
Token string `json:"token" description:"Invitation token"`
}
DeclineInvitationRequest binds the body for POST /orgs/invitations/decline.
type DeleteOrgRequest ¶ added in v1.9.0
type DeleteOrgRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
}
DeleteOrgRequest binds the path for DELETE /orgs/:orgId.
type DeleteTeamRequest ¶ added in v0.0.7
type DeleteTeamRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
TeamID string `path:"teamId" description:"Team identifier"`
}
DeleteTeamRequest binds the path for DELETE /orgs/:orgId/teams/:teamId.
type GetInvitationRequest ¶ added in v1.10.0
type GetInvitationRequest struct {
Token string `path:"inviteToken" description:"Invitation token"`
}
GetInvitationRequest binds GET /orgs/invitations/:token.
type GetOrgRequest ¶ added in v1.9.0
type GetOrgRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
}
GetOrgRequest binds the path for GET /orgs/:orgId.
type GetTeamRequest ¶ added in v1.9.0
type GetTeamRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
TeamID string `path:"teamId" description:"Team identifier"`
}
GetTeamRequest binds the path for GET /orgs/:orgId/teams/:teamId.
type InvitationListResponse ¶ added in v1.9.0
type InvitationListResponse struct {
Invitations any `json:"invitations" description:"List of invitations"`
// NextCursor is set when more remain; pass it as cursor to continue.
NextCursor string `json:"next_cursor,omitempty" description:"Cursor for the next page, absent on the last"`
}
InvitationListResponse wraps a list of invitations.
type InvitationPreview ¶ added in v1.10.0
type InvitationPreview struct {
OrgID string `json:"org_id" description:"Organization the invitation is for"`
OrgName string `json:"org_name" description:"Organization's display name"`
OrgSlug string `json:"org_slug,omitempty" description:"Organization's slug"`
InviterName string `json:"inviter_name" description:"Who sent the invitation"`
Role string `json:"role" description:"Role the invitee will hold"`
EmailMasked string `json:"email_masked" description:"Partially masked invited address"`
ExpiresAt time.Time `json:"expires_at" description:"When the invitation stops working"`
}
InvitationPreview is what the person holding an invitation token is shown before they accept it.
Deliberately not organization.Invitation. That row has no organization name and no inviter name, which are the two things the page actually needs, and it carries the token and the raw invited address, which the page does not. The email is masked so the screen can say who the invitation is for without handing the address to whoever opened the link.
type ListInvitationsRequest ¶ added in v1.9.0
type ListInvitationsRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
Limit int `query:"limit" description:"Page size (default 50, max 200)" optional:"true"`
Cursor string `query:"cursor" description:"Cursor from the previous page's next_cursor" optional:"true"`
}
ListInvitationsRequest binds the path for GET /orgs/:orgId/invitations.
type ListMembersRequest ¶ added in v0.0.7
type ListMembersRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
Limit int `query:"limit" description:"Page size (default 50, max 200)" optional:"true"`
Cursor string `query:"cursor" description:"Cursor from the previous page's next_cursor" optional:"true"`
}
ListMembersRequest binds the path for GET /orgs/:orgId/members.
type ListOrgsRequest ¶ added in v1.9.0
type ListOrgsRequest struct {
Limit int `query:"limit" description:"Page size (default 50, max 200)" optional:"true"`
Cursor string `query:"cursor" description:"Cursor from the previous page's next_cursor" optional:"true"`
}
ListOrgsRequest is an empty request for GET /orgs (user from context).
type ListTeamsRequest ¶ added in v0.0.7
type ListTeamsRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
Limit int `query:"limit" description:"Page size (default 50, max 200)" optional:"true"`
Cursor string `query:"cursor" description:"Cursor from the previous page's next_cursor" optional:"true"`
}
ListTeamsRequest binds the path for GET /orgs/:orgId/teams.
type MemberListResponse ¶ added in v1.9.0
type MemberListResponse struct {
Members any `json:"members" description:"List of members"`
// NextCursor is set when more remain; pass it as cursor to continue.
NextCursor string `json:"next_cursor,omitempty" description:"Cursor for the next page, absent on the last"`
}
MemberListResponse wraps a list of members.
type OrgListResponse ¶ added in v1.9.0
type OrgListResponse struct {
Organizations any `json:"organizations" description:"List of organizations"`
// NextCursor is set when more remain; pass it as cursor to continue.
NextCursor string `json:"next_cursor,omitempty" description:"Cursor for the next page, absent on the last"`
}
OrgListResponse wraps a list of organizations.
type Plugin ¶
type Plugin struct {
// contains filtered or unexported fields
}
Plugin is the organization management plugin.
func New ¶ added in v1.9.0
New creates a new organization plugin with optional configuration. The store is resolved automatically from the engine during OnInit.
func (*Plugin) AcceptInvitation ¶ added in v1.9.0
AcceptInvitation accepts a pending invitation by token and adds whichever account owns the invited email (primary or secondary) as a member. It does not check who is asking, so call it only from code you trust. The HTTP endpoint uses AcceptInvitationAs instead.
func (*Plugin) AcceptInvitationAs ¶ added in v1.9.0
func (p *Plugin) AcceptInvitationAs(ctx context.Context, token string, userID id.UserID) (*organization.Member, error)
AcceptInvitationAs accepts a pending invitation on behalf of userID, the signed-in caller. It succeeds only when userID owns the invited email, primary or secondary. Anyone else gets store.ErrNotFound, exactly what an unknown token gets, and the invitation is left untouched.
func (*Plugin) AdminListOrganizations ¶ added in v1.9.0
func (p *Plugin) AdminListOrganizations(ctx context.Context, appID id.AppID) ([]*organization.Organization, error)
AdminListOrganizations returns all organizations for the given app.
func (*Plugin) CreateInvitation ¶ added in v1.9.0
func (p *Plugin) CreateInvitation(ctx context.Context, inv *organization.Invitation) error
CreateInvitation creates an organization invitation. An invitation with no ExpiresAt gets one from Config.InvitationTTL (seven days by default).
func (*Plugin) CreateOrganization ¶ added in v1.9.0
func (p *Plugin) CreateOrganization(ctx context.Context, o *organization.Organization) error
CreateOrganization creates a new organization and adds the creator as owner.
func (*Plugin) CreateTeam ¶ added in v1.9.0
CreateTeam creates a new team within an organization.
func (*Plugin) DeclineInvitation ¶ added in v1.9.0
DeclineInvitation declines a pending invitation by token. It does not check who is asking, so call it only from code you trust. The HTTP endpoint uses DeclineInvitationAs instead.
func (*Plugin) DeclineInvitationAs ¶ added in v1.11.0
DeclineInvitationAs declines an invitation on behalf of userID, the signed-in caller, under the same rule as AcceptInvitationAs: only the invitee may, and anyone else gets store.ErrNotFound with nothing changed.
func (*Plugin) DeleteOrganization ¶ added in v1.9.0
DeleteOrganization deletes an organization and all of its dependent records (members, teams, invitations) before emitting the AfterOrgDelete hook so other plugins (subscription, SCIM, …) can clean up their own org-scoped data.
The cascade is delegated to Store.DeleteOrganizationCascade, which each backend implements with its native transaction primitive (PgTx / SqliteTx / MongoTx) so a midway failure rolls back atomically. EmitAfterOrgDelete only fires once the cascade returns nil, so downstream plugins never see an event for an org that wasn't actually deleted.
Once the org is gone, every session its members hold that names it as the active org has that org cleared, the same as RemoveMember does for one member. A failure there is logged, not returned.
func (*Plugin) DeleteTeam ¶ added in v1.9.0
DeleteTeam deletes a team.
func (*Plugin) ExportUserData ¶ added in v1.9.0
func (p *Plugin) ExportUserData(ctx context.Context, userID id.UserID) (label string, data any, err error)
ExportUserData returns the user's organization data for GDPR export.
func (*Plugin) GetInvitation ¶ added in v1.11.0
func (p *Plugin) GetInvitation(ctx context.Context, orgID id.OrgID, invID id.InvitationID) (*organization.Invitation, error)
GetInvitation returns an invitation of orgID by ID. An invitation that belongs to another organization is store.ErrNotFound, the same as one that does not exist.
func (*Plugin) GetOrganization ¶ added in v1.9.0
func (p *Plugin) GetOrganization(ctx context.Context, orgID id.OrgID) (*organization.Organization, error)
GetOrganization returns an organization by ID.
func (*Plugin) IsOrgSlugAvailable ¶ added in v1.9.0
IsOrgSlugAvailable checks whether a slug is available for an app.
func (*Plugin) ListInvitations ¶ added in v1.9.0
func (p *Plugin) ListInvitations(ctx context.Context, orgID id.OrgID) ([]*organization.Invitation, error)
ListInvitations lists invitations for an organization.
func (*Plugin) ListInvitationsPage ¶ added in v1.9.0
func (p *Plugin) ListInvitationsPage(ctx context.Context, orgID id.OrgID, opts page.Opts) (page.Page[*organization.Invitation], error)
ListInvitationsPage is the bounded twin for the request path.
func (*Plugin) ListMembers ¶ added in v1.9.0
ListMembers returns all members of an organization.
func (*Plugin) ListMembersPage ¶ added in v1.9.0
func (p *Plugin) ListMembersPage(ctx context.Context, orgID id.OrgID, opts page.Opts) (page.Page[*organization.Member], error)
ListMembersPage is the bounded twin for the request path.
func (*Plugin) ListTeamsPage ¶ added in v1.9.0
func (p *Plugin) ListTeamsPage(ctx context.Context, orgID id.OrgID, opts page.Opts) (page.Page[*organization.Team], error)
ListTeamsPage is the bounded twin for the request path.
func (*Plugin) ListUserOrganizations ¶ added in v1.9.0
func (p *Plugin) ListUserOrganizations(ctx context.Context, userID id.UserID) ([]*organization.Organization, error)
ListUserOrganizations returns all organizations a user belongs to.
func (*Plugin) ListUserOrganizationsPage ¶ added in v1.9.0
func (p *Plugin) ListUserOrganizationsPage(ctx context.Context, userID id.UserID, opts page.Opts) (page.Page[*organization.Organization], error)
ListUserOrganizationsPage is the bounded twin for the request path.
func (*Plugin) OnInit ¶ added in v1.9.0
OnInit captures engine capabilities for use by the plugin's service layer.
func (*Plugin) RegisterContract ¶ added in v1.9.0
func (p *Plugin) RegisterContract( d *dispatcher.Dispatcher, reg contract.Registry, wreg contract.WardenRegistry, engine plugin.Engine, ) error
func (*Plugin) RegisterRoutes ¶
RegisterRoutes registers organization management routes on a forge.Router.
func (*Plugin) RemoveMember ¶ added in v1.9.0
RemoveMember removes a member from an organization.
func (*Plugin) ResendInvitation ¶ added in v1.11.0
func (p *Plugin) ResendInvitation(ctx context.Context, orgID id.OrgID, invID id.InvitationID) (*organization.Invitation, error)
ResendInvitation gives a pending or expired invitation a new token and a fresh Config.InvitationTTL, so the link already sent stops working. The returned invitation carries the new plaintext Token; it is the only copy, so whoever delivers the invitation must take it from here. Accepted, declined and revoked invitations return ErrInvitationNotActionable, and a second send inside ResendInvitationInterval returns ErrInvitationResendTooSoon.
func (*Plugin) RevokeInvitation ¶ added in v1.11.0
func (p *Plugin) RevokeInvitation(ctx context.Context, orgID id.OrgID, invID id.InvitationID) (*organization.Invitation, error)
RevokeInvitation withdraws a pending or expired invitation. The row stays, marked revoked, so the audit trail has something to point at; its token stops working. Anything else returns ErrInvitationNotActionable.
func (*Plugin) UpdateMemberRole ¶ added in v1.9.0
func (p *Plugin) UpdateMemberRole(ctx context.Context, memberID id.MemberID, role organization.MemberRole) (*organization.Member, error)
UpdateMemberRole updates a member's role within an organization.
func (*Plugin) UpdateOrganization ¶ added in v1.9.0
func (p *Plugin) UpdateOrganization(ctx context.Context, o *organization.Organization) error
UpdateOrganization updates an existing organization.
func (*Plugin) UpdateTeam ¶ added in v1.9.0
UpdateTeam updates an existing team.
type RemoveMemberRequest ¶ added in v0.0.7
type RemoveMemberRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
MemberID string `path:"memberId" description:"Member identifier"`
}
RemoveMemberRequest binds the path for DELETE /orgs/:orgId/members/:memberId.
type ResendInvitationRequest ¶ added in v1.11.0
type ResendInvitationRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
InvitationID string `path:"invitationId" description:"Invitation identifier"`
}
ResendInvitationRequest binds the path for POST /orgs/:orgId/invitations/:invitationId/resend.
type RevokeInvitationRequest ¶ added in v1.11.0
type RevokeInvitationRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
InvitationID string `path:"invitationId" description:"Invitation identifier"`
}
RevokeInvitationRequest binds the path for DELETE /orgs/:orgId/invitations/:invitationId.
type SlugAvailableResponse ¶ added in v1.9.0
type SlugAvailableResponse struct {
Available bool `json:"available" description:"Whether the slug is available"`
}
SlugAvailableResponse reports whether a slug is available.
type StatusResponse ¶
type StatusResponse struct {
Status string `json:"status" description:"Operation status"`
}
StatusResponse is a generic status response.
type TeamListResponse ¶ added in v1.9.0
type TeamListResponse struct {
Teams any `json:"teams" description:"List of teams"`
// NextCursor is set when more remain; pass it as cursor to continue.
NextCursor string `json:"next_cursor,omitempty" description:"Cursor for the next page, absent on the last"`
}
TeamListResponse wraps a list of teams.
type UpdateMemberRequest ¶
type UpdateMemberRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
MemberID string `path:"memberId" description:"Member identifier"`
Role string `json:"role" description:"New member role (owner, admin, member)"`
}
UpdateMemberRequest binds path + body for PATCH /orgs/:orgId/members/:memberId.
type UpdateOrgRequest ¶ added in v1.9.0
type UpdateOrgRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
Name *string `json:"name,omitempty" description:"Organization name"`
Logo *string `json:"logo,omitempty" description:"Organization logo URL"`
}
UpdateOrgRequest binds path + body for PATCH /orgs/:orgId.
type UpdateTeamRequest ¶
type UpdateTeamRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
TeamID string `path:"teamId" description:"Team identifier"`
Name *string `json:"name,omitempty" description:"Team name"`
Slug *string `json:"slug,omitempty" description:"URL-safe team slug"`
}
UpdateTeamRequest binds path + body for PATCH /orgs/:orgId/teams/:teamId.