diff --git a/coderd/database/dbgen/dbgen.go b/coderd/database/dbgen/dbgen.go
index ae898d4f1f..9c8696112d 100644
--- a/coderd/database/dbgen/dbgen.go
+++ b/coderd/database/dbgen/dbgen.go
@@ -788,16 +788,17 @@ func TemplateVersion(t testing.TB, db database.Store, orig database.TemplateVers
err := db.InTx(func(db database.Store) error {
versionID := takeFirst(orig.ID, uuid.New())
err := db.InsertTemplateVersion(genCtx, database.InsertTemplateVersionParams{
- ID: versionID,
- TemplateID: takeFirst(orig.TemplateID, uuid.NullUUID{}),
- OrganizationID: takeFirst(orig.OrganizationID, uuid.New()),
- CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()),
- UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()),
- Name: takeFirst(orig.Name, testutil.GetRandomName(t)),
- Message: orig.Message,
- Readme: takeFirst(orig.Readme, testutil.GetRandomName(t)),
- JobID: takeFirst(orig.JobID, uuid.New()),
- CreatedBy: takeFirst(orig.CreatedBy, uuid.New()),
+ ID: versionID,
+ TemplateID: takeFirst(orig.TemplateID, uuid.NullUUID{}),
+ OrganizationID: takeFirst(orig.OrganizationID, uuid.New()),
+ CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()),
+ UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()),
+ Name: takeFirst(orig.Name, testutil.GetRandomName(t)),
+ Message: orig.Message,
+ Readme: takeFirst(orig.Readme, testutil.GetRandomName(t)),
+ JobID: takeFirst(orig.JobID, uuid.New()),
+ CreatedBy: takeFirst(orig.CreatedBy, uuid.New()),
+ SourceExampleID: takeFirst(orig.SourceExampleID, sql.NullString{}),
})
if err != nil {
return err
diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go
index 5583fff111..765573b311 100644
--- a/coderd/database/dbmem/dbmem.go
+++ b/coderd/database/dbmem/dbmem.go
@@ -7699,16 +7699,17 @@ func (q *FakeQuerier) InsertTemplateVersion(_ context.Context, arg database.Inse
//nolint:gosimple
version := database.TemplateVersionTable{
- ID: arg.ID,
- TemplateID: arg.TemplateID,
- OrganizationID: arg.OrganizationID,
- CreatedAt: arg.CreatedAt,
- UpdatedAt: arg.UpdatedAt,
- Name: arg.Name,
- Message: arg.Message,
- Readme: arg.Readme,
- JobID: arg.JobID,
- CreatedBy: arg.CreatedBy,
+ ID: arg.ID,
+ TemplateID: arg.TemplateID,
+ OrganizationID: arg.OrganizationID,
+ CreatedAt: arg.CreatedAt,
+ UpdatedAt: arg.UpdatedAt,
+ Name: arg.Name,
+ Message: arg.Message,
+ Readme: arg.Readme,
+ JobID: arg.JobID,
+ CreatedBy: arg.CreatedBy,
+ SourceExampleID: arg.SourceExampleID,
}
q.templateVersions = append(q.templateVersions, version)
return nil
diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql
index 9919011579..eba9b7cf10 100644
--- a/coderd/database/dump.sql
+++ b/coderd/database/dump.sql
@@ -1217,7 +1217,8 @@ CREATE TABLE template_versions (
created_by uuid NOT NULL,
external_auth_providers jsonb DEFAULT '[]'::jsonb NOT NULL,
message character varying(1048576) DEFAULT ''::character varying NOT NULL,
- archived boolean DEFAULT false NOT NULL
+ archived boolean DEFAULT false NOT NULL,
+ source_example_id text
);
COMMENT ON COLUMN template_versions.external_auth_providers IS 'IDs of External auth providers for a specific template version';
@@ -1245,6 +1246,7 @@ CREATE VIEW template_version_with_user AS
template_versions.external_auth_providers,
template_versions.message,
template_versions.archived,
+ template_versions.source_example_id,
COALESCE(visible_users.avatar_url, ''::text) AS created_by_avatar_url,
COALESCE(visible_users.username, ''::text) AS created_by_username
FROM (template_versions
diff --git a/coderd/database/migrations/000277_template_version_example_ids.down.sql b/coderd/database/migrations/000277_template_version_example_ids.down.sql
new file mode 100644
index 0000000000..ad961e9f63
--- /dev/null
+++ b/coderd/database/migrations/000277_template_version_example_ids.down.sql
@@ -0,0 +1,28 @@
+-- We cannot alter the column type while a view depends on it, so we drop it and recreate it.
+DROP VIEW template_version_with_user;
+
+ALTER TABLE
+ template_versions
+DROP COLUMN source_example_id;
+
+-- Recreate `template_version_with_user` as described in dump.sql
+CREATE VIEW template_version_with_user AS
+SELECT
+ template_versions.id,
+ template_versions.template_id,
+ template_versions.organization_id,
+ template_versions.created_at,
+ template_versions.updated_at,
+ template_versions.name,
+ template_versions.readme,
+ template_versions.job_id,
+ template_versions.created_by,
+ template_versions.external_auth_providers,
+ template_versions.message,
+ template_versions.archived,
+ COALESCE(visible_users.avatar_url, ''::text) AS created_by_avatar_url,
+ COALESCE(visible_users.username, ''::text) AS created_by_username
+FROM (template_versions
+ LEFT JOIN visible_users ON (template_versions.created_by = visible_users.id));
+
+COMMENT ON VIEW template_version_with_user IS 'Joins in the username + avatar url of the created by user.';
diff --git a/coderd/database/migrations/000277_template_version_example_ids.up.sql b/coderd/database/migrations/000277_template_version_example_ids.up.sql
new file mode 100644
index 0000000000..aca34b31de
--- /dev/null
+++ b/coderd/database/migrations/000277_template_version_example_ids.up.sql
@@ -0,0 +1,30 @@
+-- We cannot alter the column type while a view depends on it, so we drop it and recreate it.
+DROP VIEW template_version_with_user;
+
+ALTER TABLE
+ template_versions
+ADD
+ COLUMN source_example_id TEXT;
+
+-- Recreate `template_version_with_user` as described in dump.sql
+CREATE VIEW template_version_with_user AS
+SELECT
+ template_versions.id,
+ template_versions.template_id,
+ template_versions.organization_id,
+ template_versions.created_at,
+ template_versions.updated_at,
+ template_versions.name,
+ template_versions.readme,
+ template_versions.job_id,
+ template_versions.created_by,
+ template_versions.external_auth_providers,
+ template_versions.message,
+ template_versions.archived,
+ template_versions.source_example_id,
+ COALESCE(visible_users.avatar_url, ''::text) AS created_by_avatar_url,
+ COALESCE(visible_users.username, ''::text) AS created_by_username
+FROM (template_versions
+ LEFT JOIN visible_users ON (template_versions.created_by = visible_users.id));
+
+COMMENT ON VIEW template_version_with_user IS 'Joins in the username + avatar url of the created by user.';
diff --git a/coderd/database/models.go b/coderd/database/models.go
index af0a3122f7..6b99245079 100644
--- a/coderd/database/models.go
+++ b/coderd/database/models.go
@@ -2773,6 +2773,7 @@ type TemplateVersion struct {
ExternalAuthProviders json.RawMessage `db:"external_auth_providers" json:"external_auth_providers"`
Message string `db:"message" json:"message"`
Archived bool `db:"archived" json:"archived"`
+ SourceExampleID sql.NullString `db:"source_example_id" json:"source_example_id"`
CreatedByAvatarURL string `db:"created_by_avatar_url" json:"created_by_avatar_url"`
CreatedByUsername string `db:"created_by_username" json:"created_by_username"`
}
@@ -2826,8 +2827,9 @@ type TemplateVersionTable struct {
// IDs of External auth providers for a specific template version
ExternalAuthProviders json.RawMessage `db:"external_auth_providers" json:"external_auth_providers"`
// Message describing the changes in this version of the template, similar to a Git commit message. Like a commit message, this should be a short, high-level description of the changes in this version of the template. This message is immutable and should not be updated after the fact.
- Message string `db:"message" json:"message"`
- Archived bool `db:"archived" json:"archived"`
+ Message string `db:"message" json:"message"`
+ Archived bool `db:"archived" json:"archived"`
+ SourceExampleID sql.NullString `db:"source_example_id" json:"source_example_id"`
}
type TemplateVersionVariable struct {
diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go
index 4eec78cf97..33a3ce12a4 100644
--- a/coderd/database/queries.sql.go
+++ b/coderd/database/queries.sql.go
@@ -8996,7 +8996,7 @@ 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
+ id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id
FROM
template_versions
WHERE
@@ -9097,7 +9097,7 @@ func (q *sqlQuerier) ArchiveUnusedTemplateVersions(ctx context.Context, arg Arch
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
+ id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username
FROM
template_version_with_user AS template_versions
WHERE
@@ -9134,6 +9134,7 @@ func (q *sqlQuerier) GetPreviousTemplateVersion(ctx context.Context, arg GetPrev
&i.ExternalAuthProviders,
&i.Message,
&i.Archived,
+ &i.SourceExampleID,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
)
@@ -9142,7 +9143,7 @@ func (q *sqlQuerier) GetPreviousTemplateVersion(ctx context.Context, arg GetPrev
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
+ id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username
FROM
template_version_with_user AS template_versions
WHERE
@@ -9165,6 +9166,7 @@ func (q *sqlQuerier) GetTemplateVersionByID(ctx context.Context, id uuid.UUID) (
&i.ExternalAuthProviders,
&i.Message,
&i.Archived,
+ &i.SourceExampleID,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
)
@@ -9173,7 +9175,7 @@ func (q *sqlQuerier) GetTemplateVersionByID(ctx context.Context, id uuid.UUID) (
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
+ id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username
FROM
template_version_with_user AS template_versions
WHERE
@@ -9196,6 +9198,7 @@ func (q *sqlQuerier) GetTemplateVersionByJobID(ctx context.Context, jobID uuid.U
&i.ExternalAuthProviders,
&i.Message,
&i.Archived,
+ &i.SourceExampleID,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
)
@@ -9204,7 +9207,7 @@ func (q *sqlQuerier) GetTemplateVersionByJobID(ctx context.Context, jobID uuid.U
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
+ id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username
FROM
template_version_with_user AS template_versions
WHERE
@@ -9233,6 +9236,7 @@ func (q *sqlQuerier) GetTemplateVersionByTemplateIDAndName(ctx context.Context,
&i.ExternalAuthProviders,
&i.Message,
&i.Archived,
+ &i.SourceExampleID,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
)
@@ -9241,7 +9245,7 @@ func (q *sqlQuerier) GetTemplateVersionByTemplateIDAndName(ctx context.Context,
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
+ id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username
FROM
template_version_with_user AS template_versions
WHERE
@@ -9270,6 +9274,7 @@ func (q *sqlQuerier) GetTemplateVersionsByIDs(ctx context.Context, ids []uuid.UU
&i.ExternalAuthProviders,
&i.Message,
&i.Archived,
+ &i.SourceExampleID,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
); err != nil {
@@ -9288,7 +9293,7 @@ func (q *sqlQuerier) GetTemplateVersionsByIDs(ctx context.Context, ids []uuid.UU
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
+ id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username
FROM
template_version_with_user AS template_versions
WHERE
@@ -9364,6 +9369,7 @@ func (q *sqlQuerier) GetTemplateVersionsByTemplateID(ctx context.Context, arg Ge
&i.ExternalAuthProviders,
&i.Message,
&i.Archived,
+ &i.SourceExampleID,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
); err != nil {
@@ -9381,7 +9387,7 @@ func (q *sqlQuerier) GetTemplateVersionsByTemplateID(ctx context.Context, arg Ge
}
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
+SELECT id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, 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) {
@@ -9406,6 +9412,7 @@ func (q *sqlQuerier) GetTemplateVersionsCreatedAfter(ctx context.Context, create
&i.ExternalAuthProviders,
&i.Message,
&i.Archived,
+ &i.SourceExampleID,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
); err != nil {
@@ -9434,23 +9441,25 @@ INSERT INTO
message,
readme,
job_id,
- created_by
+ created_by,
+ source_example_id
)
VALUES
- ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
+ ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
`
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"`
+ 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"`
+ SourceExampleID sql.NullString `db:"source_example_id" json:"source_example_id"`
}
func (q *sqlQuerier) InsertTemplateVersion(ctx context.Context, arg InsertTemplateVersionParams) error {
@@ -9465,6 +9474,7 @@ func (q *sqlQuerier) InsertTemplateVersion(ctx context.Context, arg InsertTempla
arg.Readme,
arg.JobID,
arg.CreatedBy,
+ arg.SourceExampleID,
)
return err
}
diff --git a/coderd/database/queries/templateversions.sql b/coderd/database/queries/templateversions.sql
index 094c1b6014..0436a7f9ba 100644
--- a/coderd/database/queries/templateversions.sql
+++ b/coderd/database/queries/templateversions.sql
@@ -87,10 +87,11 @@ INSERT INTO
message,
readme,
job_id,
- created_by
+ created_by,
+ source_example_id
)
VALUES
- ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);
+ ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11);
-- name: UpdateTemplateVersionByID :exec
UPDATE
diff --git a/coderd/telemetry/telemetry.go b/coderd/telemetry/telemetry.go
index 8ad85b0b39..233450c43d 100644
--- a/coderd/telemetry/telemetry.go
+++ b/coderd/telemetry/telemetry.go
@@ -868,6 +868,9 @@ func ConvertTemplateVersion(version database.TemplateVersion) TemplateVersion {
if version.TemplateID.Valid {
snapVersion.TemplateID = &version.TemplateID.UUID
}
+ if version.SourceExampleID.Valid {
+ snapVersion.SourceExampleID = &version.SourceExampleID.String
+ }
return snapVersion
}
@@ -1116,11 +1119,12 @@ type Template struct {
}
type TemplateVersion struct {
- ID uuid.UUID `json:"id"`
- CreatedAt time.Time `json:"created_at"`
- TemplateID *uuid.UUID `json:"template_id,omitempty"`
- OrganizationID uuid.UUID `json:"organization_id"`
- JobID uuid.UUID `json:"job_id"`
+ ID uuid.UUID `json:"id"`
+ CreatedAt time.Time `json:"created_at"`
+ TemplateID *uuid.UUID `json:"template_id,omitempty"`
+ OrganizationID uuid.UUID `json:"organization_id"`
+ JobID uuid.UUID `json:"job_id"`
+ SourceExampleID *string `json:"source_example_id,omitempty"`
}
type ProvisionerJob struct {
diff --git a/coderd/telemetry/telemetry_test.go b/coderd/telemetry/telemetry_test.go
index 214d111a17..2b70cd2a6d 100644
--- a/coderd/telemetry/telemetry_test.go
+++ b/coderd/telemetry/telemetry_test.go
@@ -1,6 +1,7 @@
package telemetry_test
import (
+ "database/sql"
"encoding/json"
"net/http"
"net/http/httptest"
@@ -48,6 +49,10 @@ func TestTelemetry(t *testing.T) {
_ = dbgen.Template(t, db, database.Template{
Provisioner: database.ProvisionerTypeTerraform,
})
+ sourceExampleID := uuid.NewString()
+ _ = dbgen.TemplateVersion(t, db, database.TemplateVersion{
+ SourceExampleID: sql.NullString{String: sourceExampleID, Valid: true},
+ })
_ = dbgen.TemplateVersion(t, db, database.TemplateVersion{})
user := dbgen.User(t, db, database.User{})
_ = dbgen.Workspace(t, db, database.WorkspaceTable{})
@@ -93,7 +98,7 @@ func TestTelemetry(t *testing.T) {
require.Len(t, snapshot.ProvisionerJobs, 1)
require.Len(t, snapshot.Licenses, 1)
require.Len(t, snapshot.Templates, 1)
- require.Len(t, snapshot.TemplateVersions, 1)
+ require.Len(t, snapshot.TemplateVersions, 2)
require.Len(t, snapshot.Users, 1)
require.Len(t, snapshot.Groups, 2)
// 1 member in the everyone group + 1 member in the custom group
@@ -111,6 +116,17 @@ func TestTelemetry(t *testing.T) {
require.Len(t, wsa.Subsystems, 2)
require.Equal(t, string(database.WorkspaceAgentSubsystemEnvbox), wsa.Subsystems[0])
require.Equal(t, string(database.WorkspaceAgentSubsystemExectrace), wsa.Subsystems[1])
+
+ tvs := snapshot.TemplateVersions
+ sort.Slice(tvs, func(i, j int) bool {
+ // Sort by SourceExampleID presence (non-nil comes before nil)
+ if (tvs[i].SourceExampleID != nil) != (tvs[j].SourceExampleID != nil) {
+ return tvs[i].SourceExampleID != nil
+ }
+ return false
+ })
+ require.Equal(t, tvs[0].SourceExampleID, &sourceExampleID)
+ require.Nil(t, tvs[1].SourceExampleID)
})
t.Run("HashedEmail", func(t *testing.T) {
t.Parallel()
diff --git a/coderd/templateversions.go b/coderd/templateversions.go
index a0609c42c3..12def3e5d6 100644
--- a/coderd/templateversions.go
+++ b/coderd/templateversions.go
@@ -1582,6 +1582,10 @@ func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *ht
Readme: "",
JobID: provisionerJob.ID,
CreatedBy: apiKey.UserID,
+ SourceExampleID: sql.NullString{
+ String: req.ExampleID,
+ Valid: req.ExampleID != "",
+ },
})
if err != nil {
if database.IsUniqueViolation(err, database.UniqueTemplateVersionsTemplateIDNameKey) {
diff --git a/coderd/templateversions_test.go b/coderd/templateversions_test.go
index 5ebbd0f418..5e96de10d5 100644
--- a/coderd/templateversions_test.go
+++ b/coderd/templateversions_test.go
@@ -16,6 +16,7 @@ import (
"github.com/coder/coder/v2/coderd/audit"
"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/coderd/database"
+ "github.com/coder/coder/v2/coderd/database/dbauthz"
"github.com/coder/coder/v2/coderd/database/dbtestutil"
"github.com/coder/coder/v2/coderd/externalauth"
"github.com/coder/coder/v2/coderd/rbac"
@@ -134,7 +135,7 @@ func TestPostTemplateVersionsByOrganization(t *testing.T) {
t.Run("WithParameters", func(t *testing.T) {
t.Parallel()
auditor := audit.NewMock()
- client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true, Auditor: auditor})
+ client, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{IncludeProvisionerDaemon: true, Auditor: auditor})
user := coderdtest.CreateFirstUser(t, client)
data, err := echo.Tar(&echo.Responses{
Parse: echo.ParseComplete,
@@ -160,11 +161,17 @@ func TestPostTemplateVersionsByOrganization(t *testing.T) {
require.Len(t, auditor.AuditLogs(), 2)
assert.Equal(t, database.AuditActionCreate, auditor.AuditLogs()[1].Action)
+
+ admin, err := client.User(ctx, user.UserID.String())
+ require.NoError(t, err)
+ tvDB, err := db.GetTemplateVersionByID(dbauthz.As(ctx, coderdtest.AuthzUserSubject(admin, user.OrganizationID)), version.ID)
+ require.NoError(t, err)
+ require.False(t, tvDB.SourceExampleID.Valid)
})
t.Run("Example", func(t *testing.T) {
t.Parallel()
- client := coderdtest.New(t, nil)
+ client, db := coderdtest.NewWithDatabase(t, nil)
user := coderdtest.CreateFirstUser(t, client)
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
@@ -205,6 +212,12 @@ func TestPostTemplateVersionsByOrganization(t *testing.T) {
require.NoError(t, err)
require.Equal(t, "my-example", tv.Name)
+ admin, err := client.User(ctx, user.UserID.String())
+ require.NoError(t, err)
+ tvDB, err := db.GetTemplateVersionByID(dbauthz.As(ctx, coderdtest.AuthzUserSubject(admin, user.OrganizationID)), tv.ID)
+ require.NoError(t, err)
+ require.Equal(t, ls[0].ID, tvDB.SourceExampleID.String)
+
// ensure the template tar was uploaded correctly
fl, ct, err := client.Download(ctx, tv.Job.FileID)
require.NoError(t, err)
diff --git a/docs/admin/security/audit-logs.md b/docs/admin/security/audit-logs.md
index 3ea4e145d1..db214b0e14 100644
--- a/docs/admin/security/audit-logs.md
+++ b/docs/admin/security/audit-logs.md
@@ -24,7 +24,7 @@ We track the following resources:
| OAuth2ProviderAppSecret
|
| Field | Tracked |
|---|
| app_id | false |
| created_at | false |
| display_secret | false |
| hashed_secret | false |
| id | false |
| last_used_at | false |
| secret_prefix | false |
|
| Organization
| | Field | Tracked |
|---|
| created_at | false |
| description | true |
| display_name | true |
| icon | true |
| id | false |
| is_default | true |
| name | true |
| updated_at | true |
|
| Template
write, delete | | Field | Tracked |
|---|
| active_version_id | true |
| activity_bump | true |
| allow_user_autostart | true |
| allow_user_autostop | true |
| allow_user_cancel_workspace_jobs | true |
| autostart_block_days_of_week | true |
| autostop_requirement_days_of_week | true |
| autostop_requirement_weeks | true |
| created_at | false |
| created_by | true |
| created_by_avatar_url | false |
| created_by_username | false |
| default_ttl | true |
| deleted | false |
| deprecated | true |
| description | true |
| display_name | true |
| failure_ttl | true |
| group_acl | true |
| icon | true |
| id | true |
| max_port_sharing_level | true |
| name | true |
| organization_display_name | false |
| organization_icon | false |
| organization_id | false |
| organization_name | false |
| provisioner | true |
| require_active_version | true |
| time_til_dormant | true |
| time_til_dormant_autodelete | true |
| updated_at | false |
| user_acl | true |
|
-| TemplateVersion
create, write | | Field | Tracked |
|---|
| archived | true |
| created_at | false |
| created_by | true |
| created_by_avatar_url | false |
| created_by_username | false |
| external_auth_providers | false |
| id | true |
| job_id | false |
| message | false |
| name | true |
| organization_id | false |
| readme | true |
| template_id | true |
| updated_at | false |
|
+| TemplateVersion
create, write | | Field | Tracked |
|---|
| archived | true |
| created_at | false |
| created_by | true |
| created_by_avatar_url | false |
| created_by_username | false |
| external_auth_providers | false |
| id | true |
| job_id | false |
| message | false |
| name | true |
| organization_id | false |
| readme | true |
| source_example_id | false |
| template_id | true |
| updated_at | false |
|
| User
create, write, delete | | Field | Tracked |
|---|
| avatar_url | false |
| created_at | false |
| deleted | true |
| email | true |
| github_com_user_id | false |
| hashed_one_time_passcode | false |
| hashed_password | true |
| id | true |
| last_seen_at | false |
| login_type | true |
| name | true |
| one_time_passcode_expires_at | true |
| quiet_hours_schedule | true |
| rbac_roles | true |
| status | true |
| theme_preference | false |
| updated_at | false |
| username | true |
|
| WorkspaceBuild
start, stop | | Field | Tracked |
|---|
| build_number | false |
| created_at | false |
| daily_cost | false |
| deadline | false |
| id | false |
| initiator_by_avatar_url | false |
| initiator_by_username | false |
| initiator_id | false |
| job_id | false |
| max_deadline | false |
| provisioner_state | false |
| reason | false |
| template_version_id | true |
| transition | false |
| updated_at | false |
| workspace_id | false |
|
| WorkspaceProxy
| | Field | Tracked |
|---|
| created_at | true |
| deleted | false |
| derp_enabled | true |
| derp_only | true |
| display_name | true |
| icon | true |
| id | true |
| name | true |
| region_id | true |
| token_hashed_secret | true |
| updated_at | false |
| url | true |
| version | true |
| wildcard_hostname | true |
|
diff --git a/enterprise/audit/table.go b/enterprise/audit/table.go
index f9e74959f2..24f7dfa4b4 100644
--- a/enterprise/audit/table.go
+++ b/enterprise/audit/table.go
@@ -127,6 +127,7 @@ var auditableResourcesTypes = map[any]map[string]Action{
"created_by_avatar_url": ActionIgnore,
"created_by_username": ActionIgnore,
"archived": ActionTrack,
+ "source_example_id": ActionIgnore, // Never changes.
},
&database.User{}: {
"id": ActionTrack,