coder/coderd/database/queries.sql.go

13236 lines
385 KiB
Go

// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.25.0
package database
import (
"context"
"database/sql"
"encoding/json"
"time"
"github.com/google/uuid"
"github.com/lib/pq"
"github.com/sqlc-dev/pqtype"
)
const activityBumpWorkspace = `-- name: ActivityBumpWorkspace :exec
WITH latest AS (
SELECT
workspace_builds.id::uuid AS build_id,
workspace_builds.deadline::timestamp with time zone AS build_deadline,
workspace_builds.max_deadline::timestamp with time zone AS build_max_deadline,
workspace_builds.transition AS build_transition,
provisioner_jobs.completed_at::timestamp with time zone AS job_completed_at,
templates.activity_bump AS activity_bump,
(
CASE
-- If the extension would push us over the next_autostart
-- interval, then extend the deadline by the full TTL (NOT
-- activity bump) from the autostart time. This will essentially
-- be as if the workspace auto started at the given time and the
-- original TTL was applied.
--
-- Sadly we can't define ` + "`" + `activity_bump_interval` + "`" + ` above since
-- it won't be available for this CASE statement, so we have to
-- copy the cast twice.
WHEN NOW() + (templates.activity_bump / 1000 / 1000 / 1000 || ' seconds')::interval > $1 :: timestamptz
-- If the autostart is behind now(), then the
-- autostart schedule is either the 0 time and not provided,
-- or it was the autostart in the past, which is no longer
-- relevant. If autostart is > 0 and in the past, then
-- that is a mistake by the caller.
AND $1 > NOW()
THEN
-- Extend to the autostart, then add the activity bump
(($1 :: timestamptz) - NOW()) + CASE
WHEN templates.allow_user_autostop
THEN (workspaces.ttl / 1000 / 1000 / 1000 || ' seconds')::interval
ELSE (templates.default_ttl / 1000 / 1000 / 1000 || ' seconds')::interval
END
-- Default to the activity bump duration.
ELSE
(templates.activity_bump / 1000 / 1000 / 1000 || ' seconds')::interval
END
) AS ttl_interval
FROM workspace_builds
JOIN provisioner_jobs
ON provisioner_jobs.id = workspace_builds.job_id
JOIN workspaces
ON workspaces.id = workspace_builds.workspace_id
JOIN templates
ON templates.id = workspaces.template_id
WHERE workspace_builds.workspace_id = $2::uuid
ORDER BY workspace_builds.build_number DESC
LIMIT 1
)
UPDATE
workspace_builds wb
SET
updated_at = NOW(),
deadline = CASE
WHEN l.build_max_deadline = '0001-01-01 00:00:00+00'
-- Never reduce the deadline from activity.
THEN GREATEST(wb.deadline, NOW() + l.ttl_interval)
ELSE LEAST(GREATEST(wb.deadline, NOW() + l.ttl_interval), l.build_max_deadline)
END
FROM latest l
WHERE wb.id = l.build_id
AND l.job_completed_at IS NOT NULL
AND l.activity_bump > 0
AND l.build_transition = 'start'
AND l.ttl_interval > '0 seconds'::interval
AND l.build_deadline != '0001-01-01 00:00:00+00'
AND l.build_deadline - (l.ttl_interval * 0.95) < NOW()
`
type ActivityBumpWorkspaceParams struct {
NextAutostart time.Time `db:"next_autostart" json:"next_autostart"`
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
}
// Bumps the workspace deadline by the template's configured "activity_bump"
// duration (default 1h). If the workspace bump will cross an autostart
// threshold, then the bump is autostart + TTL. This is the deadline behavior if
// the workspace was to autostart from a stopped state.
//
// Max deadline is respected, and the deadline will never be bumped past it.
// The deadline will never decrease.
// We only bump if the template has an activity bump duration set.
// We only bump if the raw interval is positive and non-zero.
// We only bump if workspace shutdown is manual.
// We only bump when 5% of the deadline has elapsed.
func (q *sqlQuerier) ActivityBumpWorkspace(ctx context.Context, arg ActivityBumpWorkspaceParams) error {
_, err := q.db.ExecContext(ctx, activityBumpWorkspace, arg.NextAutostart, arg.WorkspaceID)
return err
}
const deleteAPIKeyByID = `-- name: DeleteAPIKeyByID :exec
DELETE FROM
api_keys
WHERE
id = $1
`
func (q *sqlQuerier) DeleteAPIKeyByID(ctx context.Context, id string) error {
_, err := q.db.ExecContext(ctx, deleteAPIKeyByID, id)
return err
}
const deleteAPIKeysByUserID = `-- name: DeleteAPIKeysByUserID :exec
DELETE FROM
api_keys
WHERE
user_id = $1
`
func (q *sqlQuerier) DeleteAPIKeysByUserID(ctx context.Context, userID uuid.UUID) error {
_, err := q.db.ExecContext(ctx, deleteAPIKeysByUserID, userID)
return err
}
const deleteApplicationConnectAPIKeysByUserID = `-- name: DeleteApplicationConnectAPIKeysByUserID :exec
DELETE FROM
api_keys
WHERE
user_id = $1 AND
scope = 'application_connect'::api_key_scope
`
func (q *sqlQuerier) DeleteApplicationConnectAPIKeysByUserID(ctx context.Context, userID uuid.UUID) error {
_, err := q.db.ExecContext(ctx, deleteApplicationConnectAPIKeysByUserID, userID)
return err
}
const getAPIKeyByID = `-- name: GetAPIKeyByID :one
SELECT
id, hashed_secret, user_id, last_used, expires_at, created_at, updated_at, login_type, lifetime_seconds, ip_address, scope, token_name
FROM
api_keys
WHERE
id = $1
LIMIT
1
`
func (q *sqlQuerier) GetAPIKeyByID(ctx context.Context, id string) (APIKey, error) {
row := q.db.QueryRowContext(ctx, getAPIKeyByID, id)
var i APIKey
err := row.Scan(
&i.ID,
&i.HashedSecret,
&i.UserID,
&i.LastUsed,
&i.ExpiresAt,
&i.CreatedAt,
&i.UpdatedAt,
&i.LoginType,
&i.LifetimeSeconds,
&i.IPAddress,
&i.Scope,
&i.TokenName,
)
return i, err
}
const getAPIKeyByName = `-- name: GetAPIKeyByName :one
SELECT
id, hashed_secret, user_id, last_used, expires_at, created_at, updated_at, login_type, lifetime_seconds, ip_address, scope, token_name
FROM
api_keys
WHERE
user_id = $1 AND
token_name = $2 AND
token_name != ''
LIMIT
1
`
type GetAPIKeyByNameParams struct {
UserID uuid.UUID `db:"user_id" json:"user_id"`
TokenName string `db:"token_name" json:"token_name"`
}
// there is no unique constraint on empty token names
func (q *sqlQuerier) GetAPIKeyByName(ctx context.Context, arg GetAPIKeyByNameParams) (APIKey, error) {
row := q.db.QueryRowContext(ctx, getAPIKeyByName, arg.UserID, arg.TokenName)
var i APIKey
err := row.Scan(
&i.ID,
&i.HashedSecret,
&i.UserID,
&i.LastUsed,
&i.ExpiresAt,
&i.CreatedAt,
&i.UpdatedAt,
&i.LoginType,
&i.LifetimeSeconds,
&i.IPAddress,
&i.Scope,
&i.TokenName,
)
return i, err
}
const getAPIKeysByLoginType = `-- name: GetAPIKeysByLoginType :many
SELECT id, hashed_secret, user_id, last_used, expires_at, created_at, updated_at, login_type, lifetime_seconds, ip_address, scope, token_name FROM api_keys WHERE login_type = $1
`
func (q *sqlQuerier) GetAPIKeysByLoginType(ctx context.Context, loginType LoginType) ([]APIKey, error) {
rows, err := q.db.QueryContext(ctx, getAPIKeysByLoginType, loginType)
if err != nil {
return nil, err
}
defer rows.Close()
var items []APIKey
for rows.Next() {
var i APIKey
if err := rows.Scan(
&i.ID,
&i.HashedSecret,
&i.UserID,
&i.LastUsed,
&i.ExpiresAt,
&i.CreatedAt,
&i.UpdatedAt,
&i.LoginType,
&i.LifetimeSeconds,
&i.IPAddress,
&i.Scope,
&i.TokenName,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getAPIKeysByUserID = `-- name: GetAPIKeysByUserID :many
SELECT id, hashed_secret, user_id, last_used, expires_at, created_at, updated_at, login_type, lifetime_seconds, ip_address, scope, token_name FROM api_keys WHERE login_type = $1 AND user_id = $2
`
type GetAPIKeysByUserIDParams struct {
LoginType LoginType `db:"login_type" json:"login_type"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
}
func (q *sqlQuerier) GetAPIKeysByUserID(ctx context.Context, arg GetAPIKeysByUserIDParams) ([]APIKey, error) {
rows, err := q.db.QueryContext(ctx, getAPIKeysByUserID, arg.LoginType, arg.UserID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []APIKey
for rows.Next() {
var i APIKey
if err := rows.Scan(
&i.ID,
&i.HashedSecret,
&i.UserID,
&i.LastUsed,
&i.ExpiresAt,
&i.CreatedAt,
&i.UpdatedAt,
&i.LoginType,
&i.LifetimeSeconds,
&i.IPAddress,
&i.Scope,
&i.TokenName,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getAPIKeysLastUsedAfter = `-- name: GetAPIKeysLastUsedAfter :many
SELECT id, hashed_secret, user_id, last_used, expires_at, created_at, updated_at, login_type, lifetime_seconds, ip_address, scope, token_name FROM api_keys WHERE last_used > $1
`
func (q *sqlQuerier) GetAPIKeysLastUsedAfter(ctx context.Context, lastUsed time.Time) ([]APIKey, error) {
rows, err := q.db.QueryContext(ctx, getAPIKeysLastUsedAfter, lastUsed)
if err != nil {
return nil, err
}
defer rows.Close()
var items []APIKey
for rows.Next() {
var i APIKey
if err := rows.Scan(
&i.ID,
&i.HashedSecret,
&i.UserID,
&i.LastUsed,
&i.ExpiresAt,
&i.CreatedAt,
&i.UpdatedAt,
&i.LoginType,
&i.LifetimeSeconds,
&i.IPAddress,
&i.Scope,
&i.TokenName,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertAPIKey = `-- name: InsertAPIKey :one
INSERT INTO
api_keys (
id,
lifetime_seconds,
hashed_secret,
ip_address,
user_id,
last_used,
expires_at,
created_at,
updated_at,
login_type,
scope,
token_name
)
VALUES
($1,
-- If the lifetime is set to 0, default to 24hrs
CASE $2::bigint
WHEN 0 THEN 86400
ELSE $2::bigint
END
, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) RETURNING id, hashed_secret, user_id, last_used, expires_at, created_at, updated_at, login_type, lifetime_seconds, ip_address, scope, token_name
`
type InsertAPIKeyParams struct {
ID string `db:"id" json:"id"`
LifetimeSeconds int64 `db:"lifetime_seconds" json:"lifetime_seconds"`
HashedSecret []byte `db:"hashed_secret" json:"hashed_secret"`
IPAddress pqtype.Inet `db:"ip_address" json:"ip_address"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
LastUsed time.Time `db:"last_used" json:"last_used"`
ExpiresAt time.Time `db:"expires_at" json:"expires_at"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
LoginType LoginType `db:"login_type" json:"login_type"`
Scope APIKeyScope `db:"scope" json:"scope"`
TokenName string `db:"token_name" json:"token_name"`
}
func (q *sqlQuerier) InsertAPIKey(ctx context.Context, arg InsertAPIKeyParams) (APIKey, error) {
row := q.db.QueryRowContext(ctx, insertAPIKey,
arg.ID,
arg.LifetimeSeconds,
arg.HashedSecret,
arg.IPAddress,
arg.UserID,
arg.LastUsed,
arg.ExpiresAt,
arg.CreatedAt,
arg.UpdatedAt,
arg.LoginType,
arg.Scope,
arg.TokenName,
)
var i APIKey
err := row.Scan(
&i.ID,
&i.HashedSecret,
&i.UserID,
&i.LastUsed,
&i.ExpiresAt,
&i.CreatedAt,
&i.UpdatedAt,
&i.LoginType,
&i.LifetimeSeconds,
&i.IPAddress,
&i.Scope,
&i.TokenName,
)
return i, err
}
const updateAPIKeyByID = `-- name: UpdateAPIKeyByID :exec
UPDATE
api_keys
SET
last_used = $2,
expires_at = $3,
ip_address = $4
WHERE
id = $1
`
type UpdateAPIKeyByIDParams struct {
ID string `db:"id" json:"id"`
LastUsed time.Time `db:"last_used" json:"last_used"`
ExpiresAt time.Time `db:"expires_at" json:"expires_at"`
IPAddress pqtype.Inet `db:"ip_address" json:"ip_address"`
}
func (q *sqlQuerier) UpdateAPIKeyByID(ctx context.Context, arg UpdateAPIKeyByIDParams) error {
_, err := q.db.ExecContext(ctx, updateAPIKeyByID,
arg.ID,
arg.LastUsed,
arg.ExpiresAt,
arg.IPAddress,
)
return err
}
const getAuditLogsOffset = `-- name: GetAuditLogsOffset :many
SELECT
audit_logs.id, audit_logs.time, audit_logs.user_id, audit_logs.organization_id, audit_logs.ip, audit_logs.user_agent, audit_logs.resource_type, audit_logs.resource_id, audit_logs.resource_target, audit_logs.action, audit_logs.diff, audit_logs.status_code, audit_logs.additional_fields, audit_logs.request_id, audit_logs.resource_icon,
users.username AS user_username,
users.email AS user_email,
users.created_at AS user_created_at,
users.status AS user_status,
users.rbac_roles AS user_roles,
users.avatar_url AS user_avatar_url,
COUNT(audit_logs.*) OVER () AS count
FROM
audit_logs
LEFT JOIN users ON audit_logs.user_id = users.id
LEFT JOIN
-- First join on workspaces to get the initial workspace create
-- to workspace build 1 id. This is because the first create is
-- is a different audit log than subsequent starts.
workspaces ON
audit_logs.resource_type = 'workspace' AND
audit_logs.resource_id = workspaces.id
LEFT JOIN
workspace_builds ON
-- Get the reason from the build if the resource type
-- is a workspace_build
(
audit_logs.resource_type = 'workspace_build'
AND audit_logs.resource_id = workspace_builds.id
)
OR
-- Get the reason from the build #1 if this is the first
-- workspace create.
(
audit_logs.resource_type = 'workspace' AND
audit_logs.action = 'create' AND
workspaces.id = workspace_builds.workspace_id AND
workspace_builds.build_number = 1
)
WHERE
-- Filter resource_type
CASE
WHEN $3 :: text != '' THEN
resource_type = $3 :: resource_type
ELSE true
END
-- Filter resource_id
AND CASE
WHEN $4 :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN
resource_id = $4
ELSE true
END
-- Filter by resource_target
AND CASE
WHEN $5 :: text != '' THEN
resource_target = $5
ELSE true
END
-- Filter action
AND CASE
WHEN $6 :: text != '' THEN
action = $6 :: audit_action
ELSE true
END
-- Filter by user_id
AND CASE
WHEN $7 :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN
user_id = $7
ELSE true
END
-- Filter by username
AND CASE
WHEN $8 :: text != '' THEN
user_id = (SELECT id FROM users WHERE lower(username) = lower($8) AND deleted = false)
ELSE true
END
-- Filter by user_email
AND CASE
WHEN $9 :: text != '' THEN
users.email = $9
ELSE true
END
-- Filter by date_from
AND CASE
WHEN $10 :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN
"time" >= $10
ELSE true
END
-- Filter by date_to
AND CASE
WHEN $11 :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN
"time" <= $11
ELSE true
END
-- Filter by build_reason
AND CASE
WHEN $12::text != '' THEN
workspace_builds.reason::text = $12
ELSE true
END
ORDER BY
"time" DESC
LIMIT
$1
OFFSET
$2
`
type GetAuditLogsOffsetParams struct {
Limit int32 `db:"limit" json:"limit"`
Offset int32 `db:"offset" json:"offset"`
ResourceType string `db:"resource_type" json:"resource_type"`
ResourceID uuid.UUID `db:"resource_id" json:"resource_id"`
ResourceTarget string `db:"resource_target" json:"resource_target"`
Action string `db:"action" json:"action"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
Username string `db:"username" json:"username"`
Email string `db:"email" json:"email"`
DateFrom time.Time `db:"date_from" json:"date_from"`
DateTo time.Time `db:"date_to" json:"date_to"`
BuildReason string `db:"build_reason" json:"build_reason"`
}
type GetAuditLogsOffsetRow struct {
ID uuid.UUID `db:"id" json:"id"`
Time time.Time `db:"time" json:"time"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
Ip pqtype.Inet `db:"ip" json:"ip"`
UserAgent sql.NullString `db:"user_agent" json:"user_agent"`
ResourceType ResourceType `db:"resource_type" json:"resource_type"`
ResourceID uuid.UUID `db:"resource_id" json:"resource_id"`
ResourceTarget string `db:"resource_target" json:"resource_target"`
Action AuditAction `db:"action" json:"action"`
Diff json.RawMessage `db:"diff" json:"diff"`
StatusCode int32 `db:"status_code" json:"status_code"`
AdditionalFields json.RawMessage `db:"additional_fields" json:"additional_fields"`
RequestID uuid.UUID `db:"request_id" json:"request_id"`
ResourceIcon string `db:"resource_icon" json:"resource_icon"`
UserUsername sql.NullString `db:"user_username" json:"user_username"`
UserEmail sql.NullString `db:"user_email" json:"user_email"`
UserCreatedAt sql.NullTime `db:"user_created_at" json:"user_created_at"`
UserStatus NullUserStatus `db:"user_status" json:"user_status"`
UserRoles pq.StringArray `db:"user_roles" json:"user_roles"`
UserAvatarUrl sql.NullString `db:"user_avatar_url" json:"user_avatar_url"`
Count int64 `db:"count" json:"count"`
}
// GetAuditLogsBefore retrieves `row_limit` number of audit logs before the provided
// ID.
func (q *sqlQuerier) GetAuditLogsOffset(ctx context.Context, arg GetAuditLogsOffsetParams) ([]GetAuditLogsOffsetRow, error) {
rows, err := q.db.QueryContext(ctx, getAuditLogsOffset,
arg.Limit,
arg.Offset,
arg.ResourceType,
arg.ResourceID,
arg.ResourceTarget,
arg.Action,
arg.UserID,
arg.Username,
arg.Email,
arg.DateFrom,
arg.DateTo,
arg.BuildReason,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetAuditLogsOffsetRow
for rows.Next() {
var i GetAuditLogsOffsetRow
if err := rows.Scan(
&i.ID,
&i.Time,
&i.UserID,
&i.OrganizationID,
&i.Ip,
&i.UserAgent,
&i.ResourceType,
&i.ResourceID,
&i.ResourceTarget,
&i.Action,
&i.Diff,
&i.StatusCode,
&i.AdditionalFields,
&i.RequestID,
&i.ResourceIcon,
&i.UserUsername,
&i.UserEmail,
&i.UserCreatedAt,
&i.UserStatus,
&i.UserRoles,
&i.UserAvatarUrl,
&i.Count,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertAuditLog = `-- name: InsertAuditLog :one
INSERT INTO
audit_logs (
id,
"time",
user_id,
organization_id,
ip,
user_agent,
resource_type,
resource_id,
resource_target,
action,
diff,
status_code,
additional_fields,
request_id,
resource_icon
)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) RETURNING id, time, user_id, organization_id, ip, user_agent, resource_type, resource_id, resource_target, action, diff, status_code, additional_fields, request_id, resource_icon
`
type InsertAuditLogParams struct {
ID uuid.UUID `db:"id" json:"id"`
Time time.Time `db:"time" json:"time"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
Ip pqtype.Inet `db:"ip" json:"ip"`
UserAgent sql.NullString `db:"user_agent" json:"user_agent"`
ResourceType ResourceType `db:"resource_type" json:"resource_type"`
ResourceID uuid.UUID `db:"resource_id" json:"resource_id"`
ResourceTarget string `db:"resource_target" json:"resource_target"`
Action AuditAction `db:"action" json:"action"`
Diff json.RawMessage `db:"diff" json:"diff"`
StatusCode int32 `db:"status_code" json:"status_code"`
AdditionalFields json.RawMessage `db:"additional_fields" json:"additional_fields"`
RequestID uuid.UUID `db:"request_id" json:"request_id"`
ResourceIcon string `db:"resource_icon" json:"resource_icon"`
}
func (q *sqlQuerier) InsertAuditLog(ctx context.Context, arg InsertAuditLogParams) (AuditLog, error) {
row := q.db.QueryRowContext(ctx, insertAuditLog,
arg.ID,
arg.Time,
arg.UserID,
arg.OrganizationID,
arg.Ip,
arg.UserAgent,
arg.ResourceType,
arg.ResourceID,
arg.ResourceTarget,
arg.Action,
arg.Diff,
arg.StatusCode,
arg.AdditionalFields,
arg.RequestID,
arg.ResourceIcon,
)
var i AuditLog
err := row.Scan(
&i.ID,
&i.Time,
&i.UserID,
&i.OrganizationID,
&i.Ip,
&i.UserAgent,
&i.ResourceType,
&i.ResourceID,
&i.ResourceTarget,
&i.Action,
&i.Diff,
&i.StatusCode,
&i.AdditionalFields,
&i.RequestID,
&i.ResourceIcon,
)
return i, err
}
const getDBCryptKeys = `-- name: GetDBCryptKeys :many
SELECT number, active_key_digest, revoked_key_digest, created_at, revoked_at, test FROM dbcrypt_keys ORDER BY number ASC
`
func (q *sqlQuerier) GetDBCryptKeys(ctx context.Context) ([]DBCryptKey, error) {
rows, err := q.db.QueryContext(ctx, getDBCryptKeys)
if err != nil {
return nil, err
}
defer rows.Close()
var items []DBCryptKey
for rows.Next() {
var i DBCryptKey
if err := rows.Scan(
&i.Number,
&i.ActiveKeyDigest,
&i.RevokedKeyDigest,
&i.CreatedAt,
&i.RevokedAt,
&i.Test,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertDBCryptKey = `-- name: InsertDBCryptKey :exec
INSERT INTO dbcrypt_keys
(number, active_key_digest, created_at, test)
VALUES ($1::int, $2::text, CURRENT_TIMESTAMP, $3::text)
`
type InsertDBCryptKeyParams struct {
Number int32 `db:"number" json:"number"`
ActiveKeyDigest string `db:"active_key_digest" json:"active_key_digest"`
Test string `db:"test" json:"test"`
}
func (q *sqlQuerier) InsertDBCryptKey(ctx context.Context, arg InsertDBCryptKeyParams) error {
_, err := q.db.ExecContext(ctx, insertDBCryptKey, arg.Number, arg.ActiveKeyDigest, arg.Test)
return err
}
const revokeDBCryptKey = `-- name: RevokeDBCryptKey :exec
UPDATE dbcrypt_keys
SET
revoked_key_digest = active_key_digest,
active_key_digest = revoked_key_digest,
revoked_at = CURRENT_TIMESTAMP
WHERE
active_key_digest = $1::text
AND
revoked_key_digest IS NULL
`
func (q *sqlQuerier) RevokeDBCryptKey(ctx context.Context, activeKeyDigest string) error {
_, err := q.db.ExecContext(ctx, revokeDBCryptKey, activeKeyDigest)
return err
}
const deleteExternalAuthLink = `-- name: DeleteExternalAuthLink :exec
DELETE FROM external_auth_links WHERE provider_id = $1 AND user_id = $2
`
type DeleteExternalAuthLinkParams struct {
ProviderID string `db:"provider_id" json:"provider_id"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
}
func (q *sqlQuerier) DeleteExternalAuthLink(ctx context.Context, arg DeleteExternalAuthLinkParams) error {
_, err := q.db.ExecContext(ctx, deleteExternalAuthLink, arg.ProviderID, arg.UserID)
return err
}
const getExternalAuthLink = `-- name: GetExternalAuthLink :one
SELECT provider_id, user_id, created_at, updated_at, oauth_access_token, oauth_refresh_token, oauth_expiry, oauth_access_token_key_id, oauth_refresh_token_key_id, oauth_extra FROM external_auth_links WHERE provider_id = $1 AND user_id = $2
`
type GetExternalAuthLinkParams struct {
ProviderID string `db:"provider_id" json:"provider_id"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
}
func (q *sqlQuerier) GetExternalAuthLink(ctx context.Context, arg GetExternalAuthLinkParams) (ExternalAuthLink, error) {
row := q.db.QueryRowContext(ctx, getExternalAuthLink, arg.ProviderID, arg.UserID)
var i ExternalAuthLink
err := row.Scan(
&i.ProviderID,
&i.UserID,
&i.CreatedAt,
&i.UpdatedAt,
&i.OAuthAccessToken,
&i.OAuthRefreshToken,
&i.OAuthExpiry,
&i.OAuthAccessTokenKeyID,
&i.OAuthRefreshTokenKeyID,
&i.OAuthExtra,
)
return i, err
}
const getExternalAuthLinksByUserID = `-- name: GetExternalAuthLinksByUserID :many
SELECT provider_id, user_id, created_at, updated_at, oauth_access_token, oauth_refresh_token, oauth_expiry, oauth_access_token_key_id, oauth_refresh_token_key_id, oauth_extra FROM external_auth_links WHERE user_id = $1
`
func (q *sqlQuerier) GetExternalAuthLinksByUserID(ctx context.Context, userID uuid.UUID) ([]ExternalAuthLink, error) {
rows, err := q.db.QueryContext(ctx, getExternalAuthLinksByUserID, userID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ExternalAuthLink
for rows.Next() {
var i ExternalAuthLink
if err := rows.Scan(
&i.ProviderID,
&i.UserID,
&i.CreatedAt,
&i.UpdatedAt,
&i.OAuthAccessToken,
&i.OAuthRefreshToken,
&i.OAuthExpiry,
&i.OAuthAccessTokenKeyID,
&i.OAuthRefreshTokenKeyID,
&i.OAuthExtra,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertExternalAuthLink = `-- name: InsertExternalAuthLink :one
INSERT INTO external_auth_links (
provider_id,
user_id,
created_at,
updated_at,
oauth_access_token,
oauth_access_token_key_id,
oauth_refresh_token,
oauth_refresh_token_key_id,
oauth_expiry,
oauth_extra
) VALUES (
$1,
$2,
$3,
$4,
$5,
$6,
$7,
$8,
$9,
$10
) RETURNING provider_id, user_id, created_at, updated_at, oauth_access_token, oauth_refresh_token, oauth_expiry, oauth_access_token_key_id, oauth_refresh_token_key_id, oauth_extra
`
type InsertExternalAuthLinkParams struct {
ProviderID string `db:"provider_id" json:"provider_id"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
OAuthAccessToken string `db:"oauth_access_token" json:"oauth_access_token"`
OAuthAccessTokenKeyID sql.NullString `db:"oauth_access_token_key_id" json:"oauth_access_token_key_id"`
OAuthRefreshToken string `db:"oauth_refresh_token" json:"oauth_refresh_token"`
OAuthRefreshTokenKeyID sql.NullString `db:"oauth_refresh_token_key_id" json:"oauth_refresh_token_key_id"`
OAuthExpiry time.Time `db:"oauth_expiry" json:"oauth_expiry"`
OAuthExtra pqtype.NullRawMessage `db:"oauth_extra" json:"oauth_extra"`
}
func (q *sqlQuerier) InsertExternalAuthLink(ctx context.Context, arg InsertExternalAuthLinkParams) (ExternalAuthLink, error) {
row := q.db.QueryRowContext(ctx, insertExternalAuthLink,
arg.ProviderID,
arg.UserID,
arg.CreatedAt,
arg.UpdatedAt,
arg.OAuthAccessToken,
arg.OAuthAccessTokenKeyID,
arg.OAuthRefreshToken,
arg.OAuthRefreshTokenKeyID,
arg.OAuthExpiry,
arg.OAuthExtra,
)
var i ExternalAuthLink
err := row.Scan(
&i.ProviderID,
&i.UserID,
&i.CreatedAt,
&i.UpdatedAt,
&i.OAuthAccessToken,
&i.OAuthRefreshToken,
&i.OAuthExpiry,
&i.OAuthAccessTokenKeyID,
&i.OAuthRefreshTokenKeyID,
&i.OAuthExtra,
)
return i, err
}
const updateExternalAuthLink = `-- name: UpdateExternalAuthLink :one
UPDATE external_auth_links SET
updated_at = $3,
oauth_access_token = $4,
oauth_access_token_key_id = $5,
oauth_refresh_token = $6,
oauth_refresh_token_key_id = $7,
oauth_expiry = $8,
oauth_extra = $9
WHERE provider_id = $1 AND user_id = $2 RETURNING provider_id, user_id, created_at, updated_at, oauth_access_token, oauth_refresh_token, oauth_expiry, oauth_access_token_key_id, oauth_refresh_token_key_id, oauth_extra
`
type UpdateExternalAuthLinkParams struct {
ProviderID string `db:"provider_id" json:"provider_id"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
OAuthAccessToken string `db:"oauth_access_token" json:"oauth_access_token"`
OAuthAccessTokenKeyID sql.NullString `db:"oauth_access_token_key_id" json:"oauth_access_token_key_id"`
OAuthRefreshToken string `db:"oauth_refresh_token" json:"oauth_refresh_token"`
OAuthRefreshTokenKeyID sql.NullString `db:"oauth_refresh_token_key_id" json:"oauth_refresh_token_key_id"`
OAuthExpiry time.Time `db:"oauth_expiry" json:"oauth_expiry"`
OAuthExtra pqtype.NullRawMessage `db:"oauth_extra" json:"oauth_extra"`
}
func (q *sqlQuerier) UpdateExternalAuthLink(ctx context.Context, arg UpdateExternalAuthLinkParams) (ExternalAuthLink, error) {
row := q.db.QueryRowContext(ctx, updateExternalAuthLink,
arg.ProviderID,
arg.UserID,
arg.UpdatedAt,
arg.OAuthAccessToken,
arg.OAuthAccessTokenKeyID,
arg.OAuthRefreshToken,
arg.OAuthRefreshTokenKeyID,
arg.OAuthExpiry,
arg.OAuthExtra,
)
var i ExternalAuthLink
err := row.Scan(
&i.ProviderID,
&i.UserID,
&i.CreatedAt,
&i.UpdatedAt,
&i.OAuthAccessToken,
&i.OAuthRefreshToken,
&i.OAuthExpiry,
&i.OAuthAccessTokenKeyID,
&i.OAuthRefreshTokenKeyID,
&i.OAuthExtra,
)
return i, err
}
const getFileByHashAndCreator = `-- name: GetFileByHashAndCreator :one
SELECT
hash, created_at, created_by, mimetype, data, id
FROM
files
WHERE
hash = $1
AND
created_by = $2
LIMIT
1
`
type GetFileByHashAndCreatorParams struct {
Hash string `db:"hash" json:"hash"`
CreatedBy uuid.UUID `db:"created_by" json:"created_by"`
}
func (q *sqlQuerier) GetFileByHashAndCreator(ctx context.Context, arg GetFileByHashAndCreatorParams) (File, error) {
row := q.db.QueryRowContext(ctx, getFileByHashAndCreator, arg.Hash, arg.CreatedBy)
var i File
err := row.Scan(
&i.Hash,
&i.CreatedAt,
&i.CreatedBy,
&i.Mimetype,
&i.Data,
&i.ID,
)
return i, err
}
const getFileByID = `-- name: GetFileByID :one
SELECT
hash, created_at, created_by, mimetype, data, id
FROM
files
WHERE
id = $1
LIMIT
1
`
func (q *sqlQuerier) GetFileByID(ctx context.Context, id uuid.UUID) (File, error) {
row := q.db.QueryRowContext(ctx, getFileByID, id)
var i File
err := row.Scan(
&i.Hash,
&i.CreatedAt,
&i.CreatedBy,
&i.Mimetype,
&i.Data,
&i.ID,
)
return i, err
}
const getFileTemplates = `-- name: GetFileTemplates :many
SELECT
files.id AS file_id,
files.created_by AS file_created_by,
templates.id AS template_id,
templates.organization_id AS template_organization_id,
templates.created_by AS template_created_by,
templates.user_acl,
templates.group_acl
FROM
templates
INNER JOIN
template_versions
ON templates.id = template_versions.template_id
INNER JOIN
provisioner_jobs
ON job_id = provisioner_jobs.id
INNER JOIN
files
ON files.id = provisioner_jobs.file_id
WHERE
-- Only fetch template version associated files.
storage_method = 'file'
AND provisioner_jobs.type = 'template_version_import'
AND file_id = $1
`
type GetFileTemplatesRow struct {
FileID uuid.UUID `db:"file_id" json:"file_id"`
FileCreatedBy uuid.UUID `db:"file_created_by" json:"file_created_by"`
TemplateID uuid.UUID `db:"template_id" json:"template_id"`
TemplateOrganizationID uuid.UUID `db:"template_organization_id" json:"template_organization_id"`
TemplateCreatedBy uuid.UUID `db:"template_created_by" json:"template_created_by"`
UserACL TemplateACL `db:"user_acl" json:"user_acl"`
GroupACL TemplateACL `db:"group_acl" json:"group_acl"`
}
// Get all templates that use a file.
func (q *sqlQuerier) GetFileTemplates(ctx context.Context, fileID uuid.UUID) ([]GetFileTemplatesRow, error) {
rows, err := q.db.QueryContext(ctx, getFileTemplates, fileID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetFileTemplatesRow
for rows.Next() {
var i GetFileTemplatesRow
if err := rows.Scan(
&i.FileID,
&i.FileCreatedBy,
&i.TemplateID,
&i.TemplateOrganizationID,
&i.TemplateCreatedBy,
&i.UserACL,
&i.GroupACL,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertFile = `-- name: InsertFile :one
INSERT INTO
files (id, hash, created_at, created_by, mimetype, "data")
VALUES
($1, $2, $3, $4, $5, $6) RETURNING hash, created_at, created_by, mimetype, data, id
`
type InsertFileParams struct {
ID uuid.UUID `db:"id" json:"id"`
Hash string `db:"hash" json:"hash"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
CreatedBy uuid.UUID `db:"created_by" json:"created_by"`
Mimetype string `db:"mimetype" json:"mimetype"`
Data []byte `db:"data" json:"data"`
}
func (q *sqlQuerier) InsertFile(ctx context.Context, arg InsertFileParams) (File, error) {
row := q.db.QueryRowContext(ctx, insertFile,
arg.ID,
arg.Hash,
arg.CreatedAt,
arg.CreatedBy,
arg.Mimetype,
arg.Data,
)
var i File
err := row.Scan(
&i.Hash,
&i.CreatedAt,
&i.CreatedBy,
&i.Mimetype,
&i.Data,
&i.ID,
)
return i, err
}
const deleteGitSSHKey = `-- name: DeleteGitSSHKey :exec
DELETE FROM
gitsshkeys
WHERE
user_id = $1
`
func (q *sqlQuerier) DeleteGitSSHKey(ctx context.Context, userID uuid.UUID) error {
_, err := q.db.ExecContext(ctx, deleteGitSSHKey, userID)
return err
}
const getGitSSHKey = `-- name: GetGitSSHKey :one
SELECT
user_id, created_at, updated_at, private_key, public_key
FROM
gitsshkeys
WHERE
user_id = $1
`
func (q *sqlQuerier) GetGitSSHKey(ctx context.Context, userID uuid.UUID) (GitSSHKey, error) {
row := q.db.QueryRowContext(ctx, getGitSSHKey, userID)
var i GitSSHKey
err := row.Scan(
&i.UserID,
&i.CreatedAt,
&i.UpdatedAt,
&i.PrivateKey,
&i.PublicKey,
)
return i, err
}
const insertGitSSHKey = `-- name: InsertGitSSHKey :one
INSERT INTO
gitsshkeys (
user_id,
created_at,
updated_at,
private_key,
public_key
)
VALUES
($1, $2, $3, $4, $5) RETURNING user_id, created_at, updated_at, private_key, public_key
`
type InsertGitSSHKeyParams struct {
UserID uuid.UUID `db:"user_id" json:"user_id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
PrivateKey string `db:"private_key" json:"private_key"`
PublicKey string `db:"public_key" json:"public_key"`
}
func (q *sqlQuerier) InsertGitSSHKey(ctx context.Context, arg InsertGitSSHKeyParams) (GitSSHKey, error) {
row := q.db.QueryRowContext(ctx, insertGitSSHKey,
arg.UserID,
arg.CreatedAt,
arg.UpdatedAt,
arg.PrivateKey,
arg.PublicKey,
)
var i GitSSHKey
err := row.Scan(
&i.UserID,
&i.CreatedAt,
&i.UpdatedAt,
&i.PrivateKey,
&i.PublicKey,
)
return i, err
}
const updateGitSSHKey = `-- name: UpdateGitSSHKey :one
UPDATE
gitsshkeys
SET
updated_at = $2,
private_key = $3,
public_key = $4
WHERE
user_id = $1
RETURNING
user_id, created_at, updated_at, private_key, public_key
`
type UpdateGitSSHKeyParams struct {
UserID uuid.UUID `db:"user_id" json:"user_id"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
PrivateKey string `db:"private_key" json:"private_key"`
PublicKey string `db:"public_key" json:"public_key"`
}
func (q *sqlQuerier) UpdateGitSSHKey(ctx context.Context, arg UpdateGitSSHKeyParams) (GitSSHKey, error) {
row := q.db.QueryRowContext(ctx, updateGitSSHKey,
arg.UserID,
arg.UpdatedAt,
arg.PrivateKey,
arg.PublicKey,
)
var i GitSSHKey
err := row.Scan(
&i.UserID,
&i.CreatedAt,
&i.UpdatedAt,
&i.PrivateKey,
&i.PublicKey,
)
return i, err
}
const deleteGroupMemberFromGroup = `-- name: DeleteGroupMemberFromGroup :exec
DELETE FROM
group_members
WHERE
user_id = $1 AND
group_id = $2
`
type DeleteGroupMemberFromGroupParams struct {
UserID uuid.UUID `db:"user_id" json:"user_id"`
GroupID uuid.UUID `db:"group_id" json:"group_id"`
}
func (q *sqlQuerier) DeleteGroupMemberFromGroup(ctx context.Context, arg DeleteGroupMemberFromGroupParams) error {
_, err := q.db.ExecContext(ctx, deleteGroupMemberFromGroup, arg.UserID, arg.GroupID)
return err
}
const getGroupMembers = `-- name: GetGroupMembers :many
SELECT
users.id, users.email, users.username, users.hashed_password, users.created_at, users.updated_at, users.status, users.rbac_roles, users.login_type, users.avatar_url, users.deleted, users.last_seen_at, users.quiet_hours_schedule, users.theme_preference, users.name
FROM
users
LEFT JOIN
group_members
ON
group_members.user_id = users.id AND
group_members.group_id = $1
LEFT JOIN
organization_members
ON
organization_members.user_id = users.id AND
organization_members.organization_id = $1
WHERE
-- In either case, the group_id will only match an org or a group.
(group_members.group_id = $1
OR
organization_members.organization_id = $1)
AND
users.deleted = 'false'
`
// If the group is a user made group, then we need to check the group_members table.
// If it is the "Everyone" group, then we need to check the organization_members table.
func (q *sqlQuerier) GetGroupMembers(ctx context.Context, groupID uuid.UUID) ([]User, error) {
rows, err := q.db.QueryContext(ctx, getGroupMembers, groupID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []User
for rows.Next() {
var i User
if err := rows.Scan(
&i.ID,
&i.Email,
&i.Username,
&i.HashedPassword,
&i.CreatedAt,
&i.UpdatedAt,
&i.Status,
&i.RBACRoles,
&i.LoginType,
&i.AvatarURL,
&i.Deleted,
&i.LastSeenAt,
&i.QuietHoursSchedule,
&i.ThemePreference,
&i.Name,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertGroupMember = `-- name: InsertGroupMember :exec
INSERT INTO
group_members (user_id, group_id)
VALUES
($1, $2)
`
type InsertGroupMemberParams struct {
UserID uuid.UUID `db:"user_id" json:"user_id"`
GroupID uuid.UUID `db:"group_id" json:"group_id"`
}
func (q *sqlQuerier) InsertGroupMember(ctx context.Context, arg InsertGroupMemberParams) error {
_, err := q.db.ExecContext(ctx, insertGroupMember, arg.UserID, arg.GroupID)
return err
}
const insertUserGroupsByName = `-- name: InsertUserGroupsByName :exec
WITH groups AS (
SELECT
id
FROM
groups
WHERE
groups.organization_id = $2 AND
groups.name = ANY($3 :: text [])
)
INSERT INTO
group_members (user_id, group_id)
SELECT
$1,
groups.id
FROM
groups
`
type InsertUserGroupsByNameParams struct {
UserID uuid.UUID `db:"user_id" json:"user_id"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
GroupNames []string `db:"group_names" json:"group_names"`
}
// InsertUserGroupsByName adds a user to all provided groups, if they exist.
func (q *sqlQuerier) InsertUserGroupsByName(ctx context.Context, arg InsertUserGroupsByNameParams) error {
_, err := q.db.ExecContext(ctx, insertUserGroupsByName, arg.UserID, arg.OrganizationID, pq.Array(arg.GroupNames))
return err
}
const removeUserFromAllGroups = `-- name: RemoveUserFromAllGroups :exec
DELETE FROM
group_members
WHERE
user_id = $1
`
func (q *sqlQuerier) RemoveUserFromAllGroups(ctx context.Context, userID uuid.UUID) error {
_, err := q.db.ExecContext(ctx, removeUserFromAllGroups, userID)
return err
}
const deleteGroupByID = `-- name: DeleteGroupByID :exec
DELETE FROM
groups
WHERE
id = $1
`
func (q *sqlQuerier) DeleteGroupByID(ctx context.Context, id uuid.UUID) error {
_, err := q.db.ExecContext(ctx, deleteGroupByID, id)
return err
}
const getGroupByID = `-- name: GetGroupByID :one
SELECT
id, name, organization_id, avatar_url, quota_allowance, display_name, source
FROM
groups
WHERE
id = $1
LIMIT
1
`
func (q *sqlQuerier) GetGroupByID(ctx context.Context, id uuid.UUID) (Group, error) {
row := q.db.QueryRowContext(ctx, getGroupByID, id)
var i Group
err := row.Scan(
&i.ID,
&i.Name,
&i.OrganizationID,
&i.AvatarURL,
&i.QuotaAllowance,
&i.DisplayName,
&i.Source,
)
return i, err
}
const getGroupByOrgAndName = `-- name: GetGroupByOrgAndName :one
SELECT
id, name, organization_id, avatar_url, quota_allowance, display_name, source
FROM
groups
WHERE
organization_id = $1
AND
name = $2
LIMIT
1
`
type GetGroupByOrgAndNameParams struct {
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
Name string `db:"name" json:"name"`
}
func (q *sqlQuerier) GetGroupByOrgAndName(ctx context.Context, arg GetGroupByOrgAndNameParams) (Group, error) {
row := q.db.QueryRowContext(ctx, getGroupByOrgAndName, arg.OrganizationID, arg.Name)
var i Group
err := row.Scan(
&i.ID,
&i.Name,
&i.OrganizationID,
&i.AvatarURL,
&i.QuotaAllowance,
&i.DisplayName,
&i.Source,
)
return i, err
}
const getGroupsByOrganizationAndUserID = `-- name: GetGroupsByOrganizationAndUserID :many
SELECT
groups.id, groups.name, groups.organization_id, groups.avatar_url, groups.quota_allowance, groups.display_name, groups.source
FROM
groups
-- If the group is a user made group, then we need to check the group_members table.
LEFT JOIN
group_members
ON
group_members.group_id = groups.id AND
group_members.user_id = $1
-- If it is the "Everyone" group, then we need to check the organization_members table.
LEFT JOIN
organization_members
ON
organization_members.organization_id = groups.id AND
organization_members.user_id = $1
WHERE
-- In either case, the group_id will only match an org or a group.
(group_members.user_id = $1 OR organization_members.user_id = $1)
AND
-- Ensure the group or organization is the specified organization.
groups.organization_id = $2
`
type GetGroupsByOrganizationAndUserIDParams struct {
UserID uuid.UUID `db:"user_id" json:"user_id"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
}
func (q *sqlQuerier) GetGroupsByOrganizationAndUserID(ctx context.Context, arg GetGroupsByOrganizationAndUserIDParams) ([]Group, error) {
rows, err := q.db.QueryContext(ctx, getGroupsByOrganizationAndUserID, arg.UserID, arg.OrganizationID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Group
for rows.Next() {
var i Group
if err := rows.Scan(
&i.ID,
&i.Name,
&i.OrganizationID,
&i.AvatarURL,
&i.QuotaAllowance,
&i.DisplayName,
&i.Source,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getGroupsByOrganizationID = `-- name: GetGroupsByOrganizationID :many
SELECT
id, name, organization_id, avatar_url, quota_allowance, display_name, source
FROM
groups
WHERE
organization_id = $1
`
func (q *sqlQuerier) GetGroupsByOrganizationID(ctx context.Context, organizationID uuid.UUID) ([]Group, error) {
rows, err := q.db.QueryContext(ctx, getGroupsByOrganizationID, organizationID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Group
for rows.Next() {
var i Group
if err := rows.Scan(
&i.ID,
&i.Name,
&i.OrganizationID,
&i.AvatarURL,
&i.QuotaAllowance,
&i.DisplayName,
&i.Source,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertAllUsersGroup = `-- name: InsertAllUsersGroup :one
INSERT INTO groups (
id,
name,
organization_id
)
VALUES
($1, 'Everyone', $1) RETURNING id, name, organization_id, avatar_url, quota_allowance, display_name, source
`
// We use the organization_id as the id
// for simplicity since all users is
// every member of the org.
func (q *sqlQuerier) InsertAllUsersGroup(ctx context.Context, organizationID uuid.UUID) (Group, error) {
row := q.db.QueryRowContext(ctx, insertAllUsersGroup, organizationID)
var i Group
err := row.Scan(
&i.ID,
&i.Name,
&i.OrganizationID,
&i.AvatarURL,
&i.QuotaAllowance,
&i.DisplayName,
&i.Source,
)
return i, err
}
const insertGroup = `-- name: InsertGroup :one
INSERT INTO groups (
id,
name,
display_name,
organization_id,
avatar_url,
quota_allowance
)
VALUES
($1, $2, $3, $4, $5, $6) RETURNING id, name, organization_id, avatar_url, quota_allowance, display_name, source
`
type InsertGroupParams struct {
ID uuid.UUID `db:"id" json:"id"`
Name string `db:"name" json:"name"`
DisplayName string `db:"display_name" json:"display_name"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
AvatarURL string `db:"avatar_url" json:"avatar_url"`
QuotaAllowance int32 `db:"quota_allowance" json:"quota_allowance"`
}
func (q *sqlQuerier) InsertGroup(ctx context.Context, arg InsertGroupParams) (Group, error) {
row := q.db.QueryRowContext(ctx, insertGroup,
arg.ID,
arg.Name,
arg.DisplayName,
arg.OrganizationID,
arg.AvatarURL,
arg.QuotaAllowance,
)
var i Group
err := row.Scan(
&i.ID,
&i.Name,
&i.OrganizationID,
&i.AvatarURL,
&i.QuotaAllowance,
&i.DisplayName,
&i.Source,
)
return i, err
}
const insertMissingGroups = `-- name: InsertMissingGroups :many
INSERT INTO groups (
id,
name,
organization_id,
source
)
SELECT
gen_random_uuid(),
group_name,
$1,
$2
FROM
UNNEST($3 :: text[]) AS group_name
ON CONFLICT DO NOTHING
RETURNING id, name, organization_id, avatar_url, quota_allowance, display_name, source
`
type InsertMissingGroupsParams struct {
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
Source GroupSource `db:"source" json:"source"`
GroupNames []string `db:"group_names" json:"group_names"`
}
// Inserts any group by name that does not exist. All new groups are given
// a random uuid, are inserted into the same organization. They have the default
// values for avatar, display name, and quota allowance (all zero values).
// If the name conflicts, do nothing.
func (q *sqlQuerier) InsertMissingGroups(ctx context.Context, arg InsertMissingGroupsParams) ([]Group, error) {
rows, err := q.db.QueryContext(ctx, insertMissingGroups, arg.OrganizationID, arg.Source, pq.Array(arg.GroupNames))
if err != nil {
return nil, err
}
defer rows.Close()
var items []Group
for rows.Next() {
var i Group
if err := rows.Scan(
&i.ID,
&i.Name,
&i.OrganizationID,
&i.AvatarURL,
&i.QuotaAllowance,
&i.DisplayName,
&i.Source,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const updateGroupByID = `-- name: UpdateGroupByID :one
UPDATE
groups
SET
name = $1,
display_name = $2,
avatar_url = $3,
quota_allowance = $4
WHERE
id = $5
RETURNING id, name, organization_id, avatar_url, quota_allowance, display_name, source
`
type UpdateGroupByIDParams struct {
Name string `db:"name" json:"name"`
DisplayName string `db:"display_name" json:"display_name"`
AvatarURL string `db:"avatar_url" json:"avatar_url"`
QuotaAllowance int32 `db:"quota_allowance" json:"quota_allowance"`
ID uuid.UUID `db:"id" json:"id"`
}
func (q *sqlQuerier) UpdateGroupByID(ctx context.Context, arg UpdateGroupByIDParams) (Group, error) {
row := q.db.QueryRowContext(ctx, updateGroupByID,
arg.Name,
arg.DisplayName,
arg.AvatarURL,
arg.QuotaAllowance,
arg.ID,
)
var i Group
err := row.Scan(
&i.ID,
&i.Name,
&i.OrganizationID,
&i.AvatarURL,
&i.QuotaAllowance,
&i.DisplayName,
&i.Source,
)
return i, err
}
const getTemplateAppInsights = `-- name: GetTemplateAppInsights :many
WITH
-- Create a list of all unique apps by template, this is used to
-- filter out irrelevant template usage stats.
apps AS (
SELECT DISTINCT ON (ws.template_id, app.slug)
ws.template_id,
app.slug,
app.display_name,
app.icon
FROM
workspaces ws
JOIN
workspace_builds AS build
ON
build.workspace_id = ws.id
JOIN
workspace_resources AS resource
ON
resource.job_id = build.job_id
JOIN
workspace_agents AS agent
ON
agent.resource_id = resource.id
JOIN
workspace_apps AS app
ON
app.agent_id = agent.id
WHERE
-- Partial query parameter filter.
CASE WHEN COALESCE(array_length($1::uuid[], 1), 0) > 0 THEN ws.template_id = ANY($1::uuid[]) ELSE TRUE END
ORDER BY
ws.template_id, app.slug, app.created_at DESC
),
-- Join apps and template usage stats to filter out irrelevant rows.
-- Note that this way of joining will eliminate all data-points that
-- aren't for "real" apps. That means ports are ignored (even though
-- they're part of the dataset), as well as are "[terminal]" entries
-- which are alternate datapoints for reconnecting pty usage.
template_usage_stats_with_apps AS (
SELECT
tus.start_time,
tus.template_id,
tus.user_id,
apps.slug,
apps.display_name,
apps.icon,
tus.app_usage_mins
FROM
apps
JOIN
template_usage_stats AS tus
ON
-- Query parameter filter.
tus.start_time >= $2::timestamptz
AND tus.end_time <= $3::timestamptz
AND CASE WHEN COALESCE(array_length($1::uuid[], 1), 0) > 0 THEN tus.template_id = ANY($1::uuid[]) ELSE TRUE END
-- Primary join condition.
AND tus.template_id = apps.template_id
AND tus.app_usage_mins ? apps.slug -- Key exists in object.
),
-- Group the app insights by interval, user and unique app. This
-- allows us to deduplicate a user using the same app across
-- multiple templates.
app_insights AS (
SELECT
user_id,
slug,
display_name,
icon,
-- See motivation in GetTemplateInsights for LEAST(SUM(n), 30).
LEAST(SUM(app_usage.value::smallint), 30) AS usage_mins
FROM
template_usage_stats_with_apps, jsonb_each(app_usage_mins) AS app_usage
WHERE
app_usage.key = slug
GROUP BY
start_time, user_id, slug, display_name, icon
),
-- Even though we allow identical apps to be aggregated across
-- templates, we still want to be able to report which templates
-- the data comes from.
templates AS (
SELECT
slug,
display_name,
icon,
array_agg(DISTINCT template_id)::uuid[] AS template_ids
FROM
template_usage_stats_with_apps
GROUP BY
slug, display_name, icon
)
SELECT
t.template_ids,
COUNT(DISTINCT ai.user_id) AS active_users,
ai.slug,
ai.display_name,
ai.icon,
(SUM(ai.usage_mins) * 60)::bigint AS usage_seconds
FROM
app_insights AS ai
JOIN
templates AS t
ON
t.slug = ai.slug
AND t.display_name = ai.display_name
AND t.icon = ai.icon
GROUP BY
t.template_ids, ai.slug, ai.display_name, ai.icon
`
type GetTemplateAppInsightsParams struct {
TemplateIDs []uuid.UUID `db:"template_ids" json:"template_ids"`
StartTime time.Time `db:"start_time" json:"start_time"`
EndTime time.Time `db:"end_time" json:"end_time"`
}
type GetTemplateAppInsightsRow struct {
TemplateIDs []uuid.UUID `db:"template_ids" json:"template_ids"`
ActiveUsers int64 `db:"active_users" json:"active_users"`
Slug string `db:"slug" json:"slug"`
DisplayName string `db:"display_name" json:"display_name"`
Icon string `db:"icon" json:"icon"`
UsageSeconds int64 `db:"usage_seconds" json:"usage_seconds"`
}
// GetTemplateAppInsights returns the aggregate usage of each app in a given
// timeframe. The result can be filtered on template_ids, meaning only user data
// from workspaces based on those templates will be included.
func (q *sqlQuerier) GetTemplateAppInsights(ctx context.Context, arg GetTemplateAppInsightsParams) ([]GetTemplateAppInsightsRow, error) {
rows, err := q.db.QueryContext(ctx, getTemplateAppInsights, pq.Array(arg.TemplateIDs), arg.StartTime, arg.EndTime)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetTemplateAppInsightsRow
for rows.Next() {
var i GetTemplateAppInsightsRow
if err := rows.Scan(
pq.Array(&i.TemplateIDs),
&i.ActiveUsers,
&i.Slug,
&i.DisplayName,
&i.Icon,
&i.UsageSeconds,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getTemplateAppInsightsByTemplate = `-- name: GetTemplateAppInsightsByTemplate :many
WITH
-- This CTE is used to explode app usage into minute buckets, then
-- flatten the users app usage within the template so that usage in
-- multiple workspaces under one template is only counted once for
-- every minute.
app_insights AS (
SELECT
w.template_id,
was.user_id,
-- Both app stats and agent stats track web terminal usage, but
-- by different means. The app stats value should be more
-- accurate so we don't want to discard it just yet.
CASE
WHEN was.access_method = 'terminal'
THEN '[terminal]' -- Unique name, app names can't contain brackets.
ELSE was.slug_or_port
END::text AS app_name,
COALESCE(wa.display_name, '') AS display_name,
(wa.slug IS NOT NULL)::boolean AS is_app,
COUNT(DISTINCT s.minute_bucket) AS app_minutes
FROM
workspace_app_stats AS was
JOIN
workspaces AS w
ON
w.id = was.workspace_id
-- We do a left join here because we want to include user IDs that have used
-- e.g. ports when counting active users.
LEFT JOIN
workspace_apps wa
ON
wa.agent_id = was.agent_id
AND wa.slug = was.slug_or_port
-- Generate a series of minute buckets for each session for computing the
-- mintes/bucket.
CROSS JOIN
generate_series(
date_trunc('minute', was.session_started_at),
-- Subtract 1 μs to avoid creating an extra series.
date_trunc('minute', was.session_ended_at - '1 microsecond'::interval),
'1 minute'::interval
) AS s(minute_bucket)
WHERE
s.minute_bucket >= $1::timestamptz
AND s.minute_bucket < $2::timestamptz
GROUP BY
w.template_id, was.user_id, was.access_method, was.slug_or_port, wa.display_name, wa.slug
)
SELECT
template_id,
app_name AS slug_or_port,
display_name AS display_name,
COUNT(DISTINCT user_id)::bigint AS active_users,
(SUM(app_minutes) * 60)::bigint AS usage_seconds
FROM
app_insights
WHERE
is_app IS TRUE
GROUP BY
template_id, slug_or_port, display_name
`
type GetTemplateAppInsightsByTemplateParams struct {
StartTime time.Time `db:"start_time" json:"start_time"`
EndTime time.Time `db:"end_time" json:"end_time"`
}
type GetTemplateAppInsightsByTemplateRow struct {
TemplateID uuid.UUID `db:"template_id" json:"template_id"`
SlugOrPort string `db:"slug_or_port" json:"slug_or_port"`
DisplayName string `db:"display_name" json:"display_name"`
ActiveUsers int64 `db:"active_users" json:"active_users"`
UsageSeconds int64 `db:"usage_seconds" json:"usage_seconds"`
}
// GetTemplateAppInsightsByTemplate is used for Prometheus metrics. Keep
// in sync with GetTemplateAppInsights and UpsertTemplateUsageStats.
func (q *sqlQuerier) GetTemplateAppInsightsByTemplate(ctx context.Context, arg GetTemplateAppInsightsByTemplateParams) ([]GetTemplateAppInsightsByTemplateRow, error) {
rows, err := q.db.QueryContext(ctx, getTemplateAppInsightsByTemplate, arg.StartTime, arg.EndTime)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetTemplateAppInsightsByTemplateRow
for rows.Next() {
var i GetTemplateAppInsightsByTemplateRow
if err := rows.Scan(
&i.TemplateID,
&i.SlugOrPort,
&i.DisplayName,
&i.ActiveUsers,
&i.UsageSeconds,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getTemplateInsights = `-- name: GetTemplateInsights :one
WITH
insights AS (
SELECT
user_id,
-- See motivation in GetTemplateInsights for LEAST(SUM(n), 30).
LEAST(SUM(usage_mins), 30) AS usage_mins,
LEAST(SUM(ssh_mins), 30) AS ssh_mins,
LEAST(SUM(sftp_mins), 30) AS sftp_mins,
LEAST(SUM(reconnecting_pty_mins), 30) AS reconnecting_pty_mins,
LEAST(SUM(vscode_mins), 30) AS vscode_mins,
LEAST(SUM(jetbrains_mins), 30) AS jetbrains_mins
FROM
template_usage_stats
WHERE
start_time >= $1::timestamptz
AND end_time <= $2::timestamptz
AND CASE WHEN COALESCE(array_length($3::uuid[], 1), 0) > 0 THEN template_id = ANY($3::uuid[]) ELSE TRUE END
GROUP BY
start_time, user_id
),
templates AS (
SELECT
array_agg(DISTINCT template_id) AS template_ids,
array_agg(DISTINCT template_id) FILTER (WHERE ssh_mins > 0) AS ssh_template_ids,
array_agg(DISTINCT template_id) FILTER (WHERE sftp_mins > 0) AS sftp_template_ids,
array_agg(DISTINCT template_id) FILTER (WHERE reconnecting_pty_mins > 0) AS reconnecting_pty_template_ids,
array_agg(DISTINCT template_id) FILTER (WHERE vscode_mins > 0) AS vscode_template_ids,
array_agg(DISTINCT template_id) FILTER (WHERE jetbrains_mins > 0) AS jetbrains_template_ids
FROM
template_usage_stats
WHERE
start_time >= $1::timestamptz
AND end_time <= $2::timestamptz
AND CASE WHEN COALESCE(array_length($3::uuid[], 1), 0) > 0 THEN template_id = ANY($3::uuid[]) ELSE TRUE END
)
SELECT
COALESCE((SELECT template_ids FROM templates), '{}')::uuid[] AS template_ids, -- Includes app usage.
COALESCE((SELECT ssh_template_ids FROM templates), '{}')::uuid[] AS ssh_template_ids,
COALESCE((SELECT sftp_template_ids FROM templates), '{}')::uuid[] AS sftp_template_ids,
COALESCE((SELECT reconnecting_pty_template_ids FROM templates), '{}')::uuid[] AS reconnecting_pty_template_ids,
COALESCE((SELECT vscode_template_ids FROM templates), '{}')::uuid[] AS vscode_template_ids,
COALESCE((SELECT jetbrains_template_ids FROM templates), '{}')::uuid[] AS jetbrains_template_ids,
COALESCE(COUNT(DISTINCT user_id), 0)::bigint AS active_users, -- Includes app usage.
COALESCE(SUM(usage_mins) * 60, 0)::bigint AS usage_total_seconds, -- Includes app usage.
COALESCE(SUM(ssh_mins) * 60, 0)::bigint AS usage_ssh_seconds,
COALESCE(SUM(sftp_mins) * 60, 0)::bigint AS usage_sftp_seconds,
COALESCE(SUM(reconnecting_pty_mins) * 60, 0)::bigint AS usage_reconnecting_pty_seconds,
COALESCE(SUM(vscode_mins) * 60, 0)::bigint AS usage_vscode_seconds,
COALESCE(SUM(jetbrains_mins) * 60, 0)::bigint AS usage_jetbrains_seconds
FROM
insights
`
type GetTemplateInsightsParams struct {
StartTime time.Time `db:"start_time" json:"start_time"`
EndTime time.Time `db:"end_time" json:"end_time"`
TemplateIDs []uuid.UUID `db:"template_ids" json:"template_ids"`
}
type GetTemplateInsightsRow struct {
TemplateIDs []uuid.UUID `db:"template_ids" json:"template_ids"`
SshTemplateIds []uuid.UUID `db:"ssh_template_ids" json:"ssh_template_ids"`
SftpTemplateIds []uuid.UUID `db:"sftp_template_ids" json:"sftp_template_ids"`
ReconnectingPtyTemplateIds []uuid.UUID `db:"reconnecting_pty_template_ids" json:"reconnecting_pty_template_ids"`
VscodeTemplateIds []uuid.UUID `db:"vscode_template_ids" json:"vscode_template_ids"`
JetbrainsTemplateIds []uuid.UUID `db:"jetbrains_template_ids" json:"jetbrains_template_ids"`
ActiveUsers int64 `db:"active_users" json:"active_users"`
UsageTotalSeconds int64 `db:"usage_total_seconds" json:"usage_total_seconds"`
UsageSshSeconds int64 `db:"usage_ssh_seconds" json:"usage_ssh_seconds"`
UsageSftpSeconds int64 `db:"usage_sftp_seconds" json:"usage_sftp_seconds"`
UsageReconnectingPtySeconds int64 `db:"usage_reconnecting_pty_seconds" json:"usage_reconnecting_pty_seconds"`
UsageVscodeSeconds int64 `db:"usage_vscode_seconds" json:"usage_vscode_seconds"`
UsageJetbrainsSeconds int64 `db:"usage_jetbrains_seconds" json:"usage_jetbrains_seconds"`
}
// GetTemplateInsights returns the aggregate user-produced usage of all
// workspaces in a given timeframe. The template IDs, active users, and
// usage_seconds all reflect any usage in the template, including apps.
//
// When combining data from multiple templates, we must make a guess at
// how the user behaved for the 30 minute interval. In this case we make
// the assumption that if the user used two workspaces for 15 minutes,
// they did so sequentially, thus we sum the usage up to a maximum of
// 30 minutes with LEAST(SUM(n), 30).
func (q *sqlQuerier) GetTemplateInsights(ctx context.Context, arg GetTemplateInsightsParams) (GetTemplateInsightsRow, error) {
row := q.db.QueryRowContext(ctx, getTemplateInsights, arg.StartTime, arg.EndTime, pq.Array(arg.TemplateIDs))
var i GetTemplateInsightsRow
err := row.Scan(
pq.Array(&i.TemplateIDs),
pq.Array(&i.SshTemplateIds),
pq.Array(&i.SftpTemplateIds),
pq.Array(&i.ReconnectingPtyTemplateIds),
pq.Array(&i.VscodeTemplateIds),
pq.Array(&i.JetbrainsTemplateIds),
&i.ActiveUsers,
&i.UsageTotalSeconds,
&i.UsageSshSeconds,
&i.UsageSftpSeconds,
&i.UsageReconnectingPtySeconds,
&i.UsageVscodeSeconds,
&i.UsageJetbrainsSeconds,
)
return i, err
}
const getTemplateInsightsByInterval = `-- name: GetTemplateInsightsByInterval :many
WITH
ts AS (
SELECT
d::timestamptz AS from_,
LEAST(
(d::timestamptz + ($2::int || ' day')::interval)::timestamptz,
$3::timestamptz
)::timestamptz AS to_
FROM
generate_series(
$4::timestamptz,
-- Subtract 1 μs to avoid creating an extra series.
($3::timestamptz) - '1 microsecond'::interval,
($2::int || ' day')::interval
) AS d
)
SELECT
ts.from_ AS start_time,
ts.to_ AS end_time,
array_remove(array_agg(DISTINCT tus.template_id), NULL)::uuid[] AS template_ids,
COUNT(DISTINCT tus.user_id) AS active_users
FROM
ts
LEFT JOIN
template_usage_stats AS tus
ON
tus.start_time >= ts.from_
AND tus.start_time < ts.to_ -- End time exclusion criteria optimization for index.
AND tus.end_time <= ts.to_
AND CASE WHEN COALESCE(array_length($1::uuid[], 1), 0) > 0 THEN tus.template_id = ANY($1::uuid[]) ELSE TRUE END
GROUP BY
ts.from_, ts.to_
`
type GetTemplateInsightsByIntervalParams struct {
TemplateIDs []uuid.UUID `db:"template_ids" json:"template_ids"`
IntervalDays int32 `db:"interval_days" json:"interval_days"`
EndTime time.Time `db:"end_time" json:"end_time"`
StartTime time.Time `db:"start_time" json:"start_time"`
}
type GetTemplateInsightsByIntervalRow struct {
StartTime time.Time `db:"start_time" json:"start_time"`
EndTime time.Time `db:"end_time" json:"end_time"`
TemplateIDs []uuid.UUID `db:"template_ids" json:"template_ids"`
ActiveUsers int64 `db:"active_users" json:"active_users"`
}
// GetTemplateInsightsByInterval returns all intervals between start and end
// time, if end time is a partial interval, it will be included in the results and
// that interval will be shorter than a full one. If there is no data for a selected
// interval/template, it will be included in the results with 0 active users.
func (q *sqlQuerier) GetTemplateInsightsByInterval(ctx context.Context, arg GetTemplateInsightsByIntervalParams) ([]GetTemplateInsightsByIntervalRow, error) {
rows, err := q.db.QueryContext(ctx, getTemplateInsightsByInterval,
pq.Array(arg.TemplateIDs),
arg.IntervalDays,
arg.EndTime,
arg.StartTime,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetTemplateInsightsByIntervalRow
for rows.Next() {
var i GetTemplateInsightsByIntervalRow
if err := rows.Scan(
&i.StartTime,
&i.EndTime,
pq.Array(&i.TemplateIDs),
&i.ActiveUsers,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getTemplateInsightsByTemplate = `-- name: GetTemplateInsightsByTemplate :many
WITH
-- This CTE is used to truncate agent usage into minute buckets, then
-- flatten the users agent usage within the template so that usage in
-- multiple workspaces under one template is only counted once for
-- every minute (per user).
insights AS (
SELECT
template_id,
user_id,
COUNT(DISTINCT CASE WHEN session_count_ssh > 0 THEN date_trunc('minute', created_at) ELSE NULL END) AS ssh_mins,
-- TODO(mafredri): Enable when we have the column.
-- COUNT(DISTINCT CASE WHEN session_count_sftp > 0 THEN date_trunc('minute', created_at) ELSE NULL END) AS sftp_mins,
COUNT(DISTINCT CASE WHEN session_count_reconnecting_pty > 0 THEN date_trunc('minute', created_at) ELSE NULL END) AS reconnecting_pty_mins,
COUNT(DISTINCT CASE WHEN session_count_vscode > 0 THEN date_trunc('minute', created_at) ELSE NULL END) AS vscode_mins,
COUNT(DISTINCT CASE WHEN session_count_jetbrains > 0 THEN date_trunc('minute', created_at) ELSE NULL END) AS jetbrains_mins,
-- NOTE(mafredri): The agent stats are currently very unreliable, and
-- sometimes the connections are missing, even during active sessions.
-- Since we can't fully rely on this, we check for "any connection
-- within this bucket". A better solution here would be preferable.
MAX(connection_count) > 0 AS has_connection
FROM
workspace_agent_stats
WHERE
created_at >= $1::timestamptz
AND created_at < $2::timestamptz
-- Inclusion criteria to filter out empty results.
AND (
session_count_ssh > 0
-- TODO(mafredri): Enable when we have the column.
-- OR session_count_sftp > 0
OR session_count_reconnecting_pty > 0
OR session_count_vscode > 0
OR session_count_jetbrains > 0
)
GROUP BY
template_id, user_id
)
SELECT
template_id,
COUNT(DISTINCT user_id)::bigint AS active_users,
(SUM(vscode_mins) * 60)::bigint AS usage_vscode_seconds,
(SUM(jetbrains_mins) * 60)::bigint AS usage_jetbrains_seconds,
(SUM(reconnecting_pty_mins) * 60)::bigint AS usage_reconnecting_pty_seconds,
(SUM(ssh_mins) * 60)::bigint AS usage_ssh_seconds
FROM
insights
WHERE
has_connection
GROUP BY
template_id
`
type GetTemplateInsightsByTemplateParams struct {
StartTime time.Time `db:"start_time" json:"start_time"`
EndTime time.Time `db:"end_time" json:"end_time"`
}
type GetTemplateInsightsByTemplateRow struct {
TemplateID uuid.UUID `db:"template_id" json:"template_id"`
ActiveUsers int64 `db:"active_users" json:"active_users"`
UsageVscodeSeconds int64 `db:"usage_vscode_seconds" json:"usage_vscode_seconds"`
UsageJetbrainsSeconds int64 `db:"usage_jetbrains_seconds" json:"usage_jetbrains_seconds"`
UsageReconnectingPtySeconds int64 `db:"usage_reconnecting_pty_seconds" json:"usage_reconnecting_pty_seconds"`
UsageSshSeconds int64 `db:"usage_ssh_seconds" json:"usage_ssh_seconds"`
}
// GetTemplateInsightsByTemplate is used for Prometheus metrics. Keep
// in sync with GetTemplateInsights and UpsertTemplateUsageStats.
func (q *sqlQuerier) GetTemplateInsightsByTemplate(ctx context.Context, arg GetTemplateInsightsByTemplateParams) ([]GetTemplateInsightsByTemplateRow, error) {
rows, err := q.db.QueryContext(ctx, getTemplateInsightsByTemplate, arg.StartTime, arg.EndTime)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetTemplateInsightsByTemplateRow
for rows.Next() {
var i GetTemplateInsightsByTemplateRow
if err := rows.Scan(
&i.TemplateID,
&i.ActiveUsers,
&i.UsageVscodeSeconds,
&i.UsageJetbrainsSeconds,
&i.UsageReconnectingPtySeconds,
&i.UsageSshSeconds,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getTemplateParameterInsights = `-- name: GetTemplateParameterInsights :many
WITH latest_workspace_builds AS (
SELECT
wb.id,
wbmax.template_id,
wb.template_version_id
FROM (
SELECT
tv.template_id, wbmax.workspace_id, MAX(wbmax.build_number) as max_build_number
FROM workspace_builds wbmax
JOIN template_versions tv ON (tv.id = wbmax.template_version_id)
WHERE
wbmax.created_at >= $1::timestamptz
AND wbmax.created_at < $2::timestamptz
AND CASE WHEN COALESCE(array_length($3::uuid[], 1), 0) > 0 THEN tv.template_id = ANY($3::uuid[]) ELSE TRUE END
GROUP BY tv.template_id, wbmax.workspace_id
) wbmax
JOIN workspace_builds wb ON (
wb.workspace_id = wbmax.workspace_id
AND wb.build_number = wbmax.max_build_number
)
), unique_template_params AS (
SELECT
ROW_NUMBER() OVER () AS num,
array_agg(DISTINCT wb.template_id)::uuid[] AS template_ids,
array_agg(wb.id)::uuid[] AS workspace_build_ids,
tvp.name,
tvp.type,
tvp.display_name,
tvp.description,
tvp.options
FROM latest_workspace_builds wb
JOIN template_version_parameters tvp ON (tvp.template_version_id = wb.template_version_id)
GROUP BY tvp.name, tvp.type, tvp.display_name, tvp.description, tvp.options
)
SELECT
utp.num,
utp.template_ids,
utp.name,
utp.type,
utp.display_name,
utp.description,
utp.options,
wbp.value,
COUNT(wbp.value) AS count
FROM unique_template_params utp
JOIN workspace_build_parameters wbp ON (utp.workspace_build_ids @> ARRAY[wbp.workspace_build_id] AND utp.name = wbp.name)
GROUP BY utp.num, utp.template_ids, utp.name, utp.type, utp.display_name, utp.description, utp.options, wbp.value
`
type GetTemplateParameterInsightsParams struct {
StartTime time.Time `db:"start_time" json:"start_time"`
EndTime time.Time `db:"end_time" json:"end_time"`
TemplateIDs []uuid.UUID `db:"template_ids" json:"template_ids"`
}
type GetTemplateParameterInsightsRow struct {
Num int64 `db:"num" json:"num"`
TemplateIDs []uuid.UUID `db:"template_ids" json:"template_ids"`
Name string `db:"name" json:"name"`
Type string `db:"type" json:"type"`
DisplayName string `db:"display_name" json:"display_name"`
Description string `db:"description" json:"description"`
Options json.RawMessage `db:"options" json:"options"`
Value string `db:"value" json:"value"`
Count int64 `db:"count" json:"count"`
}
// GetTemplateParameterInsights does for each template in a given timeframe,
// look for the latest workspace build (for every workspace) that has been
// created in the timeframe and return the aggregate usage counts of parameter
// values.
func (q *sqlQuerier) GetTemplateParameterInsights(ctx context.Context, arg GetTemplateParameterInsightsParams) ([]GetTemplateParameterInsightsRow, error) {
rows, err := q.db.QueryContext(ctx, getTemplateParameterInsights, arg.StartTime, arg.EndTime, pq.Array(arg.TemplateIDs))
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetTemplateParameterInsightsRow
for rows.Next() {
var i GetTemplateParameterInsightsRow
if err := rows.Scan(
&i.Num,
pq.Array(&i.TemplateIDs),
&i.Name,
&i.Type,
&i.DisplayName,
&i.Description,
&i.Options,
&i.Value,
&i.Count,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getTemplateUsageStats = `-- name: GetTemplateUsageStats :many
SELECT
start_time, end_time, template_id, user_id, median_latency_ms, usage_mins, ssh_mins, sftp_mins, reconnecting_pty_mins, vscode_mins, jetbrains_mins, app_usage_mins
FROM
template_usage_stats
WHERE
start_time >= $1::timestamptz
AND end_time <= $2::timestamptz
AND CASE WHEN COALESCE(array_length($3::uuid[], 1), 0) > 0 THEN template_id = ANY($3::uuid[]) ELSE TRUE END
`
type GetTemplateUsageStatsParams struct {
StartTime time.Time `db:"start_time" json:"start_time"`
EndTime time.Time `db:"end_time" json:"end_time"`
TemplateIDs []uuid.UUID `db:"template_ids" json:"template_ids"`
}
func (q *sqlQuerier) GetTemplateUsageStats(ctx context.Context, arg GetTemplateUsageStatsParams) ([]TemplateUsageStat, error) {
rows, err := q.db.QueryContext(ctx, getTemplateUsageStats, arg.StartTime, arg.EndTime, pq.Array(arg.TemplateIDs))
if err != nil {
return nil, err
}
defer rows.Close()
var items []TemplateUsageStat
for rows.Next() {
var i TemplateUsageStat
if err := rows.Scan(
&i.StartTime,
&i.EndTime,
&i.TemplateID,
&i.UserID,
&i.MedianLatencyMs,
&i.UsageMins,
&i.SshMins,
&i.SftpMins,
&i.ReconnectingPtyMins,
&i.VscodeMins,
&i.JetbrainsMins,
&i.AppUsageMins,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getUserActivityInsights = `-- name: GetUserActivityInsights :many
WITH
deployment_stats AS (
SELECT
start_time,
user_id,
array_agg(template_id) AS template_ids,
-- See motivation in GetTemplateInsights for LEAST(SUM(n), 30).
LEAST(SUM(usage_mins), 30) AS usage_mins
FROM
template_usage_stats
WHERE
start_time >= $1::timestamptz
AND end_time <= $2::timestamptz
AND CASE WHEN COALESCE(array_length($3::uuid[], 1), 0) > 0 THEN template_id = ANY($3::uuid[]) ELSE TRUE END
GROUP BY
start_time, user_id
),
template_ids AS (
SELECT
user_id,
array_agg(DISTINCT template_id) AS ids
FROM
deployment_stats, unnest(template_ids) template_id
GROUP BY
user_id
)
SELECT
ds.user_id,
u.username,
u.avatar_url,
t.ids::uuid[] AS template_ids,
(SUM(ds.usage_mins) * 60)::bigint AS usage_seconds
FROM
deployment_stats ds
JOIN
users u
ON
u.id = ds.user_id
JOIN
template_ids t
ON
ds.user_id = t.user_id
GROUP BY
ds.user_id, u.username, u.avatar_url, t.ids
ORDER BY
ds.user_id ASC
`
type GetUserActivityInsightsParams struct {
StartTime time.Time `db:"start_time" json:"start_time"`
EndTime time.Time `db:"end_time" json:"end_time"`
TemplateIDs []uuid.UUID `db:"template_ids" json:"template_ids"`
}
type GetUserActivityInsightsRow struct {
UserID uuid.UUID `db:"user_id" json:"user_id"`
Username string `db:"username" json:"username"`
AvatarURL string `db:"avatar_url" json:"avatar_url"`
TemplateIDs []uuid.UUID `db:"template_ids" json:"template_ids"`
UsageSeconds int64 `db:"usage_seconds" json:"usage_seconds"`
}
// GetUserActivityInsights returns the ranking with top active users.
// The result can be filtered on template_ids, meaning only user data
// from workspaces based on those templates will be included.
// Note: The usage_seconds and usage_seconds_cumulative differ only when
// requesting deployment-wide (or multiple template) data. Cumulative
// produces a bloated value if a user has used multiple templates
// simultaneously.
func (q *sqlQuerier) GetUserActivityInsights(ctx context.Context, arg GetUserActivityInsightsParams) ([]GetUserActivityInsightsRow, error) {
rows, err := q.db.QueryContext(ctx, getUserActivityInsights, arg.StartTime, arg.EndTime, pq.Array(arg.TemplateIDs))
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetUserActivityInsightsRow
for rows.Next() {
var i GetUserActivityInsightsRow
if err := rows.Scan(
&i.UserID,
&i.Username,
&i.AvatarURL,
pq.Array(&i.TemplateIDs),
&i.UsageSeconds,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getUserLatencyInsights = `-- name: GetUserLatencyInsights :many
SELECT
tus.user_id,
u.username,
u.avatar_url,
array_agg(DISTINCT tus.template_id)::uuid[] AS template_ids,
COALESCE((PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY tus.median_latency_ms)), -1)::float AS workspace_connection_latency_50,
COALESCE((PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY tus.median_latency_ms)), -1)::float AS workspace_connection_latency_95
FROM
template_usage_stats tus
JOIN
users u
ON
u.id = tus.user_id
WHERE
tus.start_time >= $1::timestamptz
AND tus.end_time <= $2::timestamptz
AND CASE WHEN COALESCE(array_length($3::uuid[], 1), 0) > 0 THEN tus.template_id = ANY($3::uuid[]) ELSE TRUE END
GROUP BY
tus.user_id, u.username, u.avatar_url
ORDER BY
tus.user_id ASC
`
type GetUserLatencyInsightsParams struct {
StartTime time.Time `db:"start_time" json:"start_time"`
EndTime time.Time `db:"end_time" json:"end_time"`
TemplateIDs []uuid.UUID `db:"template_ids" json:"template_ids"`
}
type GetUserLatencyInsightsRow struct {
UserID uuid.UUID `db:"user_id" json:"user_id"`
Username string `db:"username" json:"username"`
AvatarURL string `db:"avatar_url" json:"avatar_url"`
TemplateIDs []uuid.UUID `db:"template_ids" json:"template_ids"`
WorkspaceConnectionLatency50 float64 `db:"workspace_connection_latency_50" json:"workspace_connection_latency_50"`
WorkspaceConnectionLatency95 float64 `db:"workspace_connection_latency_95" json:"workspace_connection_latency_95"`
}
// GetUserLatencyInsights returns the median and 95th percentile connection
// latency that users have experienced. The result can be filtered on
// template_ids, meaning only user data from workspaces based on those templates
// will be included.
func (q *sqlQuerier) GetUserLatencyInsights(ctx context.Context, arg GetUserLatencyInsightsParams) ([]GetUserLatencyInsightsRow, error) {
rows, err := q.db.QueryContext(ctx, getUserLatencyInsights, arg.StartTime, arg.EndTime, pq.Array(arg.TemplateIDs))
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetUserLatencyInsightsRow
for rows.Next() {
var i GetUserLatencyInsightsRow
if err := rows.Scan(
&i.UserID,
&i.Username,
&i.AvatarURL,
pq.Array(&i.TemplateIDs),
&i.WorkspaceConnectionLatency50,
&i.WorkspaceConnectionLatency95,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const upsertTemplateUsageStats = `-- name: UpsertTemplateUsageStats :exec
WITH
latest_start AS (
SELECT
-- Truncate to hour so that we always look at even ranges of data.
date_trunc('hour', COALESCE(
MAX(start_time) - '1 hour'::interval,
-- Fallback when there are no template usage stats yet.
-- App stats can exist before this, but not agent stats,
-- limit the lookback to avoid inconsistency.
(SELECT MIN(created_at) FROM workspace_agent_stats)
)) AS t
FROM
template_usage_stats
),
workspace_app_stat_buckets AS (
SELECT
-- Truncate the minute to the nearest half hour, this is the bucket size
-- for the data.
date_trunc('hour', s.minute_bucket) + trunc(date_part('minute', s.minute_bucket) / 30) * 30 * '1 minute'::interval AS time_bucket,
w.template_id,
was.user_id,
-- Both app stats and agent stats track web terminal usage, but
-- by different means. The app stats value should be more
-- accurate so we don't want to discard it just yet.
CASE
WHEN was.access_method = 'terminal'
THEN '[terminal]' -- Unique name, app names can't contain brackets.
ELSE was.slug_or_port
END AS app_name,
COUNT(DISTINCT s.minute_bucket) AS app_minutes,
-- Store each unique minute bucket for later merge between datasets.
array_agg(DISTINCT s.minute_bucket) AS minute_buckets
FROM
workspace_app_stats AS was
JOIN
workspaces AS w
ON
w.id = was.workspace_id
-- Generate a series of minute buckets for each session for computing the
-- mintes/bucket.
CROSS JOIN
generate_series(
date_trunc('minute', was.session_started_at),
-- Subtract 1 μs to avoid creating an extra series.
date_trunc('minute', was.session_ended_at - '1 microsecond'::interval),
'1 minute'::interval
) AS s(minute_bucket)
WHERE
-- s.minute_bucket >= @start_time::timestamptz
-- AND s.minute_bucket < @end_time::timestamptz
s.minute_bucket >= (SELECT t FROM latest_start)
AND s.minute_bucket < NOW()
GROUP BY
time_bucket, w.template_id, was.user_id, was.access_method, was.slug_or_port
),
agent_stats_buckets AS (
SELECT
-- Truncate the minute to the nearest half hour, this is the bucket size
-- for the data.
date_trunc('hour', created_at) + trunc(date_part('minute', created_at) / 30) * 30 * '1 minute'::interval AS time_bucket,
template_id,
user_id,
-- Store each unique minute bucket for later merge between datasets.
array_agg(
DISTINCT CASE
WHEN
session_count_ssh > 0
-- TODO(mafredri): Enable when we have the column.
-- OR session_count_sftp > 0
OR session_count_reconnecting_pty > 0
OR session_count_vscode > 0
OR session_count_jetbrains > 0
THEN
date_trunc('minute', created_at)
ELSE
NULL
END
) AS minute_buckets,
COUNT(DISTINCT CASE WHEN session_count_ssh > 0 THEN date_trunc('minute', created_at) ELSE NULL END) AS ssh_mins,
-- TODO(mafredri): Enable when we have the column.
-- COUNT(DISTINCT CASE WHEN session_count_sftp > 0 THEN date_trunc('minute', created_at) ELSE NULL END) AS sftp_mins,
COUNT(DISTINCT CASE WHEN session_count_reconnecting_pty > 0 THEN date_trunc('minute', created_at) ELSE NULL END) AS reconnecting_pty_mins,
COUNT(DISTINCT CASE WHEN session_count_vscode > 0 THEN date_trunc('minute', created_at) ELSE NULL END) AS vscode_mins,
COUNT(DISTINCT CASE WHEN session_count_jetbrains > 0 THEN date_trunc('minute', created_at) ELSE NULL END) AS jetbrains_mins,
-- NOTE(mafredri): The agent stats are currently very unreliable, and
-- sometimes the connections are missing, even during active sessions.
-- Since we can't fully rely on this, we check for "any connection
-- during this half-hour". A better solution here would be preferable.
MAX(connection_count) > 0 AS has_connection
FROM
workspace_agent_stats
WHERE
-- created_at >= @start_time::timestamptz
-- AND created_at < @end_time::timestamptz
created_at >= (SELECT t FROM latest_start)
AND created_at < NOW()
-- Inclusion criteria to filter out empty results.
AND (
session_count_ssh > 0
-- TODO(mafredri): Enable when we have the column.
-- OR session_count_sftp > 0
OR session_count_reconnecting_pty > 0
OR session_count_vscode > 0
OR session_count_jetbrains > 0
)
GROUP BY
time_bucket, template_id, user_id
),
stats AS (
SELECT
stats.time_bucket AS start_time,
stats.time_bucket + '30 minutes'::interval AS end_time,
stats.template_id,
stats.user_id,
-- Sum/distinct to handle zero/duplicate values due union and to unnest.
COUNT(DISTINCT minute_bucket) AS usage_mins,
array_agg(DISTINCT minute_bucket) AS minute_buckets,
SUM(DISTINCT stats.ssh_mins) AS ssh_mins,
SUM(DISTINCT stats.sftp_mins) AS sftp_mins,
SUM(DISTINCT stats.reconnecting_pty_mins) AS reconnecting_pty_mins,
SUM(DISTINCT stats.vscode_mins) AS vscode_mins,
SUM(DISTINCT stats.jetbrains_mins) AS jetbrains_mins,
-- This is what we unnested, re-nest as json.
jsonb_object_agg(stats.app_name, stats.app_minutes) FILTER (WHERE stats.app_name IS NOT NULL) AS app_usage_mins
FROM (
SELECT
time_bucket,
template_id,
user_id,
0 AS ssh_mins,
0 AS sftp_mins,
0 AS reconnecting_pty_mins,
0 AS vscode_mins,
0 AS jetbrains_mins,
app_name,
app_minutes,
minute_buckets
FROM
workspace_app_stat_buckets
UNION ALL
SELECT
time_bucket,
template_id,
user_id,
ssh_mins,
-- TODO(mafredri): Enable when we have the column.
0 AS sftp_mins,
reconnecting_pty_mins,
vscode_mins,
jetbrains_mins,
NULL AS app_name,
NULL AS app_minutes,
minute_buckets
FROM
agent_stats_buckets
WHERE
-- See note in the agent_stats_buckets CTE.
has_connection
) AS stats, unnest(minute_buckets) AS minute_bucket
GROUP BY
stats.time_bucket, stats.template_id, stats.user_id
),
minute_buckets AS (
-- Create distinct minute buckets for user-activity, so we can filter out
-- irrelevant latencies.
SELECT DISTINCT ON (stats.start_time, stats.template_id, stats.user_id, minute_bucket)
stats.start_time,
stats.template_id,
stats.user_id,
minute_bucket
FROM
stats, unnest(minute_buckets) AS minute_bucket
),
latencies AS (
-- Select all non-zero latencies for all the minutes that a user used the
-- workspace in some way.
SELECT
mb.start_time,
mb.template_id,
mb.user_id,
-- TODO(mafredri): We're doing medians on medians here, we may want to
-- improve upon this at some point.
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY was.connection_median_latency_ms)::real AS median_latency_ms
FROM
minute_buckets AS mb
JOIN
workspace_agent_stats AS was
ON
was.created_at >= (SELECT t FROM latest_start)
AND was.created_at < NOW()
AND date_trunc('minute', was.created_at) = mb.minute_bucket
AND was.template_id = mb.template_id
AND was.user_id = mb.user_id
AND was.connection_median_latency_ms >= 0
GROUP BY
mb.start_time, mb.template_id, mb.user_id
)
INSERT INTO template_usage_stats AS tus (
start_time,
end_time,
template_id,
user_id,
usage_mins,
median_latency_ms,
ssh_mins,
sftp_mins,
reconnecting_pty_mins,
vscode_mins,
jetbrains_mins,
app_usage_mins
) (
SELECT
stats.start_time,
stats.end_time,
stats.template_id,
stats.user_id,
stats.usage_mins,
latencies.median_latency_ms,
stats.ssh_mins,
stats.sftp_mins,
stats.reconnecting_pty_mins,
stats.vscode_mins,
stats.jetbrains_mins,
stats.app_usage_mins
FROM
stats
LEFT JOIN
latencies
ON
-- The latencies group-by ensures there at most one row.
latencies.start_time = stats.start_time
AND latencies.template_id = stats.template_id
AND latencies.user_id = stats.user_id
)
ON CONFLICT
(start_time, template_id, user_id)
DO UPDATE
SET
usage_mins = EXCLUDED.usage_mins,
median_latency_ms = EXCLUDED.median_latency_ms,
ssh_mins = EXCLUDED.ssh_mins,
sftp_mins = EXCLUDED.sftp_mins,
reconnecting_pty_mins = EXCLUDED.reconnecting_pty_mins,
vscode_mins = EXCLUDED.vscode_mins,
jetbrains_mins = EXCLUDED.jetbrains_mins,
app_usage_mins = EXCLUDED.app_usage_mins
WHERE
(tus.*) IS DISTINCT FROM (EXCLUDED.*)
`
// This query aggregates the workspace_agent_stats and workspace_app_stats data
// into a single table for efficient storage and querying. Half-hour buckets are
// used to store the data, and the minutes are summed for each user and template
// combination. The result is stored in the template_usage_stats table.
func (q *sqlQuerier) UpsertTemplateUsageStats(ctx context.Context) error {
_, err := q.db.ExecContext(ctx, upsertTemplateUsageStats)
return err
}
const getJFrogXrayScanByWorkspaceAndAgentID = `-- name: GetJFrogXrayScanByWorkspaceAndAgentID :one
SELECT
agent_id, workspace_id, critical, high, medium, results_url
FROM
jfrog_xray_scans
WHERE
agent_id = $1
AND
workspace_id = $2
LIMIT
1
`
type GetJFrogXrayScanByWorkspaceAndAgentIDParams struct {
AgentID uuid.UUID `db:"agent_id" json:"agent_id"`
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
}
func (q *sqlQuerier) GetJFrogXrayScanByWorkspaceAndAgentID(ctx context.Context, arg GetJFrogXrayScanByWorkspaceAndAgentIDParams) (JfrogXrayScan, error) {
row := q.db.QueryRowContext(ctx, getJFrogXrayScanByWorkspaceAndAgentID, arg.AgentID, arg.WorkspaceID)
var i JfrogXrayScan
err := row.Scan(
&i.AgentID,
&i.WorkspaceID,
&i.Critical,
&i.High,
&i.Medium,
&i.ResultsUrl,
)
return i, err
}
const upsertJFrogXrayScanByWorkspaceAndAgentID = `-- name: UpsertJFrogXrayScanByWorkspaceAndAgentID :exec
INSERT INTO
jfrog_xray_scans (
agent_id,
workspace_id,
critical,
high,
medium,
results_url
)
VALUES
($1, $2, $3, $4, $5, $6)
ON CONFLICT (agent_id, workspace_id)
DO UPDATE SET critical = $3, high = $4, medium = $5, results_url = $6
`
type UpsertJFrogXrayScanByWorkspaceAndAgentIDParams struct {
AgentID uuid.UUID `db:"agent_id" json:"agent_id"`
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
Critical int32 `db:"critical" json:"critical"`
High int32 `db:"high" json:"high"`
Medium int32 `db:"medium" json:"medium"`
ResultsUrl string `db:"results_url" json:"results_url"`
}
func (q *sqlQuerier) UpsertJFrogXrayScanByWorkspaceAndAgentID(ctx context.Context, arg UpsertJFrogXrayScanByWorkspaceAndAgentIDParams) error {
_, err := q.db.ExecContext(ctx, upsertJFrogXrayScanByWorkspaceAndAgentID,
arg.AgentID,
arg.WorkspaceID,
arg.Critical,
arg.High,
arg.Medium,
arg.ResultsUrl,
)
return err
}
const deleteLicense = `-- name: DeleteLicense :one
DELETE
FROM licenses
WHERE id = $1
RETURNING id
`
func (q *sqlQuerier) DeleteLicense(ctx context.Context, id int32) (int32, error) {
row := q.db.QueryRowContext(ctx, deleteLicense, id)
err := row.Scan(&id)
return id, err
}
const getLicenseByID = `-- name: GetLicenseByID :one
SELECT
id, uploaded_at, jwt, exp, uuid
FROM
licenses
WHERE
id = $1
LIMIT
1
`
func (q *sqlQuerier) GetLicenseByID(ctx context.Context, id int32) (License, error) {
row := q.db.QueryRowContext(ctx, getLicenseByID, id)
var i License
err := row.Scan(
&i.ID,
&i.UploadedAt,
&i.JWT,
&i.Exp,
&i.UUID,
)
return i, err
}
const getLicenses = `-- name: GetLicenses :many
SELECT id, uploaded_at, jwt, exp, uuid
FROM licenses
ORDER BY (id)
`
func (q *sqlQuerier) GetLicenses(ctx context.Context) ([]License, error) {
rows, err := q.db.QueryContext(ctx, getLicenses)
if err != nil {
return nil, err
}
defer rows.Close()
var items []License
for rows.Next() {
var i License
if err := rows.Scan(
&i.ID,
&i.UploadedAt,
&i.JWT,
&i.Exp,
&i.UUID,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getUnexpiredLicenses = `-- name: GetUnexpiredLicenses :many
SELECT id, uploaded_at, jwt, exp, uuid
FROM licenses
WHERE exp > NOW()
ORDER BY (id)
`
func (q *sqlQuerier) GetUnexpiredLicenses(ctx context.Context) ([]License, error) {
rows, err := q.db.QueryContext(ctx, getUnexpiredLicenses)
if err != nil {
return nil, err
}
defer rows.Close()
var items []License
for rows.Next() {
var i License
if err := rows.Scan(
&i.ID,
&i.UploadedAt,
&i.JWT,
&i.Exp,
&i.UUID,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertLicense = `-- name: InsertLicense :one
INSERT INTO
licenses (
uploaded_at,
jwt,
exp,
uuid
)
VALUES
($1, $2, $3, $4) RETURNING id, uploaded_at, jwt, exp, uuid
`
type InsertLicenseParams struct {
UploadedAt time.Time `db:"uploaded_at" json:"uploaded_at"`
JWT string `db:"jwt" json:"jwt"`
Exp time.Time `db:"exp" json:"exp"`
UUID uuid.UUID `db:"uuid" json:"uuid"`
}
func (q *sqlQuerier) InsertLicense(ctx context.Context, arg InsertLicenseParams) (License, error) {
row := q.db.QueryRowContext(ctx, insertLicense,
arg.UploadedAt,
arg.JWT,
arg.Exp,
arg.UUID,
)
var i License
err := row.Scan(
&i.ID,
&i.UploadedAt,
&i.JWT,
&i.Exp,
&i.UUID,
)
return i, err
}
const acquireLock = `-- name: AcquireLock :exec
SELECT pg_advisory_xact_lock($1)
`
// Blocks until the lock is acquired.
//
// This must be called from within a transaction. The lock will be automatically
// released when the transaction ends.
func (q *sqlQuerier) AcquireLock(ctx context.Context, pgAdvisoryXactLock int64) error {
_, err := q.db.ExecContext(ctx, acquireLock, pgAdvisoryXactLock)
return err
}
const tryAcquireLock = `-- name: TryAcquireLock :one
SELECT pg_try_advisory_xact_lock($1)
`
// Non blocking lock. Returns true if the lock was acquired, false otherwise.
//
// This must be called from within a transaction. The lock will be automatically
// released when the transaction ends.
func (q *sqlQuerier) TryAcquireLock(ctx context.Context, pgTryAdvisoryXactLock int64) (bool, error) {
row := q.db.QueryRowContext(ctx, tryAcquireLock, pgTryAdvisoryXactLock)
var pg_try_advisory_xact_lock bool
err := row.Scan(&pg_try_advisory_xact_lock)
return pg_try_advisory_xact_lock, err
}
const deleteOAuth2ProviderAppByID = `-- name: DeleteOAuth2ProviderAppByID :exec
DELETE FROM oauth2_provider_apps WHERE id = $1
`
func (q *sqlQuerier) DeleteOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID) error {
_, err := q.db.ExecContext(ctx, deleteOAuth2ProviderAppByID, id)
return err
}
const deleteOAuth2ProviderAppCodeByID = `-- name: DeleteOAuth2ProviderAppCodeByID :exec
DELETE FROM oauth2_provider_app_codes WHERE id = $1
`
func (q *sqlQuerier) DeleteOAuth2ProviderAppCodeByID(ctx context.Context, id uuid.UUID) error {
_, err := q.db.ExecContext(ctx, deleteOAuth2ProviderAppCodeByID, id)
return err
}
const deleteOAuth2ProviderAppCodesByAppAndUserID = `-- name: DeleteOAuth2ProviderAppCodesByAppAndUserID :exec
DELETE FROM oauth2_provider_app_codes WHERE app_id = $1 AND user_id = $2
`
type DeleteOAuth2ProviderAppCodesByAppAndUserIDParams struct {
AppID uuid.UUID `db:"app_id" json:"app_id"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
}
func (q *sqlQuerier) DeleteOAuth2ProviderAppCodesByAppAndUserID(ctx context.Context, arg DeleteOAuth2ProviderAppCodesByAppAndUserIDParams) error {
_, err := q.db.ExecContext(ctx, deleteOAuth2ProviderAppCodesByAppAndUserID, arg.AppID, arg.UserID)
return err
}
const deleteOAuth2ProviderAppSecretByID = `-- name: DeleteOAuth2ProviderAppSecretByID :exec
DELETE FROM oauth2_provider_app_secrets WHERE id = $1
`
func (q *sqlQuerier) DeleteOAuth2ProviderAppSecretByID(ctx context.Context, id uuid.UUID) error {
_, err := q.db.ExecContext(ctx, deleteOAuth2ProviderAppSecretByID, id)
return err
}
const deleteOAuth2ProviderAppTokensByAppAndUserID = `-- name: DeleteOAuth2ProviderAppTokensByAppAndUserID :exec
DELETE FROM
oauth2_provider_app_tokens
USING
oauth2_provider_app_secrets, api_keys
WHERE
oauth2_provider_app_secrets.id = oauth2_provider_app_tokens.app_secret_id
AND api_keys.id = oauth2_provider_app_tokens.api_key_id
AND oauth2_provider_app_secrets.app_id = $1
AND api_keys.user_id = $2
`
type DeleteOAuth2ProviderAppTokensByAppAndUserIDParams struct {
AppID uuid.UUID `db:"app_id" json:"app_id"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
}
func (q *sqlQuerier) DeleteOAuth2ProviderAppTokensByAppAndUserID(ctx context.Context, arg DeleteOAuth2ProviderAppTokensByAppAndUserIDParams) error {
_, err := q.db.ExecContext(ctx, deleteOAuth2ProviderAppTokensByAppAndUserID, arg.AppID, arg.UserID)
return err
}
const getOAuth2ProviderAppByID = `-- name: GetOAuth2ProviderAppByID :one
SELECT id, created_at, updated_at, name, icon, callback_url FROM oauth2_provider_apps WHERE id = $1
`
func (q *sqlQuerier) GetOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID) (OAuth2ProviderApp, error) {
row := q.db.QueryRowContext(ctx, getOAuth2ProviderAppByID, id)
var i OAuth2ProviderApp
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Name,
&i.Icon,
&i.CallbackURL,
)
return i, err
}
const getOAuth2ProviderAppCodeByID = `-- name: GetOAuth2ProviderAppCodeByID :one
SELECT id, created_at, expires_at, secret_prefix, hashed_secret, user_id, app_id FROM oauth2_provider_app_codes WHERE id = $1
`
func (q *sqlQuerier) GetOAuth2ProviderAppCodeByID(ctx context.Context, id uuid.UUID) (OAuth2ProviderAppCode, error) {
row := q.db.QueryRowContext(ctx, getOAuth2ProviderAppCodeByID, id)
var i OAuth2ProviderAppCode
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.ExpiresAt,
&i.SecretPrefix,
&i.HashedSecret,
&i.UserID,
&i.AppID,
)
return i, err
}
const getOAuth2ProviderAppCodeByPrefix = `-- name: GetOAuth2ProviderAppCodeByPrefix :one
SELECT id, created_at, expires_at, secret_prefix, hashed_secret, user_id, app_id FROM oauth2_provider_app_codes WHERE secret_prefix = $1
`
func (q *sqlQuerier) GetOAuth2ProviderAppCodeByPrefix(ctx context.Context, secretPrefix []byte) (OAuth2ProviderAppCode, error) {
row := q.db.QueryRowContext(ctx, getOAuth2ProviderAppCodeByPrefix, secretPrefix)
var i OAuth2ProviderAppCode
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.ExpiresAt,
&i.SecretPrefix,
&i.HashedSecret,
&i.UserID,
&i.AppID,
)
return i, err
}
const getOAuth2ProviderAppSecretByID = `-- name: GetOAuth2ProviderAppSecretByID :one
SELECT id, created_at, last_used_at, hashed_secret, display_secret, app_id, secret_prefix FROM oauth2_provider_app_secrets WHERE id = $1
`
func (q *sqlQuerier) GetOAuth2ProviderAppSecretByID(ctx context.Context, id uuid.UUID) (OAuth2ProviderAppSecret, error) {
row := q.db.QueryRowContext(ctx, getOAuth2ProviderAppSecretByID, id)
var i OAuth2ProviderAppSecret
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.LastUsedAt,
&i.HashedSecret,
&i.DisplaySecret,
&i.AppID,
&i.SecretPrefix,
)
return i, err
}
const getOAuth2ProviderAppSecretByPrefix = `-- name: GetOAuth2ProviderAppSecretByPrefix :one
SELECT id, created_at, last_used_at, hashed_secret, display_secret, app_id, secret_prefix FROM oauth2_provider_app_secrets WHERE secret_prefix = $1
`
func (q *sqlQuerier) GetOAuth2ProviderAppSecretByPrefix(ctx context.Context, secretPrefix []byte) (OAuth2ProviderAppSecret, error) {
row := q.db.QueryRowContext(ctx, getOAuth2ProviderAppSecretByPrefix, secretPrefix)
var i OAuth2ProviderAppSecret
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.LastUsedAt,
&i.HashedSecret,
&i.DisplaySecret,
&i.AppID,
&i.SecretPrefix,
)
return i, err
}
const getOAuth2ProviderAppSecretsByAppID = `-- name: GetOAuth2ProviderAppSecretsByAppID :many
SELECT id, created_at, last_used_at, hashed_secret, display_secret, app_id, secret_prefix FROM oauth2_provider_app_secrets WHERE app_id = $1 ORDER BY (created_at, id) ASC
`
func (q *sqlQuerier) GetOAuth2ProviderAppSecretsByAppID(ctx context.Context, appID uuid.UUID) ([]OAuth2ProviderAppSecret, error) {
rows, err := q.db.QueryContext(ctx, getOAuth2ProviderAppSecretsByAppID, appID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []OAuth2ProviderAppSecret
for rows.Next() {
var i OAuth2ProviderAppSecret
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.LastUsedAt,
&i.HashedSecret,
&i.DisplaySecret,
&i.AppID,
&i.SecretPrefix,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getOAuth2ProviderAppTokenByPrefix = `-- name: GetOAuth2ProviderAppTokenByPrefix :one
SELECT id, created_at, expires_at, hash_prefix, refresh_hash, app_secret_id, api_key_id FROM oauth2_provider_app_tokens WHERE hash_prefix = $1
`
func (q *sqlQuerier) GetOAuth2ProviderAppTokenByPrefix(ctx context.Context, hashPrefix []byte) (OAuth2ProviderAppToken, error) {
row := q.db.QueryRowContext(ctx, getOAuth2ProviderAppTokenByPrefix, hashPrefix)
var i OAuth2ProviderAppToken
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.ExpiresAt,
&i.HashPrefix,
&i.RefreshHash,
&i.AppSecretID,
&i.APIKeyID,
)
return i, err
}
const getOAuth2ProviderApps = `-- name: GetOAuth2ProviderApps :many
SELECT id, created_at, updated_at, name, icon, callback_url FROM oauth2_provider_apps ORDER BY (name, id) ASC
`
func (q *sqlQuerier) GetOAuth2ProviderApps(ctx context.Context) ([]OAuth2ProviderApp, error) {
rows, err := q.db.QueryContext(ctx, getOAuth2ProviderApps)
if err != nil {
return nil, err
}
defer rows.Close()
var items []OAuth2ProviderApp
for rows.Next() {
var i OAuth2ProviderApp
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Name,
&i.Icon,
&i.CallbackURL,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getOAuth2ProviderAppsByUserID = `-- name: GetOAuth2ProviderAppsByUserID :many
SELECT
COUNT(DISTINCT oauth2_provider_app_tokens.id) as token_count,
oauth2_provider_apps.id, oauth2_provider_apps.created_at, oauth2_provider_apps.updated_at, oauth2_provider_apps.name, oauth2_provider_apps.icon, oauth2_provider_apps.callback_url
FROM oauth2_provider_app_tokens
INNER JOIN oauth2_provider_app_secrets
ON oauth2_provider_app_secrets.id = oauth2_provider_app_tokens.app_secret_id
INNER JOIN oauth2_provider_apps
ON oauth2_provider_apps.id = oauth2_provider_app_secrets.app_id
INNER JOIN api_keys
ON api_keys.id = oauth2_provider_app_tokens.api_key_id
WHERE
api_keys.user_id = $1
GROUP BY
oauth2_provider_apps.id
`
type GetOAuth2ProviderAppsByUserIDRow struct {
TokenCount int64 `db:"token_count" json:"token_count"`
OAuth2ProviderApp OAuth2ProviderApp `db:"oauth2_provider_app" json:"oauth2_provider_app"`
}
func (q *sqlQuerier) GetOAuth2ProviderAppsByUserID(ctx context.Context, userID uuid.UUID) ([]GetOAuth2ProviderAppsByUserIDRow, error) {
rows, err := q.db.QueryContext(ctx, getOAuth2ProviderAppsByUserID, userID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetOAuth2ProviderAppsByUserIDRow
for rows.Next() {
var i GetOAuth2ProviderAppsByUserIDRow
if err := rows.Scan(
&i.TokenCount,
&i.OAuth2ProviderApp.ID,
&i.OAuth2ProviderApp.CreatedAt,
&i.OAuth2ProviderApp.UpdatedAt,
&i.OAuth2ProviderApp.Name,
&i.OAuth2ProviderApp.Icon,
&i.OAuth2ProviderApp.CallbackURL,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertOAuth2ProviderApp = `-- name: InsertOAuth2ProviderApp :one
INSERT INTO oauth2_provider_apps (
id,
created_at,
updated_at,
name,
icon,
callback_url
) VALUES(
$1,
$2,
$3,
$4,
$5,
$6
) RETURNING id, created_at, updated_at, name, icon, callback_url
`
type InsertOAuth2ProviderAppParams struct {
ID uuid.UUID `db:"id" json:"id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
Name string `db:"name" json:"name"`
Icon string `db:"icon" json:"icon"`
CallbackURL string `db:"callback_url" json:"callback_url"`
}
func (q *sqlQuerier) InsertOAuth2ProviderApp(ctx context.Context, arg InsertOAuth2ProviderAppParams) (OAuth2ProviderApp, error) {
row := q.db.QueryRowContext(ctx, insertOAuth2ProviderApp,
arg.ID,
arg.CreatedAt,
arg.UpdatedAt,
arg.Name,
arg.Icon,
arg.CallbackURL,
)
var i OAuth2ProviderApp
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Name,
&i.Icon,
&i.CallbackURL,
)
return i, err
}
const insertOAuth2ProviderAppCode = `-- name: InsertOAuth2ProviderAppCode :one
INSERT INTO oauth2_provider_app_codes (
id,
created_at,
expires_at,
secret_prefix,
hashed_secret,
app_id,
user_id
) VALUES(
$1,
$2,
$3,
$4,
$5,
$6,
$7
) RETURNING id, created_at, expires_at, secret_prefix, hashed_secret, user_id, app_id
`
type InsertOAuth2ProviderAppCodeParams struct {
ID uuid.UUID `db:"id" json:"id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
ExpiresAt time.Time `db:"expires_at" json:"expires_at"`
SecretPrefix []byte `db:"secret_prefix" json:"secret_prefix"`
HashedSecret []byte `db:"hashed_secret" json:"hashed_secret"`
AppID uuid.UUID `db:"app_id" json:"app_id"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
}
func (q *sqlQuerier) InsertOAuth2ProviderAppCode(ctx context.Context, arg InsertOAuth2ProviderAppCodeParams) (OAuth2ProviderAppCode, error) {
row := q.db.QueryRowContext(ctx, insertOAuth2ProviderAppCode,
arg.ID,
arg.CreatedAt,
arg.ExpiresAt,
arg.SecretPrefix,
arg.HashedSecret,
arg.AppID,
arg.UserID,
)
var i OAuth2ProviderAppCode
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.ExpiresAt,
&i.SecretPrefix,
&i.HashedSecret,
&i.UserID,
&i.AppID,
)
return i, err
}
const insertOAuth2ProviderAppSecret = `-- name: InsertOAuth2ProviderAppSecret :one
INSERT INTO oauth2_provider_app_secrets (
id,
created_at,
secret_prefix,
hashed_secret,
display_secret,
app_id
) VALUES(
$1,
$2,
$3,
$4,
$5,
$6
) RETURNING id, created_at, last_used_at, hashed_secret, display_secret, app_id, secret_prefix
`
type InsertOAuth2ProviderAppSecretParams struct {
ID uuid.UUID `db:"id" json:"id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
SecretPrefix []byte `db:"secret_prefix" json:"secret_prefix"`
HashedSecret []byte `db:"hashed_secret" json:"hashed_secret"`
DisplaySecret string `db:"display_secret" json:"display_secret"`
AppID uuid.UUID `db:"app_id" json:"app_id"`
}
func (q *sqlQuerier) InsertOAuth2ProviderAppSecret(ctx context.Context, arg InsertOAuth2ProviderAppSecretParams) (OAuth2ProviderAppSecret, error) {
row := q.db.QueryRowContext(ctx, insertOAuth2ProviderAppSecret,
arg.ID,
arg.CreatedAt,
arg.SecretPrefix,
arg.HashedSecret,
arg.DisplaySecret,
arg.AppID,
)
var i OAuth2ProviderAppSecret
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.LastUsedAt,
&i.HashedSecret,
&i.DisplaySecret,
&i.AppID,
&i.SecretPrefix,
)
return i, err
}
const insertOAuth2ProviderAppToken = `-- name: InsertOAuth2ProviderAppToken :one
INSERT INTO oauth2_provider_app_tokens (
id,
created_at,
expires_at,
hash_prefix,
refresh_hash,
app_secret_id,
api_key_id
) VALUES(
$1,
$2,
$3,
$4,
$5,
$6,
$7
) RETURNING id, created_at, expires_at, hash_prefix, refresh_hash, app_secret_id, api_key_id
`
type InsertOAuth2ProviderAppTokenParams struct {
ID uuid.UUID `db:"id" json:"id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
ExpiresAt time.Time `db:"expires_at" json:"expires_at"`
HashPrefix []byte `db:"hash_prefix" json:"hash_prefix"`
RefreshHash []byte `db:"refresh_hash" json:"refresh_hash"`
AppSecretID uuid.UUID `db:"app_secret_id" json:"app_secret_id"`
APIKeyID string `db:"api_key_id" json:"api_key_id"`
}
func (q *sqlQuerier) InsertOAuth2ProviderAppToken(ctx context.Context, arg InsertOAuth2ProviderAppTokenParams) (OAuth2ProviderAppToken, error) {
row := q.db.QueryRowContext(ctx, insertOAuth2ProviderAppToken,
arg.ID,
arg.CreatedAt,
arg.ExpiresAt,
arg.HashPrefix,
arg.RefreshHash,
arg.AppSecretID,
arg.APIKeyID,
)
var i OAuth2ProviderAppToken
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.ExpiresAt,
&i.HashPrefix,
&i.RefreshHash,
&i.AppSecretID,
&i.APIKeyID,
)
return i, err
}
const updateOAuth2ProviderAppByID = `-- name: UpdateOAuth2ProviderAppByID :one
UPDATE oauth2_provider_apps SET
updated_at = $2,
name = $3,
icon = $4,
callback_url = $5
WHERE id = $1 RETURNING id, created_at, updated_at, name, icon, callback_url
`
type UpdateOAuth2ProviderAppByIDParams struct {
ID uuid.UUID `db:"id" json:"id"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
Name string `db:"name" json:"name"`
Icon string `db:"icon" json:"icon"`
CallbackURL string `db:"callback_url" json:"callback_url"`
}
func (q *sqlQuerier) UpdateOAuth2ProviderAppByID(ctx context.Context, arg UpdateOAuth2ProviderAppByIDParams) (OAuth2ProviderApp, error) {
row := q.db.QueryRowContext(ctx, updateOAuth2ProviderAppByID,
arg.ID,
arg.UpdatedAt,
arg.Name,
arg.Icon,
arg.CallbackURL,
)
var i OAuth2ProviderApp
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Name,
&i.Icon,
&i.CallbackURL,
)
return i, err
}
const updateOAuth2ProviderAppSecretByID = `-- name: UpdateOAuth2ProviderAppSecretByID :one
UPDATE oauth2_provider_app_secrets SET
last_used_at = $2
WHERE id = $1 RETURNING id, created_at, last_used_at, hashed_secret, display_secret, app_id, secret_prefix
`
type UpdateOAuth2ProviderAppSecretByIDParams struct {
ID uuid.UUID `db:"id" json:"id"`
LastUsedAt sql.NullTime `db:"last_used_at" json:"last_used_at"`
}
func (q *sqlQuerier) UpdateOAuth2ProviderAppSecretByID(ctx context.Context, arg UpdateOAuth2ProviderAppSecretByIDParams) (OAuth2ProviderAppSecret, error) {
row := q.db.QueryRowContext(ctx, updateOAuth2ProviderAppSecretByID, arg.ID, arg.LastUsedAt)
var i OAuth2ProviderAppSecret
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.LastUsedAt,
&i.HashedSecret,
&i.DisplaySecret,
&i.AppID,
&i.SecretPrefix,
)
return i, err
}
const getOrganizationIDsByMemberIDs = `-- name: GetOrganizationIDsByMemberIDs :many
SELECT
user_id, array_agg(organization_id) :: uuid [ ] AS "organization_IDs"
FROM
organization_members
WHERE
user_id = ANY($1 :: uuid [ ])
GROUP BY
user_id
`
type GetOrganizationIDsByMemberIDsRow struct {
UserID uuid.UUID `db:"user_id" json:"user_id"`
OrganizationIDs []uuid.UUID `db:"organization_IDs" json:"organization_IDs"`
}
func (q *sqlQuerier) GetOrganizationIDsByMemberIDs(ctx context.Context, ids []uuid.UUID) ([]GetOrganizationIDsByMemberIDsRow, error) {
rows, err := q.db.QueryContext(ctx, getOrganizationIDsByMemberIDs, pq.Array(ids))
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetOrganizationIDsByMemberIDsRow
for rows.Next() {
var i GetOrganizationIDsByMemberIDsRow
if err := rows.Scan(&i.UserID, pq.Array(&i.OrganizationIDs)); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getOrganizationMemberByUserID = `-- name: GetOrganizationMemberByUserID :one
SELECT
user_id, organization_id, created_at, updated_at, roles
FROM
organization_members
WHERE
organization_id = $1
AND user_id = $2
LIMIT
1
`
type GetOrganizationMemberByUserIDParams struct {
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
}
func (q *sqlQuerier) GetOrganizationMemberByUserID(ctx context.Context, arg GetOrganizationMemberByUserIDParams) (OrganizationMember, error) {
row := q.db.QueryRowContext(ctx, getOrganizationMemberByUserID, arg.OrganizationID, arg.UserID)
var i OrganizationMember
err := row.Scan(
&i.UserID,
&i.OrganizationID,
&i.CreatedAt,
&i.UpdatedAt,
pq.Array(&i.Roles),
)
return i, err
}
const getOrganizationMembershipsByUserID = `-- name: GetOrganizationMembershipsByUserID :many
SELECT
user_id, organization_id, created_at, updated_at, roles
FROM
organization_members
WHERE
user_id = $1
`
func (q *sqlQuerier) GetOrganizationMembershipsByUserID(ctx context.Context, userID uuid.UUID) ([]OrganizationMember, error) {
rows, err := q.db.QueryContext(ctx, getOrganizationMembershipsByUserID, userID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []OrganizationMember
for rows.Next() {
var i OrganizationMember
if err := rows.Scan(
&i.UserID,
&i.OrganizationID,
&i.CreatedAt,
&i.UpdatedAt,
pq.Array(&i.Roles),
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertOrganizationMember = `-- name: InsertOrganizationMember :one
INSERT INTO
organization_members (
organization_id,
user_id,
created_at,
updated_at,
roles
)
VALUES
($1, $2, $3, $4, $5) RETURNING user_id, organization_id, created_at, updated_at, roles
`
type InsertOrganizationMemberParams struct {
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
Roles []string `db:"roles" json:"roles"`
}
func (q *sqlQuerier) InsertOrganizationMember(ctx context.Context, arg InsertOrganizationMemberParams) (OrganizationMember, error) {
row := q.db.QueryRowContext(ctx, insertOrganizationMember,
arg.OrganizationID,
arg.UserID,
arg.CreatedAt,
arg.UpdatedAt,
pq.Array(arg.Roles),
)
var i OrganizationMember
err := row.Scan(
&i.UserID,
&i.OrganizationID,
&i.CreatedAt,
&i.UpdatedAt,
pq.Array(&i.Roles),
)
return i, err
}
const updateMemberRoles = `-- name: UpdateMemberRoles :one
UPDATE
organization_members
SET
-- Remove all duplicates from the roles.
roles = ARRAY(SELECT DISTINCT UNNEST($1 :: text[]))
WHERE
user_id = $2
AND organization_id = $3
RETURNING user_id, organization_id, created_at, updated_at, roles
`
type UpdateMemberRolesParams struct {
GrantedRoles []string `db:"granted_roles" json:"granted_roles"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
OrgID uuid.UUID `db:"org_id" json:"org_id"`
}
func (q *sqlQuerier) UpdateMemberRoles(ctx context.Context, arg UpdateMemberRolesParams) (OrganizationMember, error) {
row := q.db.QueryRowContext(ctx, updateMemberRoles, pq.Array(arg.GrantedRoles), arg.UserID, arg.OrgID)
var i OrganizationMember
err := row.Scan(
&i.UserID,
&i.OrganizationID,
&i.CreatedAt,
&i.UpdatedAt,
pq.Array(&i.Roles),
)
return i, err
}
const getDefaultOrganization = `-- name: GetDefaultOrganization :one
SELECT
id, name, description, created_at, updated_at, is_default
FROM
organizations
WHERE
is_default = true
LIMIT
1
`
func (q *sqlQuerier) GetDefaultOrganization(ctx context.Context) (Organization, error) {
row := q.db.QueryRowContext(ctx, getDefaultOrganization)
var i Organization
err := row.Scan(
&i.ID,
&i.Name,
&i.Description,
&i.CreatedAt,
&i.UpdatedAt,
&i.IsDefault,
)
return i, err
}
const getOrganizationByID = `-- name: GetOrganizationByID :one
SELECT
id, name, description, created_at, updated_at, is_default
FROM
organizations
WHERE
id = $1
`
func (q *sqlQuerier) GetOrganizationByID(ctx context.Context, id uuid.UUID) (Organization, error) {
row := q.db.QueryRowContext(ctx, getOrganizationByID, id)
var i Organization
err := row.Scan(
&i.ID,
&i.Name,
&i.Description,
&i.CreatedAt,
&i.UpdatedAt,
&i.IsDefault,
)
return i, err
}
const getOrganizationByName = `-- name: GetOrganizationByName :one
SELECT
id, name, description, created_at, updated_at, is_default
FROM
organizations
WHERE
LOWER("name") = LOWER($1)
LIMIT
1
`
func (q *sqlQuerier) GetOrganizationByName(ctx context.Context, name string) (Organization, error) {
row := q.db.QueryRowContext(ctx, getOrganizationByName, name)
var i Organization
err := row.Scan(
&i.ID,
&i.Name,
&i.Description,
&i.CreatedAt,
&i.UpdatedAt,
&i.IsDefault,
)
return i, err
}
const getOrganizations = `-- name: GetOrganizations :many
SELECT
id, name, description, created_at, updated_at, is_default
FROM
organizations
`
func (q *sqlQuerier) GetOrganizations(ctx context.Context) ([]Organization, error) {
rows, err := q.db.QueryContext(ctx, getOrganizations)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Organization
for rows.Next() {
var i Organization
if err := rows.Scan(
&i.ID,
&i.Name,
&i.Description,
&i.CreatedAt,
&i.UpdatedAt,
&i.IsDefault,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getOrganizationsByUserID = `-- name: GetOrganizationsByUserID :many
SELECT
id, name, description, created_at, updated_at, is_default
FROM
organizations
WHERE
id = ANY(
SELECT
organization_id
FROM
organization_members
WHERE
user_id = $1
)
`
func (q *sqlQuerier) GetOrganizationsByUserID(ctx context.Context, userID uuid.UUID) ([]Organization, error) {
rows, err := q.db.QueryContext(ctx, getOrganizationsByUserID, userID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Organization
for rows.Next() {
var i Organization
if err := rows.Scan(
&i.ID,
&i.Name,
&i.Description,
&i.CreatedAt,
&i.UpdatedAt,
&i.IsDefault,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertOrganization = `-- name: InsertOrganization :one
INSERT INTO
organizations (id, "name", description, created_at, updated_at, is_default)
VALUES
-- If no organizations exist, and this is the first, make it the default.
($1, $2, $3, $4, $5, (SELECT TRUE FROM organizations LIMIT 1) IS NULL) RETURNING id, name, description, created_at, updated_at, is_default
`
type InsertOrganizationParams struct {
ID uuid.UUID `db:"id" json:"id"`
Name string `db:"name" json:"name"`
Description string `db:"description" json:"description"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
func (q *sqlQuerier) InsertOrganization(ctx context.Context, arg InsertOrganizationParams) (Organization, error) {
row := q.db.QueryRowContext(ctx, insertOrganization,
arg.ID,
arg.Name,
arg.Description,
arg.CreatedAt,
arg.UpdatedAt,
)
var i Organization
err := row.Scan(
&i.ID,
&i.Name,
&i.Description,
&i.CreatedAt,
&i.UpdatedAt,
&i.IsDefault,
)
return i, err
}
const getParameterSchemasByJobID = `-- name: GetParameterSchemasByJobID :many
SELECT
id, created_at, job_id, name, description, default_source_scheme, default_source_value, allow_override_source, default_destination_scheme, allow_override_destination, default_refresh, redisplay_value, validation_error, validation_condition, validation_type_system, validation_value_type, index
FROM
parameter_schemas
WHERE
job_id = $1
ORDER BY
index
`
func (q *sqlQuerier) GetParameterSchemasByJobID(ctx context.Context, jobID uuid.UUID) ([]ParameterSchema, error) {
rows, err := q.db.QueryContext(ctx, getParameterSchemasByJobID, jobID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ParameterSchema
for rows.Next() {
var i ParameterSchema
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.JobID,
&i.Name,
&i.Description,
&i.DefaultSourceScheme,
&i.DefaultSourceValue,
&i.AllowOverrideSource,
&i.DefaultDestinationScheme,
&i.AllowOverrideDestination,
&i.DefaultRefresh,
&i.RedisplayValue,
&i.ValidationError,
&i.ValidationCondition,
&i.ValidationTypeSystem,
&i.ValidationValueType,
&i.Index,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const deleteOldProvisionerDaemons = `-- name: DeleteOldProvisionerDaemons :exec
DELETE FROM provisioner_daemons WHERE (
(created_at < (NOW() - INTERVAL '7 days') AND last_seen_at IS NULL) OR
(last_seen_at IS NOT NULL AND last_seen_at < (NOW() - INTERVAL '7 days'))
)
`
// Delete provisioner daemons that have been created at least a week ago
// and have not connected to coderd since a week.
// A provisioner daemon with "zeroed" last_seen_at column indicates possible
// connectivity issues (no provisioner daemon activity since registration).
func (q *sqlQuerier) DeleteOldProvisionerDaemons(ctx context.Context) error {
_, err := q.db.ExecContext(ctx, deleteOldProvisionerDaemons)
return err
}
const getProvisionerDaemons = `-- name: GetProvisionerDaemons :many
SELECT
id, created_at, name, provisioners, replica_id, tags, last_seen_at, version, api_version, organization_id
FROM
provisioner_daemons
`
func (q *sqlQuerier) GetProvisionerDaemons(ctx context.Context) ([]ProvisionerDaemon, error) {
rows, err := q.db.QueryContext(ctx, getProvisionerDaemons)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ProvisionerDaemon
for rows.Next() {
var i ProvisionerDaemon
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.Name,
pq.Array(&i.Provisioners),
&i.ReplicaID,
&i.Tags,
&i.LastSeenAt,
&i.Version,
&i.APIVersion,
&i.OrganizationID,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const updateProvisionerDaemonLastSeenAt = `-- name: UpdateProvisionerDaemonLastSeenAt :exec
UPDATE provisioner_daemons
SET
last_seen_at = $1
WHERE
id = $2
AND
last_seen_at <= $1
`
type UpdateProvisionerDaemonLastSeenAtParams struct {
LastSeenAt sql.NullTime `db:"last_seen_at" json:"last_seen_at"`
ID uuid.UUID `db:"id" json:"id"`
}
func (q *sqlQuerier) UpdateProvisionerDaemonLastSeenAt(ctx context.Context, arg UpdateProvisionerDaemonLastSeenAtParams) error {
_, err := q.db.ExecContext(ctx, updateProvisionerDaemonLastSeenAt, arg.LastSeenAt, arg.ID)
return err
}
const upsertProvisionerDaemon = `-- name: UpsertProvisionerDaemon :one
INSERT INTO
provisioner_daemons (
id,
created_at,
"name",
provisioners,
tags,
last_seen_at,
"version",
organization_id,
api_version
)
VALUES (
gen_random_uuid(),
$1,
$2,
$3,
$4,
$5,
$6,
$7,
$8
) ON CONFLICT("name", LOWER(COALESCE(tags ->> 'owner'::text, ''::text))) DO UPDATE SET
provisioners = $3,
tags = $4,
last_seen_at = $5,
"version" = $6,
api_version = $8,
organization_id = $7
WHERE
-- Only ones with the same tags are allowed clobber
provisioner_daemons.tags <@ $4 :: jsonb
RETURNING id, created_at, name, provisioners, replica_id, tags, last_seen_at, version, api_version, organization_id
`
type UpsertProvisionerDaemonParams struct {
CreatedAt time.Time `db:"created_at" json:"created_at"`
Name string `db:"name" json:"name"`
Provisioners []ProvisionerType `db:"provisioners" json:"provisioners"`
Tags StringMap `db:"tags" json:"tags"`
LastSeenAt sql.NullTime `db:"last_seen_at" json:"last_seen_at"`
Version string `db:"version" json:"version"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
APIVersion string `db:"api_version" json:"api_version"`
}
func (q *sqlQuerier) UpsertProvisionerDaemon(ctx context.Context, arg UpsertProvisionerDaemonParams) (ProvisionerDaemon, error) {
row := q.db.QueryRowContext(ctx, upsertProvisionerDaemon,
arg.CreatedAt,
arg.Name,
pq.Array(arg.Provisioners),
arg.Tags,
arg.LastSeenAt,
arg.Version,
arg.OrganizationID,
arg.APIVersion,
)
var i ProvisionerDaemon
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.Name,
pq.Array(&i.Provisioners),
&i.ReplicaID,
&i.Tags,
&i.LastSeenAt,
&i.Version,
&i.APIVersion,
&i.OrganizationID,
)
return i, err
}
const getProvisionerLogsAfterID = `-- name: GetProvisionerLogsAfterID :many
SELECT
job_id, created_at, source, level, stage, output, id
FROM
provisioner_job_logs
WHERE
job_id = $1
AND (
id > $2
) ORDER BY id ASC
`
type GetProvisionerLogsAfterIDParams struct {
JobID uuid.UUID `db:"job_id" json:"job_id"`
CreatedAfter int64 `db:"created_after" json:"created_after"`
}
func (q *sqlQuerier) GetProvisionerLogsAfterID(ctx context.Context, arg GetProvisionerLogsAfterIDParams) ([]ProvisionerJobLog, error) {
rows, err := q.db.QueryContext(ctx, getProvisionerLogsAfterID, arg.JobID, arg.CreatedAfter)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ProvisionerJobLog
for rows.Next() {
var i ProvisionerJobLog
if err := rows.Scan(
&i.JobID,
&i.CreatedAt,
&i.Source,
&i.Level,
&i.Stage,
&i.Output,
&i.ID,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertProvisionerJobLogs = `-- name: InsertProvisionerJobLogs :many
INSERT INTO
provisioner_job_logs
SELECT
$1 :: uuid AS job_id,
unnest($2 :: timestamptz [ ]) AS created_at,
unnest($3 :: log_source [ ]) AS source,
unnest($4 :: log_level [ ]) AS LEVEL,
unnest($5 :: VARCHAR(128) [ ]) AS stage,
unnest($6 :: VARCHAR(1024) [ ]) AS output RETURNING job_id, created_at, source, level, stage, output, id
`
type InsertProvisionerJobLogsParams struct {
JobID uuid.UUID `db:"job_id" json:"job_id"`
CreatedAt []time.Time `db:"created_at" json:"created_at"`
Source []LogSource `db:"source" json:"source"`
Level []LogLevel `db:"level" json:"level"`
Stage []string `db:"stage" json:"stage"`
Output []string `db:"output" json:"output"`
}
func (q *sqlQuerier) InsertProvisionerJobLogs(ctx context.Context, arg InsertProvisionerJobLogsParams) ([]ProvisionerJobLog, error) {
rows, err := q.db.QueryContext(ctx, insertProvisionerJobLogs,
arg.JobID,
pq.Array(arg.CreatedAt),
pq.Array(arg.Source),
pq.Array(arg.Level),
pq.Array(arg.Stage),
pq.Array(arg.Output),
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ProvisionerJobLog
for rows.Next() {
var i ProvisionerJobLog
if err := rows.Scan(
&i.JobID,
&i.CreatedAt,
&i.Source,
&i.Level,
&i.Stage,
&i.Output,
&i.ID,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const acquireProvisionerJob = `-- name: AcquireProvisionerJob :one
UPDATE
provisioner_jobs
SET
started_at = $1,
updated_at = $1,
worker_id = $2
WHERE
id = (
SELECT
id
FROM
provisioner_jobs AS nested
WHERE
nested.started_at IS NULL
AND nested.organization_id = $3
-- Ensure the caller has the correct provisioner.
AND nested.provisioner = ANY($4 :: provisioner_type [ ])
AND CASE
-- Special case for untagged provisioners: only match untagged jobs.
WHEN nested.tags :: jsonb = '{"scope": "organization", "owner": ""}' :: jsonb
THEN nested.tags :: jsonb = $5 :: jsonb
-- Ensure the caller satisfies all job tags.
ELSE nested.tags :: jsonb <@ $5 :: jsonb
END
ORDER BY
nested.created_at
FOR UPDATE
SKIP LOCKED
LIMIT
1
) RETURNING id, created_at, updated_at, started_at, canceled_at, completed_at, error, organization_id, initiator_id, provisioner, storage_method, type, input, worker_id, file_id, tags, error_code, trace_metadata, job_status
`
type AcquireProvisionerJobParams struct {
StartedAt sql.NullTime `db:"started_at" json:"started_at"`
WorkerID uuid.NullUUID `db:"worker_id" json:"worker_id"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
Types []ProvisionerType `db:"types" json:"types"`
Tags json.RawMessage `db:"tags" json:"tags"`
}
// Acquires the lock for a single job that isn't started, completed,
// canceled, and that matches an array of provisioner types.
//
// SKIP LOCKED is used to jump over locked rows. This prevents
// multiple provisioners from acquiring the same jobs. See:
// https://www.postgresql.org/docs/9.5/sql-select.html#SQL-FOR-UPDATE-SHARE
func (q *sqlQuerier) AcquireProvisionerJob(ctx context.Context, arg AcquireProvisionerJobParams) (ProvisionerJob, error) {
row := q.db.QueryRowContext(ctx, acquireProvisionerJob,
arg.StartedAt,
arg.WorkerID,
arg.OrganizationID,
pq.Array(arg.Types),
arg.Tags,
)
var i ProvisionerJob
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.StartedAt,
&i.CanceledAt,
&i.CompletedAt,
&i.Error,
&i.OrganizationID,
&i.InitiatorID,
&i.Provisioner,
&i.StorageMethod,
&i.Type,
&i.Input,
&i.WorkerID,
&i.FileID,
&i.Tags,
&i.ErrorCode,
&i.TraceMetadata,
&i.JobStatus,
)
return i, err
}
const getHungProvisionerJobs = `-- name: GetHungProvisionerJobs :many
SELECT
id, created_at, updated_at, started_at, canceled_at, completed_at, error, organization_id, initiator_id, provisioner, storage_method, type, input, worker_id, file_id, tags, error_code, trace_metadata, job_status
FROM
provisioner_jobs
WHERE
updated_at < $1
AND started_at IS NOT NULL
AND completed_at IS NULL
`
func (q *sqlQuerier) GetHungProvisionerJobs(ctx context.Context, updatedAt time.Time) ([]ProvisionerJob, error) {
rows, err := q.db.QueryContext(ctx, getHungProvisionerJobs, updatedAt)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ProvisionerJob
for rows.Next() {
var i ProvisionerJob
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.StartedAt,
&i.CanceledAt,
&i.CompletedAt,
&i.Error,
&i.OrganizationID,
&i.InitiatorID,
&i.Provisioner,
&i.StorageMethod,
&i.Type,
&i.Input,
&i.WorkerID,
&i.FileID,
&i.Tags,
&i.ErrorCode,
&i.TraceMetadata,
&i.JobStatus,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getProvisionerJobByID = `-- name: GetProvisionerJobByID :one
SELECT
id, created_at, updated_at, started_at, canceled_at, completed_at, error, organization_id, initiator_id, provisioner, storage_method, type, input, worker_id, file_id, tags, error_code, trace_metadata, job_status
FROM
provisioner_jobs
WHERE
id = $1
`
func (q *sqlQuerier) GetProvisionerJobByID(ctx context.Context, id uuid.UUID) (ProvisionerJob, error) {
row := q.db.QueryRowContext(ctx, getProvisionerJobByID, id)
var i ProvisionerJob
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.StartedAt,
&i.CanceledAt,
&i.CompletedAt,
&i.Error,
&i.OrganizationID,
&i.InitiatorID,
&i.Provisioner,
&i.StorageMethod,
&i.Type,
&i.Input,
&i.WorkerID,
&i.FileID,
&i.Tags,
&i.ErrorCode,
&i.TraceMetadata,
&i.JobStatus,
)
return i, err
}
const getProvisionerJobsByIDs = `-- name: GetProvisionerJobsByIDs :many
SELECT
id, created_at, updated_at, started_at, canceled_at, completed_at, error, organization_id, initiator_id, provisioner, storage_method, type, input, worker_id, file_id, tags, error_code, trace_metadata, job_status
FROM
provisioner_jobs
WHERE
id = ANY($1 :: uuid [ ])
`
func (q *sqlQuerier) GetProvisionerJobsByIDs(ctx context.Context, ids []uuid.UUID) ([]ProvisionerJob, error) {
rows, err := q.db.QueryContext(ctx, getProvisionerJobsByIDs, pq.Array(ids))
if err != nil {
return nil, err
}
defer rows.Close()
var items []ProvisionerJob
for rows.Next() {
var i ProvisionerJob
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.StartedAt,
&i.CanceledAt,
&i.CompletedAt,
&i.Error,
&i.OrganizationID,
&i.InitiatorID,
&i.Provisioner,
&i.StorageMethod,
&i.Type,
&i.Input,
&i.WorkerID,
&i.FileID,
&i.Tags,
&i.ErrorCode,
&i.TraceMetadata,
&i.JobStatus,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getProvisionerJobsByIDsWithQueuePosition = `-- name: GetProvisionerJobsByIDsWithQueuePosition :many
WITH unstarted_jobs AS (
SELECT
id, created_at
FROM
provisioner_jobs
WHERE
started_at IS NULL
),
queue_position AS (
SELECT
id,
ROW_NUMBER() OVER (ORDER BY created_at ASC) AS queue_position
FROM
unstarted_jobs
),
queue_size AS (
SELECT COUNT(*) as count FROM unstarted_jobs
)
SELECT
pj.id, pj.created_at, pj.updated_at, pj.started_at, pj.canceled_at, pj.completed_at, pj.error, pj.organization_id, pj.initiator_id, pj.provisioner, pj.storage_method, pj.type, pj.input, pj.worker_id, pj.file_id, pj.tags, pj.error_code, pj.trace_metadata, pj.job_status,
COALESCE(qp.queue_position, 0) AS queue_position,
COALESCE(qs.count, 0) AS queue_size
FROM
provisioner_jobs pj
LEFT JOIN
queue_position qp ON qp.id = pj.id
LEFT JOIN
queue_size qs ON TRUE
WHERE
pj.id = ANY($1 :: uuid [ ])
`
type GetProvisionerJobsByIDsWithQueuePositionRow struct {
ProvisionerJob ProvisionerJob `db:"provisioner_job" json:"provisioner_job"`
QueuePosition int64 `db:"queue_position" json:"queue_position"`
QueueSize int64 `db:"queue_size" json:"queue_size"`
}
func (q *sqlQuerier) GetProvisionerJobsByIDsWithQueuePosition(ctx context.Context, ids []uuid.UUID) ([]GetProvisionerJobsByIDsWithQueuePositionRow, error) {
rows, err := q.db.QueryContext(ctx, getProvisionerJobsByIDsWithQueuePosition, pq.Array(ids))
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetProvisionerJobsByIDsWithQueuePositionRow
for rows.Next() {
var i GetProvisionerJobsByIDsWithQueuePositionRow
if err := rows.Scan(
&i.ProvisionerJob.ID,
&i.ProvisionerJob.CreatedAt,
&i.ProvisionerJob.UpdatedAt,
&i.ProvisionerJob.StartedAt,
&i.ProvisionerJob.CanceledAt,
&i.ProvisionerJob.CompletedAt,
&i.ProvisionerJob.Error,
&i.ProvisionerJob.OrganizationID,
&i.ProvisionerJob.InitiatorID,
&i.ProvisionerJob.Provisioner,
&i.ProvisionerJob.StorageMethod,
&i.ProvisionerJob.Type,
&i.ProvisionerJob.Input,
&i.ProvisionerJob.WorkerID,
&i.ProvisionerJob.FileID,
&i.ProvisionerJob.Tags,
&i.ProvisionerJob.ErrorCode,
&i.ProvisionerJob.TraceMetadata,
&i.ProvisionerJob.JobStatus,
&i.QueuePosition,
&i.QueueSize,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getProvisionerJobsCreatedAfter = `-- name: GetProvisionerJobsCreatedAfter :many
SELECT id, created_at, updated_at, started_at, canceled_at, completed_at, error, organization_id, initiator_id, provisioner, storage_method, type, input, worker_id, file_id, tags, error_code, trace_metadata, job_status FROM provisioner_jobs WHERE created_at > $1
`
func (q *sqlQuerier) GetProvisionerJobsCreatedAfter(ctx context.Context, createdAt time.Time) ([]ProvisionerJob, error) {
rows, err := q.db.QueryContext(ctx, getProvisionerJobsCreatedAfter, createdAt)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ProvisionerJob
for rows.Next() {
var i ProvisionerJob
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.StartedAt,
&i.CanceledAt,
&i.CompletedAt,
&i.Error,
&i.OrganizationID,
&i.InitiatorID,
&i.Provisioner,
&i.StorageMethod,
&i.Type,
&i.Input,
&i.WorkerID,
&i.FileID,
&i.Tags,
&i.ErrorCode,
&i.TraceMetadata,
&i.JobStatus,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertProvisionerJob = `-- name: InsertProvisionerJob :one
INSERT INTO
provisioner_jobs (
id,
created_at,
updated_at,
organization_id,
initiator_id,
provisioner,
storage_method,
file_id,
"type",
"input",
tags,
trace_metadata
)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) RETURNING id, created_at, updated_at, started_at, canceled_at, completed_at, error, organization_id, initiator_id, provisioner, storage_method, type, input, worker_id, file_id, tags, error_code, trace_metadata, job_status
`
type InsertProvisionerJobParams struct {
ID uuid.UUID `db:"id" json:"id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
InitiatorID uuid.UUID `db:"initiator_id" json:"initiator_id"`
Provisioner ProvisionerType `db:"provisioner" json:"provisioner"`
StorageMethod ProvisionerStorageMethod `db:"storage_method" json:"storage_method"`
FileID uuid.UUID `db:"file_id" json:"file_id"`
Type ProvisionerJobType `db:"type" json:"type"`
Input json.RawMessage `db:"input" json:"input"`
Tags StringMap `db:"tags" json:"tags"`
TraceMetadata pqtype.NullRawMessage `db:"trace_metadata" json:"trace_metadata"`
}
func (q *sqlQuerier) InsertProvisionerJob(ctx context.Context, arg InsertProvisionerJobParams) (ProvisionerJob, error) {
row := q.db.QueryRowContext(ctx, insertProvisionerJob,
arg.ID,
arg.CreatedAt,
arg.UpdatedAt,
arg.OrganizationID,
arg.InitiatorID,
arg.Provisioner,
arg.StorageMethod,
arg.FileID,
arg.Type,
arg.Input,
arg.Tags,
arg.TraceMetadata,
)
var i ProvisionerJob
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.StartedAt,
&i.CanceledAt,
&i.CompletedAt,
&i.Error,
&i.OrganizationID,
&i.InitiatorID,
&i.Provisioner,
&i.StorageMethod,
&i.Type,
&i.Input,
&i.WorkerID,
&i.FileID,
&i.Tags,
&i.ErrorCode,
&i.TraceMetadata,
&i.JobStatus,
)
return i, err
}
const updateProvisionerJobByID = `-- name: UpdateProvisionerJobByID :exec
UPDATE
provisioner_jobs
SET
updated_at = $2
WHERE
id = $1
`
type UpdateProvisionerJobByIDParams struct {
ID uuid.UUID `db:"id" json:"id"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
func (q *sqlQuerier) UpdateProvisionerJobByID(ctx context.Context, arg UpdateProvisionerJobByIDParams) error {
_, err := q.db.ExecContext(ctx, updateProvisionerJobByID, arg.ID, arg.UpdatedAt)
return err
}
const updateProvisionerJobWithCancelByID = `-- name: UpdateProvisionerJobWithCancelByID :exec
UPDATE
provisioner_jobs
SET
canceled_at = $2,
completed_at = $3
WHERE
id = $1
`
type UpdateProvisionerJobWithCancelByIDParams struct {
ID uuid.UUID `db:"id" json:"id"`
CanceledAt sql.NullTime `db:"canceled_at" json:"canceled_at"`
CompletedAt sql.NullTime `db:"completed_at" json:"completed_at"`
}
func (q *sqlQuerier) UpdateProvisionerJobWithCancelByID(ctx context.Context, arg UpdateProvisionerJobWithCancelByIDParams) error {
_, err := q.db.ExecContext(ctx, updateProvisionerJobWithCancelByID, arg.ID, arg.CanceledAt, arg.CompletedAt)
return err
}
const updateProvisionerJobWithCompleteByID = `-- name: UpdateProvisionerJobWithCompleteByID :exec
UPDATE
provisioner_jobs
SET
updated_at = $2,
completed_at = $3,
error = $4,
error_code = $5
WHERE
id = $1
`
type UpdateProvisionerJobWithCompleteByIDParams struct {
ID uuid.UUID `db:"id" json:"id"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
CompletedAt sql.NullTime `db:"completed_at" json:"completed_at"`
Error sql.NullString `db:"error" json:"error"`
ErrorCode sql.NullString `db:"error_code" json:"error_code"`
}
func (q *sqlQuerier) UpdateProvisionerJobWithCompleteByID(ctx context.Context, arg UpdateProvisionerJobWithCompleteByIDParams) error {
_, err := q.db.ExecContext(ctx, updateProvisionerJobWithCompleteByID,
arg.ID,
arg.UpdatedAt,
arg.CompletedAt,
arg.Error,
arg.ErrorCode,
)
return err
}
const getWorkspaceProxies = `-- name: GetWorkspaceProxies :many
SELECT
id, name, display_name, icon, url, wildcard_hostname, created_at, updated_at, deleted, token_hashed_secret, region_id, derp_enabled, derp_only, version
FROM
workspace_proxies
WHERE
deleted = false
`
func (q *sqlQuerier) GetWorkspaceProxies(ctx context.Context) ([]WorkspaceProxy, error) {
rows, err := q.db.QueryContext(ctx, getWorkspaceProxies)
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceProxy
for rows.Next() {
var i WorkspaceProxy
if err := rows.Scan(
&i.ID,
&i.Name,
&i.DisplayName,
&i.Icon,
&i.Url,
&i.WildcardHostname,
&i.CreatedAt,
&i.UpdatedAt,
&i.Deleted,
&i.TokenHashedSecret,
&i.RegionID,
&i.DerpEnabled,
&i.DerpOnly,
&i.Version,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getWorkspaceProxyByHostname = `-- name: GetWorkspaceProxyByHostname :one
SELECT
id, name, display_name, icon, url, wildcard_hostname, created_at, updated_at, deleted, token_hashed_secret, region_id, derp_enabled, derp_only, version
FROM
workspace_proxies
WHERE
-- Validate that the @hostname has been sanitized and is not empty. This
-- doesn't prevent SQL injection (already prevented by using prepared
-- queries), but it does prevent carefully crafted hostnames from matching
-- when they shouldn't.
--
-- Periods don't need to be escaped because they're not special characters
-- in SQL matches unlike regular expressions.
$1 :: text SIMILAR TO '[a-zA-Z0-9._-]+' AND
deleted = false AND
-- Validate that the hostname matches either the wildcard hostname or the
-- access URL (ignoring scheme, port and path).
(
(
$2 :: bool = true AND
url SIMILAR TO '[^:]*://' || $1 :: text || '([:/]?%)*'
) OR
(
$3 :: bool = true AND
$1 :: text LIKE replace(wildcard_hostname, '*', '%')
)
)
LIMIT
1
`
type GetWorkspaceProxyByHostnameParams struct {
Hostname string `db:"hostname" json:"hostname"`
AllowAccessUrl bool `db:"allow_access_url" json:"allow_access_url"`
AllowWildcardHostname bool `db:"allow_wildcard_hostname" json:"allow_wildcard_hostname"`
}
// Finds a workspace proxy that has an access URL or app hostname that matches
// the provided hostname. This is to check if a hostname matches any workspace
// proxy.
//
// The hostname must be sanitized to only contain [a-zA-Z0-9.-] before calling
// this query. The scheme, port and path should be stripped.
func (q *sqlQuerier) GetWorkspaceProxyByHostname(ctx context.Context, arg GetWorkspaceProxyByHostnameParams) (WorkspaceProxy, error) {
row := q.db.QueryRowContext(ctx, getWorkspaceProxyByHostname, arg.Hostname, arg.AllowAccessUrl, arg.AllowWildcardHostname)
var i WorkspaceProxy
err := row.Scan(
&i.ID,
&i.Name,
&i.DisplayName,
&i.Icon,
&i.Url,
&i.WildcardHostname,
&i.CreatedAt,
&i.UpdatedAt,
&i.Deleted,
&i.TokenHashedSecret,
&i.RegionID,
&i.DerpEnabled,
&i.DerpOnly,
&i.Version,
)
return i, err
}
const getWorkspaceProxyByID = `-- name: GetWorkspaceProxyByID :one
SELECT
id, name, display_name, icon, url, wildcard_hostname, created_at, updated_at, deleted, token_hashed_secret, region_id, derp_enabled, derp_only, version
FROM
workspace_proxies
WHERE
id = $1
LIMIT
1
`
func (q *sqlQuerier) GetWorkspaceProxyByID(ctx context.Context, id uuid.UUID) (WorkspaceProxy, error) {
row := q.db.QueryRowContext(ctx, getWorkspaceProxyByID, id)
var i WorkspaceProxy
err := row.Scan(
&i.ID,
&i.Name,
&i.DisplayName,
&i.Icon,
&i.Url,
&i.WildcardHostname,
&i.CreatedAt,
&i.UpdatedAt,
&i.Deleted,
&i.TokenHashedSecret,
&i.RegionID,
&i.DerpEnabled,
&i.DerpOnly,
&i.Version,
)
return i, err
}
const getWorkspaceProxyByName = `-- name: GetWorkspaceProxyByName :one
SELECT
id, name, display_name, icon, url, wildcard_hostname, created_at, updated_at, deleted, token_hashed_secret, region_id, derp_enabled, derp_only, version
FROM
workspace_proxies
WHERE
name = $1
AND deleted = false
LIMIT
1
`
func (q *sqlQuerier) GetWorkspaceProxyByName(ctx context.Context, name string) (WorkspaceProxy, error) {
row := q.db.QueryRowContext(ctx, getWorkspaceProxyByName, name)
var i WorkspaceProxy
err := row.Scan(
&i.ID,
&i.Name,
&i.DisplayName,
&i.Icon,
&i.Url,
&i.WildcardHostname,
&i.CreatedAt,
&i.UpdatedAt,
&i.Deleted,
&i.TokenHashedSecret,
&i.RegionID,
&i.DerpEnabled,
&i.DerpOnly,
&i.Version,
)
return i, err
}
const insertWorkspaceProxy = `-- name: InsertWorkspaceProxy :one
INSERT INTO
workspace_proxies (
id,
url,
wildcard_hostname,
name,
display_name,
icon,
derp_enabled,
derp_only,
token_hashed_secret,
created_at,
updated_at,
deleted
)
VALUES
($1, '', '', $2, $3, $4, $5, $6, $7, $8, $9, false) RETURNING id, name, display_name, icon, url, wildcard_hostname, created_at, updated_at, deleted, token_hashed_secret, region_id, derp_enabled, derp_only, version
`
type InsertWorkspaceProxyParams struct {
ID uuid.UUID `db:"id" json:"id"`
Name string `db:"name" json:"name"`
DisplayName string `db:"display_name" json:"display_name"`
Icon string `db:"icon" json:"icon"`
DerpEnabled bool `db:"derp_enabled" json:"derp_enabled"`
DerpOnly bool `db:"derp_only" json:"derp_only"`
TokenHashedSecret []byte `db:"token_hashed_secret" json:"token_hashed_secret"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
func (q *sqlQuerier) InsertWorkspaceProxy(ctx context.Context, arg InsertWorkspaceProxyParams) (WorkspaceProxy, error) {
row := q.db.QueryRowContext(ctx, insertWorkspaceProxy,
arg.ID,
arg.Name,
arg.DisplayName,
arg.Icon,
arg.DerpEnabled,
arg.DerpOnly,
arg.TokenHashedSecret,
arg.CreatedAt,
arg.UpdatedAt,
)
var i WorkspaceProxy
err := row.Scan(
&i.ID,
&i.Name,
&i.DisplayName,
&i.Icon,
&i.Url,
&i.WildcardHostname,
&i.CreatedAt,
&i.UpdatedAt,
&i.Deleted,
&i.TokenHashedSecret,
&i.RegionID,
&i.DerpEnabled,
&i.DerpOnly,
&i.Version,
)
return i, err
}
const registerWorkspaceProxy = `-- name: RegisterWorkspaceProxy :one
UPDATE
workspace_proxies
SET
url = $1 :: text,
wildcard_hostname = $2 :: text,
derp_enabled = $3 :: boolean,
derp_only = $4 :: boolean,
version = $5 :: text,
updated_at = Now()
WHERE
id = $6
RETURNING id, name, display_name, icon, url, wildcard_hostname, created_at, updated_at, deleted, token_hashed_secret, region_id, derp_enabled, derp_only, version
`
type RegisterWorkspaceProxyParams struct {
Url string `db:"url" json:"url"`
WildcardHostname string `db:"wildcard_hostname" json:"wildcard_hostname"`
DerpEnabled bool `db:"derp_enabled" json:"derp_enabled"`
DerpOnly bool `db:"derp_only" json:"derp_only"`
Version string `db:"version" json:"version"`
ID uuid.UUID `db:"id" json:"id"`
}
func (q *sqlQuerier) RegisterWorkspaceProxy(ctx context.Context, arg RegisterWorkspaceProxyParams) (WorkspaceProxy, error) {
row := q.db.QueryRowContext(ctx, registerWorkspaceProxy,
arg.Url,
arg.WildcardHostname,
arg.DerpEnabled,
arg.DerpOnly,
arg.Version,
arg.ID,
)
var i WorkspaceProxy
err := row.Scan(
&i.ID,
&i.Name,
&i.DisplayName,
&i.Icon,
&i.Url,
&i.WildcardHostname,
&i.CreatedAt,
&i.UpdatedAt,
&i.Deleted,
&i.TokenHashedSecret,
&i.RegionID,
&i.DerpEnabled,
&i.DerpOnly,
&i.Version,
)
return i, err
}
const updateWorkspaceProxy = `-- name: UpdateWorkspaceProxy :one
UPDATE
workspace_proxies
SET
-- These values should always be provided.
name = $1,
display_name = $2,
icon = $3,
-- Only update the token if a new one is provided.
-- So this is an optional field.
token_hashed_secret = CASE
WHEN length($4 :: bytea) > 0 THEN $4 :: bytea
ELSE workspace_proxies.token_hashed_secret
END,
-- Always update this timestamp.
updated_at = Now()
WHERE
id = $5
RETURNING id, name, display_name, icon, url, wildcard_hostname, created_at, updated_at, deleted, token_hashed_secret, region_id, derp_enabled, derp_only, version
`
type UpdateWorkspaceProxyParams struct {
Name string `db:"name" json:"name"`
DisplayName string `db:"display_name" json:"display_name"`
Icon string `db:"icon" json:"icon"`
TokenHashedSecret []byte `db:"token_hashed_secret" json:"token_hashed_secret"`
ID uuid.UUID `db:"id" json:"id"`
}
// This allows editing the properties of a workspace proxy.
func (q *sqlQuerier) UpdateWorkspaceProxy(ctx context.Context, arg UpdateWorkspaceProxyParams) (WorkspaceProxy, error) {
row := q.db.QueryRowContext(ctx, updateWorkspaceProxy,
arg.Name,
arg.DisplayName,
arg.Icon,
arg.TokenHashedSecret,
arg.ID,
)
var i WorkspaceProxy
err := row.Scan(
&i.ID,
&i.Name,
&i.DisplayName,
&i.Icon,
&i.Url,
&i.WildcardHostname,
&i.CreatedAt,
&i.UpdatedAt,
&i.Deleted,
&i.TokenHashedSecret,
&i.RegionID,
&i.DerpEnabled,
&i.DerpOnly,
&i.Version,
)
return i, err
}
const updateWorkspaceProxyDeleted = `-- name: UpdateWorkspaceProxyDeleted :exec
UPDATE
workspace_proxies
SET
updated_at = Now(),
deleted = $1
WHERE
id = $2
`
type UpdateWorkspaceProxyDeletedParams struct {
Deleted bool `db:"deleted" json:"deleted"`
ID uuid.UUID `db:"id" json:"id"`
}
func (q *sqlQuerier) UpdateWorkspaceProxyDeleted(ctx context.Context, arg UpdateWorkspaceProxyDeletedParams) error {
_, err := q.db.ExecContext(ctx, updateWorkspaceProxyDeleted, arg.Deleted, arg.ID)
return err
}
const getQuotaAllowanceForUser = `-- name: GetQuotaAllowanceForUser :one
SELECT
coalesce(SUM(quota_allowance), 0)::BIGINT
FROM
groups g
LEFT JOIN group_members gm ON
g.id = gm.group_id
WHERE
user_id = $1
OR
g.id = g.organization_id
`
func (q *sqlQuerier) GetQuotaAllowanceForUser(ctx context.Context, userID uuid.UUID) (int64, error) {
row := q.db.QueryRowContext(ctx, getQuotaAllowanceForUser, userID)
var column_1 int64
err := row.Scan(&column_1)
return column_1, err
}
const getQuotaConsumedForUser = `-- name: GetQuotaConsumedForUser :one
WITH latest_builds AS (
SELECT
DISTINCT ON
(workspace_id) id,
workspace_id,
daily_cost
FROM
workspace_builds wb
ORDER BY
workspace_id,
created_at DESC
)
SELECT
coalesce(SUM(daily_cost), 0)::BIGINT
FROM
workspaces
JOIN latest_builds ON
latest_builds.workspace_id = workspaces.id
WHERE NOT deleted AND workspaces.owner_id = $1
`
func (q *sqlQuerier) GetQuotaConsumedForUser(ctx context.Context, ownerID uuid.UUID) (int64, error) {
row := q.db.QueryRowContext(ctx, getQuotaConsumedForUser, ownerID)
var column_1 int64
err := row.Scan(&column_1)
return column_1, err
}
const deleteReplicasUpdatedBefore = `-- name: DeleteReplicasUpdatedBefore :exec
DELETE FROM replicas WHERE updated_at < $1
`
func (q *sqlQuerier) DeleteReplicasUpdatedBefore(ctx context.Context, updatedAt time.Time) error {
_, err := q.db.ExecContext(ctx, deleteReplicasUpdatedBefore, updatedAt)
return err
}
const getReplicaByID = `-- name: GetReplicaByID :one
SELECT id, created_at, started_at, stopped_at, updated_at, hostname, region_id, relay_address, database_latency, version, error, "primary" FROM replicas WHERE id = $1
`
func (q *sqlQuerier) GetReplicaByID(ctx context.Context, id uuid.UUID) (Replica, error) {
row := q.db.QueryRowContext(ctx, getReplicaByID, id)
var i Replica
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.StartedAt,
&i.StoppedAt,
&i.UpdatedAt,
&i.Hostname,
&i.RegionID,
&i.RelayAddress,
&i.DatabaseLatency,
&i.Version,
&i.Error,
&i.Primary,
)
return i, err
}
const getReplicasUpdatedAfter = `-- name: GetReplicasUpdatedAfter :many
SELECT id, created_at, started_at, stopped_at, updated_at, hostname, region_id, relay_address, database_latency, version, error, "primary" FROM replicas WHERE updated_at > $1 AND stopped_at IS NULL
`
func (q *sqlQuerier) GetReplicasUpdatedAfter(ctx context.Context, updatedAt time.Time) ([]Replica, error) {
rows, err := q.db.QueryContext(ctx, getReplicasUpdatedAfter, updatedAt)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Replica
for rows.Next() {
var i Replica
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.StartedAt,
&i.StoppedAt,
&i.UpdatedAt,
&i.Hostname,
&i.RegionID,
&i.RelayAddress,
&i.DatabaseLatency,
&i.Version,
&i.Error,
&i.Primary,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertReplica = `-- name: InsertReplica :one
INSERT INTO replicas (
id,
created_at,
started_at,
updated_at,
hostname,
region_id,
relay_address,
version,
database_latency,
"primary"
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id, created_at, started_at, stopped_at, updated_at, hostname, region_id, relay_address, database_latency, version, error, "primary"
`
type InsertReplicaParams struct {
ID uuid.UUID `db:"id" json:"id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
StartedAt time.Time `db:"started_at" json:"started_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
Hostname string `db:"hostname" json:"hostname"`
RegionID int32 `db:"region_id" json:"region_id"`
RelayAddress string `db:"relay_address" json:"relay_address"`
Version string `db:"version" json:"version"`
DatabaseLatency int32 `db:"database_latency" json:"database_latency"`
Primary bool `db:"primary" json:"primary"`
}
func (q *sqlQuerier) InsertReplica(ctx context.Context, arg InsertReplicaParams) (Replica, error) {
row := q.db.QueryRowContext(ctx, insertReplica,
arg.ID,
arg.CreatedAt,
arg.StartedAt,
arg.UpdatedAt,
arg.Hostname,
arg.RegionID,
arg.RelayAddress,
arg.Version,
arg.DatabaseLatency,
arg.Primary,
)
var i Replica
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.StartedAt,
&i.StoppedAt,
&i.UpdatedAt,
&i.Hostname,
&i.RegionID,
&i.RelayAddress,
&i.DatabaseLatency,
&i.Version,
&i.Error,
&i.Primary,
)
return i, err
}
const updateReplica = `-- name: UpdateReplica :one
UPDATE replicas SET
updated_at = $2,
started_at = $3,
stopped_at = $4,
relay_address = $5,
region_id = $6,
hostname = $7,
version = $8,
error = $9,
database_latency = $10,
"primary" = $11
WHERE id = $1 RETURNING id, created_at, started_at, stopped_at, updated_at, hostname, region_id, relay_address, database_latency, version, error, "primary"
`
type UpdateReplicaParams struct {
ID uuid.UUID `db:"id" json:"id"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
StartedAt time.Time `db:"started_at" json:"started_at"`
StoppedAt sql.NullTime `db:"stopped_at" json:"stopped_at"`
RelayAddress string `db:"relay_address" json:"relay_address"`
RegionID int32 `db:"region_id" json:"region_id"`
Hostname string `db:"hostname" json:"hostname"`
Version string `db:"version" json:"version"`
Error string `db:"error" json:"error"`
DatabaseLatency int32 `db:"database_latency" json:"database_latency"`
Primary bool `db:"primary" json:"primary"`
}
func (q *sqlQuerier) UpdateReplica(ctx context.Context, arg UpdateReplicaParams) (Replica, error) {
row := q.db.QueryRowContext(ctx, updateReplica,
arg.ID,
arg.UpdatedAt,
arg.StartedAt,
arg.StoppedAt,
arg.RelayAddress,
arg.RegionID,
arg.Hostname,
arg.Version,
arg.Error,
arg.DatabaseLatency,
arg.Primary,
)
var i Replica
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.StartedAt,
&i.StoppedAt,
&i.UpdatedAt,
&i.Hostname,
&i.RegionID,
&i.RelayAddress,
&i.DatabaseLatency,
&i.Version,
&i.Error,
&i.Primary,
)
return i, err
}
const getAppSecurityKey = `-- name: GetAppSecurityKey :one
SELECT value FROM site_configs WHERE key = 'app_signing_key'
`
func (q *sqlQuerier) GetAppSecurityKey(ctx context.Context) (string, error) {
row := q.db.QueryRowContext(ctx, getAppSecurityKey)
var value string
err := row.Scan(&value)
return value, err
}
const getApplicationName = `-- name: GetApplicationName :one
SELECT value FROM site_configs WHERE key = 'application_name'
`
func (q *sqlQuerier) GetApplicationName(ctx context.Context) (string, error) {
row := q.db.QueryRowContext(ctx, getApplicationName)
var value string
err := row.Scan(&value)
return value, err
}
const getDERPMeshKey = `-- name: GetDERPMeshKey :one
SELECT value FROM site_configs WHERE key = 'derp_mesh_key'
`
func (q *sqlQuerier) GetDERPMeshKey(ctx context.Context) (string, error) {
row := q.db.QueryRowContext(ctx, getDERPMeshKey)
var value string
err := row.Scan(&value)
return value, err
}
const getDefaultProxyConfig = `-- name: GetDefaultProxyConfig :one
SELECT
COALESCE((SELECT value FROM site_configs WHERE key = 'default_proxy_display_name'), 'Default') :: text AS display_name,
COALESCE((SELECT value FROM site_configs WHERE key = 'default_proxy_icon_url'), '/emojis/1f3e1.png') :: text AS icon_url
`
type GetDefaultProxyConfigRow struct {
DisplayName string `db:"display_name" json:"display_name"`
IconUrl string `db:"icon_url" json:"icon_url"`
}
func (q *sqlQuerier) GetDefaultProxyConfig(ctx context.Context) (GetDefaultProxyConfigRow, error) {
row := q.db.QueryRowContext(ctx, getDefaultProxyConfig)
var i GetDefaultProxyConfigRow
err := row.Scan(&i.DisplayName, &i.IconUrl)
return i, err
}
const getDeploymentID = `-- name: GetDeploymentID :one
SELECT value FROM site_configs WHERE key = 'deployment_id'
`
func (q *sqlQuerier) GetDeploymentID(ctx context.Context) (string, error) {
row := q.db.QueryRowContext(ctx, getDeploymentID)
var value string
err := row.Scan(&value)
return value, err
}
const getHealthSettings = `-- name: GetHealthSettings :one
SELECT
COALESCE((SELECT value FROM site_configs WHERE key = 'health_settings'), '{}') :: text AS health_settings
`
func (q *sqlQuerier) GetHealthSettings(ctx context.Context) (string, error) {
row := q.db.QueryRowContext(ctx, getHealthSettings)
var health_settings string
err := row.Scan(&health_settings)
return health_settings, err
}
const getLastUpdateCheck = `-- name: GetLastUpdateCheck :one
SELECT value FROM site_configs WHERE key = 'last_update_check'
`
func (q *sqlQuerier) GetLastUpdateCheck(ctx context.Context) (string, error) {
row := q.db.QueryRowContext(ctx, getLastUpdateCheck)
var value string
err := row.Scan(&value)
return value, err
}
const getLogoURL = `-- name: GetLogoURL :one
SELECT value FROM site_configs WHERE key = 'logo_url'
`
func (q *sqlQuerier) GetLogoURL(ctx context.Context) (string, error) {
row := q.db.QueryRowContext(ctx, getLogoURL)
var value string
err := row.Scan(&value)
return value, err
}
const getNotificationBanners = `-- name: GetNotificationBanners :one
SELECT value FROM site_configs WHERE key = 'notification_banners'
`
func (q *sqlQuerier) GetNotificationBanners(ctx context.Context) (string, error) {
row := q.db.QueryRowContext(ctx, getNotificationBanners)
var value string
err := row.Scan(&value)
return value, err
}
const getOAuthSigningKey = `-- name: GetOAuthSigningKey :one
SELECT value FROM site_configs WHERE key = 'oauth_signing_key'
`
func (q *sqlQuerier) GetOAuthSigningKey(ctx context.Context) (string, error) {
row := q.db.QueryRowContext(ctx, getOAuthSigningKey)
var value string
err := row.Scan(&value)
return value, err
}
const insertDERPMeshKey = `-- name: InsertDERPMeshKey :exec
INSERT INTO site_configs (key, value) VALUES ('derp_mesh_key', $1)
`
func (q *sqlQuerier) InsertDERPMeshKey(ctx context.Context, value string) error {
_, err := q.db.ExecContext(ctx, insertDERPMeshKey, value)
return err
}
const insertDeploymentID = `-- name: InsertDeploymentID :exec
INSERT INTO site_configs (key, value) VALUES ('deployment_id', $1)
`
func (q *sqlQuerier) InsertDeploymentID(ctx context.Context, value string) error {
_, err := q.db.ExecContext(ctx, insertDeploymentID, value)
return err
}
const upsertAppSecurityKey = `-- name: UpsertAppSecurityKey :exec
INSERT INTO site_configs (key, value) VALUES ('app_signing_key', $1)
ON CONFLICT (key) DO UPDATE set value = $1 WHERE site_configs.key = 'app_signing_key'
`
func (q *sqlQuerier) UpsertAppSecurityKey(ctx context.Context, value string) error {
_, err := q.db.ExecContext(ctx, upsertAppSecurityKey, value)
return err
}
const upsertApplicationName = `-- name: UpsertApplicationName :exec
INSERT INTO site_configs (key, value) VALUES ('application_name', $1)
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'application_name'
`
func (q *sqlQuerier) UpsertApplicationName(ctx context.Context, value string) error {
_, err := q.db.ExecContext(ctx, upsertApplicationName, value)
return err
}
const upsertDefaultProxy = `-- name: UpsertDefaultProxy :exec
INSERT INTO site_configs (key, value)
VALUES
('default_proxy_display_name', $1 :: text),
('default_proxy_icon_url', $2 :: text)
ON CONFLICT
(key)
DO UPDATE SET value = EXCLUDED.value WHERE site_configs.key = EXCLUDED.key
`
type UpsertDefaultProxyParams struct {
DisplayName string `db:"display_name" json:"display_name"`
IconUrl string `db:"icon_url" json:"icon_url"`
}
// The default proxy is implied and not actually stored in the database.
// So we need to store it's configuration here for display purposes.
// The functional values are immutable and controlled implicitly.
func (q *sqlQuerier) UpsertDefaultProxy(ctx context.Context, arg UpsertDefaultProxyParams) error {
_, err := q.db.ExecContext(ctx, upsertDefaultProxy, arg.DisplayName, arg.IconUrl)
return err
}
const upsertHealthSettings = `-- name: UpsertHealthSettings :exec
INSERT INTO site_configs (key, value) VALUES ('health_settings', $1)
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'health_settings'
`
func (q *sqlQuerier) UpsertHealthSettings(ctx context.Context, value string) error {
_, err := q.db.ExecContext(ctx, upsertHealthSettings, value)
return err
}
const upsertLastUpdateCheck = `-- name: UpsertLastUpdateCheck :exec
INSERT INTO site_configs (key, value) VALUES ('last_update_check', $1)
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'last_update_check'
`
func (q *sqlQuerier) UpsertLastUpdateCheck(ctx context.Context, value string) error {
_, err := q.db.ExecContext(ctx, upsertLastUpdateCheck, value)
return err
}
const upsertLogoURL = `-- name: UpsertLogoURL :exec
INSERT INTO site_configs (key, value) VALUES ('logo_url', $1)
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'logo_url'
`
func (q *sqlQuerier) UpsertLogoURL(ctx context.Context, value string) error {
_, err := q.db.ExecContext(ctx, upsertLogoURL, value)
return err
}
const upsertNotificationBanners = `-- name: UpsertNotificationBanners :exec
INSERT INTO site_configs (key, value) VALUES ('notification_banners', $1)
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'notification_banners'
`
func (q *sqlQuerier) UpsertNotificationBanners(ctx context.Context, value string) error {
_, err := q.db.ExecContext(ctx, upsertNotificationBanners, value)
return err
}
const upsertOAuthSigningKey = `-- name: UpsertOAuthSigningKey :exec
INSERT INTO site_configs (key, value) VALUES ('oauth_signing_key', $1)
ON CONFLICT (key) DO UPDATE set value = $1 WHERE site_configs.key = 'oauth_signing_key'
`
func (q *sqlQuerier) UpsertOAuthSigningKey(ctx context.Context, value string) error {
_, err := q.db.ExecContext(ctx, upsertOAuthSigningKey, value)
return err
}
const cleanTailnetCoordinators = `-- name: CleanTailnetCoordinators :exec
DELETE
FROM tailnet_coordinators
WHERE heartbeat_at < now() - INTERVAL '24 HOURS'
`
func (q *sqlQuerier) CleanTailnetCoordinators(ctx context.Context) error {
_, err := q.db.ExecContext(ctx, cleanTailnetCoordinators)
return err
}
const cleanTailnetLostPeers = `-- name: CleanTailnetLostPeers :exec
DELETE
FROM tailnet_peers
WHERE updated_at < now() - INTERVAL '24 HOURS' AND status = 'lost'::tailnet_status
`
func (q *sqlQuerier) CleanTailnetLostPeers(ctx context.Context) error {
_, err := q.db.ExecContext(ctx, cleanTailnetLostPeers)
return err
}
const cleanTailnetTunnels = `-- name: CleanTailnetTunnels :exec
DELETE FROM tailnet_tunnels
WHERE updated_at < now() - INTERVAL '24 HOURS' AND
NOT EXISTS (
SELECT 1 FROM tailnet_peers
WHERE id = tailnet_tunnels.src_id AND coordinator_id = tailnet_tunnels.coordinator_id
)
`
func (q *sqlQuerier) CleanTailnetTunnels(ctx context.Context) error {
_, err := q.db.ExecContext(ctx, cleanTailnetTunnels)
return err
}
const deleteAllTailnetClientSubscriptions = `-- name: DeleteAllTailnetClientSubscriptions :exec
DELETE
FROM tailnet_client_subscriptions
WHERE client_id = $1 and coordinator_id = $2
`
type DeleteAllTailnetClientSubscriptionsParams struct {
ClientID uuid.UUID `db:"client_id" json:"client_id"`
CoordinatorID uuid.UUID `db:"coordinator_id" json:"coordinator_id"`
}
func (q *sqlQuerier) DeleteAllTailnetClientSubscriptions(ctx context.Context, arg DeleteAllTailnetClientSubscriptionsParams) error {
_, err := q.db.ExecContext(ctx, deleteAllTailnetClientSubscriptions, arg.ClientID, arg.CoordinatorID)
return err
}
const deleteAllTailnetTunnels = `-- name: DeleteAllTailnetTunnels :exec
DELETE
FROM tailnet_tunnels
WHERE coordinator_id = $1 and src_id = $2
`
type DeleteAllTailnetTunnelsParams struct {
CoordinatorID uuid.UUID `db:"coordinator_id" json:"coordinator_id"`
SrcID uuid.UUID `db:"src_id" json:"src_id"`
}
func (q *sqlQuerier) DeleteAllTailnetTunnels(ctx context.Context, arg DeleteAllTailnetTunnelsParams) error {
_, err := q.db.ExecContext(ctx, deleteAllTailnetTunnels, arg.CoordinatorID, arg.SrcID)
return err
}
const deleteCoordinator = `-- name: DeleteCoordinator :exec
DELETE
FROM tailnet_coordinators
WHERE id = $1
`
func (q *sqlQuerier) DeleteCoordinator(ctx context.Context, id uuid.UUID) error {
_, err := q.db.ExecContext(ctx, deleteCoordinator, id)
return err
}
const deleteTailnetAgent = `-- name: DeleteTailnetAgent :one
DELETE
FROM tailnet_agents
WHERE id = $1 and coordinator_id = $2
RETURNING id, coordinator_id
`
type DeleteTailnetAgentParams struct {
ID uuid.UUID `db:"id" json:"id"`
CoordinatorID uuid.UUID `db:"coordinator_id" json:"coordinator_id"`
}
type DeleteTailnetAgentRow struct {
ID uuid.UUID `db:"id" json:"id"`
CoordinatorID uuid.UUID `db:"coordinator_id" json:"coordinator_id"`
}
func (q *sqlQuerier) DeleteTailnetAgent(ctx context.Context, arg DeleteTailnetAgentParams) (DeleteTailnetAgentRow, error) {
row := q.db.QueryRowContext(ctx, deleteTailnetAgent, arg.ID, arg.CoordinatorID)
var i DeleteTailnetAgentRow
err := row.Scan(&i.ID, &i.CoordinatorID)
return i, err
}
const deleteTailnetClient = `-- name: DeleteTailnetClient :one
DELETE
FROM tailnet_clients
WHERE id = $1 and coordinator_id = $2
RETURNING id, coordinator_id
`
type DeleteTailnetClientParams struct {
ID uuid.UUID `db:"id" json:"id"`
CoordinatorID uuid.UUID `db:"coordinator_id" json:"coordinator_id"`
}
type DeleteTailnetClientRow struct {
ID uuid.UUID `db:"id" json:"id"`
CoordinatorID uuid.UUID `db:"coordinator_id" json:"coordinator_id"`
}
func (q *sqlQuerier) DeleteTailnetClient(ctx context.Context, arg DeleteTailnetClientParams) (DeleteTailnetClientRow, error) {
row := q.db.QueryRowContext(ctx, deleteTailnetClient, arg.ID, arg.CoordinatorID)
var i DeleteTailnetClientRow
err := row.Scan(&i.ID, &i.CoordinatorID)
return i, err
}
const deleteTailnetClientSubscription = `-- name: DeleteTailnetClientSubscription :exec
DELETE
FROM tailnet_client_subscriptions
WHERE client_id = $1 and agent_id = $2 and coordinator_id = $3
`
type DeleteTailnetClientSubscriptionParams struct {
ClientID uuid.UUID `db:"client_id" json:"client_id"`
AgentID uuid.UUID `db:"agent_id" json:"agent_id"`
CoordinatorID uuid.UUID `db:"coordinator_id" json:"coordinator_id"`
}
func (q *sqlQuerier) DeleteTailnetClientSubscription(ctx context.Context, arg DeleteTailnetClientSubscriptionParams) error {
_, err := q.db.ExecContext(ctx, deleteTailnetClientSubscription, arg.ClientID, arg.AgentID, arg.CoordinatorID)
return err
}
const deleteTailnetPeer = `-- name: DeleteTailnetPeer :one
DELETE
FROM tailnet_peers
WHERE id = $1 and coordinator_id = $2
RETURNING id, coordinator_id
`
type DeleteTailnetPeerParams struct {
ID uuid.UUID `db:"id" json:"id"`
CoordinatorID uuid.UUID `db:"coordinator_id" json:"coordinator_id"`
}
type DeleteTailnetPeerRow struct {
ID uuid.UUID `db:"id" json:"id"`
CoordinatorID uuid.UUID `db:"coordinator_id" json:"coordinator_id"`
}
func (q *sqlQuerier) DeleteTailnetPeer(ctx context.Context, arg DeleteTailnetPeerParams) (DeleteTailnetPeerRow, error) {
row := q.db.QueryRowContext(ctx, deleteTailnetPeer, arg.ID, arg.CoordinatorID)
var i DeleteTailnetPeerRow
err := row.Scan(&i.ID, &i.CoordinatorID)
return i, err
}
const deleteTailnetTunnel = `-- name: DeleteTailnetTunnel :one
DELETE
FROM tailnet_tunnels
WHERE coordinator_id = $1 and src_id = $2 and dst_id = $3
RETURNING coordinator_id, src_id, dst_id
`
type DeleteTailnetTunnelParams struct {
CoordinatorID uuid.UUID `db:"coordinator_id" json:"coordinator_id"`
SrcID uuid.UUID `db:"src_id" json:"src_id"`
DstID uuid.UUID `db:"dst_id" json:"dst_id"`
}
type DeleteTailnetTunnelRow struct {
CoordinatorID uuid.UUID `db:"coordinator_id" json:"coordinator_id"`
SrcID uuid.UUID `db:"src_id" json:"src_id"`
DstID uuid.UUID `db:"dst_id" json:"dst_id"`
}
func (q *sqlQuerier) DeleteTailnetTunnel(ctx context.Context, arg DeleteTailnetTunnelParams) (DeleteTailnetTunnelRow, error) {
row := q.db.QueryRowContext(ctx, deleteTailnetTunnel, arg.CoordinatorID, arg.SrcID, arg.DstID)
var i DeleteTailnetTunnelRow
err := row.Scan(&i.CoordinatorID, &i.SrcID, &i.DstID)
return i, err
}
const getAllTailnetAgents = `-- name: GetAllTailnetAgents :many
SELECT id, coordinator_id, updated_at, node
FROM tailnet_agents
`
func (q *sqlQuerier) GetAllTailnetAgents(ctx context.Context) ([]TailnetAgent, error) {
rows, err := q.db.QueryContext(ctx, getAllTailnetAgents)
if err != nil {
return nil, err
}
defer rows.Close()
var items []TailnetAgent
for rows.Next() {
var i TailnetAgent
if err := rows.Scan(
&i.ID,
&i.CoordinatorID,
&i.UpdatedAt,
&i.Node,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getAllTailnetCoordinators = `-- name: GetAllTailnetCoordinators :many
SELECT id, heartbeat_at FROM tailnet_coordinators
`
// For PG Coordinator HTMLDebug
func (q *sqlQuerier) GetAllTailnetCoordinators(ctx context.Context) ([]TailnetCoordinator, error) {
rows, err := q.db.QueryContext(ctx, getAllTailnetCoordinators)
if err != nil {
return nil, err
}
defer rows.Close()
var items []TailnetCoordinator
for rows.Next() {
var i TailnetCoordinator
if err := rows.Scan(&i.ID, &i.HeartbeatAt); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getAllTailnetPeers = `-- name: GetAllTailnetPeers :many
SELECT id, coordinator_id, updated_at, node, status FROM tailnet_peers
`
func (q *sqlQuerier) GetAllTailnetPeers(ctx context.Context) ([]TailnetPeer, error) {
rows, err := q.db.QueryContext(ctx, getAllTailnetPeers)
if err != nil {
return nil, err
}
defer rows.Close()
var items []TailnetPeer
for rows.Next() {
var i TailnetPeer
if err := rows.Scan(
&i.ID,
&i.CoordinatorID,
&i.UpdatedAt,
&i.Node,
&i.Status,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getAllTailnetTunnels = `-- name: GetAllTailnetTunnels :many
SELECT coordinator_id, src_id, dst_id, updated_at FROM tailnet_tunnels
`
func (q *sqlQuerier) GetAllTailnetTunnels(ctx context.Context) ([]TailnetTunnel, error) {
rows, err := q.db.QueryContext(ctx, getAllTailnetTunnels)
if err != nil {
return nil, err
}
defer rows.Close()
var items []TailnetTunnel
for rows.Next() {
var i TailnetTunnel
if err := rows.Scan(
&i.CoordinatorID,
&i.SrcID,
&i.DstID,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getTailnetAgents = `-- name: GetTailnetAgents :many
SELECT id, coordinator_id, updated_at, node
FROM tailnet_agents
WHERE id = $1
`
func (q *sqlQuerier) GetTailnetAgents(ctx context.Context, id uuid.UUID) ([]TailnetAgent, error) {
rows, err := q.db.QueryContext(ctx, getTailnetAgents, id)
if err != nil {
return nil, err
}
defer rows.Close()
var items []TailnetAgent
for rows.Next() {
var i TailnetAgent
if err := rows.Scan(
&i.ID,
&i.CoordinatorID,
&i.UpdatedAt,
&i.Node,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getTailnetClientsForAgent = `-- name: GetTailnetClientsForAgent :many
SELECT id, coordinator_id, updated_at, node
FROM tailnet_clients
WHERE id IN (
SELECT tailnet_client_subscriptions.client_id
FROM tailnet_client_subscriptions
WHERE tailnet_client_subscriptions.agent_id = $1
)
`
func (q *sqlQuerier) GetTailnetClientsForAgent(ctx context.Context, agentID uuid.UUID) ([]TailnetClient, error) {
rows, err := q.db.QueryContext(ctx, getTailnetClientsForAgent, agentID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []TailnetClient
for rows.Next() {
var i TailnetClient
if err := rows.Scan(
&i.ID,
&i.CoordinatorID,
&i.UpdatedAt,
&i.Node,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getTailnetPeers = `-- name: GetTailnetPeers :many
SELECT id, coordinator_id, updated_at, node, status FROM tailnet_peers WHERE id = $1
`
func (q *sqlQuerier) GetTailnetPeers(ctx context.Context, id uuid.UUID) ([]TailnetPeer, error) {
rows, err := q.db.QueryContext(ctx, getTailnetPeers, id)
if err != nil {
return nil, err
}
defer rows.Close()
var items []TailnetPeer
for rows.Next() {
var i TailnetPeer
if err := rows.Scan(
&i.ID,
&i.CoordinatorID,
&i.UpdatedAt,
&i.Node,
&i.Status,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getTailnetTunnelPeerBindings = `-- name: GetTailnetTunnelPeerBindings :many
SELECT tailnet_tunnels.dst_id as peer_id, tailnet_peers.coordinator_id, tailnet_peers.updated_at, tailnet_peers.node, tailnet_peers.status
FROM tailnet_tunnels
INNER JOIN tailnet_peers ON tailnet_tunnels.dst_id = tailnet_peers.id
WHERE tailnet_tunnels.src_id = $1
UNION
SELECT tailnet_tunnels.src_id as peer_id, tailnet_peers.coordinator_id, tailnet_peers.updated_at, tailnet_peers.node, tailnet_peers.status
FROM tailnet_tunnels
INNER JOIN tailnet_peers ON tailnet_tunnels.src_id = tailnet_peers.id
WHERE tailnet_tunnels.dst_id = $1
`
type GetTailnetTunnelPeerBindingsRow struct {
PeerID uuid.UUID `db:"peer_id" json:"peer_id"`
CoordinatorID uuid.UUID `db:"coordinator_id" json:"coordinator_id"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
Node []byte `db:"node" json:"node"`
Status TailnetStatus `db:"status" json:"status"`
}
func (q *sqlQuerier) GetTailnetTunnelPeerBindings(ctx context.Context, srcID uuid.UUID) ([]GetTailnetTunnelPeerBindingsRow, error) {
rows, err := q.db.QueryContext(ctx, getTailnetTunnelPeerBindings, srcID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetTailnetTunnelPeerBindingsRow
for rows.Next() {
var i GetTailnetTunnelPeerBindingsRow
if err := rows.Scan(
&i.PeerID,
&i.CoordinatorID,
&i.UpdatedAt,
&i.Node,
&i.Status,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getTailnetTunnelPeerIDs = `-- name: GetTailnetTunnelPeerIDs :many
SELECT dst_id as peer_id, coordinator_id, updated_at
FROM tailnet_tunnels
WHERE tailnet_tunnels.src_id = $1
UNION
SELECT src_id as peer_id, coordinator_id, updated_at
FROM tailnet_tunnels
WHERE tailnet_tunnels.dst_id = $1
`
type GetTailnetTunnelPeerIDsRow struct {
PeerID uuid.UUID `db:"peer_id" json:"peer_id"`
CoordinatorID uuid.UUID `db:"coordinator_id" json:"coordinator_id"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
func (q *sqlQuerier) GetTailnetTunnelPeerIDs(ctx context.Context, srcID uuid.UUID) ([]GetTailnetTunnelPeerIDsRow, error) {
rows, err := q.db.QueryContext(ctx, getTailnetTunnelPeerIDs, srcID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetTailnetTunnelPeerIDsRow
for rows.Next() {
var i GetTailnetTunnelPeerIDsRow
if err := rows.Scan(&i.PeerID, &i.CoordinatorID, &i.UpdatedAt); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const upsertTailnetAgent = `-- name: UpsertTailnetAgent :one
INSERT INTO
tailnet_agents (
id,
coordinator_id,
node,
updated_at
)
VALUES
($1, $2, $3, now() at time zone 'utc')
ON CONFLICT (id, coordinator_id)
DO UPDATE SET
id = $1,
coordinator_id = $2,
node = $3,
updated_at = now() at time zone 'utc'
RETURNING id, coordinator_id, updated_at, node
`
type UpsertTailnetAgentParams struct {
ID uuid.UUID `db:"id" json:"id"`
CoordinatorID uuid.UUID `db:"coordinator_id" json:"coordinator_id"`
Node json.RawMessage `db:"node" json:"node"`
}
func (q *sqlQuerier) UpsertTailnetAgent(ctx context.Context, arg UpsertTailnetAgentParams) (TailnetAgent, error) {
row := q.db.QueryRowContext(ctx, upsertTailnetAgent, arg.ID, arg.CoordinatorID, arg.Node)
var i TailnetAgent
err := row.Scan(
&i.ID,
&i.CoordinatorID,
&i.UpdatedAt,
&i.Node,
)
return i, err
}
const upsertTailnetClient = `-- name: UpsertTailnetClient :one
INSERT INTO
tailnet_clients (
id,
coordinator_id,
node,
updated_at
)
VALUES
($1, $2, $3, now() at time zone 'utc')
ON CONFLICT (id, coordinator_id)
DO UPDATE SET
id = $1,
coordinator_id = $2,
node = $3,
updated_at = now() at time zone 'utc'
RETURNING id, coordinator_id, updated_at, node
`
type UpsertTailnetClientParams struct {
ID uuid.UUID `db:"id" json:"id"`
CoordinatorID uuid.UUID `db:"coordinator_id" json:"coordinator_id"`
Node json.RawMessage `db:"node" json:"node"`
}
func (q *sqlQuerier) UpsertTailnetClient(ctx context.Context, arg UpsertTailnetClientParams) (TailnetClient, error) {
row := q.db.QueryRowContext(ctx, upsertTailnetClient, arg.ID, arg.CoordinatorID, arg.Node)
var i TailnetClient
err := row.Scan(
&i.ID,
&i.CoordinatorID,
&i.UpdatedAt,
&i.Node,
)
return i, err
}
const upsertTailnetClientSubscription = `-- name: UpsertTailnetClientSubscription :exec
INSERT INTO
tailnet_client_subscriptions (
client_id,
coordinator_id,
agent_id,
updated_at
)
VALUES
($1, $2, $3, now() at time zone 'utc')
ON CONFLICT (client_id, coordinator_id, agent_id)
DO UPDATE SET
client_id = $1,
coordinator_id = $2,
agent_id = $3,
updated_at = now() at time zone 'utc'
`
type UpsertTailnetClientSubscriptionParams struct {
ClientID uuid.UUID `db:"client_id" json:"client_id"`
CoordinatorID uuid.UUID `db:"coordinator_id" json:"coordinator_id"`
AgentID uuid.UUID `db:"agent_id" json:"agent_id"`
}
func (q *sqlQuerier) UpsertTailnetClientSubscription(ctx context.Context, arg UpsertTailnetClientSubscriptionParams) error {
_, err := q.db.ExecContext(ctx, upsertTailnetClientSubscription, arg.ClientID, arg.CoordinatorID, arg.AgentID)
return err
}
const upsertTailnetCoordinator = `-- name: UpsertTailnetCoordinator :one
INSERT INTO
tailnet_coordinators (
id,
heartbeat_at
)
VALUES
($1, now() at time zone 'utc')
ON CONFLICT (id)
DO UPDATE SET
id = $1,
heartbeat_at = now() at time zone 'utc'
RETURNING id, heartbeat_at
`
func (q *sqlQuerier) UpsertTailnetCoordinator(ctx context.Context, id uuid.UUID) (TailnetCoordinator, error) {
row := q.db.QueryRowContext(ctx, upsertTailnetCoordinator, id)
var i TailnetCoordinator
err := row.Scan(&i.ID, &i.HeartbeatAt)
return i, err
}
const upsertTailnetPeer = `-- name: UpsertTailnetPeer :one
INSERT INTO
tailnet_peers (
id,
coordinator_id,
node,
status,
updated_at
)
VALUES
($1, $2, $3, $4, now() at time zone 'utc')
ON CONFLICT (id, coordinator_id)
DO UPDATE SET
id = $1,
coordinator_id = $2,
node = $3,
status = $4,
updated_at = now() at time zone 'utc'
RETURNING id, coordinator_id, updated_at, node, status
`
type UpsertTailnetPeerParams struct {
ID uuid.UUID `db:"id" json:"id"`
CoordinatorID uuid.UUID `db:"coordinator_id" json:"coordinator_id"`
Node []byte `db:"node" json:"node"`
Status TailnetStatus `db:"status" json:"status"`
}
func (q *sqlQuerier) UpsertTailnetPeer(ctx context.Context, arg UpsertTailnetPeerParams) (TailnetPeer, error) {
row := q.db.QueryRowContext(ctx, upsertTailnetPeer,
arg.ID,
arg.CoordinatorID,
arg.Node,
arg.Status,
)
var i TailnetPeer
err := row.Scan(
&i.ID,
&i.CoordinatorID,
&i.UpdatedAt,
&i.Node,
&i.Status,
)
return i, err
}
const upsertTailnetTunnel = `-- name: UpsertTailnetTunnel :one
INSERT INTO
tailnet_tunnels (
coordinator_id,
src_id,
dst_id,
updated_at
)
VALUES
($1, $2, $3, now() at time zone 'utc')
ON CONFLICT (coordinator_id, src_id, dst_id)
DO UPDATE SET
coordinator_id = $1,
src_id = $2,
dst_id = $3,
updated_at = now() at time zone 'utc'
RETURNING coordinator_id, src_id, dst_id, updated_at
`
type UpsertTailnetTunnelParams struct {
CoordinatorID uuid.UUID `db:"coordinator_id" json:"coordinator_id"`
SrcID uuid.UUID `db:"src_id" json:"src_id"`
DstID uuid.UUID `db:"dst_id" json:"dst_id"`
}
func (q *sqlQuerier) UpsertTailnetTunnel(ctx context.Context, arg UpsertTailnetTunnelParams) (TailnetTunnel, error) {
row := q.db.QueryRowContext(ctx, upsertTailnetTunnel, arg.CoordinatorID, arg.SrcID, arg.DstID)
var i TailnetTunnel
err := row.Scan(
&i.CoordinatorID,
&i.SrcID,
&i.DstID,
&i.UpdatedAt,
)
return i, err
}
const getTemplateAverageBuildTime = `-- name: GetTemplateAverageBuildTime :one
WITH build_times AS (
SELECT
EXTRACT(EPOCH FROM (pj.completed_at - pj.started_at))::FLOAT AS exec_time_sec,
workspace_builds.transition
FROM
workspace_builds
JOIN template_versions ON
workspace_builds.template_version_id = template_versions.id
JOIN provisioner_jobs pj ON
workspace_builds.job_id = pj.id
WHERE
template_versions.template_id = $1 AND
(pj.completed_at IS NOT NULL) AND (pj.started_at IS NOT NULL) AND
(pj.started_at > $2) AND
(pj.canceled_at IS NULL) AND
((pj.error IS NULL) OR (pj.error = ''))
ORDER BY
workspace_builds.created_at DESC
)
SELECT
-- Postgres offers no clear way to DRY this short of a function or other
-- complexities.
coalesce((PERCENTILE_DISC(0.5) WITHIN GROUP(ORDER BY exec_time_sec) FILTER (WHERE transition = 'start')), -1)::FLOAT AS start_50,
coalesce((PERCENTILE_DISC(0.5) WITHIN GROUP(ORDER BY exec_time_sec) FILTER (WHERE transition = 'stop')), -1)::FLOAT AS stop_50,
coalesce((PERCENTILE_DISC(0.5) WITHIN GROUP(ORDER BY exec_time_sec) FILTER (WHERE transition = 'delete')), -1)::FLOAT AS delete_50,
coalesce((PERCENTILE_DISC(0.95) WITHIN GROUP(ORDER BY exec_time_sec) FILTER (WHERE transition = 'start')), -1)::FLOAT AS start_95,
coalesce((PERCENTILE_DISC(0.95) WITHIN GROUP(ORDER BY exec_time_sec) FILTER (WHERE transition = 'stop')), -1)::FLOAT AS stop_95,
coalesce((PERCENTILE_DISC(0.95) WITHIN GROUP(ORDER BY exec_time_sec) FILTER (WHERE transition = 'delete')), -1)::FLOAT AS delete_95
FROM build_times
`
type GetTemplateAverageBuildTimeParams struct {
TemplateID uuid.NullUUID `db:"template_id" json:"template_id"`
StartTime sql.NullTime `db:"start_time" json:"start_time"`
}
type GetTemplateAverageBuildTimeRow struct {
Start50 float64 `db:"start_50" json:"start_50"`
Stop50 float64 `db:"stop_50" json:"stop_50"`
Delete50 float64 `db:"delete_50" json:"delete_50"`
Start95 float64 `db:"start_95" json:"start_95"`
Stop95 float64 `db:"stop_95" json:"stop_95"`
Delete95 float64 `db:"delete_95" json:"delete_95"`
}
func (q *sqlQuerier) GetTemplateAverageBuildTime(ctx context.Context, arg GetTemplateAverageBuildTimeParams) (GetTemplateAverageBuildTimeRow, error) {
row := q.db.QueryRowContext(ctx, getTemplateAverageBuildTime, arg.TemplateID, arg.StartTime)
var i GetTemplateAverageBuildTimeRow
err := row.Scan(
&i.Start50,
&i.Stop50,
&i.Delete50,
&i.Start95,
&i.Stop95,
&i.Delete95,
)
return i, err
}
const getTemplateByID = `-- name: GetTemplateByID :one
SELECT
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, created_by_avatar_url, created_by_username
FROM
template_with_users
WHERE
id = $1
LIMIT
1
`
func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Template, error) {
row := q.db.QueryRowContext(ctx, getTemplateByID, id)
var i Template
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.OrganizationID,
&i.Deleted,
&i.Name,
&i.Provisioner,
&i.ActiveVersionID,
&i.Description,
&i.DefaultTTL,
&i.CreatedBy,
&i.Icon,
&i.UserACL,
&i.GroupACL,
&i.DisplayName,
&i.AllowUserCancelWorkspaceJobs,
&i.AllowUserAutostart,
&i.AllowUserAutostop,
&i.FailureTTL,
&i.TimeTilDormant,
&i.TimeTilDormantAutoDelete,
&i.AutostopRequirementDaysOfWeek,
&i.AutostopRequirementWeeks,
&i.AutostartBlockDaysOfWeek,
&i.RequireActiveVersion,
&i.Deprecated,
&i.ActivityBump,
&i.MaxPortSharingLevel,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
)
return i, err
}
const getTemplateByOrganizationAndName = `-- name: GetTemplateByOrganizationAndName :one
SELECT
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, created_by_avatar_url, created_by_username
FROM
template_with_users AS templates
WHERE
organization_id = $1
AND deleted = $2
AND LOWER("name") = LOWER($3)
LIMIT
1
`
type GetTemplateByOrganizationAndNameParams struct {
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
Deleted bool `db:"deleted" json:"deleted"`
Name string `db:"name" json:"name"`
}
func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg GetTemplateByOrganizationAndNameParams) (Template, error) {
row := q.db.QueryRowContext(ctx, getTemplateByOrganizationAndName, arg.OrganizationID, arg.Deleted, arg.Name)
var i Template
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.OrganizationID,
&i.Deleted,
&i.Name,
&i.Provisioner,
&i.ActiveVersionID,
&i.Description,
&i.DefaultTTL,
&i.CreatedBy,
&i.Icon,
&i.UserACL,
&i.GroupACL,
&i.DisplayName,
&i.AllowUserCancelWorkspaceJobs,
&i.AllowUserAutostart,
&i.AllowUserAutostop,
&i.FailureTTL,
&i.TimeTilDormant,
&i.TimeTilDormantAutoDelete,
&i.AutostopRequirementDaysOfWeek,
&i.AutostopRequirementWeeks,
&i.AutostartBlockDaysOfWeek,
&i.RequireActiveVersion,
&i.Deprecated,
&i.ActivityBump,
&i.MaxPortSharingLevel,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
)
return i, err
}
const getTemplates = `-- name: GetTemplates :many
SELECT id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, created_by_avatar_url, created_by_username FROM template_with_users AS templates
ORDER BY (name, id) ASC
`
func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) {
rows, err := q.db.QueryContext(ctx, getTemplates)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Template
for rows.Next() {
var i Template
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.OrganizationID,
&i.Deleted,
&i.Name,
&i.Provisioner,
&i.ActiveVersionID,
&i.Description,
&i.DefaultTTL,
&i.CreatedBy,
&i.Icon,
&i.UserACL,
&i.GroupACL,
&i.DisplayName,
&i.AllowUserCancelWorkspaceJobs,
&i.AllowUserAutostart,
&i.AllowUserAutostop,
&i.FailureTTL,
&i.TimeTilDormant,
&i.TimeTilDormantAutoDelete,
&i.AutostopRequirementDaysOfWeek,
&i.AutostopRequirementWeeks,
&i.AutostartBlockDaysOfWeek,
&i.RequireActiveVersion,
&i.Deprecated,
&i.ActivityBump,
&i.MaxPortSharingLevel,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getTemplatesWithFilter = `-- name: GetTemplatesWithFilter :many
SELECT
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, created_by_avatar_url, created_by_username
FROM
template_with_users AS templates
WHERE
-- Optionally include deleted templates
templates.deleted = $1
-- Filter by organization_id
AND CASE
WHEN $2 :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN
organization_id = $2
ELSE true
END
-- Filter by exact name
AND CASE
WHEN $3 :: text != '' THEN
LOWER("name") = LOWER($3)
ELSE true
END
-- Filter by ids
AND CASE
WHEN array_length($4 :: uuid[], 1) > 0 THEN
id = ANY($4)
ELSE true
END
-- Filter by deprecated
AND CASE
WHEN $5 :: boolean IS NOT NULL THEN
CASE
WHEN $5 :: boolean THEN
deprecated != ''
ELSE
deprecated = ''
END
ELSE true
END
-- Authorize Filter clause will be injected below in GetAuthorizedTemplates
-- @authorize_filter
ORDER BY (name, id) ASC
`
type GetTemplatesWithFilterParams struct {
Deleted bool `db:"deleted" json:"deleted"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
ExactName string `db:"exact_name" json:"exact_name"`
IDs []uuid.UUID `db:"ids" json:"ids"`
Deprecated sql.NullBool `db:"deprecated" json:"deprecated"`
}
func (q *sqlQuerier) GetTemplatesWithFilter(ctx context.Context, arg GetTemplatesWithFilterParams) ([]Template, error) {
rows, err := q.db.QueryContext(ctx, getTemplatesWithFilter,
arg.Deleted,
arg.OrganizationID,
arg.ExactName,
pq.Array(arg.IDs),
arg.Deprecated,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Template
for rows.Next() {
var i Template
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.OrganizationID,
&i.Deleted,
&i.Name,
&i.Provisioner,
&i.ActiveVersionID,
&i.Description,
&i.DefaultTTL,
&i.CreatedBy,
&i.Icon,
&i.UserACL,
&i.GroupACL,
&i.DisplayName,
&i.AllowUserCancelWorkspaceJobs,
&i.AllowUserAutostart,
&i.AllowUserAutostop,
&i.FailureTTL,
&i.TimeTilDormant,
&i.TimeTilDormantAutoDelete,
&i.AutostopRequirementDaysOfWeek,
&i.AutostopRequirementWeeks,
&i.AutostartBlockDaysOfWeek,
&i.RequireActiveVersion,
&i.Deprecated,
&i.ActivityBump,
&i.MaxPortSharingLevel,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertTemplate = `-- name: InsertTemplate :exec
INSERT INTO
templates (
id,
created_at,
updated_at,
organization_id,
"name",
provisioner,
active_version_id,
description,
created_by,
icon,
user_acl,
group_acl,
display_name,
allow_user_cancel_workspace_jobs,
max_port_sharing_level
)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
`
type InsertTemplateParams struct {
ID uuid.UUID `db:"id" json:"id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
Name string `db:"name" json:"name"`
Provisioner ProvisionerType `db:"provisioner" json:"provisioner"`
ActiveVersionID uuid.UUID `db:"active_version_id" json:"active_version_id"`
Description string `db:"description" json:"description"`
CreatedBy uuid.UUID `db:"created_by" json:"created_by"`
Icon string `db:"icon" json:"icon"`
UserACL TemplateACL `db:"user_acl" json:"user_acl"`
GroupACL TemplateACL `db:"group_acl" json:"group_acl"`
DisplayName string `db:"display_name" json:"display_name"`
AllowUserCancelWorkspaceJobs bool `db:"allow_user_cancel_workspace_jobs" json:"allow_user_cancel_workspace_jobs"`
MaxPortSharingLevel AppSharingLevel `db:"max_port_sharing_level" json:"max_port_sharing_level"`
}
func (q *sqlQuerier) InsertTemplate(ctx context.Context, arg InsertTemplateParams) error {
_, err := q.db.ExecContext(ctx, insertTemplate,
arg.ID,
arg.CreatedAt,
arg.UpdatedAt,
arg.OrganizationID,
arg.Name,
arg.Provisioner,
arg.ActiveVersionID,
arg.Description,
arg.CreatedBy,
arg.Icon,
arg.UserACL,
arg.GroupACL,
arg.DisplayName,
arg.AllowUserCancelWorkspaceJobs,
arg.MaxPortSharingLevel,
)
return err
}
const updateTemplateACLByID = `-- name: UpdateTemplateACLByID :exec
UPDATE
templates
SET
group_acl = $1,
user_acl = $2
WHERE
id = $3
`
type UpdateTemplateACLByIDParams struct {
GroupACL TemplateACL `db:"group_acl" json:"group_acl"`
UserACL TemplateACL `db:"user_acl" json:"user_acl"`
ID uuid.UUID `db:"id" json:"id"`
}
func (q *sqlQuerier) UpdateTemplateACLByID(ctx context.Context, arg UpdateTemplateACLByIDParams) error {
_, err := q.db.ExecContext(ctx, updateTemplateACLByID, arg.GroupACL, arg.UserACL, arg.ID)
return err
}
const updateTemplateAccessControlByID = `-- name: UpdateTemplateAccessControlByID :exec
UPDATE
templates
SET
require_active_version = $2,
deprecated = $3
WHERE
id = $1
`
type UpdateTemplateAccessControlByIDParams struct {
ID uuid.UUID `db:"id" json:"id"`
RequireActiveVersion bool `db:"require_active_version" json:"require_active_version"`
Deprecated string `db:"deprecated" json:"deprecated"`
}
func (q *sqlQuerier) UpdateTemplateAccessControlByID(ctx context.Context, arg UpdateTemplateAccessControlByIDParams) error {
_, err := q.db.ExecContext(ctx, updateTemplateAccessControlByID, arg.ID, arg.RequireActiveVersion, arg.Deprecated)
return err
}
const updateTemplateActiveVersionByID = `-- name: UpdateTemplateActiveVersionByID :exec
UPDATE
templates
SET
active_version_id = $2,
updated_at = $3
WHERE
id = $1
`
type UpdateTemplateActiveVersionByIDParams struct {
ID uuid.UUID `db:"id" json:"id"`
ActiveVersionID uuid.UUID `db:"active_version_id" json:"active_version_id"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
func (q *sqlQuerier) UpdateTemplateActiveVersionByID(ctx context.Context, arg UpdateTemplateActiveVersionByIDParams) error {
_, err := q.db.ExecContext(ctx, updateTemplateActiveVersionByID, arg.ID, arg.ActiveVersionID, arg.UpdatedAt)
return err
}
const updateTemplateDeletedByID = `-- name: UpdateTemplateDeletedByID :exec
UPDATE
templates
SET
deleted = $2,
updated_at = $3
WHERE
id = $1
`
type UpdateTemplateDeletedByIDParams struct {
ID uuid.UUID `db:"id" json:"id"`
Deleted bool `db:"deleted" json:"deleted"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
func (q *sqlQuerier) UpdateTemplateDeletedByID(ctx context.Context, arg UpdateTemplateDeletedByIDParams) error {
_, err := q.db.ExecContext(ctx, updateTemplateDeletedByID, arg.ID, arg.Deleted, arg.UpdatedAt)
return err
}
const updateTemplateMetaByID = `-- name: UpdateTemplateMetaByID :exec
UPDATE
templates
SET
updated_at = $2,
description = $3,
name = $4,
icon = $5,
display_name = $6,
allow_user_cancel_workspace_jobs = $7,
group_acl = $8,
max_port_sharing_level = $9
WHERE
id = $1
`
type UpdateTemplateMetaByIDParams struct {
ID uuid.UUID `db:"id" json:"id"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
Description string `db:"description" json:"description"`
Name string `db:"name" json:"name"`
Icon string `db:"icon" json:"icon"`
DisplayName string `db:"display_name" json:"display_name"`
AllowUserCancelWorkspaceJobs bool `db:"allow_user_cancel_workspace_jobs" json:"allow_user_cancel_workspace_jobs"`
GroupACL TemplateACL `db:"group_acl" json:"group_acl"`
MaxPortSharingLevel AppSharingLevel `db:"max_port_sharing_level" json:"max_port_sharing_level"`
}
func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTemplateMetaByIDParams) error {
_, err := q.db.ExecContext(ctx, updateTemplateMetaByID,
arg.ID,
arg.UpdatedAt,
arg.Description,
arg.Name,
arg.Icon,
arg.DisplayName,
arg.AllowUserCancelWorkspaceJobs,
arg.GroupACL,
arg.MaxPortSharingLevel,
)
return err
}
const updateTemplateScheduleByID = `-- name: UpdateTemplateScheduleByID :exec
UPDATE
templates
SET
updated_at = $2,
allow_user_autostart = $3,
allow_user_autostop = $4,
default_ttl = $5,
activity_bump = $6,
autostop_requirement_days_of_week = $7,
autostop_requirement_weeks = $8,
autostart_block_days_of_week = $9,
failure_ttl = $10,
time_til_dormant = $11,
time_til_dormant_autodelete = $12
WHERE
id = $1
`
type UpdateTemplateScheduleByIDParams struct {
ID uuid.UUID `db:"id" json:"id"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
AllowUserAutostart bool `db:"allow_user_autostart" json:"allow_user_autostart"`
AllowUserAutostop bool `db:"allow_user_autostop" json:"allow_user_autostop"`
DefaultTTL int64 `db:"default_ttl" json:"default_ttl"`
ActivityBump int64 `db:"activity_bump" json:"activity_bump"`
AutostopRequirementDaysOfWeek int16 `db:"autostop_requirement_days_of_week" json:"autostop_requirement_days_of_week"`
AutostopRequirementWeeks int64 `db:"autostop_requirement_weeks" json:"autostop_requirement_weeks"`
AutostartBlockDaysOfWeek int16 `db:"autostart_block_days_of_week" json:"autostart_block_days_of_week"`
FailureTTL int64 `db:"failure_ttl" json:"failure_ttl"`
TimeTilDormant int64 `db:"time_til_dormant" json:"time_til_dormant"`
TimeTilDormantAutoDelete int64 `db:"time_til_dormant_autodelete" json:"time_til_dormant_autodelete"`
}
func (q *sqlQuerier) UpdateTemplateScheduleByID(ctx context.Context, arg UpdateTemplateScheduleByIDParams) error {
_, err := q.db.ExecContext(ctx, updateTemplateScheduleByID,
arg.ID,
arg.UpdatedAt,
arg.AllowUserAutostart,
arg.AllowUserAutostop,
arg.DefaultTTL,
arg.ActivityBump,
arg.AutostopRequirementDaysOfWeek,
arg.AutostopRequirementWeeks,
arg.AutostartBlockDaysOfWeek,
arg.FailureTTL,
arg.TimeTilDormant,
arg.TimeTilDormantAutoDelete,
)
return err
}
const getTemplateVersionParameters = `-- name: GetTemplateVersionParameters :many
SELECT template_version_id, name, description, type, mutable, default_value, icon, options, validation_regex, validation_min, validation_max, validation_error, validation_monotonic, required, display_name, display_order, ephemeral FROM template_version_parameters WHERE template_version_id = $1 ORDER BY display_order ASC, LOWER(name) ASC
`
func (q *sqlQuerier) GetTemplateVersionParameters(ctx context.Context, templateVersionID uuid.UUID) ([]TemplateVersionParameter, error) {
rows, err := q.db.QueryContext(ctx, getTemplateVersionParameters, templateVersionID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []TemplateVersionParameter
for rows.Next() {
var i TemplateVersionParameter
if err := rows.Scan(
&i.TemplateVersionID,
&i.Name,
&i.Description,
&i.Type,
&i.Mutable,
&i.DefaultValue,
&i.Icon,
&i.Options,
&i.ValidationRegex,
&i.ValidationMin,
&i.ValidationMax,
&i.ValidationError,
&i.ValidationMonotonic,
&i.Required,
&i.DisplayName,
&i.DisplayOrder,
&i.Ephemeral,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertTemplateVersionParameter = `-- name: InsertTemplateVersionParameter :one
INSERT INTO
template_version_parameters (
template_version_id,
name,
description,
type,
mutable,
default_value,
icon,
options,
validation_regex,
validation_min,
validation_max,
validation_error,
validation_monotonic,
required,
display_name,
display_order,
ephemeral
)
VALUES
(
$1,
$2,
$3,
$4,
$5,
$6,
$7,
$8,
$9,
$10,
$11,
$12,
$13,
$14,
$15,
$16,
$17
) RETURNING template_version_id, name, description, type, mutable, default_value, icon, options, validation_regex, validation_min, validation_max, validation_error, validation_monotonic, required, display_name, display_order, ephemeral
`
type InsertTemplateVersionParameterParams struct {
TemplateVersionID uuid.UUID `db:"template_version_id" json:"template_version_id"`
Name string `db:"name" json:"name"`
Description string `db:"description" json:"description"`
Type string `db:"type" json:"type"`
Mutable bool `db:"mutable" json:"mutable"`
DefaultValue string `db:"default_value" json:"default_value"`
Icon string `db:"icon" json:"icon"`
Options json.RawMessage `db:"options" json:"options"`
ValidationRegex string `db:"validation_regex" json:"validation_regex"`
ValidationMin sql.NullInt32 `db:"validation_min" json:"validation_min"`
ValidationMax sql.NullInt32 `db:"validation_max" json:"validation_max"`
ValidationError string `db:"validation_error" json:"validation_error"`
ValidationMonotonic string `db:"validation_monotonic" json:"validation_monotonic"`
Required bool `db:"required" json:"required"`
DisplayName string `db:"display_name" json:"display_name"`
DisplayOrder int32 `db:"display_order" json:"display_order"`
Ephemeral bool `db:"ephemeral" json:"ephemeral"`
}
func (q *sqlQuerier) InsertTemplateVersionParameter(ctx context.Context, arg InsertTemplateVersionParameterParams) (TemplateVersionParameter, error) {
row := q.db.QueryRowContext(ctx, insertTemplateVersionParameter,
arg.TemplateVersionID,
arg.Name,
arg.Description,
arg.Type,
arg.Mutable,
arg.DefaultValue,
arg.Icon,
arg.Options,
arg.ValidationRegex,
arg.ValidationMin,
arg.ValidationMax,
arg.ValidationError,
arg.ValidationMonotonic,
arg.Required,
arg.DisplayName,
arg.DisplayOrder,
arg.Ephemeral,
)
var i TemplateVersionParameter
err := row.Scan(
&i.TemplateVersionID,
&i.Name,
&i.Description,
&i.Type,
&i.Mutable,
&i.DefaultValue,
&i.Icon,
&i.Options,
&i.ValidationRegex,
&i.ValidationMin,
&i.ValidationMax,
&i.ValidationError,
&i.ValidationMonotonic,
&i.Required,
&i.DisplayName,
&i.DisplayOrder,
&i.Ephemeral,
)
return i, err
}
const archiveUnusedTemplateVersions = `-- name: ArchiveUnusedTemplateVersions :many
UPDATE
template_versions
SET
archived = true,
updated_at = $1
FROM
-- Archive all versions that are returned from this query.
(
SELECT
scoped_template_versions.id
FROM
-- Scope an archive to a single template and ignore already archived template versions
(
SELECT
id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived
FROM
template_versions
WHERE
template_versions.template_id = $2 :: uuid
AND
archived = false
AND
-- This allows archiving a specific template version.
CASE
WHEN $3::uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN
template_versions.id = $3 :: uuid
ELSE
true
END
) AS scoped_template_versions
LEFT JOIN
provisioner_jobs ON scoped_template_versions.job_id = provisioner_jobs.id
LEFT JOIN
templates ON scoped_template_versions.template_id = templates.id
WHERE
-- Actively used template versions (meaning the latest build is using
-- the version) are never archived. A "restart" command on the workspace,
-- even if failed, would use the version. So it cannot be archived until
-- the build is outdated.
NOT EXISTS (
-- Return all "used" versions, where "used" is defined as being
-- used by a latest workspace build.
SELECT template_version_id FROM (
SELECT
DISTINCT ON (workspace_id) template_version_id, transition
FROM
workspace_builds
ORDER BY workspace_id, build_number DESC
) AS used_versions
WHERE
used_versions.transition != 'delete'
AND
scoped_template_versions.id = used_versions.template_version_id
)
-- Also never archive the active template version
AND active_version_id != scoped_template_versions.id
AND CASE
-- Optionally, only archive versions that match a given
-- job status like 'failed'.
WHEN $4 :: provisioner_job_status IS NOT NULL THEN
provisioner_jobs.job_status = $4 :: provisioner_job_status
ELSE
true
END
-- Pending or running jobs should not be archived, as they are "in progress"
AND provisioner_jobs.job_status != 'running'
AND provisioner_jobs.job_status != 'pending'
) AS archived_versions
WHERE
template_versions.id IN (archived_versions.id)
RETURNING template_versions.id
`
type ArchiveUnusedTemplateVersionsParams struct {
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
TemplateID uuid.UUID `db:"template_id" json:"template_id"`
TemplateVersionID uuid.UUID `db:"template_version_id" json:"template_version_id"`
JobStatus NullProvisionerJobStatus `db:"job_status" json:"job_status"`
}
// Archiving templates is a soft delete action, so is reversible.
// Archiving prevents the version from being used and discovered
// by listing.
// Only unused template versions will be archived, which are any versions not
// referenced by the latest build of a workspace.
func (q *sqlQuerier) ArchiveUnusedTemplateVersions(ctx context.Context, arg ArchiveUnusedTemplateVersionsParams) ([]uuid.UUID, error) {
rows, err := q.db.QueryContext(ctx, archiveUnusedTemplateVersions,
arg.UpdatedAt,
arg.TemplateID,
arg.TemplateVersionID,
arg.JobStatus,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []uuid.UUID
for rows.Next() {
var id uuid.UUID
if err := rows.Scan(&id); err != nil {
return nil, err
}
items = append(items, id)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getPreviousTemplateVersion = `-- name: GetPreviousTemplateVersion :one
SELECT
id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, created_by_avatar_url, created_by_username
FROM
template_version_with_user AS template_versions
WHERE
created_at < (
SELECT created_at
FROM template_version_with_user AS tv
WHERE tv.organization_id = $1 AND tv.name = $2 AND tv.template_id = $3
)
AND organization_id = $1
AND template_id = $3
ORDER BY created_at DESC
LIMIT 1
`
type GetPreviousTemplateVersionParams struct {
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
Name string `db:"name" json:"name"`
TemplateID uuid.NullUUID `db:"template_id" json:"template_id"`
}
func (q *sqlQuerier) GetPreviousTemplateVersion(ctx context.Context, arg GetPreviousTemplateVersionParams) (TemplateVersion, error) {
row := q.db.QueryRowContext(ctx, getPreviousTemplateVersion, arg.OrganizationID, arg.Name, arg.TemplateID)
var i TemplateVersion
err := row.Scan(
&i.ID,
&i.TemplateID,
&i.OrganizationID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Name,
&i.Readme,
&i.JobID,
&i.CreatedBy,
&i.ExternalAuthProviders,
&i.Message,
&i.Archived,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
)
return i, err
}
const getTemplateVersionByID = `-- name: GetTemplateVersionByID :one
SELECT
id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, created_by_avatar_url, created_by_username
FROM
template_version_with_user AS template_versions
WHERE
id = $1
`
func (q *sqlQuerier) GetTemplateVersionByID(ctx context.Context, id uuid.UUID) (TemplateVersion, error) {
row := q.db.QueryRowContext(ctx, getTemplateVersionByID, id)
var i TemplateVersion
err := row.Scan(
&i.ID,
&i.TemplateID,
&i.OrganizationID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Name,
&i.Readme,
&i.JobID,
&i.CreatedBy,
&i.ExternalAuthProviders,
&i.Message,
&i.Archived,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
)
return i, err
}
const getTemplateVersionByJobID = `-- name: GetTemplateVersionByJobID :one
SELECT
id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, created_by_avatar_url, created_by_username
FROM
template_version_with_user AS template_versions
WHERE
job_id = $1
`
func (q *sqlQuerier) GetTemplateVersionByJobID(ctx context.Context, jobID uuid.UUID) (TemplateVersion, error) {
row := q.db.QueryRowContext(ctx, getTemplateVersionByJobID, jobID)
var i TemplateVersion
err := row.Scan(
&i.ID,
&i.TemplateID,
&i.OrganizationID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Name,
&i.Readme,
&i.JobID,
&i.CreatedBy,
&i.ExternalAuthProviders,
&i.Message,
&i.Archived,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
)
return i, err
}
const getTemplateVersionByTemplateIDAndName = `-- name: GetTemplateVersionByTemplateIDAndName :one
SELECT
id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, created_by_avatar_url, created_by_username
FROM
template_version_with_user AS template_versions
WHERE
template_id = $1
AND "name" = $2
`
type GetTemplateVersionByTemplateIDAndNameParams struct {
TemplateID uuid.NullUUID `db:"template_id" json:"template_id"`
Name string `db:"name" json:"name"`
}
func (q *sqlQuerier) GetTemplateVersionByTemplateIDAndName(ctx context.Context, arg GetTemplateVersionByTemplateIDAndNameParams) (TemplateVersion, error) {
row := q.db.QueryRowContext(ctx, getTemplateVersionByTemplateIDAndName, arg.TemplateID, arg.Name)
var i TemplateVersion
err := row.Scan(
&i.ID,
&i.TemplateID,
&i.OrganizationID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Name,
&i.Readme,
&i.JobID,
&i.CreatedBy,
&i.ExternalAuthProviders,
&i.Message,
&i.Archived,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
)
return i, err
}
const getTemplateVersionsByIDs = `-- name: GetTemplateVersionsByIDs :many
SELECT
id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, created_by_avatar_url, created_by_username
FROM
template_version_with_user AS template_versions
WHERE
id = ANY($1 :: uuid [ ])
`
func (q *sqlQuerier) GetTemplateVersionsByIDs(ctx context.Context, ids []uuid.UUID) ([]TemplateVersion, error) {
rows, err := q.db.QueryContext(ctx, getTemplateVersionsByIDs, pq.Array(ids))
if err != nil {
return nil, err
}
defer rows.Close()
var items []TemplateVersion
for rows.Next() {
var i TemplateVersion
if err := rows.Scan(
&i.ID,
&i.TemplateID,
&i.OrganizationID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Name,
&i.Readme,
&i.JobID,
&i.CreatedBy,
&i.ExternalAuthProviders,
&i.Message,
&i.Archived,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getTemplateVersionsByTemplateID = `-- name: GetTemplateVersionsByTemplateID :many
SELECT
id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, created_by_avatar_url, created_by_username
FROM
template_version_with_user AS template_versions
WHERE
template_id = $1 :: uuid
AND CASE
-- If no filter is provided, default to returning ALL template versions.
-- The called should always provide a filter if they want to omit
-- archived versions.
WHEN $2 :: boolean IS NULL THEN true
ELSE template_versions.archived = $2 :: boolean
END
AND CASE
-- This allows using the last element on a page as effectively a cursor.
-- This is an important option for scripts that need to paginate without
-- duplicating or missing data.
WHEN $3 :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN (
-- The pagination cursor is the last ID of the previous page.
-- The query is ordered by the created_at field, so select all
-- rows after the cursor.
(created_at, id) > (
SELECT
created_at, id
FROM
template_versions
WHERE
id = $3
)
)
ELSE true
END
ORDER BY
-- Deterministic and consistent ordering of all rows, even if they share
-- a timestamp. This is to ensure consistent pagination.
(created_at, id) ASC OFFSET $4
LIMIT
-- A null limit means "no limit", so 0 means return all
NULLIF($5 :: int, 0)
`
type GetTemplateVersionsByTemplateIDParams struct {
TemplateID uuid.UUID `db:"template_id" json:"template_id"`
Archived sql.NullBool `db:"archived" json:"archived"`
AfterID uuid.UUID `db:"after_id" json:"after_id"`
OffsetOpt int32 `db:"offset_opt" json:"offset_opt"`
LimitOpt int32 `db:"limit_opt" json:"limit_opt"`
}
func (q *sqlQuerier) GetTemplateVersionsByTemplateID(ctx context.Context, arg GetTemplateVersionsByTemplateIDParams) ([]TemplateVersion, error) {
rows, err := q.db.QueryContext(ctx, getTemplateVersionsByTemplateID,
arg.TemplateID,
arg.Archived,
arg.AfterID,
arg.OffsetOpt,
arg.LimitOpt,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []TemplateVersion
for rows.Next() {
var i TemplateVersion
if err := rows.Scan(
&i.ID,
&i.TemplateID,
&i.OrganizationID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Name,
&i.Readme,
&i.JobID,
&i.CreatedBy,
&i.ExternalAuthProviders,
&i.Message,
&i.Archived,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getTemplateVersionsCreatedAfter = `-- name: GetTemplateVersionsCreatedAfter :many
SELECT id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, created_by_avatar_url, created_by_username FROM template_version_with_user AS template_versions WHERE created_at > $1
`
func (q *sqlQuerier) GetTemplateVersionsCreatedAfter(ctx context.Context, createdAt time.Time) ([]TemplateVersion, error) {
rows, err := q.db.QueryContext(ctx, getTemplateVersionsCreatedAfter, createdAt)
if err != nil {
return nil, err
}
defer rows.Close()
var items []TemplateVersion
for rows.Next() {
var i TemplateVersion
if err := rows.Scan(
&i.ID,
&i.TemplateID,
&i.OrganizationID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Name,
&i.Readme,
&i.JobID,
&i.CreatedBy,
&i.ExternalAuthProviders,
&i.Message,
&i.Archived,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertTemplateVersion = `-- name: InsertTemplateVersion :exec
INSERT INTO
template_versions (
id,
template_id,
organization_id,
created_at,
updated_at,
"name",
message,
readme,
job_id,
created_by
)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
`
type InsertTemplateVersionParams struct {
ID uuid.UUID `db:"id" json:"id"`
TemplateID uuid.NullUUID `db:"template_id" json:"template_id"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
Name string `db:"name" json:"name"`
Message string `db:"message" json:"message"`
Readme string `db:"readme" json:"readme"`
JobID uuid.UUID `db:"job_id" json:"job_id"`
CreatedBy uuid.UUID `db:"created_by" json:"created_by"`
}
func (q *sqlQuerier) InsertTemplateVersion(ctx context.Context, arg InsertTemplateVersionParams) error {
_, err := q.db.ExecContext(ctx, insertTemplateVersion,
arg.ID,
arg.TemplateID,
arg.OrganizationID,
arg.CreatedAt,
arg.UpdatedAt,
arg.Name,
arg.Message,
arg.Readme,
arg.JobID,
arg.CreatedBy,
)
return err
}
const unarchiveTemplateVersion = `-- name: UnarchiveTemplateVersion :exec
UPDATE
template_versions
SET
archived = false,
updated_at = $1
WHERE
id = $2
`
type UnarchiveTemplateVersionParams struct {
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
TemplateVersionID uuid.UUID `db:"template_version_id" json:"template_version_id"`
}
// This will always work regardless of the current state of the template version.
func (q *sqlQuerier) UnarchiveTemplateVersion(ctx context.Context, arg UnarchiveTemplateVersionParams) error {
_, err := q.db.ExecContext(ctx, unarchiveTemplateVersion, arg.UpdatedAt, arg.TemplateVersionID)
return err
}
const updateTemplateVersionByID = `-- name: UpdateTemplateVersionByID :exec
UPDATE
template_versions
SET
template_id = $2,
updated_at = $3,
name = $4,
message = $5
WHERE
id = $1
`
type UpdateTemplateVersionByIDParams struct {
ID uuid.UUID `db:"id" json:"id"`
TemplateID uuid.NullUUID `db:"template_id" json:"template_id"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
Name string `db:"name" json:"name"`
Message string `db:"message" json:"message"`
}
func (q *sqlQuerier) UpdateTemplateVersionByID(ctx context.Context, arg UpdateTemplateVersionByIDParams) error {
_, err := q.db.ExecContext(ctx, updateTemplateVersionByID,
arg.ID,
arg.TemplateID,
arg.UpdatedAt,
arg.Name,
arg.Message,
)
return err
}
const updateTemplateVersionDescriptionByJobID = `-- name: UpdateTemplateVersionDescriptionByJobID :exec
UPDATE
template_versions
SET
readme = $2,
updated_at = $3
WHERE
job_id = $1
`
type UpdateTemplateVersionDescriptionByJobIDParams struct {
JobID uuid.UUID `db:"job_id" json:"job_id"`
Readme string `db:"readme" json:"readme"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
func (q *sqlQuerier) UpdateTemplateVersionDescriptionByJobID(ctx context.Context, arg UpdateTemplateVersionDescriptionByJobIDParams) error {
_, err := q.db.ExecContext(ctx, updateTemplateVersionDescriptionByJobID, arg.JobID, arg.Readme, arg.UpdatedAt)
return err
}
const updateTemplateVersionExternalAuthProvidersByJobID = `-- name: UpdateTemplateVersionExternalAuthProvidersByJobID :exec
UPDATE
template_versions
SET
external_auth_providers = $2,
updated_at = $3
WHERE
job_id = $1
`
type UpdateTemplateVersionExternalAuthProvidersByJobIDParams struct {
JobID uuid.UUID `db:"job_id" json:"job_id"`
ExternalAuthProviders json.RawMessage `db:"external_auth_providers" json:"external_auth_providers"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
func (q *sqlQuerier) UpdateTemplateVersionExternalAuthProvidersByJobID(ctx context.Context, arg UpdateTemplateVersionExternalAuthProvidersByJobIDParams) error {
_, err := q.db.ExecContext(ctx, updateTemplateVersionExternalAuthProvidersByJobID, arg.JobID, arg.ExternalAuthProviders, arg.UpdatedAt)
return err
}
const getTemplateVersionVariables = `-- name: GetTemplateVersionVariables :many
SELECT template_version_id, name, description, type, value, default_value, required, sensitive FROM template_version_variables WHERE template_version_id = $1
`
func (q *sqlQuerier) GetTemplateVersionVariables(ctx context.Context, templateVersionID uuid.UUID) ([]TemplateVersionVariable, error) {
rows, err := q.db.QueryContext(ctx, getTemplateVersionVariables, templateVersionID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []TemplateVersionVariable
for rows.Next() {
var i TemplateVersionVariable
if err := rows.Scan(
&i.TemplateVersionID,
&i.Name,
&i.Description,
&i.Type,
&i.Value,
&i.DefaultValue,
&i.Required,
&i.Sensitive,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertTemplateVersionVariable = `-- name: InsertTemplateVersionVariable :one
INSERT INTO
template_version_variables (
template_version_id,
name,
description,
type,
value,
default_value,
required,
sensitive
)
VALUES
(
$1,
$2,
$3,
$4,
$5,
$6,
$7,
$8
) RETURNING template_version_id, name, description, type, value, default_value, required, sensitive
`
type InsertTemplateVersionVariableParams struct {
TemplateVersionID uuid.UUID `db:"template_version_id" json:"template_version_id"`
Name string `db:"name" json:"name"`
Description string `db:"description" json:"description"`
Type string `db:"type" json:"type"`
Value string `db:"value" json:"value"`
DefaultValue string `db:"default_value" json:"default_value"`
Required bool `db:"required" json:"required"`
Sensitive bool `db:"sensitive" json:"sensitive"`
}
func (q *sqlQuerier) InsertTemplateVersionVariable(ctx context.Context, arg InsertTemplateVersionVariableParams) (TemplateVersionVariable, error) {
row := q.db.QueryRowContext(ctx, insertTemplateVersionVariable,
arg.TemplateVersionID,
arg.Name,
arg.Description,
arg.Type,
arg.Value,
arg.DefaultValue,
arg.Required,
arg.Sensitive,
)
var i TemplateVersionVariable
err := row.Scan(
&i.TemplateVersionID,
&i.Name,
&i.Description,
&i.Type,
&i.Value,
&i.DefaultValue,
&i.Required,
&i.Sensitive,
)
return i, err
}
const getUserLinkByLinkedID = `-- name: GetUserLinkByLinkedID :one
SELECT
user_links.user_id, user_links.login_type, user_links.linked_id, user_links.oauth_access_token, user_links.oauth_refresh_token, user_links.oauth_expiry, user_links.oauth_access_token_key_id, user_links.oauth_refresh_token_key_id, user_links.debug_context
FROM
user_links
INNER JOIN
users ON user_links.user_id = users.id
WHERE
linked_id = $1
AND
deleted = false
`
func (q *sqlQuerier) GetUserLinkByLinkedID(ctx context.Context, linkedID string) (UserLink, error) {
row := q.db.QueryRowContext(ctx, getUserLinkByLinkedID, linkedID)
var i UserLink
err := row.Scan(
&i.UserID,
&i.LoginType,
&i.LinkedID,
&i.OAuthAccessToken,
&i.OAuthRefreshToken,
&i.OAuthExpiry,
&i.OAuthAccessTokenKeyID,
&i.OAuthRefreshTokenKeyID,
&i.DebugContext,
)
return i, err
}
const getUserLinkByUserIDLoginType = `-- name: GetUserLinkByUserIDLoginType :one
SELECT
user_id, login_type, linked_id, oauth_access_token, oauth_refresh_token, oauth_expiry, oauth_access_token_key_id, oauth_refresh_token_key_id, debug_context
FROM
user_links
WHERE
user_id = $1 AND login_type = $2
`
type GetUserLinkByUserIDLoginTypeParams struct {
UserID uuid.UUID `db:"user_id" json:"user_id"`
LoginType LoginType `db:"login_type" json:"login_type"`
}
func (q *sqlQuerier) GetUserLinkByUserIDLoginType(ctx context.Context, arg GetUserLinkByUserIDLoginTypeParams) (UserLink, error) {
row := q.db.QueryRowContext(ctx, getUserLinkByUserIDLoginType, arg.UserID, arg.LoginType)
var i UserLink
err := row.Scan(
&i.UserID,
&i.LoginType,
&i.LinkedID,
&i.OAuthAccessToken,
&i.OAuthRefreshToken,
&i.OAuthExpiry,
&i.OAuthAccessTokenKeyID,
&i.OAuthRefreshTokenKeyID,
&i.DebugContext,
)
return i, err
}
const getUserLinksByUserID = `-- name: GetUserLinksByUserID :many
SELECT user_id, login_type, linked_id, oauth_access_token, oauth_refresh_token, oauth_expiry, oauth_access_token_key_id, oauth_refresh_token_key_id, debug_context FROM user_links WHERE user_id = $1
`
func (q *sqlQuerier) GetUserLinksByUserID(ctx context.Context, userID uuid.UUID) ([]UserLink, error) {
rows, err := q.db.QueryContext(ctx, getUserLinksByUserID, userID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []UserLink
for rows.Next() {
var i UserLink
if err := rows.Scan(
&i.UserID,
&i.LoginType,
&i.LinkedID,
&i.OAuthAccessToken,
&i.OAuthRefreshToken,
&i.OAuthExpiry,
&i.OAuthAccessTokenKeyID,
&i.OAuthRefreshTokenKeyID,
&i.DebugContext,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertUserLink = `-- name: InsertUserLink :one
INSERT INTO
user_links (
user_id,
login_type,
linked_id,
oauth_access_token,
oauth_access_token_key_id,
oauth_refresh_token,
oauth_refresh_token_key_id,
oauth_expiry,
debug_context
)
VALUES
( $1, $2, $3, $4, $5, $6, $7, $8, $9 ) RETURNING user_id, login_type, linked_id, oauth_access_token, oauth_refresh_token, oauth_expiry, oauth_access_token_key_id, oauth_refresh_token_key_id, debug_context
`
type InsertUserLinkParams struct {
UserID uuid.UUID `db:"user_id" json:"user_id"`
LoginType LoginType `db:"login_type" json:"login_type"`
LinkedID string `db:"linked_id" json:"linked_id"`
OAuthAccessToken string `db:"oauth_access_token" json:"oauth_access_token"`
OAuthAccessTokenKeyID sql.NullString `db:"oauth_access_token_key_id" json:"oauth_access_token_key_id"`
OAuthRefreshToken string `db:"oauth_refresh_token" json:"oauth_refresh_token"`
OAuthRefreshTokenKeyID sql.NullString `db:"oauth_refresh_token_key_id" json:"oauth_refresh_token_key_id"`
OAuthExpiry time.Time `db:"oauth_expiry" json:"oauth_expiry"`
DebugContext json.RawMessage `db:"debug_context" json:"debug_context"`
}
func (q *sqlQuerier) InsertUserLink(ctx context.Context, arg InsertUserLinkParams) (UserLink, error) {
row := q.db.QueryRowContext(ctx, insertUserLink,
arg.UserID,
arg.LoginType,
arg.LinkedID,
arg.OAuthAccessToken,
arg.OAuthAccessTokenKeyID,
arg.OAuthRefreshToken,
arg.OAuthRefreshTokenKeyID,
arg.OAuthExpiry,
arg.DebugContext,
)
var i UserLink
err := row.Scan(
&i.UserID,
&i.LoginType,
&i.LinkedID,
&i.OAuthAccessToken,
&i.OAuthRefreshToken,
&i.OAuthExpiry,
&i.OAuthAccessTokenKeyID,
&i.OAuthRefreshTokenKeyID,
&i.DebugContext,
)
return i, err
}
const updateUserLink = `-- name: UpdateUserLink :one
UPDATE
user_links
SET
oauth_access_token = $1,
oauth_access_token_key_id = $2,
oauth_refresh_token = $3,
oauth_refresh_token_key_id = $4,
oauth_expiry = $5,
debug_context = $6
WHERE
user_id = $7 AND login_type = $8 RETURNING user_id, login_type, linked_id, oauth_access_token, oauth_refresh_token, oauth_expiry, oauth_access_token_key_id, oauth_refresh_token_key_id, debug_context
`
type UpdateUserLinkParams struct {
OAuthAccessToken string `db:"oauth_access_token" json:"oauth_access_token"`
OAuthAccessTokenKeyID sql.NullString `db:"oauth_access_token_key_id" json:"oauth_access_token_key_id"`
OAuthRefreshToken string `db:"oauth_refresh_token" json:"oauth_refresh_token"`
OAuthRefreshTokenKeyID sql.NullString `db:"oauth_refresh_token_key_id" json:"oauth_refresh_token_key_id"`
OAuthExpiry time.Time `db:"oauth_expiry" json:"oauth_expiry"`
DebugContext json.RawMessage `db:"debug_context" json:"debug_context"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
LoginType LoginType `db:"login_type" json:"login_type"`
}
func (q *sqlQuerier) UpdateUserLink(ctx context.Context, arg UpdateUserLinkParams) (UserLink, error) {
row := q.db.QueryRowContext(ctx, updateUserLink,
arg.OAuthAccessToken,
arg.OAuthAccessTokenKeyID,
arg.OAuthRefreshToken,
arg.OAuthRefreshTokenKeyID,
arg.OAuthExpiry,
arg.DebugContext,
arg.UserID,
arg.LoginType,
)
var i UserLink
err := row.Scan(
&i.UserID,
&i.LoginType,
&i.LinkedID,
&i.OAuthAccessToken,
&i.OAuthRefreshToken,
&i.OAuthExpiry,
&i.OAuthAccessTokenKeyID,
&i.OAuthRefreshTokenKeyID,
&i.DebugContext,
)
return i, err
}
const updateUserLinkedID = `-- name: UpdateUserLinkedID :one
UPDATE
user_links
SET
linked_id = $1
WHERE
user_id = $2 AND login_type = $3 RETURNING user_id, login_type, linked_id, oauth_access_token, oauth_refresh_token, oauth_expiry, oauth_access_token_key_id, oauth_refresh_token_key_id, debug_context
`
type UpdateUserLinkedIDParams struct {
LinkedID string `db:"linked_id" json:"linked_id"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
LoginType LoginType `db:"login_type" json:"login_type"`
}
func (q *sqlQuerier) UpdateUserLinkedID(ctx context.Context, arg UpdateUserLinkedIDParams) (UserLink, error) {
row := q.db.QueryRowContext(ctx, updateUserLinkedID, arg.LinkedID, arg.UserID, arg.LoginType)
var i UserLink
err := row.Scan(
&i.UserID,
&i.LoginType,
&i.LinkedID,
&i.OAuthAccessToken,
&i.OAuthRefreshToken,
&i.OAuthExpiry,
&i.OAuthAccessTokenKeyID,
&i.OAuthRefreshTokenKeyID,
&i.DebugContext,
)
return i, err
}
const allUserIDs = `-- name: AllUserIDs :many
SELECT DISTINCT id FROM USERS
`
// AllUserIDs returns all UserIDs regardless of user status or deletion.
func (q *sqlQuerier) AllUserIDs(ctx context.Context) ([]uuid.UUID, error) {
rows, err := q.db.QueryContext(ctx, allUserIDs)
if err != nil {
return nil, err
}
defer rows.Close()
var items []uuid.UUID
for rows.Next() {
var id uuid.UUID
if err := rows.Scan(&id); err != nil {
return nil, err
}
items = append(items, id)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getActiveUserCount = `-- name: GetActiveUserCount :one
SELECT
COUNT(*)
FROM
users
WHERE
status = 'active'::user_status AND deleted = false
`
func (q *sqlQuerier) GetActiveUserCount(ctx context.Context) (int64, error) {
row := q.db.QueryRowContext(ctx, getActiveUserCount)
var count int64
err := row.Scan(&count)
return count, err
}
const getAuthorizationUserRoles = `-- name: GetAuthorizationUserRoles :one
SELECT
-- username is returned just to help for logging purposes
-- status is used to enforce 'suspended' users, as all roles are ignored
-- when suspended.
id, username, status,
-- All user roles, including their org roles.
array_cat(
-- All users are members
array_append(users.rbac_roles, 'member'),
(
SELECT
array_agg(org_roles)
FROM
organization_members,
-- All org_members get the org-member role for their orgs
unnest(
array_append(roles, 'organization-member:' || organization_members.organization_id::text)
) AS org_roles
WHERE
user_id = users.id
)
) :: text[] AS roles,
-- All groups the user is in.
(
SELECT
array_agg(
group_members.group_id :: text
)
FROM
group_members
WHERE
user_id = users.id
) :: text[] AS groups
FROM
users
WHERE
id = $1
`
type GetAuthorizationUserRolesRow struct {
ID uuid.UUID `db:"id" json:"id"`
Username string `db:"username" json:"username"`
Status UserStatus `db:"status" json:"status"`
Roles []string `db:"roles" json:"roles"`
Groups []string `db:"groups" json:"groups"`
}
// This function returns roles for authorization purposes. Implied member roles
// are included.
func (q *sqlQuerier) GetAuthorizationUserRoles(ctx context.Context, userID uuid.UUID) (GetAuthorizationUserRolesRow, error) {
row := q.db.QueryRowContext(ctx, getAuthorizationUserRoles, userID)
var i GetAuthorizationUserRolesRow
err := row.Scan(
&i.ID,
&i.Username,
&i.Status,
pq.Array(&i.Roles),
pq.Array(&i.Groups),
)
return i, err
}
const getUserByEmailOrUsername = `-- name: GetUserByEmailOrUsername :one
SELECT
id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, login_type, avatar_url, deleted, last_seen_at, quiet_hours_schedule, theme_preference, name
FROM
users
WHERE
(LOWER(username) = LOWER($1) OR LOWER(email) = LOWER($2)) AND
deleted = false
LIMIT
1
`
type GetUserByEmailOrUsernameParams struct {
Username string `db:"username" json:"username"`
Email string `db:"email" json:"email"`
}
func (q *sqlQuerier) GetUserByEmailOrUsername(ctx context.Context, arg GetUserByEmailOrUsernameParams) (User, error) {
row := q.db.QueryRowContext(ctx, getUserByEmailOrUsername, arg.Username, arg.Email)
var i User
err := row.Scan(
&i.ID,
&i.Email,
&i.Username,
&i.HashedPassword,
&i.CreatedAt,
&i.UpdatedAt,
&i.Status,
&i.RBACRoles,
&i.LoginType,
&i.AvatarURL,
&i.Deleted,
&i.LastSeenAt,
&i.QuietHoursSchedule,
&i.ThemePreference,
&i.Name,
)
return i, err
}
const getUserByID = `-- name: GetUserByID :one
SELECT
id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, login_type, avatar_url, deleted, last_seen_at, quiet_hours_schedule, theme_preference, name
FROM
users
WHERE
id = $1
LIMIT
1
`
func (q *sqlQuerier) GetUserByID(ctx context.Context, id uuid.UUID) (User, error) {
row := q.db.QueryRowContext(ctx, getUserByID, id)
var i User
err := row.Scan(
&i.ID,
&i.Email,
&i.Username,
&i.HashedPassword,
&i.CreatedAt,
&i.UpdatedAt,
&i.Status,
&i.RBACRoles,
&i.LoginType,
&i.AvatarURL,
&i.Deleted,
&i.LastSeenAt,
&i.QuietHoursSchedule,
&i.ThemePreference,
&i.Name,
)
return i, err
}
const getUserCount = `-- name: GetUserCount :one
SELECT
COUNT(*)
FROM
users
WHERE
deleted = false
`
func (q *sqlQuerier) GetUserCount(ctx context.Context) (int64, error) {
row := q.db.QueryRowContext(ctx, getUserCount)
var count int64
err := row.Scan(&count)
return count, err
}
const getUsers = `-- name: GetUsers :many
SELECT
id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, login_type, avatar_url, deleted, last_seen_at, quiet_hours_schedule, theme_preference, name, COUNT(*) OVER() AS count
FROM
users
WHERE
users.deleted = false
AND CASE
-- This allows using the last element on a page as effectively a cursor.
-- This is an important option for scripts that need to paginate without
-- duplicating or missing data.
WHEN $1 :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN (
-- The pagination cursor is the last ID of the previous page.
-- The query is ordered by the username field, so select all
-- rows after the cursor.
(LOWER(username)) > (
SELECT
LOWER(username)
FROM
users
WHERE
id = $1
)
)
ELSE true
END
-- Start filters
-- Filter by name, email or username
AND CASE
WHEN $2 :: text != '' THEN (
email ILIKE concat('%', $2, '%')
OR username ILIKE concat('%', $2, '%')
)
ELSE true
END
-- Filter by status
AND CASE
-- @status needs to be a text because it can be empty, If it was
-- user_status enum, it would not.
WHEN cardinality($3 :: user_status[]) > 0 THEN
status = ANY($3 :: user_status[])
ELSE true
END
-- Filter by rbac_roles
AND CASE
-- @rbac_role allows filtering by rbac roles. If 'member' is included, show everyone, as
-- everyone is a member.
WHEN cardinality($4 :: text[]) > 0 AND 'member' != ANY($4 :: text[]) THEN
rbac_roles && $4 :: text[]
ELSE true
END
-- Filter by last_seen
AND CASE
WHEN $5 :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN
last_seen_at <= $5
ELSE true
END
AND CASE
WHEN $6 :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN
last_seen_at >= $6
ELSE true
END
-- End of filters
-- Authorize Filter clause will be injected below in GetAuthorizedUsers
-- @authorize_filter
ORDER BY
-- Deterministic and consistent ordering of all users. This is to ensure consistent pagination.
LOWER(username) ASC OFFSET $7
LIMIT
-- A null limit means "no limit", so 0 means return all
NULLIF($8 :: int, 0)
`
type GetUsersParams struct {
AfterID uuid.UUID `db:"after_id" json:"after_id"`
Search string `db:"search" json:"search"`
Status []UserStatus `db:"status" json:"status"`
RbacRole []string `db:"rbac_role" json:"rbac_role"`
LastSeenBefore time.Time `db:"last_seen_before" json:"last_seen_before"`
LastSeenAfter time.Time `db:"last_seen_after" json:"last_seen_after"`
OffsetOpt int32 `db:"offset_opt" json:"offset_opt"`
LimitOpt int32 `db:"limit_opt" json:"limit_opt"`
}
type GetUsersRow struct {
ID uuid.UUID `db:"id" json:"id"`
Email string `db:"email" json:"email"`
Username string `db:"username" json:"username"`
HashedPassword []byte `db:"hashed_password" json:"hashed_password"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
Status UserStatus `db:"status" json:"status"`
RBACRoles pq.StringArray `db:"rbac_roles" json:"rbac_roles"`
LoginType LoginType `db:"login_type" json:"login_type"`
AvatarURL string `db:"avatar_url" json:"avatar_url"`
Deleted bool `db:"deleted" json:"deleted"`
LastSeenAt time.Time `db:"last_seen_at" json:"last_seen_at"`
QuietHoursSchedule string `db:"quiet_hours_schedule" json:"quiet_hours_schedule"`
ThemePreference string `db:"theme_preference" json:"theme_preference"`
Name string `db:"name" json:"name"`
Count int64 `db:"count" json:"count"`
}
// This will never return deleted users.
func (q *sqlQuerier) GetUsers(ctx context.Context, arg GetUsersParams) ([]GetUsersRow, error) {
rows, err := q.db.QueryContext(ctx, getUsers,
arg.AfterID,
arg.Search,
pq.Array(arg.Status),
pq.Array(arg.RbacRole),
arg.LastSeenBefore,
arg.LastSeenAfter,
arg.OffsetOpt,
arg.LimitOpt,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetUsersRow
for rows.Next() {
var i GetUsersRow
if err := rows.Scan(
&i.ID,
&i.Email,
&i.Username,
&i.HashedPassword,
&i.CreatedAt,
&i.UpdatedAt,
&i.Status,
&i.RBACRoles,
&i.LoginType,
&i.AvatarURL,
&i.Deleted,
&i.LastSeenAt,
&i.QuietHoursSchedule,
&i.ThemePreference,
&i.Name,
&i.Count,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getUsersByIDs = `-- name: GetUsersByIDs :many
SELECT id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, login_type, avatar_url, deleted, last_seen_at, quiet_hours_schedule, theme_preference, name FROM users WHERE id = ANY($1 :: uuid [ ])
`
// This shouldn't check for deleted, because it's frequently used
// to look up references to actions. eg. a user could build a workspace
// for another user, then be deleted... we still want them to appear!
func (q *sqlQuerier) GetUsersByIDs(ctx context.Context, ids []uuid.UUID) ([]User, error) {
rows, err := q.db.QueryContext(ctx, getUsersByIDs, pq.Array(ids))
if err != nil {
return nil, err
}
defer rows.Close()
var items []User
for rows.Next() {
var i User
if err := rows.Scan(
&i.ID,
&i.Email,
&i.Username,
&i.HashedPassword,
&i.CreatedAt,
&i.UpdatedAt,
&i.Status,
&i.RBACRoles,
&i.LoginType,
&i.AvatarURL,
&i.Deleted,
&i.LastSeenAt,
&i.QuietHoursSchedule,
&i.ThemePreference,
&i.Name,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertUser = `-- name: InsertUser :one
INSERT INTO
users (
id,
email,
username,
hashed_password,
created_at,
updated_at,
rbac_roles,
login_type
)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, login_type, avatar_url, deleted, last_seen_at, quiet_hours_schedule, theme_preference, name
`
type InsertUserParams struct {
ID uuid.UUID `db:"id" json:"id"`
Email string `db:"email" json:"email"`
Username string `db:"username" json:"username"`
HashedPassword []byte `db:"hashed_password" json:"hashed_password"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
RBACRoles pq.StringArray `db:"rbac_roles" json:"rbac_roles"`
LoginType LoginType `db:"login_type" json:"login_type"`
}
func (q *sqlQuerier) InsertUser(ctx context.Context, arg InsertUserParams) (User, error) {
row := q.db.QueryRowContext(ctx, insertUser,
arg.ID,
arg.Email,
arg.Username,
arg.HashedPassword,
arg.CreatedAt,
arg.UpdatedAt,
arg.RBACRoles,
arg.LoginType,
)
var i User
err := row.Scan(
&i.ID,
&i.Email,
&i.Username,
&i.HashedPassword,
&i.CreatedAt,
&i.UpdatedAt,
&i.Status,
&i.RBACRoles,
&i.LoginType,
&i.AvatarURL,
&i.Deleted,
&i.LastSeenAt,
&i.QuietHoursSchedule,
&i.ThemePreference,
&i.Name,
)
return i, err
}
const updateInactiveUsersToDormant = `-- name: UpdateInactiveUsersToDormant :many
UPDATE
users
SET
status = 'dormant'::user_status,
updated_at = $1
WHERE
last_seen_at < $2 :: timestamp
AND status = 'active'::user_status
RETURNING id, email, last_seen_at
`
type UpdateInactiveUsersToDormantParams struct {
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
LastSeenAfter time.Time `db:"last_seen_after" json:"last_seen_after"`
}
type UpdateInactiveUsersToDormantRow struct {
ID uuid.UUID `db:"id" json:"id"`
Email string `db:"email" json:"email"`
LastSeenAt time.Time `db:"last_seen_at" json:"last_seen_at"`
}
func (q *sqlQuerier) UpdateInactiveUsersToDormant(ctx context.Context, arg UpdateInactiveUsersToDormantParams) ([]UpdateInactiveUsersToDormantRow, error) {
rows, err := q.db.QueryContext(ctx, updateInactiveUsersToDormant, arg.UpdatedAt, arg.LastSeenAfter)
if err != nil {
return nil, err
}
defer rows.Close()
var items []UpdateInactiveUsersToDormantRow
for rows.Next() {
var i UpdateInactiveUsersToDormantRow
if err := rows.Scan(&i.ID, &i.Email, &i.LastSeenAt); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const updateUserAppearanceSettings = `-- name: UpdateUserAppearanceSettings :one
UPDATE
users
SET
theme_preference = $2,
updated_at = $3
WHERE
id = $1
RETURNING id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, login_type, avatar_url, deleted, last_seen_at, quiet_hours_schedule, theme_preference, name
`
type UpdateUserAppearanceSettingsParams struct {
ID uuid.UUID `db:"id" json:"id"`
ThemePreference string `db:"theme_preference" json:"theme_preference"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
func (q *sqlQuerier) UpdateUserAppearanceSettings(ctx context.Context, arg UpdateUserAppearanceSettingsParams) (User, error) {
row := q.db.QueryRowContext(ctx, updateUserAppearanceSettings, arg.ID, arg.ThemePreference, arg.UpdatedAt)
var i User
err := row.Scan(
&i.ID,
&i.Email,
&i.Username,
&i.HashedPassword,
&i.CreatedAt,
&i.UpdatedAt,
&i.Status,
&i.RBACRoles,
&i.LoginType,
&i.AvatarURL,
&i.Deleted,
&i.LastSeenAt,
&i.QuietHoursSchedule,
&i.ThemePreference,
&i.Name,
)
return i, err
}
const updateUserDeletedByID = `-- name: UpdateUserDeletedByID :exec
UPDATE
users
SET
deleted = true
WHERE
id = $1
`
func (q *sqlQuerier) UpdateUserDeletedByID(ctx context.Context, id uuid.UUID) error {
_, err := q.db.ExecContext(ctx, updateUserDeletedByID, id)
return err
}
const updateUserHashedPassword = `-- name: UpdateUserHashedPassword :exec
UPDATE
users
SET
hashed_password = $2
WHERE
id = $1
`
type UpdateUserHashedPasswordParams struct {
ID uuid.UUID `db:"id" json:"id"`
HashedPassword []byte `db:"hashed_password" json:"hashed_password"`
}
func (q *sqlQuerier) UpdateUserHashedPassword(ctx context.Context, arg UpdateUserHashedPasswordParams) error {
_, err := q.db.ExecContext(ctx, updateUserHashedPassword, arg.ID, arg.HashedPassword)
return err
}
const updateUserLastSeenAt = `-- name: UpdateUserLastSeenAt :one
UPDATE
users
SET
last_seen_at = $2,
updated_at = $3
WHERE
id = $1 RETURNING id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, login_type, avatar_url, deleted, last_seen_at, quiet_hours_schedule, theme_preference, name
`
type UpdateUserLastSeenAtParams struct {
ID uuid.UUID `db:"id" json:"id"`
LastSeenAt time.Time `db:"last_seen_at" json:"last_seen_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
func (q *sqlQuerier) UpdateUserLastSeenAt(ctx context.Context, arg UpdateUserLastSeenAtParams) (User, error) {
row := q.db.QueryRowContext(ctx, updateUserLastSeenAt, arg.ID, arg.LastSeenAt, arg.UpdatedAt)
var i User
err := row.Scan(
&i.ID,
&i.Email,
&i.Username,
&i.HashedPassword,
&i.CreatedAt,
&i.UpdatedAt,
&i.Status,
&i.RBACRoles,
&i.LoginType,
&i.AvatarURL,
&i.Deleted,
&i.LastSeenAt,
&i.QuietHoursSchedule,
&i.ThemePreference,
&i.Name,
)
return i, err
}
const updateUserLoginType = `-- name: UpdateUserLoginType :one
UPDATE
users
SET
login_type = $1,
hashed_password = CASE WHEN $1 = 'password' :: login_type THEN
users.hashed_password
ELSE
-- If the login type is not password, then the password should be
-- cleared.
'':: bytea
END
WHERE
id = $2 RETURNING id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, login_type, avatar_url, deleted, last_seen_at, quiet_hours_schedule, theme_preference, name
`
type UpdateUserLoginTypeParams struct {
NewLoginType LoginType `db:"new_login_type" json:"new_login_type"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
}
func (q *sqlQuerier) UpdateUserLoginType(ctx context.Context, arg UpdateUserLoginTypeParams) (User, error) {
row := q.db.QueryRowContext(ctx, updateUserLoginType, arg.NewLoginType, arg.UserID)
var i User
err := row.Scan(
&i.ID,
&i.Email,
&i.Username,
&i.HashedPassword,
&i.CreatedAt,
&i.UpdatedAt,
&i.Status,
&i.RBACRoles,
&i.LoginType,
&i.AvatarURL,
&i.Deleted,
&i.LastSeenAt,
&i.QuietHoursSchedule,
&i.ThemePreference,
&i.Name,
)
return i, err
}
const updateUserProfile = `-- name: UpdateUserProfile :one
UPDATE
users
SET
email = $2,
username = $3,
avatar_url = $4,
updated_at = $5,
name = $6
WHERE
id = $1
RETURNING id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, login_type, avatar_url, deleted, last_seen_at, quiet_hours_schedule, theme_preference, name
`
type UpdateUserProfileParams struct {
ID uuid.UUID `db:"id" json:"id"`
Email string `db:"email" json:"email"`
Username string `db:"username" json:"username"`
AvatarURL string `db:"avatar_url" json:"avatar_url"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
Name string `db:"name" json:"name"`
}
func (q *sqlQuerier) UpdateUserProfile(ctx context.Context, arg UpdateUserProfileParams) (User, error) {
row := q.db.QueryRowContext(ctx, updateUserProfile,
arg.ID,
arg.Email,
arg.Username,
arg.AvatarURL,
arg.UpdatedAt,
arg.Name,
)
var i User
err := row.Scan(
&i.ID,
&i.Email,
&i.Username,
&i.HashedPassword,
&i.CreatedAt,
&i.UpdatedAt,
&i.Status,
&i.RBACRoles,
&i.LoginType,
&i.AvatarURL,
&i.Deleted,
&i.LastSeenAt,
&i.QuietHoursSchedule,
&i.ThemePreference,
&i.Name,
)
return i, err
}
const updateUserQuietHoursSchedule = `-- name: UpdateUserQuietHoursSchedule :one
UPDATE
users
SET
quiet_hours_schedule = $2
WHERE
id = $1
RETURNING id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, login_type, avatar_url, deleted, last_seen_at, quiet_hours_schedule, theme_preference, name
`
type UpdateUserQuietHoursScheduleParams struct {
ID uuid.UUID `db:"id" json:"id"`
QuietHoursSchedule string `db:"quiet_hours_schedule" json:"quiet_hours_schedule"`
}
func (q *sqlQuerier) UpdateUserQuietHoursSchedule(ctx context.Context, arg UpdateUserQuietHoursScheduleParams) (User, error) {
row := q.db.QueryRowContext(ctx, updateUserQuietHoursSchedule, arg.ID, arg.QuietHoursSchedule)
var i User
err := row.Scan(
&i.ID,
&i.Email,
&i.Username,
&i.HashedPassword,
&i.CreatedAt,
&i.UpdatedAt,
&i.Status,
&i.RBACRoles,
&i.LoginType,
&i.AvatarURL,
&i.Deleted,
&i.LastSeenAt,
&i.QuietHoursSchedule,
&i.ThemePreference,
&i.Name,
)
return i, err
}
const updateUserRoles = `-- name: UpdateUserRoles :one
UPDATE
users
SET
-- Remove all duplicates from the roles.
rbac_roles = ARRAY(SELECT DISTINCT UNNEST($1 :: text[]))
WHERE
id = $2
RETURNING id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, login_type, avatar_url, deleted, last_seen_at, quiet_hours_schedule, theme_preference, name
`
type UpdateUserRolesParams struct {
GrantedRoles []string `db:"granted_roles" json:"granted_roles"`
ID uuid.UUID `db:"id" json:"id"`
}
func (q *sqlQuerier) UpdateUserRoles(ctx context.Context, arg UpdateUserRolesParams) (User, error) {
row := q.db.QueryRowContext(ctx, updateUserRoles, pq.Array(arg.GrantedRoles), arg.ID)
var i User
err := row.Scan(
&i.ID,
&i.Email,
&i.Username,
&i.HashedPassword,
&i.CreatedAt,
&i.UpdatedAt,
&i.Status,
&i.RBACRoles,
&i.LoginType,
&i.AvatarURL,
&i.Deleted,
&i.LastSeenAt,
&i.QuietHoursSchedule,
&i.ThemePreference,
&i.Name,
)
return i, err
}
const updateUserStatus = `-- name: UpdateUserStatus :one
UPDATE
users
SET
status = $2,
updated_at = $3
WHERE
id = $1 RETURNING id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, login_type, avatar_url, deleted, last_seen_at, quiet_hours_schedule, theme_preference, name
`
type UpdateUserStatusParams struct {
ID uuid.UUID `db:"id" json:"id"`
Status UserStatus `db:"status" json:"status"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
func (q *sqlQuerier) UpdateUserStatus(ctx context.Context, arg UpdateUserStatusParams) (User, error) {
row := q.db.QueryRowContext(ctx, updateUserStatus, arg.ID, arg.Status, arg.UpdatedAt)
var i User
err := row.Scan(
&i.ID,
&i.Email,
&i.Username,
&i.HashedPassword,
&i.CreatedAt,
&i.UpdatedAt,
&i.Status,
&i.RBACRoles,
&i.LoginType,
&i.AvatarURL,
&i.Deleted,
&i.LastSeenAt,
&i.QuietHoursSchedule,
&i.ThemePreference,
&i.Name,
)
return i, err
}
const deleteWorkspaceAgentPortShare = `-- name: DeleteWorkspaceAgentPortShare :exec
DELETE FROM
workspace_agent_port_share
WHERE
workspace_id = $1
AND agent_name = $2
AND port = $3
`
type DeleteWorkspaceAgentPortShareParams struct {
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
AgentName string `db:"agent_name" json:"agent_name"`
Port int32 `db:"port" json:"port"`
}
func (q *sqlQuerier) DeleteWorkspaceAgentPortShare(ctx context.Context, arg DeleteWorkspaceAgentPortShareParams) error {
_, err := q.db.ExecContext(ctx, deleteWorkspaceAgentPortShare, arg.WorkspaceID, arg.AgentName, arg.Port)
return err
}
const deleteWorkspaceAgentPortSharesByTemplate = `-- name: DeleteWorkspaceAgentPortSharesByTemplate :exec
DELETE FROM
workspace_agent_port_share
WHERE
workspace_id IN (
SELECT
id
FROM
workspaces
WHERE
template_id = $1
)
`
func (q *sqlQuerier) DeleteWorkspaceAgentPortSharesByTemplate(ctx context.Context, templateID uuid.UUID) error {
_, err := q.db.ExecContext(ctx, deleteWorkspaceAgentPortSharesByTemplate, templateID)
return err
}
const getWorkspaceAgentPortShare = `-- name: GetWorkspaceAgentPortShare :one
SELECT
workspace_id, agent_name, port, share_level, protocol
FROM
workspace_agent_port_share
WHERE
workspace_id = $1
AND agent_name = $2
AND port = $3
`
type GetWorkspaceAgentPortShareParams struct {
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
AgentName string `db:"agent_name" json:"agent_name"`
Port int32 `db:"port" json:"port"`
}
func (q *sqlQuerier) GetWorkspaceAgentPortShare(ctx context.Context, arg GetWorkspaceAgentPortShareParams) (WorkspaceAgentPortShare, error) {
row := q.db.QueryRowContext(ctx, getWorkspaceAgentPortShare, arg.WorkspaceID, arg.AgentName, arg.Port)
var i WorkspaceAgentPortShare
err := row.Scan(
&i.WorkspaceID,
&i.AgentName,
&i.Port,
&i.ShareLevel,
&i.Protocol,
)
return i, err
}
const listWorkspaceAgentPortShares = `-- name: ListWorkspaceAgentPortShares :many
SELECT
workspace_id, agent_name, port, share_level, protocol
FROM
workspace_agent_port_share
WHERE
workspace_id = $1
`
func (q *sqlQuerier) ListWorkspaceAgentPortShares(ctx context.Context, workspaceID uuid.UUID) ([]WorkspaceAgentPortShare, error) {
rows, err := q.db.QueryContext(ctx, listWorkspaceAgentPortShares, workspaceID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceAgentPortShare
for rows.Next() {
var i WorkspaceAgentPortShare
if err := rows.Scan(
&i.WorkspaceID,
&i.AgentName,
&i.Port,
&i.ShareLevel,
&i.Protocol,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const reduceWorkspaceAgentShareLevelToAuthenticatedByTemplate = `-- name: ReduceWorkspaceAgentShareLevelToAuthenticatedByTemplate :exec
UPDATE
workspace_agent_port_share
SET
share_level = 'authenticated'
WHERE
share_level = 'public'
AND workspace_id IN (
SELECT
id
FROM
workspaces
WHERE
template_id = $1
)
`
func (q *sqlQuerier) ReduceWorkspaceAgentShareLevelToAuthenticatedByTemplate(ctx context.Context, templateID uuid.UUID) error {
_, err := q.db.ExecContext(ctx, reduceWorkspaceAgentShareLevelToAuthenticatedByTemplate, templateID)
return err
}
const upsertWorkspaceAgentPortShare = `-- name: UpsertWorkspaceAgentPortShare :one
INSERT INTO
workspace_agent_port_share (
workspace_id,
agent_name,
port,
share_level,
protocol
)
VALUES (
$1,
$2,
$3,
$4,
$5
)
ON CONFLICT (
workspace_id,
agent_name,
port
)
DO UPDATE SET
share_level = $4,
protocol = $5
RETURNING workspace_id, agent_name, port, share_level, protocol
`
type UpsertWorkspaceAgentPortShareParams struct {
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
AgentName string `db:"agent_name" json:"agent_name"`
Port int32 `db:"port" json:"port"`
ShareLevel AppSharingLevel `db:"share_level" json:"share_level"`
Protocol PortShareProtocol `db:"protocol" json:"protocol"`
}
func (q *sqlQuerier) UpsertWorkspaceAgentPortShare(ctx context.Context, arg UpsertWorkspaceAgentPortShareParams) (WorkspaceAgentPortShare, error) {
row := q.db.QueryRowContext(ctx, upsertWorkspaceAgentPortShare,
arg.WorkspaceID,
arg.AgentName,
arg.Port,
arg.ShareLevel,
arg.Protocol,
)
var i WorkspaceAgentPortShare
err := row.Scan(
&i.WorkspaceID,
&i.AgentName,
&i.Port,
&i.ShareLevel,
&i.Protocol,
)
return i, err
}
const deleteOldWorkspaceAgentLogs = `-- name: DeleteOldWorkspaceAgentLogs :exec
DELETE FROM workspace_agent_logs WHERE agent_id IN
(SELECT id FROM workspace_agents WHERE last_connected_at IS NOT NULL
AND last_connected_at < NOW() - INTERVAL '7 day')
`
// If an agent hasn't connected in the last 7 days, we purge it's logs.
// Logs can take up a lot of space, so it's important we clean up frequently.
func (q *sqlQuerier) DeleteOldWorkspaceAgentLogs(ctx context.Context) error {
_, err := q.db.ExecContext(ctx, deleteOldWorkspaceAgentLogs)
return err
}
const getWorkspaceAgentAndLatestBuildByAuthToken = `-- name: GetWorkspaceAgentAndLatestBuildByAuthToken :one
SELECT
workspaces.id, workspaces.created_at, workspaces.updated_at, workspaces.owner_id, workspaces.organization_id, workspaces.template_id, workspaces.deleted, workspaces.name, workspaces.autostart_schedule, workspaces.ttl, workspaces.last_used_at, workspaces.dormant_at, workspaces.deleting_at, workspaces.automatic_updates, workspaces.favorite,
workspace_agents.id, workspace_agents.created_at, workspace_agents.updated_at, workspace_agents.name, workspace_agents.first_connected_at, workspace_agents.last_connected_at, workspace_agents.disconnected_at, workspace_agents.resource_id, workspace_agents.auth_token, workspace_agents.auth_instance_id, workspace_agents.architecture, workspace_agents.environment_variables, workspace_agents.operating_system, workspace_agents.instance_metadata, workspace_agents.resource_metadata, workspace_agents.directory, workspace_agents.version, workspace_agents.last_connected_replica_id, workspace_agents.connection_timeout_seconds, workspace_agents.troubleshooting_url, workspace_agents.motd_file, workspace_agents.lifecycle_state, workspace_agents.expanded_directory, workspace_agents.logs_length, workspace_agents.logs_overflowed, workspace_agents.started_at, workspace_agents.ready_at, workspace_agents.subsystems, workspace_agents.display_apps, workspace_agents.api_version, workspace_agents.display_order,
workspace_build_with_user.id, workspace_build_with_user.created_at, workspace_build_with_user.updated_at, workspace_build_with_user.workspace_id, workspace_build_with_user.template_version_id, workspace_build_with_user.build_number, workspace_build_with_user.transition, workspace_build_with_user.initiator_id, workspace_build_with_user.provisioner_state, workspace_build_with_user.job_id, workspace_build_with_user.deadline, workspace_build_with_user.reason, workspace_build_with_user.daily_cost, workspace_build_with_user.max_deadline, workspace_build_with_user.initiator_by_avatar_url, workspace_build_with_user.initiator_by_username
FROM
workspace_agents
JOIN
workspace_resources
ON
workspace_agents.resource_id = workspace_resources.id
JOIN
workspace_build_with_user
ON
workspace_resources.job_id = workspace_build_with_user.job_id
JOIN
workspaces
ON
workspace_build_with_user.workspace_id = workspaces.id
WHERE
-- This should only match 1 agent, so 1 returned row or 0.
workspace_agents.auth_token = $1::uuid
AND workspaces.deleted = FALSE
-- Filter out builds that are not the latest.
AND workspace_build_with_user.build_number = (
-- Select from workspace_builds as it's one less join compared
-- to workspace_build_with_user.
SELECT
MAX(build_number)
FROM
workspace_builds
WHERE
workspace_id = workspace_build_with_user.workspace_id
)
`
type GetWorkspaceAgentAndLatestBuildByAuthTokenRow struct {
Workspace Workspace `db:"workspace" json:"workspace"`
WorkspaceAgent WorkspaceAgent `db:"workspace_agent" json:"workspace_agent"`
WorkspaceBuild WorkspaceBuild `db:"workspace_build" json:"workspace_build"`
}
func (q *sqlQuerier) GetWorkspaceAgentAndLatestBuildByAuthToken(ctx context.Context, authToken uuid.UUID) (GetWorkspaceAgentAndLatestBuildByAuthTokenRow, error) {
row := q.db.QueryRowContext(ctx, getWorkspaceAgentAndLatestBuildByAuthToken, authToken)
var i GetWorkspaceAgentAndLatestBuildByAuthTokenRow
err := row.Scan(
&i.Workspace.ID,
&i.Workspace.CreatedAt,
&i.Workspace.UpdatedAt,
&i.Workspace.OwnerID,
&i.Workspace.OrganizationID,
&i.Workspace.TemplateID,
&i.Workspace.Deleted,
&i.Workspace.Name,
&i.Workspace.AutostartSchedule,
&i.Workspace.Ttl,
&i.Workspace.LastUsedAt,
&i.Workspace.DormantAt,
&i.Workspace.DeletingAt,
&i.Workspace.AutomaticUpdates,
&i.Workspace.Favorite,
&i.WorkspaceAgent.ID,
&i.WorkspaceAgent.CreatedAt,
&i.WorkspaceAgent.UpdatedAt,
&i.WorkspaceAgent.Name,
&i.WorkspaceAgent.FirstConnectedAt,
&i.WorkspaceAgent.LastConnectedAt,
&i.WorkspaceAgent.DisconnectedAt,
&i.WorkspaceAgent.ResourceID,
&i.WorkspaceAgent.AuthToken,
&i.WorkspaceAgent.AuthInstanceID,
&i.WorkspaceAgent.Architecture,
&i.WorkspaceAgent.EnvironmentVariables,
&i.WorkspaceAgent.OperatingSystem,
&i.WorkspaceAgent.InstanceMetadata,
&i.WorkspaceAgent.ResourceMetadata,
&i.WorkspaceAgent.Directory,
&i.WorkspaceAgent.Version,
&i.WorkspaceAgent.LastConnectedReplicaID,
&i.WorkspaceAgent.ConnectionTimeoutSeconds,
&i.WorkspaceAgent.TroubleshootingURL,
&i.WorkspaceAgent.MOTDFile,
&i.WorkspaceAgent.LifecycleState,
&i.WorkspaceAgent.ExpandedDirectory,
&i.WorkspaceAgent.LogsLength,
&i.WorkspaceAgent.LogsOverflowed,
&i.WorkspaceAgent.StartedAt,
&i.WorkspaceAgent.ReadyAt,
pq.Array(&i.WorkspaceAgent.Subsystems),
pq.Array(&i.WorkspaceAgent.DisplayApps),
&i.WorkspaceAgent.APIVersion,
&i.WorkspaceAgent.DisplayOrder,
&i.WorkspaceBuild.ID,
&i.WorkspaceBuild.CreatedAt,
&i.WorkspaceBuild.UpdatedAt,
&i.WorkspaceBuild.WorkspaceID,
&i.WorkspaceBuild.TemplateVersionID,
&i.WorkspaceBuild.BuildNumber,
&i.WorkspaceBuild.Transition,
&i.WorkspaceBuild.InitiatorID,
&i.WorkspaceBuild.ProvisionerState,
&i.WorkspaceBuild.JobID,
&i.WorkspaceBuild.Deadline,
&i.WorkspaceBuild.Reason,
&i.WorkspaceBuild.DailyCost,
&i.WorkspaceBuild.MaxDeadline,
&i.WorkspaceBuild.InitiatorByAvatarUrl,
&i.WorkspaceBuild.InitiatorByUsername,
)
return i, err
}
const getWorkspaceAgentByID = `-- name: GetWorkspaceAgentByID :one
SELECT
id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, expanded_directory, logs_length, logs_overflowed, started_at, ready_at, subsystems, display_apps, api_version, display_order
FROM
workspace_agents
WHERE
id = $1
`
func (q *sqlQuerier) GetWorkspaceAgentByID(ctx context.Context, id uuid.UUID) (WorkspaceAgent, error) {
row := q.db.QueryRowContext(ctx, getWorkspaceAgentByID, id)
var i WorkspaceAgent
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Name,
&i.FirstConnectedAt,
&i.LastConnectedAt,
&i.DisconnectedAt,
&i.ResourceID,
&i.AuthToken,
&i.AuthInstanceID,
&i.Architecture,
&i.EnvironmentVariables,
&i.OperatingSystem,
&i.InstanceMetadata,
&i.ResourceMetadata,
&i.Directory,
&i.Version,
&i.LastConnectedReplicaID,
&i.ConnectionTimeoutSeconds,
&i.TroubleshootingURL,
&i.MOTDFile,
&i.LifecycleState,
&i.ExpandedDirectory,
&i.LogsLength,
&i.LogsOverflowed,
&i.StartedAt,
&i.ReadyAt,
pq.Array(&i.Subsystems),
pq.Array(&i.DisplayApps),
&i.APIVersion,
&i.DisplayOrder,
)
return i, err
}
const getWorkspaceAgentByInstanceID = `-- name: GetWorkspaceAgentByInstanceID :one
SELECT
id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, expanded_directory, logs_length, logs_overflowed, started_at, ready_at, subsystems, display_apps, api_version, display_order
FROM
workspace_agents
WHERE
auth_instance_id = $1 :: TEXT
ORDER BY
created_at DESC
`
func (q *sqlQuerier) GetWorkspaceAgentByInstanceID(ctx context.Context, authInstanceID string) (WorkspaceAgent, error) {
row := q.db.QueryRowContext(ctx, getWorkspaceAgentByInstanceID, authInstanceID)
var i WorkspaceAgent
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Name,
&i.FirstConnectedAt,
&i.LastConnectedAt,
&i.DisconnectedAt,
&i.ResourceID,
&i.AuthToken,
&i.AuthInstanceID,
&i.Architecture,
&i.EnvironmentVariables,
&i.OperatingSystem,
&i.InstanceMetadata,
&i.ResourceMetadata,
&i.Directory,
&i.Version,
&i.LastConnectedReplicaID,
&i.ConnectionTimeoutSeconds,
&i.TroubleshootingURL,
&i.MOTDFile,
&i.LifecycleState,
&i.ExpandedDirectory,
&i.LogsLength,
&i.LogsOverflowed,
&i.StartedAt,
&i.ReadyAt,
pq.Array(&i.Subsystems),
pq.Array(&i.DisplayApps),
&i.APIVersion,
&i.DisplayOrder,
)
return i, err
}
const getWorkspaceAgentLifecycleStateByID = `-- name: GetWorkspaceAgentLifecycleStateByID :one
SELECT
lifecycle_state,
started_at,
ready_at
FROM
workspace_agents
WHERE
id = $1
`
type GetWorkspaceAgentLifecycleStateByIDRow struct {
LifecycleState WorkspaceAgentLifecycleState `db:"lifecycle_state" json:"lifecycle_state"`
StartedAt sql.NullTime `db:"started_at" json:"started_at"`
ReadyAt sql.NullTime `db:"ready_at" json:"ready_at"`
}
func (q *sqlQuerier) GetWorkspaceAgentLifecycleStateByID(ctx context.Context, id uuid.UUID) (GetWorkspaceAgentLifecycleStateByIDRow, error) {
row := q.db.QueryRowContext(ctx, getWorkspaceAgentLifecycleStateByID, id)
var i GetWorkspaceAgentLifecycleStateByIDRow
err := row.Scan(&i.LifecycleState, &i.StartedAt, &i.ReadyAt)
return i, err
}
const getWorkspaceAgentLogSourcesByAgentIDs = `-- name: GetWorkspaceAgentLogSourcesByAgentIDs :many
SELECT workspace_agent_id, id, created_at, display_name, icon FROM workspace_agent_log_sources WHERE workspace_agent_id = ANY($1 :: uuid [ ])
`
func (q *sqlQuerier) GetWorkspaceAgentLogSourcesByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAgentLogSource, error) {
rows, err := q.db.QueryContext(ctx, getWorkspaceAgentLogSourcesByAgentIDs, pq.Array(ids))
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceAgentLogSource
for rows.Next() {
var i WorkspaceAgentLogSource
if err := rows.Scan(
&i.WorkspaceAgentID,
&i.ID,
&i.CreatedAt,
&i.DisplayName,
&i.Icon,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getWorkspaceAgentLogsAfter = `-- name: GetWorkspaceAgentLogsAfter :many
SELECT
agent_id, created_at, output, id, level, log_source_id
FROM
workspace_agent_logs
WHERE
agent_id = $1
AND (
id > $2
) ORDER BY id ASC
`
type GetWorkspaceAgentLogsAfterParams struct {
AgentID uuid.UUID `db:"agent_id" json:"agent_id"`
CreatedAfter int64 `db:"created_after" json:"created_after"`
}
func (q *sqlQuerier) GetWorkspaceAgentLogsAfter(ctx context.Context, arg GetWorkspaceAgentLogsAfterParams) ([]WorkspaceAgentLog, error) {
rows, err := q.db.QueryContext(ctx, getWorkspaceAgentLogsAfter, arg.AgentID, arg.CreatedAfter)
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceAgentLog
for rows.Next() {
var i WorkspaceAgentLog
if err := rows.Scan(
&i.AgentID,
&i.CreatedAt,
&i.Output,
&i.ID,
&i.Level,
&i.LogSourceID,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getWorkspaceAgentMetadata = `-- name: GetWorkspaceAgentMetadata :many
SELECT
workspace_agent_id, display_name, key, script, value, error, timeout, interval, collected_at, display_order
FROM
workspace_agent_metadata
WHERE
workspace_agent_id = $1
AND CASE WHEN COALESCE(array_length($2::text[], 1), 0) > 0 THEN key = ANY($2::text[]) ELSE TRUE END
`
type GetWorkspaceAgentMetadataParams struct {
WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"`
Keys []string `db:"keys" json:"keys"`
}
func (q *sqlQuerier) GetWorkspaceAgentMetadata(ctx context.Context, arg GetWorkspaceAgentMetadataParams) ([]WorkspaceAgentMetadatum, error) {
rows, err := q.db.QueryContext(ctx, getWorkspaceAgentMetadata, arg.WorkspaceAgentID, pq.Array(arg.Keys))
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceAgentMetadatum
for rows.Next() {
var i WorkspaceAgentMetadatum
if err := rows.Scan(
&i.WorkspaceAgentID,
&i.DisplayName,
&i.Key,
&i.Script,
&i.Value,
&i.Error,
&i.Timeout,
&i.Interval,
&i.CollectedAt,
&i.DisplayOrder,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getWorkspaceAgentsByResourceIDs = `-- name: GetWorkspaceAgentsByResourceIDs :many
SELECT
id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, expanded_directory, logs_length, logs_overflowed, started_at, ready_at, subsystems, display_apps, api_version, display_order
FROM
workspace_agents
WHERE
resource_id = ANY($1 :: uuid [ ])
`
func (q *sqlQuerier) GetWorkspaceAgentsByResourceIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAgent, error) {
rows, err := q.db.QueryContext(ctx, getWorkspaceAgentsByResourceIDs, pq.Array(ids))
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceAgent
for rows.Next() {
var i WorkspaceAgent
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Name,
&i.FirstConnectedAt,
&i.LastConnectedAt,
&i.DisconnectedAt,
&i.ResourceID,
&i.AuthToken,
&i.AuthInstanceID,
&i.Architecture,
&i.EnvironmentVariables,
&i.OperatingSystem,
&i.InstanceMetadata,
&i.ResourceMetadata,
&i.Directory,
&i.Version,
&i.LastConnectedReplicaID,
&i.ConnectionTimeoutSeconds,
&i.TroubleshootingURL,
&i.MOTDFile,
&i.LifecycleState,
&i.ExpandedDirectory,
&i.LogsLength,
&i.LogsOverflowed,
&i.StartedAt,
&i.ReadyAt,
pq.Array(&i.Subsystems),
pq.Array(&i.DisplayApps),
&i.APIVersion,
&i.DisplayOrder,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getWorkspaceAgentsCreatedAfter = `-- name: GetWorkspaceAgentsCreatedAfter :many
SELECT id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, expanded_directory, logs_length, logs_overflowed, started_at, ready_at, subsystems, display_apps, api_version, display_order FROM workspace_agents WHERE created_at > $1
`
func (q *sqlQuerier) GetWorkspaceAgentsCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceAgent, error) {
rows, err := q.db.QueryContext(ctx, getWorkspaceAgentsCreatedAfter, createdAt)
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceAgent
for rows.Next() {
var i WorkspaceAgent
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Name,
&i.FirstConnectedAt,
&i.LastConnectedAt,
&i.DisconnectedAt,
&i.ResourceID,
&i.AuthToken,
&i.AuthInstanceID,
&i.Architecture,
&i.EnvironmentVariables,
&i.OperatingSystem,
&i.InstanceMetadata,
&i.ResourceMetadata,
&i.Directory,
&i.Version,
&i.LastConnectedReplicaID,
&i.ConnectionTimeoutSeconds,
&i.TroubleshootingURL,
&i.MOTDFile,
&i.LifecycleState,
&i.ExpandedDirectory,
&i.LogsLength,
&i.LogsOverflowed,
&i.StartedAt,
&i.ReadyAt,
pq.Array(&i.Subsystems),
pq.Array(&i.DisplayApps),
&i.APIVersion,
&i.DisplayOrder,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getWorkspaceAgentsInLatestBuildByWorkspaceID = `-- name: GetWorkspaceAgentsInLatestBuildByWorkspaceID :many
SELECT
workspace_agents.id, workspace_agents.created_at, workspace_agents.updated_at, workspace_agents.name, workspace_agents.first_connected_at, workspace_agents.last_connected_at, workspace_agents.disconnected_at, workspace_agents.resource_id, workspace_agents.auth_token, workspace_agents.auth_instance_id, workspace_agents.architecture, workspace_agents.environment_variables, workspace_agents.operating_system, workspace_agents.instance_metadata, workspace_agents.resource_metadata, workspace_agents.directory, workspace_agents.version, workspace_agents.last_connected_replica_id, workspace_agents.connection_timeout_seconds, workspace_agents.troubleshooting_url, workspace_agents.motd_file, workspace_agents.lifecycle_state, workspace_agents.expanded_directory, workspace_agents.logs_length, workspace_agents.logs_overflowed, workspace_agents.started_at, workspace_agents.ready_at, workspace_agents.subsystems, workspace_agents.display_apps, workspace_agents.api_version, workspace_agents.display_order
FROM
workspace_agents
JOIN
workspace_resources ON workspace_agents.resource_id = workspace_resources.id
JOIN
workspace_builds ON workspace_resources.job_id = workspace_builds.job_id
WHERE
workspace_builds.workspace_id = $1 :: uuid AND
workspace_builds.build_number = (
SELECT
MAX(build_number)
FROM
workspace_builds AS wb
WHERE
wb.workspace_id = $1 :: uuid
)
`
func (q *sqlQuerier) GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) ([]WorkspaceAgent, error) {
rows, err := q.db.QueryContext(ctx, getWorkspaceAgentsInLatestBuildByWorkspaceID, workspaceID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceAgent
for rows.Next() {
var i WorkspaceAgent
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Name,
&i.FirstConnectedAt,
&i.LastConnectedAt,
&i.DisconnectedAt,
&i.ResourceID,
&i.AuthToken,
&i.AuthInstanceID,
&i.Architecture,
&i.EnvironmentVariables,
&i.OperatingSystem,
&i.InstanceMetadata,
&i.ResourceMetadata,
&i.Directory,
&i.Version,
&i.LastConnectedReplicaID,
&i.ConnectionTimeoutSeconds,
&i.TroubleshootingURL,
&i.MOTDFile,
&i.LifecycleState,
&i.ExpandedDirectory,
&i.LogsLength,
&i.LogsOverflowed,
&i.StartedAt,
&i.ReadyAt,
pq.Array(&i.Subsystems),
pq.Array(&i.DisplayApps),
&i.APIVersion,
&i.DisplayOrder,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertWorkspaceAgent = `-- name: InsertWorkspaceAgent :one
INSERT INTO
workspace_agents (
id,
created_at,
updated_at,
name,
resource_id,
auth_token,
auth_instance_id,
architecture,
environment_variables,
operating_system,
directory,
instance_metadata,
resource_metadata,
connection_timeout_seconds,
troubleshooting_url,
motd_file,
display_apps,
display_order
)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18) RETURNING id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, expanded_directory, logs_length, logs_overflowed, started_at, ready_at, subsystems, display_apps, api_version, display_order
`
type InsertWorkspaceAgentParams struct {
ID uuid.UUID `db:"id" json:"id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
Name string `db:"name" json:"name"`
ResourceID uuid.UUID `db:"resource_id" json:"resource_id"`
AuthToken uuid.UUID `db:"auth_token" json:"auth_token"`
AuthInstanceID sql.NullString `db:"auth_instance_id" json:"auth_instance_id"`
Architecture string `db:"architecture" json:"architecture"`
EnvironmentVariables pqtype.NullRawMessage `db:"environment_variables" json:"environment_variables"`
OperatingSystem string `db:"operating_system" json:"operating_system"`
Directory string `db:"directory" json:"directory"`
InstanceMetadata pqtype.NullRawMessage `db:"instance_metadata" json:"instance_metadata"`
ResourceMetadata pqtype.NullRawMessage `db:"resource_metadata" json:"resource_metadata"`
ConnectionTimeoutSeconds int32 `db:"connection_timeout_seconds" json:"connection_timeout_seconds"`
TroubleshootingURL string `db:"troubleshooting_url" json:"troubleshooting_url"`
MOTDFile string `db:"motd_file" json:"motd_file"`
DisplayApps []DisplayApp `db:"display_apps" json:"display_apps"`
DisplayOrder int32 `db:"display_order" json:"display_order"`
}
func (q *sqlQuerier) InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspaceAgentParams) (WorkspaceAgent, error) {
row := q.db.QueryRowContext(ctx, insertWorkspaceAgent,
arg.ID,
arg.CreatedAt,
arg.UpdatedAt,
arg.Name,
arg.ResourceID,
arg.AuthToken,
arg.AuthInstanceID,
arg.Architecture,
arg.EnvironmentVariables,
arg.OperatingSystem,
arg.Directory,
arg.InstanceMetadata,
arg.ResourceMetadata,
arg.ConnectionTimeoutSeconds,
arg.TroubleshootingURL,
arg.MOTDFile,
pq.Array(arg.DisplayApps),
arg.DisplayOrder,
)
var i WorkspaceAgent
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Name,
&i.FirstConnectedAt,
&i.LastConnectedAt,
&i.DisconnectedAt,
&i.ResourceID,
&i.AuthToken,
&i.AuthInstanceID,
&i.Architecture,
&i.EnvironmentVariables,
&i.OperatingSystem,
&i.InstanceMetadata,
&i.ResourceMetadata,
&i.Directory,
&i.Version,
&i.LastConnectedReplicaID,
&i.ConnectionTimeoutSeconds,
&i.TroubleshootingURL,
&i.MOTDFile,
&i.LifecycleState,
&i.ExpandedDirectory,
&i.LogsLength,
&i.LogsOverflowed,
&i.StartedAt,
&i.ReadyAt,
pq.Array(&i.Subsystems),
pq.Array(&i.DisplayApps),
&i.APIVersion,
&i.DisplayOrder,
)
return i, err
}
const insertWorkspaceAgentLogSources = `-- name: InsertWorkspaceAgentLogSources :many
INSERT INTO
workspace_agent_log_sources (workspace_agent_id, created_at, id, display_name, icon)
SELECT
$1 :: uuid AS workspace_agent_id,
$2 :: timestamptz AS created_at,
unnest($3 :: uuid [ ]) AS id,
unnest($4 :: VARCHAR(127) [ ]) AS display_name,
unnest($5 :: text [ ]) AS icon
RETURNING workspace_agent_log_sources.workspace_agent_id, workspace_agent_log_sources.id, workspace_agent_log_sources.created_at, workspace_agent_log_sources.display_name, workspace_agent_log_sources.icon
`
type InsertWorkspaceAgentLogSourcesParams struct {
WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
ID []uuid.UUID `db:"id" json:"id"`
DisplayName []string `db:"display_name" json:"display_name"`
Icon []string `db:"icon" json:"icon"`
}
func (q *sqlQuerier) InsertWorkspaceAgentLogSources(ctx context.Context, arg InsertWorkspaceAgentLogSourcesParams) ([]WorkspaceAgentLogSource, error) {
rows, err := q.db.QueryContext(ctx, insertWorkspaceAgentLogSources,
arg.WorkspaceAgentID,
arg.CreatedAt,
pq.Array(arg.ID),
pq.Array(arg.DisplayName),
pq.Array(arg.Icon),
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceAgentLogSource
for rows.Next() {
var i WorkspaceAgentLogSource
if err := rows.Scan(
&i.WorkspaceAgentID,
&i.ID,
&i.CreatedAt,
&i.DisplayName,
&i.Icon,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertWorkspaceAgentLogs = `-- name: InsertWorkspaceAgentLogs :many
WITH new_length AS (
UPDATE workspace_agents SET
logs_length = logs_length + $6 WHERE workspace_agents.id = $1
)
INSERT INTO
workspace_agent_logs (agent_id, created_at, output, level, log_source_id)
SELECT
$1 :: uuid AS agent_id,
$2 :: timestamptz AS created_at,
unnest($3 :: VARCHAR(1024) [ ]) AS output,
unnest($4 :: log_level [ ]) AS level,
$5 :: uuid AS log_source_id
RETURNING workspace_agent_logs.agent_id, workspace_agent_logs.created_at, workspace_agent_logs.output, workspace_agent_logs.id, workspace_agent_logs.level, workspace_agent_logs.log_source_id
`
type InsertWorkspaceAgentLogsParams struct {
AgentID uuid.UUID `db:"agent_id" json:"agent_id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
Output []string `db:"output" json:"output"`
Level []LogLevel `db:"level" json:"level"`
LogSourceID uuid.UUID `db:"log_source_id" json:"log_source_id"`
OutputLength int32 `db:"output_length" json:"output_length"`
}
func (q *sqlQuerier) InsertWorkspaceAgentLogs(ctx context.Context, arg InsertWorkspaceAgentLogsParams) ([]WorkspaceAgentLog, error) {
rows, err := q.db.QueryContext(ctx, insertWorkspaceAgentLogs,
arg.AgentID,
arg.CreatedAt,
pq.Array(arg.Output),
pq.Array(arg.Level),
arg.LogSourceID,
arg.OutputLength,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceAgentLog
for rows.Next() {
var i WorkspaceAgentLog
if err := rows.Scan(
&i.AgentID,
&i.CreatedAt,
&i.Output,
&i.ID,
&i.Level,
&i.LogSourceID,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertWorkspaceAgentMetadata = `-- name: InsertWorkspaceAgentMetadata :exec
INSERT INTO
workspace_agent_metadata (
workspace_agent_id,
display_name,
key,
script,
timeout,
interval,
display_order
)
VALUES
($1, $2, $3, $4, $5, $6, $7)
`
type InsertWorkspaceAgentMetadataParams struct {
WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"`
DisplayName string `db:"display_name" json:"display_name"`
Key string `db:"key" json:"key"`
Script string `db:"script" json:"script"`
Timeout int64 `db:"timeout" json:"timeout"`
Interval int64 `db:"interval" json:"interval"`
DisplayOrder int32 `db:"display_order" json:"display_order"`
}
func (q *sqlQuerier) InsertWorkspaceAgentMetadata(ctx context.Context, arg InsertWorkspaceAgentMetadataParams) error {
_, err := q.db.ExecContext(ctx, insertWorkspaceAgentMetadata,
arg.WorkspaceAgentID,
arg.DisplayName,
arg.Key,
arg.Script,
arg.Timeout,
arg.Interval,
arg.DisplayOrder,
)
return err
}
const updateWorkspaceAgentConnectionByID = `-- name: UpdateWorkspaceAgentConnectionByID :exec
UPDATE
workspace_agents
SET
first_connected_at = $2,
last_connected_at = $3,
last_connected_replica_id = $4,
disconnected_at = $5,
updated_at = $6
WHERE
id = $1
`
type UpdateWorkspaceAgentConnectionByIDParams struct {
ID uuid.UUID `db:"id" json:"id"`
FirstConnectedAt sql.NullTime `db:"first_connected_at" json:"first_connected_at"`
LastConnectedAt sql.NullTime `db:"last_connected_at" json:"last_connected_at"`
LastConnectedReplicaID uuid.NullUUID `db:"last_connected_replica_id" json:"last_connected_replica_id"`
DisconnectedAt sql.NullTime `db:"disconnected_at" json:"disconnected_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
func (q *sqlQuerier) UpdateWorkspaceAgentConnectionByID(ctx context.Context, arg UpdateWorkspaceAgentConnectionByIDParams) error {
_, err := q.db.ExecContext(ctx, updateWorkspaceAgentConnectionByID,
arg.ID,
arg.FirstConnectedAt,
arg.LastConnectedAt,
arg.LastConnectedReplicaID,
arg.DisconnectedAt,
arg.UpdatedAt,
)
return err
}
const updateWorkspaceAgentLifecycleStateByID = `-- name: UpdateWorkspaceAgentLifecycleStateByID :exec
UPDATE
workspace_agents
SET
lifecycle_state = $2,
started_at = $3,
ready_at = $4
WHERE
id = $1
`
type UpdateWorkspaceAgentLifecycleStateByIDParams struct {
ID uuid.UUID `db:"id" json:"id"`
LifecycleState WorkspaceAgentLifecycleState `db:"lifecycle_state" json:"lifecycle_state"`
StartedAt sql.NullTime `db:"started_at" json:"started_at"`
ReadyAt sql.NullTime `db:"ready_at" json:"ready_at"`
}
func (q *sqlQuerier) UpdateWorkspaceAgentLifecycleStateByID(ctx context.Context, arg UpdateWorkspaceAgentLifecycleStateByIDParams) error {
_, err := q.db.ExecContext(ctx, updateWorkspaceAgentLifecycleStateByID,
arg.ID,
arg.LifecycleState,
arg.StartedAt,
arg.ReadyAt,
)
return err
}
const updateWorkspaceAgentLogOverflowByID = `-- name: UpdateWorkspaceAgentLogOverflowByID :exec
UPDATE
workspace_agents
SET
logs_overflowed = $2
WHERE
id = $1
`
type UpdateWorkspaceAgentLogOverflowByIDParams struct {
ID uuid.UUID `db:"id" json:"id"`
LogsOverflowed bool `db:"logs_overflowed" json:"logs_overflowed"`
}
func (q *sqlQuerier) UpdateWorkspaceAgentLogOverflowByID(ctx context.Context, arg UpdateWorkspaceAgentLogOverflowByIDParams) error {
_, err := q.db.ExecContext(ctx, updateWorkspaceAgentLogOverflowByID, arg.ID, arg.LogsOverflowed)
return err
}
const updateWorkspaceAgentMetadata = `-- name: UpdateWorkspaceAgentMetadata :exec
WITH metadata AS (
SELECT
unnest($2::text[]) AS key,
unnest($3::text[]) AS value,
unnest($4::text[]) AS error,
unnest($5::timestamptz[]) AS collected_at
)
UPDATE
workspace_agent_metadata wam
SET
value = m.value,
error = m.error,
collected_at = m.collected_at
FROM
metadata m
WHERE
wam.workspace_agent_id = $1
AND wam.key = m.key
`
type UpdateWorkspaceAgentMetadataParams struct {
WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"`
Key []string `db:"key" json:"key"`
Value []string `db:"value" json:"value"`
Error []string `db:"error" json:"error"`
CollectedAt []time.Time `db:"collected_at" json:"collected_at"`
}
func (q *sqlQuerier) UpdateWorkspaceAgentMetadata(ctx context.Context, arg UpdateWorkspaceAgentMetadataParams) error {
_, err := q.db.ExecContext(ctx, updateWorkspaceAgentMetadata,
arg.WorkspaceAgentID,
pq.Array(arg.Key),
pq.Array(arg.Value),
pq.Array(arg.Error),
pq.Array(arg.CollectedAt),
)
return err
}
const updateWorkspaceAgentStartupByID = `-- name: UpdateWorkspaceAgentStartupByID :exec
UPDATE
workspace_agents
SET
version = $2,
expanded_directory = $3,
subsystems = $4,
api_version = $5
WHERE
id = $1
`
type UpdateWorkspaceAgentStartupByIDParams struct {
ID uuid.UUID `db:"id" json:"id"`
Version string `db:"version" json:"version"`
ExpandedDirectory string `db:"expanded_directory" json:"expanded_directory"`
Subsystems []WorkspaceAgentSubsystem `db:"subsystems" json:"subsystems"`
APIVersion string `db:"api_version" json:"api_version"`
}
func (q *sqlQuerier) UpdateWorkspaceAgentStartupByID(ctx context.Context, arg UpdateWorkspaceAgentStartupByIDParams) error {
_, err := q.db.ExecContext(ctx, updateWorkspaceAgentStartupByID,
arg.ID,
arg.Version,
arg.ExpandedDirectory,
pq.Array(arg.Subsystems),
arg.APIVersion,
)
return err
}
const deleteOldWorkspaceAgentStats = `-- name: DeleteOldWorkspaceAgentStats :exec
DELETE FROM
workspace_agent_stats
WHERE
created_at < (
SELECT
COALESCE(
-- When generating initial template usage stats, all the
-- raw agent stats are needed, after that only ~30 mins
-- from last rollup is needed. Deployment stats seem to
-- use between 15 mins and 1 hour of data. We keep a
-- little bit more (1 day) just in case.
MAX(start_time) - '1 days'::interval,
-- Fall back to 6 months ago if there are no template
-- usage stats so that we don't delete the data before
-- it's rolled up.
NOW() - '6 months'::interval
)
FROM
template_usage_stats
)
AND created_at < (
-- Delete at most in batches of 4 hours (with this batch size, assuming
-- 1 iteration / 10 minutes, we can clear out the previous 6 months of
-- data in 7.5 days) whilst keeping the DB load low.
SELECT
COALESCE(MIN(created_at) + '4 hours'::interval, NOW())
FROM
workspace_agent_stats
)
`
func (q *sqlQuerier) DeleteOldWorkspaceAgentStats(ctx context.Context) error {
_, err := q.db.ExecContext(ctx, deleteOldWorkspaceAgentStats)
return err
}
const getDeploymentDAUs = `-- name: GetDeploymentDAUs :many
SELECT
(created_at at TIME ZONE cast($1::integer as text))::date as date,
user_id
FROM
workspace_agent_stats
WHERE
connection_count > 0
GROUP BY
date, user_id
ORDER BY
date ASC
`
type GetDeploymentDAUsRow struct {
Date time.Time `db:"date" json:"date"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
}
func (q *sqlQuerier) GetDeploymentDAUs(ctx context.Context, tzOffset int32) ([]GetDeploymentDAUsRow, error) {
rows, err := q.db.QueryContext(ctx, getDeploymentDAUs, tzOffset)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetDeploymentDAUsRow
for rows.Next() {
var i GetDeploymentDAUsRow
if err := rows.Scan(&i.Date, &i.UserID); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getDeploymentWorkspaceAgentStats = `-- name: GetDeploymentWorkspaceAgentStats :one
WITH agent_stats AS (
SELECT
coalesce(SUM(rx_bytes), 0)::bigint AS workspace_rx_bytes,
coalesce(SUM(tx_bytes), 0)::bigint AS workspace_tx_bytes,
coalesce((PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY connection_median_latency_ms)), -1)::FLOAT AS workspace_connection_latency_50,
coalesce((PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY connection_median_latency_ms)), -1)::FLOAT AS workspace_connection_latency_95
FROM workspace_agent_stats
-- The greater than 0 is to support legacy agents that don't report connection_median_latency_ms.
WHERE workspace_agent_stats.created_at > $1 AND connection_median_latency_ms > 0
), latest_agent_stats AS (
SELECT
coalesce(SUM(session_count_vscode), 0)::bigint AS session_count_vscode,
coalesce(SUM(session_count_ssh), 0)::bigint AS session_count_ssh,
coalesce(SUM(session_count_jetbrains), 0)::bigint AS session_count_jetbrains,
coalesce(SUM(session_count_reconnecting_pty), 0)::bigint AS session_count_reconnecting_pty
FROM (
SELECT id, created_at, user_id, agent_id, workspace_id, template_id, connections_by_proto, connection_count, rx_packets, rx_bytes, tx_packets, tx_bytes, connection_median_latency_ms, session_count_vscode, session_count_jetbrains, session_count_reconnecting_pty, session_count_ssh, ROW_NUMBER() OVER(PARTITION BY agent_id ORDER BY created_at DESC) AS rn
FROM workspace_agent_stats WHERE created_at > $1
) AS a WHERE a.rn = 1
)
SELECT workspace_rx_bytes, workspace_tx_bytes, workspace_connection_latency_50, workspace_connection_latency_95, session_count_vscode, session_count_ssh, session_count_jetbrains, session_count_reconnecting_pty FROM agent_stats, latest_agent_stats
`
type GetDeploymentWorkspaceAgentStatsRow struct {
WorkspaceRxBytes int64 `db:"workspace_rx_bytes" json:"workspace_rx_bytes"`
WorkspaceTxBytes int64 `db:"workspace_tx_bytes" json:"workspace_tx_bytes"`
WorkspaceConnectionLatency50 float64 `db:"workspace_connection_latency_50" json:"workspace_connection_latency_50"`
WorkspaceConnectionLatency95 float64 `db:"workspace_connection_latency_95" json:"workspace_connection_latency_95"`
SessionCountVSCode int64 `db:"session_count_vscode" json:"session_count_vscode"`
SessionCountSSH int64 `db:"session_count_ssh" json:"session_count_ssh"`
SessionCountJetBrains int64 `db:"session_count_jetbrains" json:"session_count_jetbrains"`
SessionCountReconnectingPTY int64 `db:"session_count_reconnecting_pty" json:"session_count_reconnecting_pty"`
}
func (q *sqlQuerier) GetDeploymentWorkspaceAgentStats(ctx context.Context, createdAt time.Time) (GetDeploymentWorkspaceAgentStatsRow, error) {
row := q.db.QueryRowContext(ctx, getDeploymentWorkspaceAgentStats, createdAt)
var i GetDeploymentWorkspaceAgentStatsRow
err := row.Scan(
&i.WorkspaceRxBytes,
&i.WorkspaceTxBytes,
&i.WorkspaceConnectionLatency50,
&i.WorkspaceConnectionLatency95,
&i.SessionCountVSCode,
&i.SessionCountSSH,
&i.SessionCountJetBrains,
&i.SessionCountReconnectingPTY,
)
return i, err
}
const getTemplateDAUs = `-- name: GetTemplateDAUs :many
SELECT
(created_at at TIME ZONE cast($2::integer as text))::date as date,
user_id
FROM
workspace_agent_stats
WHERE
template_id = $1 AND
connection_count > 0
GROUP BY
date, user_id
ORDER BY
date ASC
`
type GetTemplateDAUsParams struct {
TemplateID uuid.UUID `db:"template_id" json:"template_id"`
TzOffset int32 `db:"tz_offset" json:"tz_offset"`
}
type GetTemplateDAUsRow struct {
Date time.Time `db:"date" json:"date"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
}
func (q *sqlQuerier) GetTemplateDAUs(ctx context.Context, arg GetTemplateDAUsParams) ([]GetTemplateDAUsRow, error) {
rows, err := q.db.QueryContext(ctx, getTemplateDAUs, arg.TemplateID, arg.TzOffset)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetTemplateDAUsRow
for rows.Next() {
var i GetTemplateDAUsRow
if err := rows.Scan(&i.Date, &i.UserID); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getWorkspaceAgentStats = `-- name: GetWorkspaceAgentStats :many
WITH agent_stats AS (
SELECT
user_id,
agent_id,
workspace_id,
template_id,
MIN(created_at)::timestamptz AS aggregated_from,
coalesce(SUM(rx_bytes), 0)::bigint AS workspace_rx_bytes,
coalesce(SUM(tx_bytes), 0)::bigint AS workspace_tx_bytes,
coalesce((PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY connection_median_latency_ms)), -1)::FLOAT AS workspace_connection_latency_50,
coalesce((PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY connection_median_latency_ms)), -1)::FLOAT AS workspace_connection_latency_95
FROM workspace_agent_stats
-- The greater than 0 is to support legacy agents that don't report connection_median_latency_ms.
WHERE workspace_agent_stats.created_at > $1 AND connection_median_latency_ms > 0 GROUP BY user_id, agent_id, workspace_id, template_id
), latest_agent_stats AS (
SELECT
a.agent_id,
coalesce(SUM(session_count_vscode), 0)::bigint AS session_count_vscode,
coalesce(SUM(session_count_ssh), 0)::bigint AS session_count_ssh,
coalesce(SUM(session_count_jetbrains), 0)::bigint AS session_count_jetbrains,
coalesce(SUM(session_count_reconnecting_pty), 0)::bigint AS session_count_reconnecting_pty
FROM (
SELECT id, created_at, user_id, agent_id, workspace_id, template_id, connections_by_proto, connection_count, rx_packets, rx_bytes, tx_packets, tx_bytes, connection_median_latency_ms, session_count_vscode, session_count_jetbrains, session_count_reconnecting_pty, session_count_ssh, ROW_NUMBER() OVER(PARTITION BY agent_id ORDER BY created_at DESC) AS rn
FROM workspace_agent_stats WHERE created_at > $1
) AS a WHERE a.rn = 1 GROUP BY a.user_id, a.agent_id, a.workspace_id, a.template_id
)
SELECT user_id, agent_stats.agent_id, workspace_id, template_id, aggregated_from, workspace_rx_bytes, workspace_tx_bytes, workspace_connection_latency_50, workspace_connection_latency_95, latest_agent_stats.agent_id, session_count_vscode, session_count_ssh, session_count_jetbrains, session_count_reconnecting_pty FROM agent_stats JOIN latest_agent_stats ON agent_stats.agent_id = latest_agent_stats.agent_id
`
type GetWorkspaceAgentStatsRow struct {
UserID uuid.UUID `db:"user_id" json:"user_id"`
AgentID uuid.UUID `db:"agent_id" json:"agent_id"`
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
TemplateID uuid.UUID `db:"template_id" json:"template_id"`
AggregatedFrom time.Time `db:"aggregated_from" json:"aggregated_from"`
WorkspaceRxBytes int64 `db:"workspace_rx_bytes" json:"workspace_rx_bytes"`
WorkspaceTxBytes int64 `db:"workspace_tx_bytes" json:"workspace_tx_bytes"`
WorkspaceConnectionLatency50 float64 `db:"workspace_connection_latency_50" json:"workspace_connection_latency_50"`
WorkspaceConnectionLatency95 float64 `db:"workspace_connection_latency_95" json:"workspace_connection_latency_95"`
AgentID_2 uuid.UUID `db:"agent_id_2" json:"agent_id_2"`
SessionCountVSCode int64 `db:"session_count_vscode" json:"session_count_vscode"`
SessionCountSSH int64 `db:"session_count_ssh" json:"session_count_ssh"`
SessionCountJetBrains int64 `db:"session_count_jetbrains" json:"session_count_jetbrains"`
SessionCountReconnectingPTY int64 `db:"session_count_reconnecting_pty" json:"session_count_reconnecting_pty"`
}
func (q *sqlQuerier) GetWorkspaceAgentStats(ctx context.Context, createdAt time.Time) ([]GetWorkspaceAgentStatsRow, error) {
rows, err := q.db.QueryContext(ctx, getWorkspaceAgentStats, createdAt)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetWorkspaceAgentStatsRow
for rows.Next() {
var i GetWorkspaceAgentStatsRow
if err := rows.Scan(
&i.UserID,
&i.AgentID,
&i.WorkspaceID,
&i.TemplateID,
&i.AggregatedFrom,
&i.WorkspaceRxBytes,
&i.WorkspaceTxBytes,
&i.WorkspaceConnectionLatency50,
&i.WorkspaceConnectionLatency95,
&i.AgentID_2,
&i.SessionCountVSCode,
&i.SessionCountSSH,
&i.SessionCountJetBrains,
&i.SessionCountReconnectingPTY,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getWorkspaceAgentStatsAndLabels = `-- name: GetWorkspaceAgentStatsAndLabels :many
WITH agent_stats AS (
SELECT
user_id,
agent_id,
workspace_id,
coalesce(SUM(rx_bytes), 0)::bigint AS rx_bytes,
coalesce(SUM(tx_bytes), 0)::bigint AS tx_bytes
FROM workspace_agent_stats
WHERE workspace_agent_stats.created_at > $1
GROUP BY user_id, agent_id, workspace_id
), latest_agent_stats AS (
SELECT
a.agent_id,
coalesce(SUM(session_count_vscode), 0)::bigint AS session_count_vscode,
coalesce(SUM(session_count_ssh), 0)::bigint AS session_count_ssh,
coalesce(SUM(session_count_jetbrains), 0)::bigint AS session_count_jetbrains,
coalesce(SUM(session_count_reconnecting_pty), 0)::bigint AS session_count_reconnecting_pty,
coalesce(SUM(connection_count), 0)::bigint AS connection_count,
coalesce(MAX(connection_median_latency_ms), 0)::float AS connection_median_latency_ms
FROM (
SELECT id, created_at, user_id, agent_id, workspace_id, template_id, connections_by_proto, connection_count, rx_packets, rx_bytes, tx_packets, tx_bytes, connection_median_latency_ms, session_count_vscode, session_count_jetbrains, session_count_reconnecting_pty, session_count_ssh, ROW_NUMBER() OVER(PARTITION BY agent_id ORDER BY created_at DESC) AS rn
FROM workspace_agent_stats
-- The greater than 0 is to support legacy agents that don't report connection_median_latency_ms.
WHERE created_at > $1 AND connection_median_latency_ms > 0
) AS a
WHERE a.rn = 1
GROUP BY a.user_id, a.agent_id, a.workspace_id
)
SELECT
users.username, workspace_agents.name AS agent_name, workspaces.name AS workspace_name, rx_bytes, tx_bytes,
session_count_vscode, session_count_ssh, session_count_jetbrains, session_count_reconnecting_pty,
connection_count, connection_median_latency_ms
FROM
agent_stats
JOIN
latest_agent_stats
ON
agent_stats.agent_id = latest_agent_stats.agent_id
JOIN
users
ON
users.id = agent_stats.user_id
JOIN
workspace_agents
ON
workspace_agents.id = agent_stats.agent_id
JOIN
workspaces
ON
workspaces.id = agent_stats.workspace_id
`
type GetWorkspaceAgentStatsAndLabelsRow struct {
Username string `db:"username" json:"username"`
AgentName string `db:"agent_name" json:"agent_name"`
WorkspaceName string `db:"workspace_name" json:"workspace_name"`
RxBytes int64 `db:"rx_bytes" json:"rx_bytes"`
TxBytes int64 `db:"tx_bytes" json:"tx_bytes"`
SessionCountVSCode int64 `db:"session_count_vscode" json:"session_count_vscode"`
SessionCountSSH int64 `db:"session_count_ssh" json:"session_count_ssh"`
SessionCountJetBrains int64 `db:"session_count_jetbrains" json:"session_count_jetbrains"`
SessionCountReconnectingPTY int64 `db:"session_count_reconnecting_pty" json:"session_count_reconnecting_pty"`
ConnectionCount int64 `db:"connection_count" json:"connection_count"`
ConnectionMedianLatencyMS float64 `db:"connection_median_latency_ms" json:"connection_median_latency_ms"`
}
func (q *sqlQuerier) GetWorkspaceAgentStatsAndLabels(ctx context.Context, createdAt time.Time) ([]GetWorkspaceAgentStatsAndLabelsRow, error) {
rows, err := q.db.QueryContext(ctx, getWorkspaceAgentStatsAndLabels, createdAt)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetWorkspaceAgentStatsAndLabelsRow
for rows.Next() {
var i GetWorkspaceAgentStatsAndLabelsRow
if err := rows.Scan(
&i.Username,
&i.AgentName,
&i.WorkspaceName,
&i.RxBytes,
&i.TxBytes,
&i.SessionCountVSCode,
&i.SessionCountSSH,
&i.SessionCountJetBrains,
&i.SessionCountReconnectingPTY,
&i.ConnectionCount,
&i.ConnectionMedianLatencyMS,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertWorkspaceAgentStats = `-- name: InsertWorkspaceAgentStats :exec
INSERT INTO
workspace_agent_stats (
id,
created_at,
user_id,
workspace_id,
template_id,
agent_id,
connections_by_proto,
connection_count,
rx_packets,
rx_bytes,
tx_packets,
tx_bytes,
session_count_vscode,
session_count_jetbrains,
session_count_reconnecting_pty,
session_count_ssh,
connection_median_latency_ms
)
SELECT
unnest($1 :: uuid[]) AS id,
unnest($2 :: timestamptz[]) AS created_at,
unnest($3 :: uuid[]) AS user_id,
unnest($4 :: uuid[]) AS workspace_id,
unnest($5 :: uuid[]) AS template_id,
unnest($6 :: uuid[]) AS agent_id,
jsonb_array_elements($7 :: jsonb) AS connections_by_proto,
unnest($8 :: bigint[]) AS connection_count,
unnest($9 :: bigint[]) AS rx_packets,
unnest($10 :: bigint[]) AS rx_bytes,
unnest($11 :: bigint[]) AS tx_packets,
unnest($12 :: bigint[]) AS tx_bytes,
unnest($13 :: bigint[]) AS session_count_vscode,
unnest($14 :: bigint[]) AS session_count_jetbrains,
unnest($15 :: bigint[]) AS session_count_reconnecting_pty,
unnest($16 :: bigint[]) AS session_count_ssh,
unnest($17 :: double precision[]) AS connection_median_latency_ms
`
type InsertWorkspaceAgentStatsParams struct {
ID []uuid.UUID `db:"id" json:"id"`
CreatedAt []time.Time `db:"created_at" json:"created_at"`
UserID []uuid.UUID `db:"user_id" json:"user_id"`
WorkspaceID []uuid.UUID `db:"workspace_id" json:"workspace_id"`
TemplateID []uuid.UUID `db:"template_id" json:"template_id"`
AgentID []uuid.UUID `db:"agent_id" json:"agent_id"`
ConnectionsByProto json.RawMessage `db:"connections_by_proto" json:"connections_by_proto"`
ConnectionCount []int64 `db:"connection_count" json:"connection_count"`
RxPackets []int64 `db:"rx_packets" json:"rx_packets"`
RxBytes []int64 `db:"rx_bytes" json:"rx_bytes"`
TxPackets []int64 `db:"tx_packets" json:"tx_packets"`
TxBytes []int64 `db:"tx_bytes" json:"tx_bytes"`
SessionCountVSCode []int64 `db:"session_count_vscode" json:"session_count_vscode"`
SessionCountJetBrains []int64 `db:"session_count_jetbrains" json:"session_count_jetbrains"`
SessionCountReconnectingPTY []int64 `db:"session_count_reconnecting_pty" json:"session_count_reconnecting_pty"`
SessionCountSSH []int64 `db:"session_count_ssh" json:"session_count_ssh"`
ConnectionMedianLatencyMS []float64 `db:"connection_median_latency_ms" json:"connection_median_latency_ms"`
}
func (q *sqlQuerier) InsertWorkspaceAgentStats(ctx context.Context, arg InsertWorkspaceAgentStatsParams) error {
_, err := q.db.ExecContext(ctx, insertWorkspaceAgentStats,
pq.Array(arg.ID),
pq.Array(arg.CreatedAt),
pq.Array(arg.UserID),
pq.Array(arg.WorkspaceID),
pq.Array(arg.TemplateID),
pq.Array(arg.AgentID),
arg.ConnectionsByProto,
pq.Array(arg.ConnectionCount),
pq.Array(arg.RxPackets),
pq.Array(arg.RxBytes),
pq.Array(arg.TxPackets),
pq.Array(arg.TxBytes),
pq.Array(arg.SessionCountVSCode),
pq.Array(arg.SessionCountJetBrains),
pq.Array(arg.SessionCountReconnectingPTY),
pq.Array(arg.SessionCountSSH),
pq.Array(arg.ConnectionMedianLatencyMS),
)
return err
}
const getWorkspaceAppByAgentIDAndSlug = `-- name: GetWorkspaceAppByAgentIDAndSlug :one
SELECT id, created_at, agent_id, display_name, icon, command, url, healthcheck_url, healthcheck_interval, healthcheck_threshold, health, subdomain, sharing_level, slug, external, display_order FROM workspace_apps WHERE agent_id = $1 AND slug = $2
`
type GetWorkspaceAppByAgentIDAndSlugParams struct {
AgentID uuid.UUID `db:"agent_id" json:"agent_id"`
Slug string `db:"slug" json:"slug"`
}
func (q *sqlQuerier) GetWorkspaceAppByAgentIDAndSlug(ctx context.Context, arg GetWorkspaceAppByAgentIDAndSlugParams) (WorkspaceApp, error) {
row := q.db.QueryRowContext(ctx, getWorkspaceAppByAgentIDAndSlug, arg.AgentID, arg.Slug)
var i WorkspaceApp
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.AgentID,
&i.DisplayName,
&i.Icon,
&i.Command,
&i.Url,
&i.HealthcheckUrl,
&i.HealthcheckInterval,
&i.HealthcheckThreshold,
&i.Health,
&i.Subdomain,
&i.SharingLevel,
&i.Slug,
&i.External,
&i.DisplayOrder,
)
return i, err
}
const getWorkspaceAppsByAgentID = `-- name: GetWorkspaceAppsByAgentID :many
SELECT id, created_at, agent_id, display_name, icon, command, url, healthcheck_url, healthcheck_interval, healthcheck_threshold, health, subdomain, sharing_level, slug, external, display_order FROM workspace_apps WHERE agent_id = $1 ORDER BY slug ASC
`
func (q *sqlQuerier) GetWorkspaceAppsByAgentID(ctx context.Context, agentID uuid.UUID) ([]WorkspaceApp, error) {
rows, err := q.db.QueryContext(ctx, getWorkspaceAppsByAgentID, agentID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceApp
for rows.Next() {
var i WorkspaceApp
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.AgentID,
&i.DisplayName,
&i.Icon,
&i.Command,
&i.Url,
&i.HealthcheckUrl,
&i.HealthcheckInterval,
&i.HealthcheckThreshold,
&i.Health,
&i.Subdomain,
&i.SharingLevel,
&i.Slug,
&i.External,
&i.DisplayOrder,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getWorkspaceAppsByAgentIDs = `-- name: GetWorkspaceAppsByAgentIDs :many
SELECT id, created_at, agent_id, display_name, icon, command, url, healthcheck_url, healthcheck_interval, healthcheck_threshold, health, subdomain, sharing_level, slug, external, display_order FROM workspace_apps WHERE agent_id = ANY($1 :: uuid [ ]) ORDER BY slug ASC
`
func (q *sqlQuerier) GetWorkspaceAppsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceApp, error) {
rows, err := q.db.QueryContext(ctx, getWorkspaceAppsByAgentIDs, pq.Array(ids))
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceApp
for rows.Next() {
var i WorkspaceApp
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.AgentID,
&i.DisplayName,
&i.Icon,
&i.Command,
&i.Url,
&i.HealthcheckUrl,
&i.HealthcheckInterval,
&i.HealthcheckThreshold,
&i.Health,
&i.Subdomain,
&i.SharingLevel,
&i.Slug,
&i.External,
&i.DisplayOrder,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getWorkspaceAppsCreatedAfter = `-- name: GetWorkspaceAppsCreatedAfter :many
SELECT id, created_at, agent_id, display_name, icon, command, url, healthcheck_url, healthcheck_interval, healthcheck_threshold, health, subdomain, sharing_level, slug, external, display_order FROM workspace_apps WHERE created_at > $1 ORDER BY slug ASC
`
func (q *sqlQuerier) GetWorkspaceAppsCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceApp, error) {
rows, err := q.db.QueryContext(ctx, getWorkspaceAppsCreatedAfter, createdAt)
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceApp
for rows.Next() {
var i WorkspaceApp
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.AgentID,
&i.DisplayName,
&i.Icon,
&i.Command,
&i.Url,
&i.HealthcheckUrl,
&i.HealthcheckInterval,
&i.HealthcheckThreshold,
&i.Health,
&i.Subdomain,
&i.SharingLevel,
&i.Slug,
&i.External,
&i.DisplayOrder,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertWorkspaceApp = `-- name: InsertWorkspaceApp :one
INSERT INTO
workspace_apps (
id,
created_at,
agent_id,
slug,
display_name,
icon,
command,
url,
external,
subdomain,
sharing_level,
healthcheck_url,
healthcheck_interval,
healthcheck_threshold,
health,
display_order
)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16) RETURNING id, created_at, agent_id, display_name, icon, command, url, healthcheck_url, healthcheck_interval, healthcheck_threshold, health, subdomain, sharing_level, slug, external, display_order
`
type InsertWorkspaceAppParams struct {
ID uuid.UUID `db:"id" json:"id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
AgentID uuid.UUID `db:"agent_id" json:"agent_id"`
Slug string `db:"slug" json:"slug"`
DisplayName string `db:"display_name" json:"display_name"`
Icon string `db:"icon" json:"icon"`
Command sql.NullString `db:"command" json:"command"`
Url sql.NullString `db:"url" json:"url"`
External bool `db:"external" json:"external"`
Subdomain bool `db:"subdomain" json:"subdomain"`
SharingLevel AppSharingLevel `db:"sharing_level" json:"sharing_level"`
HealthcheckUrl string `db:"healthcheck_url" json:"healthcheck_url"`
HealthcheckInterval int32 `db:"healthcheck_interval" json:"healthcheck_interval"`
HealthcheckThreshold int32 `db:"healthcheck_threshold" json:"healthcheck_threshold"`
Health WorkspaceAppHealth `db:"health" json:"health"`
DisplayOrder int32 `db:"display_order" json:"display_order"`
}
func (q *sqlQuerier) InsertWorkspaceApp(ctx context.Context, arg InsertWorkspaceAppParams) (WorkspaceApp, error) {
row := q.db.QueryRowContext(ctx, insertWorkspaceApp,
arg.ID,
arg.CreatedAt,
arg.AgentID,
arg.Slug,
arg.DisplayName,
arg.Icon,
arg.Command,
arg.Url,
arg.External,
arg.Subdomain,
arg.SharingLevel,
arg.HealthcheckUrl,
arg.HealthcheckInterval,
arg.HealthcheckThreshold,
arg.Health,
arg.DisplayOrder,
)
var i WorkspaceApp
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.AgentID,
&i.DisplayName,
&i.Icon,
&i.Command,
&i.Url,
&i.HealthcheckUrl,
&i.HealthcheckInterval,
&i.HealthcheckThreshold,
&i.Health,
&i.Subdomain,
&i.SharingLevel,
&i.Slug,
&i.External,
&i.DisplayOrder,
)
return i, err
}
const updateWorkspaceAppHealthByID = `-- name: UpdateWorkspaceAppHealthByID :exec
UPDATE
workspace_apps
SET
health = $2
WHERE
id = $1
`
type UpdateWorkspaceAppHealthByIDParams struct {
ID uuid.UUID `db:"id" json:"id"`
Health WorkspaceAppHealth `db:"health" json:"health"`
}
func (q *sqlQuerier) UpdateWorkspaceAppHealthByID(ctx context.Context, arg UpdateWorkspaceAppHealthByIDParams) error {
_, err := q.db.ExecContext(ctx, updateWorkspaceAppHealthByID, arg.ID, arg.Health)
return err
}
const insertWorkspaceAppStats = `-- name: InsertWorkspaceAppStats :exec
INSERT INTO
workspace_app_stats (
user_id,
workspace_id,
agent_id,
access_method,
slug_or_port,
session_id,
session_started_at,
session_ended_at,
requests
)
SELECT
unnest($1::uuid[]) AS user_id,
unnest($2::uuid[]) AS workspace_id,
unnest($3::uuid[]) AS agent_id,
unnest($4::text[]) AS access_method,
unnest($5::text[]) AS slug_or_port,
unnest($6::uuid[]) AS session_id,
unnest($7::timestamptz[]) AS session_started_at,
unnest($8::timestamptz[]) AS session_ended_at,
unnest($9::int[]) AS requests
ON CONFLICT
(user_id, agent_id, session_id)
DO
UPDATE SET
session_ended_at = EXCLUDED.session_ended_at,
requests = EXCLUDED.requests
WHERE
workspace_app_stats.user_id = EXCLUDED.user_id
AND workspace_app_stats.agent_id = EXCLUDED.agent_id
AND workspace_app_stats.session_id = EXCLUDED.session_id
-- Since stats are updated in place as time progresses, we only
-- want to update this row if it's fresh.
AND workspace_app_stats.session_ended_at <= EXCLUDED.session_ended_at
AND workspace_app_stats.requests <= EXCLUDED.requests
`
type InsertWorkspaceAppStatsParams struct {
UserID []uuid.UUID `db:"user_id" json:"user_id"`
WorkspaceID []uuid.UUID `db:"workspace_id" json:"workspace_id"`
AgentID []uuid.UUID `db:"agent_id" json:"agent_id"`
AccessMethod []string `db:"access_method" json:"access_method"`
SlugOrPort []string `db:"slug_or_port" json:"slug_or_port"`
SessionID []uuid.UUID `db:"session_id" json:"session_id"`
SessionStartedAt []time.Time `db:"session_started_at" json:"session_started_at"`
SessionEndedAt []time.Time `db:"session_ended_at" json:"session_ended_at"`
Requests []int32 `db:"requests" json:"requests"`
}
func (q *sqlQuerier) InsertWorkspaceAppStats(ctx context.Context, arg InsertWorkspaceAppStatsParams) error {
_, err := q.db.ExecContext(ctx, insertWorkspaceAppStats,
pq.Array(arg.UserID),
pq.Array(arg.WorkspaceID),
pq.Array(arg.AgentID),
pq.Array(arg.AccessMethod),
pq.Array(arg.SlugOrPort),
pq.Array(arg.SessionID),
pq.Array(arg.SessionStartedAt),
pq.Array(arg.SessionEndedAt),
pq.Array(arg.Requests),
)
return err
}
const getUserWorkspaceBuildParameters = `-- name: GetUserWorkspaceBuildParameters :many
SELECT name, value
FROM (
SELECT DISTINCT ON (tvp.name)
tvp.name,
wbp.value,
wb.created_at
FROM
workspace_build_parameters wbp
JOIN
workspace_builds wb ON wb.id = wbp.workspace_build_id
JOIN
workspaces w ON w.id = wb.workspace_id
JOIN
template_version_parameters tvp ON tvp.template_version_id = wb.template_version_id
WHERE
w.owner_id = $1
AND wb.transition = 'start'
AND w.template_id = $2
AND tvp.ephemeral = false
AND tvp.name = wbp.name
ORDER BY
tvp.name, wb.created_at DESC
) q1
ORDER BY created_at DESC, name
LIMIT 100
`
type GetUserWorkspaceBuildParametersParams struct {
OwnerID uuid.UUID `db:"owner_id" json:"owner_id"`
TemplateID uuid.UUID `db:"template_id" json:"template_id"`
}
type GetUserWorkspaceBuildParametersRow struct {
Name string `db:"name" json:"name"`
Value string `db:"value" json:"value"`
}
func (q *sqlQuerier) GetUserWorkspaceBuildParameters(ctx context.Context, arg GetUserWorkspaceBuildParametersParams) ([]GetUserWorkspaceBuildParametersRow, error) {
rows, err := q.db.QueryContext(ctx, getUserWorkspaceBuildParameters, arg.OwnerID, arg.TemplateID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetUserWorkspaceBuildParametersRow
for rows.Next() {
var i GetUserWorkspaceBuildParametersRow
if err := rows.Scan(&i.Name, &i.Value); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getWorkspaceBuildParameters = `-- name: GetWorkspaceBuildParameters :many
SELECT
workspace_build_id, name, value
FROM
workspace_build_parameters
WHERE
workspace_build_id = $1
`
func (q *sqlQuerier) GetWorkspaceBuildParameters(ctx context.Context, workspaceBuildID uuid.UUID) ([]WorkspaceBuildParameter, error) {
rows, err := q.db.QueryContext(ctx, getWorkspaceBuildParameters, workspaceBuildID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceBuildParameter
for rows.Next() {
var i WorkspaceBuildParameter
if err := rows.Scan(&i.WorkspaceBuildID, &i.Name, &i.Value); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertWorkspaceBuildParameters = `-- name: InsertWorkspaceBuildParameters :exec
INSERT INTO
workspace_build_parameters (workspace_build_id, name, value)
SELECT
$1 :: uuid AS workspace_build_id,
unnest($2 :: text[]) AS name,
unnest($3 :: text[]) AS value
RETURNING workspace_build_id, name, value
`
type InsertWorkspaceBuildParametersParams struct {
WorkspaceBuildID uuid.UUID `db:"workspace_build_id" json:"workspace_build_id"`
Name []string `db:"name" json:"name"`
Value []string `db:"value" json:"value"`
}
func (q *sqlQuerier) InsertWorkspaceBuildParameters(ctx context.Context, arg InsertWorkspaceBuildParametersParams) error {
_, err := q.db.ExecContext(ctx, insertWorkspaceBuildParameters, arg.WorkspaceBuildID, pq.Array(arg.Name), pq.Array(arg.Value))
return err
}
const getActiveWorkspaceBuildsByTemplateID = `-- name: GetActiveWorkspaceBuildsByTemplateID :many
SELECT wb.id, wb.created_at, wb.updated_at, wb.workspace_id, wb.template_version_id, wb.build_number, wb.transition, wb.initiator_id, wb.provisioner_state, wb.job_id, wb.deadline, wb.reason, wb.daily_cost, wb.max_deadline, wb.initiator_by_avatar_url, wb.initiator_by_username
FROM (
SELECT
workspace_id, MAX(build_number) as max_build_number
FROM
workspace_build_with_user AS workspace_builds
WHERE
workspace_id IN (
SELECT
id
FROM
workspaces
WHERE
template_id = $1
)
GROUP BY
workspace_id
) m
JOIN
workspace_build_with_user AS wb
ON m.workspace_id = wb.workspace_id AND m.max_build_number = wb.build_number
JOIN
provisioner_jobs AS pj
ON wb.job_id = pj.id
WHERE
wb.transition = 'start'::workspace_transition
AND
pj.completed_at IS NOT NULL
`
func (q *sqlQuerier) GetActiveWorkspaceBuildsByTemplateID(ctx context.Context, templateID uuid.UUID) ([]WorkspaceBuild, error) {
rows, err := q.db.QueryContext(ctx, getActiveWorkspaceBuildsByTemplateID, templateID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceBuild
for rows.Next() {
var i WorkspaceBuild
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.WorkspaceID,
&i.TemplateVersionID,
&i.BuildNumber,
&i.Transition,
&i.InitiatorID,
&i.ProvisionerState,
&i.JobID,
&i.Deadline,
&i.Reason,
&i.DailyCost,
&i.MaxDeadline,
&i.InitiatorByAvatarUrl,
&i.InitiatorByUsername,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getLatestWorkspaceBuildByWorkspaceID = `-- name: GetLatestWorkspaceBuildByWorkspaceID :one
SELECT
id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, initiator_by_avatar_url, initiator_by_username
FROM
workspace_build_with_user AS workspace_builds
WHERE
workspace_id = $1
ORDER BY
build_number desc
LIMIT
1
`
func (q *sqlQuerier) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) (WorkspaceBuild, error) {
row := q.db.QueryRowContext(ctx, getLatestWorkspaceBuildByWorkspaceID, workspaceID)
var i WorkspaceBuild
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.WorkspaceID,
&i.TemplateVersionID,
&i.BuildNumber,
&i.Transition,
&i.InitiatorID,
&i.ProvisionerState,
&i.JobID,
&i.Deadline,
&i.Reason,
&i.DailyCost,
&i.MaxDeadline,
&i.InitiatorByAvatarUrl,
&i.InitiatorByUsername,
)
return i, err
}
const getLatestWorkspaceBuilds = `-- name: GetLatestWorkspaceBuilds :many
SELECT wb.id, wb.created_at, wb.updated_at, wb.workspace_id, wb.template_version_id, wb.build_number, wb.transition, wb.initiator_id, wb.provisioner_state, wb.job_id, wb.deadline, wb.reason, wb.daily_cost, wb.max_deadline, wb.initiator_by_avatar_url, wb.initiator_by_username
FROM (
SELECT
workspace_id, MAX(build_number) as max_build_number
FROM
workspace_build_with_user AS workspace_builds
GROUP BY
workspace_id
) m
JOIN
workspace_build_with_user AS wb
ON m.workspace_id = wb.workspace_id AND m.max_build_number = wb.build_number
`
func (q *sqlQuerier) GetLatestWorkspaceBuilds(ctx context.Context) ([]WorkspaceBuild, error) {
rows, err := q.db.QueryContext(ctx, getLatestWorkspaceBuilds)
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceBuild
for rows.Next() {
var i WorkspaceBuild
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.WorkspaceID,
&i.TemplateVersionID,
&i.BuildNumber,
&i.Transition,
&i.InitiatorID,
&i.ProvisionerState,
&i.JobID,
&i.Deadline,
&i.Reason,
&i.DailyCost,
&i.MaxDeadline,
&i.InitiatorByAvatarUrl,
&i.InitiatorByUsername,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getLatestWorkspaceBuildsByWorkspaceIDs = `-- name: GetLatestWorkspaceBuildsByWorkspaceIDs :many
SELECT wb.id, wb.created_at, wb.updated_at, wb.workspace_id, wb.template_version_id, wb.build_number, wb.transition, wb.initiator_id, wb.provisioner_state, wb.job_id, wb.deadline, wb.reason, wb.daily_cost, wb.max_deadline, wb.initiator_by_avatar_url, wb.initiator_by_username
FROM (
SELECT
workspace_id, MAX(build_number) as max_build_number
FROM
workspace_build_with_user AS workspace_builds
WHERE
workspace_id = ANY($1 :: uuid [ ])
GROUP BY
workspace_id
) m
JOIN
workspace_build_with_user AS wb
ON m.workspace_id = wb.workspace_id AND m.max_build_number = wb.build_number
`
func (q *sqlQuerier) GetLatestWorkspaceBuildsByWorkspaceIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceBuild, error) {
rows, err := q.db.QueryContext(ctx, getLatestWorkspaceBuildsByWorkspaceIDs, pq.Array(ids))
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceBuild
for rows.Next() {
var i WorkspaceBuild
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.WorkspaceID,
&i.TemplateVersionID,
&i.BuildNumber,
&i.Transition,
&i.InitiatorID,
&i.ProvisionerState,
&i.JobID,
&i.Deadline,
&i.Reason,
&i.DailyCost,
&i.MaxDeadline,
&i.InitiatorByAvatarUrl,
&i.InitiatorByUsername,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getWorkspaceBuildByID = `-- name: GetWorkspaceBuildByID :one
SELECT
id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, initiator_by_avatar_url, initiator_by_username
FROM
workspace_build_with_user AS workspace_builds
WHERE
id = $1
LIMIT
1
`
func (q *sqlQuerier) GetWorkspaceBuildByID(ctx context.Context, id uuid.UUID) (WorkspaceBuild, error) {
row := q.db.QueryRowContext(ctx, getWorkspaceBuildByID, id)
var i WorkspaceBuild
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.WorkspaceID,
&i.TemplateVersionID,
&i.BuildNumber,
&i.Transition,
&i.InitiatorID,
&i.ProvisionerState,
&i.JobID,
&i.Deadline,
&i.Reason,
&i.DailyCost,
&i.MaxDeadline,
&i.InitiatorByAvatarUrl,
&i.InitiatorByUsername,
)
return i, err
}
const getWorkspaceBuildByJobID = `-- name: GetWorkspaceBuildByJobID :one
SELECT
id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, initiator_by_avatar_url, initiator_by_username
FROM
workspace_build_with_user AS workspace_builds
WHERE
job_id = $1
LIMIT
1
`
func (q *sqlQuerier) GetWorkspaceBuildByJobID(ctx context.Context, jobID uuid.UUID) (WorkspaceBuild, error) {
row := q.db.QueryRowContext(ctx, getWorkspaceBuildByJobID, jobID)
var i WorkspaceBuild
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.WorkspaceID,
&i.TemplateVersionID,
&i.BuildNumber,
&i.Transition,
&i.InitiatorID,
&i.ProvisionerState,
&i.JobID,
&i.Deadline,
&i.Reason,
&i.DailyCost,
&i.MaxDeadline,
&i.InitiatorByAvatarUrl,
&i.InitiatorByUsername,
)
return i, err
}
const getWorkspaceBuildByWorkspaceIDAndBuildNumber = `-- name: GetWorkspaceBuildByWorkspaceIDAndBuildNumber :one
SELECT
id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, initiator_by_avatar_url, initiator_by_username
FROM
workspace_build_with_user AS workspace_builds
WHERE
workspace_id = $1
AND build_number = $2
`
type GetWorkspaceBuildByWorkspaceIDAndBuildNumberParams struct {
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
BuildNumber int32 `db:"build_number" json:"build_number"`
}
func (q *sqlQuerier) GetWorkspaceBuildByWorkspaceIDAndBuildNumber(ctx context.Context, arg GetWorkspaceBuildByWorkspaceIDAndBuildNumberParams) (WorkspaceBuild, error) {
row := q.db.QueryRowContext(ctx, getWorkspaceBuildByWorkspaceIDAndBuildNumber, arg.WorkspaceID, arg.BuildNumber)
var i WorkspaceBuild
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.WorkspaceID,
&i.TemplateVersionID,
&i.BuildNumber,
&i.Transition,
&i.InitiatorID,
&i.ProvisionerState,
&i.JobID,
&i.Deadline,
&i.Reason,
&i.DailyCost,
&i.MaxDeadline,
&i.InitiatorByAvatarUrl,
&i.InitiatorByUsername,
)
return i, err
}
const getWorkspaceBuildsByWorkspaceID = `-- name: GetWorkspaceBuildsByWorkspaceID :many
SELECT
id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, initiator_by_avatar_url, initiator_by_username
FROM
workspace_build_with_user AS workspace_builds
WHERE
workspace_builds.workspace_id = $1
AND workspace_builds.created_at > $2
AND CASE
-- This allows using the last element on a page as effectively a cursor.
-- This is an important option for scripts that need to paginate without
-- duplicating or missing data.
WHEN $3 :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN (
-- The pagination cursor is the last ID of the previous page.
-- The query is ordered by the build_number field, so select all
-- rows after the cursor.
build_number > (
SELECT
build_number
FROM
workspace_builds
WHERE
id = $3
)
)
ELSE true
END
ORDER BY
build_number desc OFFSET $4
LIMIT
-- A null limit means "no limit", so 0 means return all
NULLIF($5 :: int, 0)
`
type GetWorkspaceBuildsByWorkspaceIDParams struct {
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
Since time.Time `db:"since" json:"since"`
AfterID uuid.UUID `db:"after_id" json:"after_id"`
OffsetOpt int32 `db:"offset_opt" json:"offset_opt"`
LimitOpt int32 `db:"limit_opt" json:"limit_opt"`
}
func (q *sqlQuerier) GetWorkspaceBuildsByWorkspaceID(ctx context.Context, arg GetWorkspaceBuildsByWorkspaceIDParams) ([]WorkspaceBuild, error) {
rows, err := q.db.QueryContext(ctx, getWorkspaceBuildsByWorkspaceID,
arg.WorkspaceID,
arg.Since,
arg.AfterID,
arg.OffsetOpt,
arg.LimitOpt,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceBuild
for rows.Next() {
var i WorkspaceBuild
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.WorkspaceID,
&i.TemplateVersionID,
&i.BuildNumber,
&i.Transition,
&i.InitiatorID,
&i.ProvisionerState,
&i.JobID,
&i.Deadline,
&i.Reason,
&i.DailyCost,
&i.MaxDeadline,
&i.InitiatorByAvatarUrl,
&i.InitiatorByUsername,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getWorkspaceBuildsCreatedAfter = `-- name: GetWorkspaceBuildsCreatedAfter :many
SELECT id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, initiator_by_avatar_url, initiator_by_username FROM workspace_build_with_user WHERE created_at > $1
`
func (q *sqlQuerier) GetWorkspaceBuildsCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceBuild, error) {
rows, err := q.db.QueryContext(ctx, getWorkspaceBuildsCreatedAfter, createdAt)
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceBuild
for rows.Next() {
var i WorkspaceBuild
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.WorkspaceID,
&i.TemplateVersionID,
&i.BuildNumber,
&i.Transition,
&i.InitiatorID,
&i.ProvisionerState,
&i.JobID,
&i.Deadline,
&i.Reason,
&i.DailyCost,
&i.MaxDeadline,
&i.InitiatorByAvatarUrl,
&i.InitiatorByUsername,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertWorkspaceBuild = `-- name: InsertWorkspaceBuild :exec
INSERT INTO
workspace_builds (
id,
created_at,
updated_at,
workspace_id,
template_version_id,
"build_number",
transition,
initiator_id,
job_id,
provisioner_state,
deadline,
max_deadline,
reason
)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
`
type InsertWorkspaceBuildParams struct {
ID uuid.UUID `db:"id" json:"id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
TemplateVersionID uuid.UUID `db:"template_version_id" json:"template_version_id"`
BuildNumber int32 `db:"build_number" json:"build_number"`
Transition WorkspaceTransition `db:"transition" json:"transition"`
InitiatorID uuid.UUID `db:"initiator_id" json:"initiator_id"`
JobID uuid.UUID `db:"job_id" json:"job_id"`
ProvisionerState []byte `db:"provisioner_state" json:"provisioner_state"`
Deadline time.Time `db:"deadline" json:"deadline"`
MaxDeadline time.Time `db:"max_deadline" json:"max_deadline"`
Reason BuildReason `db:"reason" json:"reason"`
}
func (q *sqlQuerier) InsertWorkspaceBuild(ctx context.Context, arg InsertWorkspaceBuildParams) error {
_, err := q.db.ExecContext(ctx, insertWorkspaceBuild,
arg.ID,
arg.CreatedAt,
arg.UpdatedAt,
arg.WorkspaceID,
arg.TemplateVersionID,
arg.BuildNumber,
arg.Transition,
arg.InitiatorID,
arg.JobID,
arg.ProvisionerState,
arg.Deadline,
arg.MaxDeadline,
arg.Reason,
)
return err
}
const updateWorkspaceBuildCostByID = `-- name: UpdateWorkspaceBuildCostByID :exec
UPDATE
workspace_builds
SET
daily_cost = $2
WHERE
id = $1
`
type UpdateWorkspaceBuildCostByIDParams struct {
ID uuid.UUID `db:"id" json:"id"`
DailyCost int32 `db:"daily_cost" json:"daily_cost"`
}
func (q *sqlQuerier) UpdateWorkspaceBuildCostByID(ctx context.Context, arg UpdateWorkspaceBuildCostByIDParams) error {
_, err := q.db.ExecContext(ctx, updateWorkspaceBuildCostByID, arg.ID, arg.DailyCost)
return err
}
const updateWorkspaceBuildDeadlineByID = `-- name: UpdateWorkspaceBuildDeadlineByID :exec
UPDATE
workspace_builds
SET
deadline = $1::timestamptz,
max_deadline = $2::timestamptz,
updated_at = $3::timestamptz
WHERE id = $4::uuid
`
type UpdateWorkspaceBuildDeadlineByIDParams struct {
Deadline time.Time `db:"deadline" json:"deadline"`
MaxDeadline time.Time `db:"max_deadline" json:"max_deadline"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
ID uuid.UUID `db:"id" json:"id"`
}
func (q *sqlQuerier) UpdateWorkspaceBuildDeadlineByID(ctx context.Context, arg UpdateWorkspaceBuildDeadlineByIDParams) error {
_, err := q.db.ExecContext(ctx, updateWorkspaceBuildDeadlineByID,
arg.Deadline,
arg.MaxDeadline,
arg.UpdatedAt,
arg.ID,
)
return err
}
const updateWorkspaceBuildProvisionerStateByID = `-- name: UpdateWorkspaceBuildProvisionerStateByID :exec
UPDATE
workspace_builds
SET
provisioner_state = $1::bytea,
updated_at = $2::timestamptz
WHERE id = $3::uuid
`
type UpdateWorkspaceBuildProvisionerStateByIDParams struct {
ProvisionerState []byte `db:"provisioner_state" json:"provisioner_state"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
ID uuid.UUID `db:"id" json:"id"`
}
func (q *sqlQuerier) UpdateWorkspaceBuildProvisionerStateByID(ctx context.Context, arg UpdateWorkspaceBuildProvisionerStateByIDParams) error {
_, err := q.db.ExecContext(ctx, updateWorkspaceBuildProvisionerStateByID, arg.ProvisionerState, arg.UpdatedAt, arg.ID)
return err
}
const getWorkspaceResourceByID = `-- name: GetWorkspaceResourceByID :one
SELECT
id, created_at, job_id, transition, type, name, hide, icon, instance_type, daily_cost
FROM
workspace_resources
WHERE
id = $1
`
func (q *sqlQuerier) GetWorkspaceResourceByID(ctx context.Context, id uuid.UUID) (WorkspaceResource, error) {
row := q.db.QueryRowContext(ctx, getWorkspaceResourceByID, id)
var i WorkspaceResource
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.JobID,
&i.Transition,
&i.Type,
&i.Name,
&i.Hide,
&i.Icon,
&i.InstanceType,
&i.DailyCost,
)
return i, err
}
const getWorkspaceResourceMetadataByResourceIDs = `-- name: GetWorkspaceResourceMetadataByResourceIDs :many
SELECT
workspace_resource_id, key, value, sensitive, id
FROM
workspace_resource_metadata
WHERE
workspace_resource_id = ANY($1 :: uuid [ ]) ORDER BY id ASC
`
func (q *sqlQuerier) GetWorkspaceResourceMetadataByResourceIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceResourceMetadatum, error) {
rows, err := q.db.QueryContext(ctx, getWorkspaceResourceMetadataByResourceIDs, pq.Array(ids))
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceResourceMetadatum
for rows.Next() {
var i WorkspaceResourceMetadatum
if err := rows.Scan(
&i.WorkspaceResourceID,
&i.Key,
&i.Value,
&i.Sensitive,
&i.ID,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getWorkspaceResourceMetadataCreatedAfter = `-- name: GetWorkspaceResourceMetadataCreatedAfter :many
SELECT workspace_resource_id, key, value, sensitive, id FROM workspace_resource_metadata WHERE workspace_resource_id = ANY(
SELECT id FROM workspace_resources WHERE created_at > $1
)
`
func (q *sqlQuerier) GetWorkspaceResourceMetadataCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceResourceMetadatum, error) {
rows, err := q.db.QueryContext(ctx, getWorkspaceResourceMetadataCreatedAfter, createdAt)
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceResourceMetadatum
for rows.Next() {
var i WorkspaceResourceMetadatum
if err := rows.Scan(
&i.WorkspaceResourceID,
&i.Key,
&i.Value,
&i.Sensitive,
&i.ID,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getWorkspaceResourcesByJobID = `-- name: GetWorkspaceResourcesByJobID :many
SELECT
id, created_at, job_id, transition, type, name, hide, icon, instance_type, daily_cost
FROM
workspace_resources
WHERE
job_id = $1
`
func (q *sqlQuerier) GetWorkspaceResourcesByJobID(ctx context.Context, jobID uuid.UUID) ([]WorkspaceResource, error) {
rows, err := q.db.QueryContext(ctx, getWorkspaceResourcesByJobID, jobID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceResource
for rows.Next() {
var i WorkspaceResource
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.JobID,
&i.Transition,
&i.Type,
&i.Name,
&i.Hide,
&i.Icon,
&i.InstanceType,
&i.DailyCost,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getWorkspaceResourcesByJobIDs = `-- name: GetWorkspaceResourcesByJobIDs :many
SELECT
id, created_at, job_id, transition, type, name, hide, icon, instance_type, daily_cost
FROM
workspace_resources
WHERE
job_id = ANY($1 :: uuid [ ])
`
func (q *sqlQuerier) GetWorkspaceResourcesByJobIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceResource, error) {
rows, err := q.db.QueryContext(ctx, getWorkspaceResourcesByJobIDs, pq.Array(ids))
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceResource
for rows.Next() {
var i WorkspaceResource
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.JobID,
&i.Transition,
&i.Type,
&i.Name,
&i.Hide,
&i.Icon,
&i.InstanceType,
&i.DailyCost,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getWorkspaceResourcesCreatedAfter = `-- name: GetWorkspaceResourcesCreatedAfter :many
SELECT id, created_at, job_id, transition, type, name, hide, icon, instance_type, daily_cost FROM workspace_resources WHERE created_at > $1
`
func (q *sqlQuerier) GetWorkspaceResourcesCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceResource, error) {
rows, err := q.db.QueryContext(ctx, getWorkspaceResourcesCreatedAfter, createdAt)
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceResource
for rows.Next() {
var i WorkspaceResource
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.JobID,
&i.Transition,
&i.Type,
&i.Name,
&i.Hide,
&i.Icon,
&i.InstanceType,
&i.DailyCost,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertWorkspaceResource = `-- name: InsertWorkspaceResource :one
INSERT INTO
workspace_resources (id, created_at, job_id, transition, type, name, hide, icon, instance_type, daily_cost)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id, created_at, job_id, transition, type, name, hide, icon, instance_type, daily_cost
`
type InsertWorkspaceResourceParams struct {
ID uuid.UUID `db:"id" json:"id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
JobID uuid.UUID `db:"job_id" json:"job_id"`
Transition WorkspaceTransition `db:"transition" json:"transition"`
Type string `db:"type" json:"type"`
Name string `db:"name" json:"name"`
Hide bool `db:"hide" json:"hide"`
Icon string `db:"icon" json:"icon"`
InstanceType sql.NullString `db:"instance_type" json:"instance_type"`
DailyCost int32 `db:"daily_cost" json:"daily_cost"`
}
func (q *sqlQuerier) InsertWorkspaceResource(ctx context.Context, arg InsertWorkspaceResourceParams) (WorkspaceResource, error) {
row := q.db.QueryRowContext(ctx, insertWorkspaceResource,
arg.ID,
arg.CreatedAt,
arg.JobID,
arg.Transition,
arg.Type,
arg.Name,
arg.Hide,
arg.Icon,
arg.InstanceType,
arg.DailyCost,
)
var i WorkspaceResource
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.JobID,
&i.Transition,
&i.Type,
&i.Name,
&i.Hide,
&i.Icon,
&i.InstanceType,
&i.DailyCost,
)
return i, err
}
const insertWorkspaceResourceMetadata = `-- name: InsertWorkspaceResourceMetadata :many
INSERT INTO
workspace_resource_metadata
SELECT
$1 :: uuid AS workspace_resource_id,
unnest($2 :: text [ ]) AS key,
unnest($3 :: text [ ]) AS value,
unnest($4 :: boolean [ ]) AS sensitive RETURNING workspace_resource_id, key, value, sensitive, id
`
type InsertWorkspaceResourceMetadataParams struct {
WorkspaceResourceID uuid.UUID `db:"workspace_resource_id" json:"workspace_resource_id"`
Key []string `db:"key" json:"key"`
Value []string `db:"value" json:"value"`
Sensitive []bool `db:"sensitive" json:"sensitive"`
}
func (q *sqlQuerier) InsertWorkspaceResourceMetadata(ctx context.Context, arg InsertWorkspaceResourceMetadataParams) ([]WorkspaceResourceMetadatum, error) {
rows, err := q.db.QueryContext(ctx, insertWorkspaceResourceMetadata,
arg.WorkspaceResourceID,
pq.Array(arg.Key),
pq.Array(arg.Value),
pq.Array(arg.Sensitive),
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceResourceMetadatum
for rows.Next() {
var i WorkspaceResourceMetadatum
if err := rows.Scan(
&i.WorkspaceResourceID,
&i.Key,
&i.Value,
&i.Sensitive,
&i.ID,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const batchUpdateWorkspaceLastUsedAt = `-- name: BatchUpdateWorkspaceLastUsedAt :exec
UPDATE
workspaces
SET
last_used_at = $1
WHERE
id = ANY($2 :: uuid[])
AND
-- Do not overwrite with older data
last_used_at < $1
`
type BatchUpdateWorkspaceLastUsedAtParams struct {
LastUsedAt time.Time `db:"last_used_at" json:"last_used_at"`
IDs []uuid.UUID `db:"ids" json:"ids"`
}
func (q *sqlQuerier) BatchUpdateWorkspaceLastUsedAt(ctx context.Context, arg BatchUpdateWorkspaceLastUsedAtParams) error {
_, err := q.db.ExecContext(ctx, batchUpdateWorkspaceLastUsedAt, arg.LastUsedAt, pq.Array(arg.IDs))
return err
}
const favoriteWorkspace = `-- name: FavoriteWorkspace :exec
UPDATE workspaces SET favorite = true WHERE id = $1
`
func (q *sqlQuerier) FavoriteWorkspace(ctx context.Context, id uuid.UUID) error {
_, err := q.db.ExecContext(ctx, favoriteWorkspace, id)
return err
}
const getDeploymentWorkspaceStats = `-- name: GetDeploymentWorkspaceStats :one
WITH workspaces_with_jobs AS (
SELECT
latest_build.transition, latest_build.provisioner_job_id, latest_build.started_at, latest_build.updated_at, latest_build.canceled_at, latest_build.completed_at, latest_build.error FROM workspaces
LEFT JOIN LATERAL (
SELECT
workspace_builds.transition,
provisioner_jobs.id AS provisioner_job_id,
provisioner_jobs.started_at,
provisioner_jobs.updated_at,
provisioner_jobs.canceled_at,
provisioner_jobs.completed_at,
provisioner_jobs.error
FROM
workspace_builds
LEFT JOIN
provisioner_jobs
ON
provisioner_jobs.id = workspace_builds.job_id
WHERE
workspace_builds.workspace_id = workspaces.id
ORDER BY
build_number DESC
LIMIT
1
) latest_build ON TRUE WHERE deleted = false
), pending_workspaces AS (
SELECT COUNT(*) AS count FROM workspaces_with_jobs WHERE
started_at IS NULL
), building_workspaces AS (
SELECT COUNT(*) AS count FROM workspaces_with_jobs WHERE
started_at IS NOT NULL AND
canceled_at IS NULL AND
completed_at IS NULL AND
updated_at - INTERVAL '30 seconds' < NOW()
), running_workspaces AS (
SELECT COUNT(*) AS count FROM workspaces_with_jobs WHERE
completed_at IS NOT NULL AND
canceled_at IS NULL AND
error IS NULL AND
transition = 'start'::workspace_transition
), failed_workspaces AS (
SELECT COUNT(*) AS count FROM workspaces_with_jobs WHERE
(canceled_at IS NOT NULL AND
error IS NOT NULL) OR
(completed_at IS NOT NULL AND
error IS NOT NULL)
), stopped_workspaces AS (
SELECT COUNT(*) AS count FROM workspaces_with_jobs WHERE
completed_at IS NOT NULL AND
canceled_at IS NULL AND
error IS NULL AND
transition = 'stop'::workspace_transition
)
SELECT
pending_workspaces.count AS pending_workspaces,
building_workspaces.count AS building_workspaces,
running_workspaces.count AS running_workspaces,
failed_workspaces.count AS failed_workspaces,
stopped_workspaces.count AS stopped_workspaces
FROM pending_workspaces, building_workspaces, running_workspaces, failed_workspaces, stopped_workspaces
`
type GetDeploymentWorkspaceStatsRow struct {
PendingWorkspaces int64 `db:"pending_workspaces" json:"pending_workspaces"`
BuildingWorkspaces int64 `db:"building_workspaces" json:"building_workspaces"`
RunningWorkspaces int64 `db:"running_workspaces" json:"running_workspaces"`
FailedWorkspaces int64 `db:"failed_workspaces" json:"failed_workspaces"`
StoppedWorkspaces int64 `db:"stopped_workspaces" json:"stopped_workspaces"`
}
func (q *sqlQuerier) GetDeploymentWorkspaceStats(ctx context.Context) (GetDeploymentWorkspaceStatsRow, error) {
row := q.db.QueryRowContext(ctx, getDeploymentWorkspaceStats)
var i GetDeploymentWorkspaceStatsRow
err := row.Scan(
&i.PendingWorkspaces,
&i.BuildingWorkspaces,
&i.RunningWorkspaces,
&i.FailedWorkspaces,
&i.StoppedWorkspaces,
)
return i, err
}
const getWorkspaceByAgentID = `-- name: GetWorkspaceByAgentID :one
SELECT
workspaces.id, workspaces.created_at, workspaces.updated_at, workspaces.owner_id, workspaces.organization_id, workspaces.template_id, workspaces.deleted, workspaces.name, workspaces.autostart_schedule, workspaces.ttl, workspaces.last_used_at, workspaces.dormant_at, workspaces.deleting_at, workspaces.automatic_updates, workspaces.favorite,
templates.name as template_name
FROM
workspaces
INNER JOIN
templates ON workspaces.template_id = templates.id
WHERE
workspaces.id = (
SELECT
workspace_id
FROM
workspace_builds
WHERE
workspace_builds.job_id = (
SELECT
job_id
FROM
workspace_resources
WHERE
workspace_resources.id = (
SELECT
resource_id
FROM
workspace_agents
WHERE
workspace_agents.id = $1
)
)
)
`
type GetWorkspaceByAgentIDRow struct {
Workspace Workspace `db:"workspace" json:"workspace"`
TemplateName string `db:"template_name" json:"template_name"`
}
func (q *sqlQuerier) GetWorkspaceByAgentID(ctx context.Context, agentID uuid.UUID) (GetWorkspaceByAgentIDRow, error) {
row := q.db.QueryRowContext(ctx, getWorkspaceByAgentID, agentID)
var i GetWorkspaceByAgentIDRow
err := row.Scan(
&i.Workspace.ID,
&i.Workspace.CreatedAt,
&i.Workspace.UpdatedAt,
&i.Workspace.OwnerID,
&i.Workspace.OrganizationID,
&i.Workspace.TemplateID,
&i.Workspace.Deleted,
&i.Workspace.Name,
&i.Workspace.AutostartSchedule,
&i.Workspace.Ttl,
&i.Workspace.LastUsedAt,
&i.Workspace.DormantAt,
&i.Workspace.DeletingAt,
&i.Workspace.AutomaticUpdates,
&i.Workspace.Favorite,
&i.TemplateName,
)
return i, err
}
const getWorkspaceByID = `-- name: GetWorkspaceByID :one
SELECT
id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at, automatic_updates, favorite
FROM
workspaces
WHERE
id = $1
LIMIT
1
`
func (q *sqlQuerier) GetWorkspaceByID(ctx context.Context, id uuid.UUID) (Workspace, error) {
row := q.db.QueryRowContext(ctx, getWorkspaceByID, id)
var i Workspace
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.OwnerID,
&i.OrganizationID,
&i.TemplateID,
&i.Deleted,
&i.Name,
&i.AutostartSchedule,
&i.Ttl,
&i.LastUsedAt,
&i.DormantAt,
&i.DeletingAt,
&i.AutomaticUpdates,
&i.Favorite,
)
return i, err
}
const getWorkspaceByOwnerIDAndName = `-- name: GetWorkspaceByOwnerIDAndName :one
SELECT
id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at, automatic_updates, favorite
FROM
workspaces
WHERE
owner_id = $1
AND deleted = $2
AND LOWER("name") = LOWER($3)
ORDER BY created_at DESC
`
type GetWorkspaceByOwnerIDAndNameParams struct {
OwnerID uuid.UUID `db:"owner_id" json:"owner_id"`
Deleted bool `db:"deleted" json:"deleted"`
Name string `db:"name" json:"name"`
}
func (q *sqlQuerier) GetWorkspaceByOwnerIDAndName(ctx context.Context, arg GetWorkspaceByOwnerIDAndNameParams) (Workspace, error) {
row := q.db.QueryRowContext(ctx, getWorkspaceByOwnerIDAndName, arg.OwnerID, arg.Deleted, arg.Name)
var i Workspace
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.OwnerID,
&i.OrganizationID,
&i.TemplateID,
&i.Deleted,
&i.Name,
&i.AutostartSchedule,
&i.Ttl,
&i.LastUsedAt,
&i.DormantAt,
&i.DeletingAt,
&i.AutomaticUpdates,
&i.Favorite,
)
return i, err
}
const getWorkspaceByWorkspaceAppID = `-- name: GetWorkspaceByWorkspaceAppID :one
SELECT
id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at, automatic_updates, favorite
FROM
workspaces
WHERE
workspaces.id = (
SELECT
workspace_id
FROM
workspace_builds
WHERE
workspace_builds.job_id = (
SELECT
job_id
FROM
workspace_resources
WHERE
workspace_resources.id = (
SELECT
resource_id
FROM
workspace_agents
WHERE
workspace_agents.id = (
SELECT
agent_id
FROM
workspace_apps
WHERE
workspace_apps.id = $1
)
)
)
)
`
func (q *sqlQuerier) GetWorkspaceByWorkspaceAppID(ctx context.Context, workspaceAppID uuid.UUID) (Workspace, error) {
row := q.db.QueryRowContext(ctx, getWorkspaceByWorkspaceAppID, workspaceAppID)
var i Workspace
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.OwnerID,
&i.OrganizationID,
&i.TemplateID,
&i.Deleted,
&i.Name,
&i.AutostartSchedule,
&i.Ttl,
&i.LastUsedAt,
&i.DormantAt,
&i.DeletingAt,
&i.AutomaticUpdates,
&i.Favorite,
)
return i, err
}
const getWorkspaceUniqueOwnerCountByTemplateIDs = `-- name: GetWorkspaceUniqueOwnerCountByTemplateIDs :many
SELECT
template_id, COUNT(DISTINCT owner_id) AS unique_owners_sum
FROM
workspaces
WHERE
template_id = ANY($1 :: uuid[]) AND deleted = false
GROUP BY template_id
`
type GetWorkspaceUniqueOwnerCountByTemplateIDsRow struct {
TemplateID uuid.UUID `db:"template_id" json:"template_id"`
UniqueOwnersSum int64 `db:"unique_owners_sum" json:"unique_owners_sum"`
}
func (q *sqlQuerier) GetWorkspaceUniqueOwnerCountByTemplateIDs(ctx context.Context, templateIds []uuid.UUID) ([]GetWorkspaceUniqueOwnerCountByTemplateIDsRow, error) {
rows, err := q.db.QueryContext(ctx, getWorkspaceUniqueOwnerCountByTemplateIDs, pq.Array(templateIds))
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetWorkspaceUniqueOwnerCountByTemplateIDsRow
for rows.Next() {
var i GetWorkspaceUniqueOwnerCountByTemplateIDsRow
if err := rows.Scan(&i.TemplateID, &i.UniqueOwnersSum); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getWorkspaces = `-- name: GetWorkspaces :many
WITH
build_params AS (
SELECT
LOWER(unnest($1 :: text[])) AS name,
LOWER(unnest($2 :: text[])) AS value
),
filtered_workspaces AS (
SELECT
workspaces.id, workspaces.created_at, workspaces.updated_at, workspaces.owner_id, workspaces.organization_id, workspaces.template_id, workspaces.deleted, workspaces.name, workspaces.autostart_schedule, workspaces.ttl, workspaces.last_used_at, workspaces.dormant_at, workspaces.deleting_at, workspaces.automatic_updates, workspaces.favorite,
COALESCE(template.name, 'unknown') as template_name,
latest_build.template_version_id,
latest_build.template_version_name,
users.username as username,
latest_build.completed_at as latest_build_completed_at,
latest_build.canceled_at as latest_build_canceled_at,
latest_build.error as latest_build_error,
latest_build.transition as latest_build_transition,
latest_build.job_status as latest_build_status
FROM
workspaces
JOIN
users
ON
workspaces.owner_id = users.id
LEFT JOIN LATERAL (
SELECT
workspace_builds.id,
workspace_builds.transition,
workspace_builds.template_version_id,
template_versions.name AS template_version_name,
provisioner_jobs.id AS provisioner_job_id,
provisioner_jobs.started_at,
provisioner_jobs.updated_at,
provisioner_jobs.canceled_at,
provisioner_jobs.completed_at,
provisioner_jobs.error,
provisioner_jobs.job_status
FROM
workspace_builds
JOIN
provisioner_jobs
ON
provisioner_jobs.id = workspace_builds.job_id
LEFT JOIN
template_versions
ON
template_versions.id = workspace_builds.template_version_id
WHERE
workspace_builds.workspace_id = workspaces.id
ORDER BY
build_number DESC
LIMIT
1
) latest_build ON TRUE
LEFT JOIN LATERAL (
SELECT
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level
FROM
templates
WHERE
templates.id = workspaces.template_id
) template ON true
WHERE
-- Optionally include deleted workspaces
workspaces.deleted = $3
AND CASE
WHEN $4 :: text != '' THEN
CASE
-- Some workspace specific status refer to the transition
-- type. By default, the standard provisioner job status
-- search strings are supported.
-- 'running' states
WHEN $4 = 'starting' THEN
latest_build.job_status = 'running'::provisioner_job_status AND
latest_build.transition = 'start'::workspace_transition
WHEN $4 = 'stopping' THEN
latest_build.job_status = 'running'::provisioner_job_status AND
latest_build.transition = 'stop'::workspace_transition
WHEN $4 = 'deleting' THEN
latest_build.job_status = 'running' AND
latest_build.transition = 'delete'::workspace_transition
-- 'succeeded' states
WHEN $4 = 'deleted' THEN
latest_build.job_status = 'succeeded'::provisioner_job_status AND
latest_build.transition = 'delete'::workspace_transition
WHEN $4 = 'stopped' THEN
latest_build.job_status = 'succeeded'::provisioner_job_status AND
latest_build.transition = 'stop'::workspace_transition
WHEN $4 = 'started' THEN
latest_build.job_status = 'succeeded'::provisioner_job_status AND
latest_build.transition = 'start'::workspace_transition
-- Special case where the provisioner status and workspace status
-- differ. A workspace is "running" if the job is "succeeded" and
-- the transition is "start". This is because a workspace starts
-- running when a job is complete.
WHEN $4 = 'running' THEN
latest_build.job_status = 'succeeded'::provisioner_job_status AND
latest_build.transition = 'start'::workspace_transition
WHEN $4 != '' THEN
-- By default just match the job status exactly
latest_build.job_status = $4::provisioner_job_status
ELSE
true
END
ELSE true
END
-- Filter by owner_id
AND CASE
WHEN $5 :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN
workspaces.owner_id = $5
ELSE true
END
-- Filter by build parameter
-- @has_param will match any build that includes the parameter.
AND CASE WHEN array_length($6 :: text[], 1) > 0 THEN
EXISTS (
SELECT
1
FROM
workspace_build_parameters
WHERE
workspace_build_parameters.workspace_build_id = latest_build.id AND
-- ILIKE is case insensitive
workspace_build_parameters.name ILIKE ANY($6)
)
ELSE true
END
-- @param_value will match param name an value.
-- requires 2 arrays, @param_names and @param_values to be passed in.
-- Array index must match between the 2 arrays for name=value
AND CASE WHEN array_length($1 :: text[], 1) > 0 THEN
EXISTS (
SELECT
1
FROM
workspace_build_parameters
INNER JOIN
build_params
ON
LOWER(workspace_build_parameters.name) = build_params.name AND
LOWER(workspace_build_parameters.value) = build_params.value AND
workspace_build_parameters.workspace_build_id = latest_build.id
)
ELSE true
END
-- Filter by owner_name
AND CASE
WHEN $7 :: text != '' THEN
workspaces.owner_id = (SELECT id FROM users WHERE lower(username) = lower($7) AND deleted = false)
ELSE true
END
-- Filter by template_name
-- There can be more than 1 template with the same name across organizations.
-- Use the organization filter to restrict to 1 org if needed.
AND CASE
WHEN $8 :: text != '' THEN
workspaces.template_id = ANY(SELECT id FROM templates WHERE lower(name) = lower($8) AND deleted = false)
ELSE true
END
-- Filter by template_ids
AND CASE
WHEN array_length($9 :: uuid[], 1) > 0 THEN
workspaces.template_id = ANY($9)
ELSE true
END
-- Filter by workspace_ids
AND CASE
WHEN array_length($10 :: uuid[], 1) > 0 THEN
workspaces.id = ANY($10)
ELSE true
END
-- Filter by name, matching on substring
AND CASE
WHEN $11 :: text != '' THEN
workspaces.name ILIKE '%' || $11 || '%'
ELSE true
END
-- Filter by agent status
-- has-agent: is only applicable for workspaces in "start" transition. Stopped and deleted workspaces don't have agents.
AND CASE
WHEN $12 :: text != '' THEN
(
SELECT COUNT(*)
FROM
workspace_resources
JOIN
workspace_agents
ON
workspace_agents.resource_id = workspace_resources.id
WHERE
workspace_resources.job_id = latest_build.provisioner_job_id AND
latest_build.transition = 'start'::workspace_transition AND
$12 = (
CASE
WHEN workspace_agents.first_connected_at IS NULL THEN
CASE
WHEN workspace_agents.connection_timeout_seconds > 0 AND NOW() - workspace_agents.created_at > workspace_agents.connection_timeout_seconds * INTERVAL '1 second' THEN
'timeout'
ELSE
'connecting'
END
WHEN workspace_agents.disconnected_at > workspace_agents.last_connected_at THEN
'disconnected'
WHEN NOW() - workspace_agents.last_connected_at > INTERVAL '1 second' * $13 :: bigint THEN
'disconnected'
WHEN workspace_agents.last_connected_at IS NOT NULL THEN
'connected'
ELSE
NULL
END
)
) > 0
ELSE true
END
-- Filter by dormant workspaces.
AND CASE
WHEN $14 :: boolean != 'false' THEN
dormant_at IS NOT NULL
ELSE true
END
-- Filter by last_used
AND CASE
WHEN $15 :: timestamp with time zone > '0001-01-01 00:00:00Z' THEN
workspaces.last_used_at <= $15
ELSE true
END
AND CASE
WHEN $16 :: timestamp with time zone > '0001-01-01 00:00:00Z' THEN
workspaces.last_used_at >= $16
ELSE true
END
AND CASE
WHEN $17 :: boolean IS NOT NULL THEN
(latest_build.template_version_id = template.active_version_id) = $17 :: boolean
ELSE true
END
-- Authorize Filter clause will be injected below in GetAuthorizedWorkspaces
-- @authorize_filter
), filtered_workspaces_order AS (
SELECT
fw.id, fw.created_at, fw.updated_at, fw.owner_id, fw.organization_id, fw.template_id, fw.deleted, fw.name, fw.autostart_schedule, fw.ttl, fw.last_used_at, fw.dormant_at, fw.deleting_at, fw.automatic_updates, fw.favorite, fw.template_name, fw.template_version_id, fw.template_version_name, fw.username, fw.latest_build_completed_at, fw.latest_build_canceled_at, fw.latest_build_error, fw.latest_build_transition, fw.latest_build_status
FROM
filtered_workspaces fw
ORDER BY
-- To ensure that 'favorite' workspaces show up first in the list only for their owner.
CASE WHEN owner_id = $18 AND favorite THEN 0 ELSE 1 END ASC,
(latest_build_completed_at IS NOT NULL AND
latest_build_canceled_at IS NULL AND
latest_build_error IS NULL AND
latest_build_transition = 'start'::workspace_transition) DESC,
LOWER(username) ASC,
LOWER(name) ASC
LIMIT
CASE
WHEN $20 :: integer > 0 THEN
$20
END
OFFSET
$19
), filtered_workspaces_order_with_summary AS (
SELECT
fwo.id, fwo.created_at, fwo.updated_at, fwo.owner_id, fwo.organization_id, fwo.template_id, fwo.deleted, fwo.name, fwo.autostart_schedule, fwo.ttl, fwo.last_used_at, fwo.dormant_at, fwo.deleting_at, fwo.automatic_updates, fwo.favorite, fwo.template_name, fwo.template_version_id, fwo.template_version_name, fwo.username, fwo.latest_build_completed_at, fwo.latest_build_canceled_at, fwo.latest_build_error, fwo.latest_build_transition, fwo.latest_build_status
FROM
filtered_workspaces_order fwo
-- Return a technical summary row with total count of workspaces.
-- It is used to present the correct count if pagination goes beyond the offset.
UNION ALL
SELECT
'00000000-0000-0000-0000-000000000000'::uuid, -- id
'0001-01-01 00:00:00+00'::timestamptz, -- created_at
'0001-01-01 00:00:00+00'::timestamptz, -- updated_at
'00000000-0000-0000-0000-000000000000'::uuid, -- owner_id
'00000000-0000-0000-0000-000000000000'::uuid, -- organization_id
'00000000-0000-0000-0000-000000000000'::uuid, -- template_id
false, -- deleted
'**TECHNICAL_ROW**', -- name
'', -- autostart_schedule
0, -- ttl
'0001-01-01 00:00:00+00'::timestamptz, -- last_used_at
'0001-01-01 00:00:00+00'::timestamptz, -- dormant_at
'0001-01-01 00:00:00+00'::timestamptz, -- deleting_at
'never'::automatic_updates, -- automatic_updates
false, -- favorite
-- Extra columns added to ` + "`" + `filtered_workspaces` + "`" + `
'', -- template_name
'00000000-0000-0000-0000-000000000000'::uuid, -- template_version_id
'', -- template_version_name
'', -- username
'0001-01-01 00:00:00+00'::timestamptz, -- latest_build_completed_at,
'0001-01-01 00:00:00+00'::timestamptz, -- latest_build_canceled_at,
'', -- latest_build_error
'start'::workspace_transition, -- latest_build_transition
'unknown'::provisioner_job_status -- latest_build_status
WHERE
$21 :: boolean = true
), total_count AS (
SELECT
count(*) AS count
FROM
filtered_workspaces
)
SELECT
fwos.id, fwos.created_at, fwos.updated_at, fwos.owner_id, fwos.organization_id, fwos.template_id, fwos.deleted, fwos.name, fwos.autostart_schedule, fwos.ttl, fwos.last_used_at, fwos.dormant_at, fwos.deleting_at, fwos.automatic_updates, fwos.favorite, fwos.template_name, fwos.template_version_id, fwos.template_version_name, fwos.username, fwos.latest_build_completed_at, fwos.latest_build_canceled_at, fwos.latest_build_error, fwos.latest_build_transition, fwos.latest_build_status,
tc.count
FROM
filtered_workspaces_order_with_summary fwos
CROSS JOIN
total_count tc
`
type GetWorkspacesParams struct {
ParamNames []string `db:"param_names" json:"param_names"`
ParamValues []string `db:"param_values" json:"param_values"`
Deleted bool `db:"deleted" json:"deleted"`
Status string `db:"status" json:"status"`
OwnerID uuid.UUID `db:"owner_id" json:"owner_id"`
HasParam []string `db:"has_param" json:"has_param"`
OwnerUsername string `db:"owner_username" json:"owner_username"`
TemplateName string `db:"template_name" json:"template_name"`
TemplateIDs []uuid.UUID `db:"template_ids" json:"template_ids"`
WorkspaceIds []uuid.UUID `db:"workspace_ids" json:"workspace_ids"`
Name string `db:"name" json:"name"`
HasAgent string `db:"has_agent" json:"has_agent"`
AgentInactiveDisconnectTimeoutSeconds int64 `db:"agent_inactive_disconnect_timeout_seconds" json:"agent_inactive_disconnect_timeout_seconds"`
Dormant bool `db:"dormant" json:"dormant"`
LastUsedBefore time.Time `db:"last_used_before" json:"last_used_before"`
LastUsedAfter time.Time `db:"last_used_after" json:"last_used_after"`
UsingActive sql.NullBool `db:"using_active" json:"using_active"`
RequesterID uuid.UUID `db:"requester_id" json:"requester_id"`
Offset int32 `db:"offset_" json:"offset_"`
Limit int32 `db:"limit_" json:"limit_"`
WithSummary bool `db:"with_summary" json:"with_summary"`
}
type GetWorkspacesRow struct {
ID uuid.UUID `db:"id" json:"id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
OwnerID uuid.UUID `db:"owner_id" json:"owner_id"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
TemplateID uuid.UUID `db:"template_id" json:"template_id"`
Deleted bool `db:"deleted" json:"deleted"`
Name string `db:"name" json:"name"`
AutostartSchedule sql.NullString `db:"autostart_schedule" json:"autostart_schedule"`
Ttl sql.NullInt64 `db:"ttl" json:"ttl"`
LastUsedAt time.Time `db:"last_used_at" json:"last_used_at"`
DormantAt sql.NullTime `db:"dormant_at" json:"dormant_at"`
DeletingAt sql.NullTime `db:"deleting_at" json:"deleting_at"`
AutomaticUpdates AutomaticUpdates `db:"automatic_updates" json:"automatic_updates"`
Favorite bool `db:"favorite" json:"favorite"`
TemplateName string `db:"template_name" json:"template_name"`
TemplateVersionID uuid.UUID `db:"template_version_id" json:"template_version_id"`
TemplateVersionName sql.NullString `db:"template_version_name" json:"template_version_name"`
Username string `db:"username" json:"username"`
LatestBuildCompletedAt sql.NullTime `db:"latest_build_completed_at" json:"latest_build_completed_at"`
LatestBuildCanceledAt sql.NullTime `db:"latest_build_canceled_at" json:"latest_build_canceled_at"`
LatestBuildError sql.NullString `db:"latest_build_error" json:"latest_build_error"`
LatestBuildTransition WorkspaceTransition `db:"latest_build_transition" json:"latest_build_transition"`
LatestBuildStatus ProvisionerJobStatus `db:"latest_build_status" json:"latest_build_status"`
Count int64 `db:"count" json:"count"`
}
// build_params is used to filter by build parameters if present.
// It has to be a CTE because the set returning function 'unnest' cannot
// be used in a WHERE clause.
func (q *sqlQuerier) GetWorkspaces(ctx context.Context, arg GetWorkspacesParams) ([]GetWorkspacesRow, error) {
rows, err := q.db.QueryContext(ctx, getWorkspaces,
pq.Array(arg.ParamNames),
pq.Array(arg.ParamValues),
arg.Deleted,
arg.Status,
arg.OwnerID,
pq.Array(arg.HasParam),
arg.OwnerUsername,
arg.TemplateName,
pq.Array(arg.TemplateIDs),
pq.Array(arg.WorkspaceIds),
arg.Name,
arg.HasAgent,
arg.AgentInactiveDisconnectTimeoutSeconds,
arg.Dormant,
arg.LastUsedBefore,
arg.LastUsedAfter,
arg.UsingActive,
arg.RequesterID,
arg.Offset,
arg.Limit,
arg.WithSummary,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetWorkspacesRow
for rows.Next() {
var i GetWorkspacesRow
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.OwnerID,
&i.OrganizationID,
&i.TemplateID,
&i.Deleted,
&i.Name,
&i.AutostartSchedule,
&i.Ttl,
&i.LastUsedAt,
&i.DormantAt,
&i.DeletingAt,
&i.AutomaticUpdates,
&i.Favorite,
&i.TemplateName,
&i.TemplateVersionID,
&i.TemplateVersionName,
&i.Username,
&i.LatestBuildCompletedAt,
&i.LatestBuildCanceledAt,
&i.LatestBuildError,
&i.LatestBuildTransition,
&i.LatestBuildStatus,
&i.Count,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getWorkspacesEligibleForTransition = `-- name: GetWorkspacesEligibleForTransition :many
SELECT
workspaces.id, workspaces.created_at, workspaces.updated_at, workspaces.owner_id, workspaces.organization_id, workspaces.template_id, workspaces.deleted, workspaces.name, workspaces.autostart_schedule, workspaces.ttl, workspaces.last_used_at, workspaces.dormant_at, workspaces.deleting_at, workspaces.automatic_updates, workspaces.favorite
FROM
workspaces
LEFT JOIN
workspace_builds ON workspace_builds.workspace_id = workspaces.id
INNER JOIN
provisioner_jobs ON workspace_builds.job_id = provisioner_jobs.id
INNER JOIN
templates ON workspaces.template_id = templates.id
WHERE
workspace_builds.build_number = (
SELECT
MAX(build_number)
FROM
workspace_builds
WHERE
workspace_builds.workspace_id = workspaces.id
) AND
(
-- If the workspace build was a start transition, the workspace is
-- potentially eligible for autostop if it's past the deadline. The
-- deadline is computed at build time upon success and is bumped based
-- on activity (up the max deadline if set). We don't need to check
-- license here since that's done when the values are written to the build.
(
workspace_builds.transition = 'start'::workspace_transition AND
workspace_builds.deadline IS NOT NULL AND
workspace_builds.deadline < $1 :: timestamptz
) OR
-- If the workspace build was a stop transition, the workspace is
-- potentially eligible for autostart if it has a schedule set. The
-- caller must check if the template allows autostart in a license-aware
-- fashion as we cannot check it here.
(
workspace_builds.transition = 'stop'::workspace_transition AND
workspaces.autostart_schedule IS NOT NULL
) OR
-- If the workspace's most recent job resulted in an error
-- it may be eligible for failed stop.
(
provisioner_jobs.error IS NOT NULL AND
provisioner_jobs.error != '' AND
workspace_builds.transition = 'start'::workspace_transition
) OR
-- If the workspace's template has an inactivity_ttl set
-- it may be eligible for dormancy.
(
templates.time_til_dormant > 0 AND
workspaces.dormant_at IS NULL
) OR
-- If the workspace's template has a time_til_dormant_autodelete set
-- and the workspace is already dormant.
(
templates.time_til_dormant_autodelete > 0 AND
workspaces.dormant_at IS NOT NULL
)
) AND workspaces.deleted = 'false'
`
func (q *sqlQuerier) GetWorkspacesEligibleForTransition(ctx context.Context, now time.Time) ([]Workspace, error) {
rows, err := q.db.QueryContext(ctx, getWorkspacesEligibleForTransition, now)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Workspace
for rows.Next() {
var i Workspace
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.OwnerID,
&i.OrganizationID,
&i.TemplateID,
&i.Deleted,
&i.Name,
&i.AutostartSchedule,
&i.Ttl,
&i.LastUsedAt,
&i.DormantAt,
&i.DeletingAt,
&i.AutomaticUpdates,
&i.Favorite,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertWorkspace = `-- name: InsertWorkspace :one
INSERT INTO
workspaces (
id,
created_at,
updated_at,
owner_id,
organization_id,
template_id,
name,
autostart_schedule,
ttl,
last_used_at,
automatic_updates
)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at, automatic_updates, favorite
`
type InsertWorkspaceParams struct {
ID uuid.UUID `db:"id" json:"id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
OwnerID uuid.UUID `db:"owner_id" json:"owner_id"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
TemplateID uuid.UUID `db:"template_id" json:"template_id"`
Name string `db:"name" json:"name"`
AutostartSchedule sql.NullString `db:"autostart_schedule" json:"autostart_schedule"`
Ttl sql.NullInt64 `db:"ttl" json:"ttl"`
LastUsedAt time.Time `db:"last_used_at" json:"last_used_at"`
AutomaticUpdates AutomaticUpdates `db:"automatic_updates" json:"automatic_updates"`
}
func (q *sqlQuerier) InsertWorkspace(ctx context.Context, arg InsertWorkspaceParams) (Workspace, error) {
row := q.db.QueryRowContext(ctx, insertWorkspace,
arg.ID,
arg.CreatedAt,
arg.UpdatedAt,
arg.OwnerID,
arg.OrganizationID,
arg.TemplateID,
arg.Name,
arg.AutostartSchedule,
arg.Ttl,
arg.LastUsedAt,
arg.AutomaticUpdates,
)
var i Workspace
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.OwnerID,
&i.OrganizationID,
&i.TemplateID,
&i.Deleted,
&i.Name,
&i.AutostartSchedule,
&i.Ttl,
&i.LastUsedAt,
&i.DormantAt,
&i.DeletingAt,
&i.AutomaticUpdates,
&i.Favorite,
)
return i, err
}
const unfavoriteWorkspace = `-- name: UnfavoriteWorkspace :exec
UPDATE workspaces SET favorite = false WHERE id = $1
`
func (q *sqlQuerier) UnfavoriteWorkspace(ctx context.Context, id uuid.UUID) error {
_, err := q.db.ExecContext(ctx, unfavoriteWorkspace, id)
return err
}
const updateTemplateWorkspacesLastUsedAt = `-- name: UpdateTemplateWorkspacesLastUsedAt :exec
UPDATE workspaces
SET
last_used_at = $1::timestamptz
WHERE
template_id = $2
`
type UpdateTemplateWorkspacesLastUsedAtParams struct {
LastUsedAt time.Time `db:"last_used_at" json:"last_used_at"`
TemplateID uuid.UUID `db:"template_id" json:"template_id"`
}
func (q *sqlQuerier) UpdateTemplateWorkspacesLastUsedAt(ctx context.Context, arg UpdateTemplateWorkspacesLastUsedAtParams) error {
_, err := q.db.ExecContext(ctx, updateTemplateWorkspacesLastUsedAt, arg.LastUsedAt, arg.TemplateID)
return err
}
const updateWorkspace = `-- name: UpdateWorkspace :one
UPDATE
workspaces
SET
name = $2
WHERE
id = $1
AND deleted = false
RETURNING id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at, automatic_updates, favorite
`
type UpdateWorkspaceParams struct {
ID uuid.UUID `db:"id" json:"id"`
Name string `db:"name" json:"name"`
}
func (q *sqlQuerier) UpdateWorkspace(ctx context.Context, arg UpdateWorkspaceParams) (Workspace, error) {
row := q.db.QueryRowContext(ctx, updateWorkspace, arg.ID, arg.Name)
var i Workspace
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.OwnerID,
&i.OrganizationID,
&i.TemplateID,
&i.Deleted,
&i.Name,
&i.AutostartSchedule,
&i.Ttl,
&i.LastUsedAt,
&i.DormantAt,
&i.DeletingAt,
&i.AutomaticUpdates,
&i.Favorite,
)
return i, err
}
const updateWorkspaceAutomaticUpdates = `-- name: UpdateWorkspaceAutomaticUpdates :exec
UPDATE
workspaces
SET
automatic_updates = $2
WHERE
id = $1
`
type UpdateWorkspaceAutomaticUpdatesParams struct {
ID uuid.UUID `db:"id" json:"id"`
AutomaticUpdates AutomaticUpdates `db:"automatic_updates" json:"automatic_updates"`
}
func (q *sqlQuerier) UpdateWorkspaceAutomaticUpdates(ctx context.Context, arg UpdateWorkspaceAutomaticUpdatesParams) error {
_, err := q.db.ExecContext(ctx, updateWorkspaceAutomaticUpdates, arg.ID, arg.AutomaticUpdates)
return err
}
const updateWorkspaceAutostart = `-- name: UpdateWorkspaceAutostart :exec
UPDATE
workspaces
SET
autostart_schedule = $2
WHERE
id = $1
`
type UpdateWorkspaceAutostartParams struct {
ID uuid.UUID `db:"id" json:"id"`
AutostartSchedule sql.NullString `db:"autostart_schedule" json:"autostart_schedule"`
}
func (q *sqlQuerier) UpdateWorkspaceAutostart(ctx context.Context, arg UpdateWorkspaceAutostartParams) error {
_, err := q.db.ExecContext(ctx, updateWorkspaceAutostart, arg.ID, arg.AutostartSchedule)
return err
}
const updateWorkspaceDeletedByID = `-- name: UpdateWorkspaceDeletedByID :exec
UPDATE
workspaces
SET
deleted = $2
WHERE
id = $1
`
type UpdateWorkspaceDeletedByIDParams struct {
ID uuid.UUID `db:"id" json:"id"`
Deleted bool `db:"deleted" json:"deleted"`
}
func (q *sqlQuerier) UpdateWorkspaceDeletedByID(ctx context.Context, arg UpdateWorkspaceDeletedByIDParams) error {
_, err := q.db.ExecContext(ctx, updateWorkspaceDeletedByID, arg.ID, arg.Deleted)
return err
}
const updateWorkspaceDormantDeletingAt = `-- name: UpdateWorkspaceDormantDeletingAt :one
UPDATE
workspaces
SET
dormant_at = $2,
-- When a workspace is active we want to update the last_used_at to avoid the workspace going
-- immediately dormant. If we're transition the workspace to dormant then we leave it alone.
last_used_at = CASE WHEN $2::timestamptz IS NULL THEN
now() at time zone 'utc'
ELSE
last_used_at
END,
-- If dormant_at is null (meaning active) or the template-defined time_til_dormant_autodelete is 0 we should set
-- deleting_at to NULL else set it to the dormant_at + time_til_dormant_autodelete duration.
deleting_at = CASE WHEN $2::timestamptz IS NULL OR templates.time_til_dormant_autodelete = 0 THEN
NULL
ELSE
$2::timestamptz + (INTERVAL '1 millisecond' * (templates.time_til_dormant_autodelete / 1000000))
END
FROM
templates
WHERE
workspaces.id = $1
AND templates.id = workspaces.template_id
RETURNING
workspaces.id, workspaces.created_at, workspaces.updated_at, workspaces.owner_id, workspaces.organization_id, workspaces.template_id, workspaces.deleted, workspaces.name, workspaces.autostart_schedule, workspaces.ttl, workspaces.last_used_at, workspaces.dormant_at, workspaces.deleting_at, workspaces.automatic_updates, workspaces.favorite
`
type UpdateWorkspaceDormantDeletingAtParams struct {
ID uuid.UUID `db:"id" json:"id"`
DormantAt sql.NullTime `db:"dormant_at" json:"dormant_at"`
}
func (q *sqlQuerier) UpdateWorkspaceDormantDeletingAt(ctx context.Context, arg UpdateWorkspaceDormantDeletingAtParams) (Workspace, error) {
row := q.db.QueryRowContext(ctx, updateWorkspaceDormantDeletingAt, arg.ID, arg.DormantAt)
var i Workspace
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.OwnerID,
&i.OrganizationID,
&i.TemplateID,
&i.Deleted,
&i.Name,
&i.AutostartSchedule,
&i.Ttl,
&i.LastUsedAt,
&i.DormantAt,
&i.DeletingAt,
&i.AutomaticUpdates,
&i.Favorite,
)
return i, err
}
const updateWorkspaceLastUsedAt = `-- name: UpdateWorkspaceLastUsedAt :exec
UPDATE
workspaces
SET
last_used_at = $2
WHERE
id = $1
`
type UpdateWorkspaceLastUsedAtParams struct {
ID uuid.UUID `db:"id" json:"id"`
LastUsedAt time.Time `db:"last_used_at" json:"last_used_at"`
}
func (q *sqlQuerier) UpdateWorkspaceLastUsedAt(ctx context.Context, arg UpdateWorkspaceLastUsedAtParams) error {
_, err := q.db.ExecContext(ctx, updateWorkspaceLastUsedAt, arg.ID, arg.LastUsedAt)
return err
}
const updateWorkspaceTTL = `-- name: UpdateWorkspaceTTL :exec
UPDATE
workspaces
SET
ttl = $2
WHERE
id = $1
`
type UpdateWorkspaceTTLParams struct {
ID uuid.UUID `db:"id" json:"id"`
Ttl sql.NullInt64 `db:"ttl" json:"ttl"`
}
func (q *sqlQuerier) UpdateWorkspaceTTL(ctx context.Context, arg UpdateWorkspaceTTLParams) error {
_, err := q.db.ExecContext(ctx, updateWorkspaceTTL, arg.ID, arg.Ttl)
return err
}
const updateWorkspacesDormantDeletingAtByTemplateID = `-- name: UpdateWorkspacesDormantDeletingAtByTemplateID :exec
UPDATE workspaces
SET
deleting_at = CASE
WHEN $1::bigint = 0 THEN NULL
WHEN $2::timestamptz > '0001-01-01 00:00:00+00'::timestamptz THEN ($2::timestamptz) + interval '1 milliseconds' * $1::bigint
ELSE dormant_at + interval '1 milliseconds' * $1::bigint
END,
dormant_at = CASE WHEN $2::timestamptz > '0001-01-01 00:00:00+00'::timestamptz THEN $2::timestamptz ELSE dormant_at END
WHERE
template_id = $3
AND
dormant_at IS NOT NULL
`
type UpdateWorkspacesDormantDeletingAtByTemplateIDParams struct {
TimeTilDormantAutodeleteMs int64 `db:"time_til_dormant_autodelete_ms" json:"time_til_dormant_autodelete_ms"`
DormantAt time.Time `db:"dormant_at" json:"dormant_at"`
TemplateID uuid.UUID `db:"template_id" json:"template_id"`
}
func (q *sqlQuerier) UpdateWorkspacesDormantDeletingAtByTemplateID(ctx context.Context, arg UpdateWorkspacesDormantDeletingAtByTemplateIDParams) error {
_, err := q.db.ExecContext(ctx, updateWorkspacesDormantDeletingAtByTemplateID, arg.TimeTilDormantAutodeleteMs, arg.DormantAt, arg.TemplateID)
return err
}
const getWorkspaceAgentScriptsByAgentIDs = `-- name: GetWorkspaceAgentScriptsByAgentIDs :many
SELECT workspace_agent_id, log_source_id, log_path, created_at, script, cron, start_blocks_login, run_on_start, run_on_stop, timeout_seconds FROM workspace_agent_scripts WHERE workspace_agent_id = ANY($1 :: uuid [ ])
`
func (q *sqlQuerier) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAgentScript, error) {
rows, err := q.db.QueryContext(ctx, getWorkspaceAgentScriptsByAgentIDs, pq.Array(ids))
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceAgentScript
for rows.Next() {
var i WorkspaceAgentScript
if err := rows.Scan(
&i.WorkspaceAgentID,
&i.LogSourceID,
&i.LogPath,
&i.CreatedAt,
&i.Script,
&i.Cron,
&i.StartBlocksLogin,
&i.RunOnStart,
&i.RunOnStop,
&i.TimeoutSeconds,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertWorkspaceAgentScripts = `-- name: InsertWorkspaceAgentScripts :many
INSERT INTO
workspace_agent_scripts (workspace_agent_id, created_at, log_source_id, log_path, script, cron, start_blocks_login, run_on_start, run_on_stop, timeout_seconds)
SELECT
$1 :: uuid AS workspace_agent_id,
$2 :: timestamptz AS created_at,
unnest($3 :: uuid [ ]) AS log_source_id,
unnest($4 :: text [ ]) AS log_path,
unnest($5 :: text [ ]) AS script,
unnest($6 :: text [ ]) AS cron,
unnest($7 :: boolean [ ]) AS start_blocks_login,
unnest($8 :: boolean [ ]) AS run_on_start,
unnest($9 :: boolean [ ]) AS run_on_stop,
unnest($10 :: integer [ ]) AS timeout_seconds
RETURNING workspace_agent_scripts.workspace_agent_id, workspace_agent_scripts.log_source_id, workspace_agent_scripts.log_path, workspace_agent_scripts.created_at, workspace_agent_scripts.script, workspace_agent_scripts.cron, workspace_agent_scripts.start_blocks_login, workspace_agent_scripts.run_on_start, workspace_agent_scripts.run_on_stop, workspace_agent_scripts.timeout_seconds
`
type InsertWorkspaceAgentScriptsParams struct {
WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
LogSourceID []uuid.UUID `db:"log_source_id" json:"log_source_id"`
LogPath []string `db:"log_path" json:"log_path"`
Script []string `db:"script" json:"script"`
Cron []string `db:"cron" json:"cron"`
StartBlocksLogin []bool `db:"start_blocks_login" json:"start_blocks_login"`
RunOnStart []bool `db:"run_on_start" json:"run_on_start"`
RunOnStop []bool `db:"run_on_stop" json:"run_on_stop"`
TimeoutSeconds []int32 `db:"timeout_seconds" json:"timeout_seconds"`
}
func (q *sqlQuerier) InsertWorkspaceAgentScripts(ctx context.Context, arg InsertWorkspaceAgentScriptsParams) ([]WorkspaceAgentScript, error) {
rows, err := q.db.QueryContext(ctx, insertWorkspaceAgentScripts,
arg.WorkspaceAgentID,
arg.CreatedAt,
pq.Array(arg.LogSourceID),
pq.Array(arg.LogPath),
pq.Array(arg.Script),
pq.Array(arg.Cron),
pq.Array(arg.StartBlocksLogin),
pq.Array(arg.RunOnStart),
pq.Array(arg.RunOnStop),
pq.Array(arg.TimeoutSeconds),
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []WorkspaceAgentScript
for rows.Next() {
var i WorkspaceAgentScript
if err := rows.Scan(
&i.WorkspaceAgentID,
&i.LogSourceID,
&i.LogPath,
&i.CreatedAt,
&i.Script,
&i.Cron,
&i.StartBlocksLogin,
&i.RunOnStart,
&i.RunOnStop,
&i.TimeoutSeconds,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}