feat: add user-level parameter autofill (#11731)

This PR solves #10478 by auto-filling previously used template values in create and update workspace flows.

I decided against explicit user values in settings for these reasons:

* Autofill is far easier to implement
* Users benefit from autofill _by default_ — we don't need to teach them new concepts
* If we decide that autofill creates more harm than good, we can remove it without breaking compatibility
This commit is contained in:
Ammar Bandukwala
2024-01-30 16:02:21 -06:00
committed by GitHub
parent aeb4112513
commit adbb025e74
44 changed files with 862 additions and 137 deletions
+124
View File
@@ -23,6 +23,7 @@ import (
"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/dbfake"
"github.com/coder/coder/v2/coderd/database/dbgen"
"github.com/coder/coder/v2/coderd/database/dbtime"
"github.com/coder/coder/v2/coderd/rbac"
@@ -1721,6 +1722,129 @@ func TestSuspendedPagination(t *testing.T) {
require.Equal(t, expected, page.Users, "expected page")
}
func TestUserAutofillParameters(t *testing.T) {
t.Parallel()
t.Run("NotSelf", func(t *testing.T) {
t.Parallel()
client1, _, api := coderdtest.NewWithAPI(t, &coderdtest.Options{})
u1 := coderdtest.CreateFirstUser(t, client1)
client2, u2 := coderdtest.CreateAnotherUser(t, client1, u1.OrganizationID)
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()
db := api.Database
version := dbfake.TemplateVersion(t, db).Seed(database.TemplateVersion{
CreatedBy: u1.UserID,
OrganizationID: u1.OrganizationID,
}).Params(database.TemplateVersionParameter{
Name: "param",
Required: true,
}).Do()
_, err := client2.UserAutofillParameters(
ctx,
u1.UserID.String(),
version.Template.ID,
)
var apiErr *codersdk.Error
require.ErrorAs(t, err, &apiErr)
require.Equal(t, http.StatusBadRequest, apiErr.StatusCode())
// u1 should be able to read u2's parameters as u1 is site admin.
_, err = client1.UserAutofillParameters(
ctx,
u2.ID.String(),
version.Template.ID,
)
require.NoError(t, err)
})
t.Run("FindsParameters", func(t *testing.T) {
t.Parallel()
client1, _, api := coderdtest.NewWithAPI(t, &coderdtest.Options{})
u1 := coderdtest.CreateFirstUser(t, client1)
client2, u2 := coderdtest.CreateAnotherUser(t, client1, u1.OrganizationID)
db := api.Database
version := dbfake.TemplateVersion(t, db).Seed(database.TemplateVersion{
CreatedBy: u1.UserID,
OrganizationID: u1.OrganizationID,
}).Params(database.TemplateVersionParameter{
Name: "param",
Required: true,
},
database.TemplateVersionParameter{
Name: "param2",
Ephemeral: true,
},
).Do()
dbfake.WorkspaceBuild(t, db, database.Workspace{
OwnerID: u2.ID,
TemplateID: version.Template.ID,
OrganizationID: u1.OrganizationID,
}).Params(
database.WorkspaceBuildParameter{
Name: "param",
Value: "foo",
},
database.WorkspaceBuildParameter{
Name: "param2",
Value: "bar",
},
).Do()
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()
// Use client2 since client1 is site admin, so
// we don't get good coverage on RBAC working.
params, err := client2.UserAutofillParameters(
ctx,
u2.ID.String(),
version.Template.ID,
)
require.NoError(t, err)
require.Equal(t, 1, len(params))
require.Equal(t, "param", params[0].Name)
require.Equal(t, "foo", params[0].Value)
// Verify that latest parameter value is returned.
dbfake.WorkspaceBuild(t, db, database.Workspace{
OrganizationID: u1.OrganizationID,
OwnerID: u2.ID,
TemplateID: version.Template.ID,
}).Params(
database.WorkspaceBuildParameter{
Name: "param",
Value: "foo_new",
},
).Do()
params, err = client2.UserAutofillParameters(
ctx,
u2.ID.String(),
version.Template.ID,
)
require.NoError(t, err)
require.Equal(t, 1, len(params))
require.Equal(t, "param", params[0].Name)
require.Equal(t, "foo_new", params[0].Value)
})
}
// TestPaginatedUsers creates a list of users, then tries to paginate through
// them using different page sizes.
func TestPaginatedUsers(t *testing.T) {