Fix Update Project Logic (#54)

* carve out scope into a separate branch for a PR

* refactor getOpts to be private

* narrowed to just the scope proto

* wrap in a transaction

* remove user from scopes PR

* make scope type immutable

* refactored with more referential integrity (thanks mike)

* refactor based on pair programming with Mike

* better VetForWrite that supports db.Options but that requires GetOpts and Options to be exported.

* stop printing out all the db logs

* refactor withScope to be private

* refactor the VetForWrite a bit based on Todd's PR comment

* fix typo

Co-authored-by: Michael Gaffney <mgaffney@users.noreply.github.com>

* fix ctx propagation and Scope returns

* fix ctx propagation and use a const for std retry count

* change Std to Crud

* fix ctx propagation

* make sure update with field masks doesn't update a field not in the mask and force a read after update.

* add description

* remove comment about future

* clean error strings

* clean up enum string rep

* stdMeta

* fix return value to be just nil

* fix returns and errors

* update test to match new error string

* change len of domain to 24

* provide common function for new public ids

* use common db.NewPublicId() and provide the correct prefix

* move list out of CrudActions and provide a CrudlActions shortcut as well

* some comments about the purpose of VetForWrite()

* move to random 10

* Adding project API interface.

* Creation of Project handler and Tests

* Move to using the iam objects.

* Setting up testify framework for mocking and assertions.

* Fix iam options typo references.

* Adding tests for CreateProject and UpdateProject.

This also adds some additional argument validation.

* Merging in PR 40

* Renamed fakeRepo to mockRepo

* Changing around TODOs to capture current plans with the code.

* Remove the repo interface and use a real DB for tests.

* Fixed a few tests.

* Adding checks for properly formatted identifiers.

* Updating code to account for recent merges.  Relaxing some restrictions on what an id can look like.

* Added small comment explaining why RegisterGrpcGatewayer interface exists.

* Move project service up a call level.

Long term I think it would make sense to define these services even higher up the call stack, potentially at the initialization of the controller server, near where the DB is initialized.

* Making these fields align with those in the resources under hosts/...

* Hard code the service initialization in the handler registration code.

* Regenerate project related code proto code.

* Removing type field for project.

* Adds basic project handling in CLI/SDK (#47)

*    Adds SDK for creating and reading projects
*    Reorganizes a bit
*    Adds CLI for creating and reading projects
*    Fixes some some generation bugs
*    Adds redirect support for default org ID

* Fix protobuf import references being moved.

* Update client test to look for "orgs" and "projects" instead of "org" and "project"

* Update expected segments test

* Fix setAddr handling when default organization is in use

* Set client in target after create or read

* Adding functionality and tests for update masks.

* Additional tests for update.  Disable update non existant project test.

* Make not providing an update mask be an invalid argument.

* Make empty update mask paths and no mutable fields in the path an InvalidArgument error.

* Fix get Project test.

* Adding back in the Update a non existant project test case.

* Adding seperate path structured tests.

* Attach an issue id to field mask validation TODO.

* Generate Project Update SDK code (#58)

* Created the UpdateFunction method.

* Regenerate protobuf derived code since pull #50 updated dependencies.

* Special case IDs to not be passed for updates.

* Updated the tools file since imports reordered it.

* Add spaces between each tools import/comment grouping.

Co-authored-by: Jim Lambert <jimlambrt@Jims-MBP-3.home>
Co-authored-by: Jim <jlambert@hashicorp.com>
Co-authored-by: Michael Gaffney <mgaffney@users.noreply.github.com>
Co-authored-by: Jeff Mitchell <jeffrey.mitchell@gmail.com>
pull/65/head
Todd Knight 6 years ago committed by GitHub
parent d08c26517b
commit 7f0dfa2344
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -5,4 +5,5 @@ func main() {
writeStructTemplates()
writeCreateFuncs()
writeReadFuncs()
writeUpdateFuncs()
}

@ -58,6 +58,7 @@ func writeStructTemplates() {
}
}
// TODO: Add documentation around SetDefault and how it behaves when the field corresponding to the provided key is already set.
var structTemplate = template.Must(template.New("").Parse(
`// Code generated by "make api"; DO NOT EDIT.
package {{ .Package }}

@ -0,0 +1,112 @@
package main
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"strings"
"text/template"
)
type updateInfo struct {
baseType string
targetType string
path string
}
var updateFuncs = map[string][]*updateInfo{
"scopes": {
{
"Organization",
"Project",
"projects/%s",
},
},
}
func writeUpdateFuncs() {
for outPkg, funcs := range updateFuncs {
outFile := os.Getenv("GEN_BASEPATH") + fmt.Sprintf("/api/%s/update.gen.go", outPkg)
outBuf := bytes.NewBuffer([]byte(fmt.Sprintf(
`// Code generated by "make api"; DO NOT EDIT.
package %s
`, outPkg)))
for _, updateInfo := range funcs {
updateFuncTemplate.Execute(outBuf, struct {
BaseType string
TargetType string
LowerTargetType string
Path string
}{
BaseType: updateInfo.baseType,
TargetType: updateInfo.targetType,
LowerTargetType: strings.ToLower(updateInfo.targetType),
Path: updateInfo.path,
})
}
if err := ioutil.WriteFile(outFile, outBuf.Bytes(), 0644); err != nil {
fmt.Printf("error writing file %q: %v\n", outFile, err)
os.Exit(1)
}
}
}
var updateFuncTemplate = template.Must(template.New("").Parse(
`
func (s {{ .BaseType }}) Update{{ .TargetType }}(ctx context.Context, {{ .LowerTargetType }} *{{ .TargetType }}) (*{{ .TargetType }}, *api.Error, error) {
if s.Client == nil {
return nil, nil, fmt.Errorf("nil client in Create{{ .TargetType }} request")
}
if s.Id == "" {
{{ if (eq .BaseType "Organization") }}
// Assume the client has been configured with organization already and
// move on
{{ else if (eq .BaseType "Project") }}
// Assume the client has been configured with project already and move
// on
{{ else }}
return nil, nil, fmt.Errorf("missing {{ .BaseType}} ID in Create{{ .TargetType }} request")
{{ end }}
} else {
// If it's explicitly set here, override anything that might be in the
// client
{{ if (eq .BaseType "Organization") }}
ctx = context.WithValue(ctx, "org", s.Id)
{{ else if (eq .BaseType "Project") }}
ctx = context.WithValue(ctx, "project", s.Id)
{{ end }}
}
id := {{ .LowerTargetType }}.Id
{{ .LowerTargetType }}.Id = ""
req, err := s.Client.NewRequest(ctx, "PATCH", fmt.Sprintf("{{ .Path }}", id), {{ .LowerTargetType }})
if err != nil {
return nil, nil, fmt.Errorf("error creating Create{{ .TargetType }} request: %w", err)
}
resp, err := s.Client.Do(req)
if err != nil {
return nil, nil, fmt.Errorf("error performing client request during Update{{ .TargetType }} call: %w", err)
}
target := new({{ .TargetType }})
apiErr, err := resp.Decode(target)
if err != nil {
return nil, nil, fmt.Errorf("error decoding Update{{ .TargetType }} repsonse: %w", err)
}
{{ if (eq .TargetType "Organization") }}
target.Client = s.Client.Clone()
target.Client.SetOrgnization(target.Id)
{{ else if (eq .TargetType "Project") }}
target.Client = s.Client.Clone()
target.Client.SetProject(target.Id)
{{ else }}
target.Client = s.Client
{{ end }}
return target, apiErr, nil
}
`))

@ -17,24 +17,26 @@ func TestProjects_Crud(t *testing.T) {
Client: client,
}
name := "foo"
checkProject := func(step string, p *Project, apiErr *api.Error, err error) {
checkProject := func(step string, p *Project, apiErr *api.Error, err error, wantedName string) {
assert := assert.New(t)
assert.NoError(err, step)
assert.Nil(apiErr, step)
assert.NotNil(p, "returned project", step)
assert.NotNil(p, "returned project name", step)
assert.Equal(name, *p.Name, step)
assert.Equal(wantedName, *p.Name, step)
}
p, apiErr, err := org.CreateProject(tc.Context(), &Project{Name: api.String(name)})
checkProject("create", p, apiErr, err)
p, apiErr, err := org.CreateProject(tc.Context(), &Project{Name: api.String("foo")})
checkProject("create", p, apiErr, err, "foo")
p, apiErr, err = org.ReadProject(tc.Context(), &Project{Id: p.Id})
checkProject("read", p, apiErr, err)
checkProject("read", p, apiErr, err, "foo")
p = &Project{Id: p.Id}
p.Name = api.String("bar")
p, apiErr, err = org.UpdateProject(tc.Context(), p)
checkProject("update", p, apiErr, err, "bar")
// TODO: Update and Delete
// TODO: Delete
// TODO: Error conditions once the proper errors are being returned.
// Probably as parallel subtests against the same DB.

@ -0,0 +1,51 @@
// Code generated by "make api"; DO NOT EDIT.
package scopes
import (
"context"
"fmt"
"github.com/hashicorp/watchtower/api"
)
func (s Organization) UpdateProject(ctx context.Context, project *Project) (*Project, *api.Error, error) {
if s.Client == nil {
return nil, nil, fmt.Errorf("nil client in CreateProject request")
}
if s.Id == "" {
// Assume the client has been configured with organization already and
// move on
} else {
// If it's explicitly set here, override anything that might be in the
// client
ctx = context.WithValue(ctx, "org", s.Id)
}
id := project.Id
project.Id = ""
req, err := s.Client.NewRequest(ctx, "PATCH", fmt.Sprintf("projects/%s", id), project)
if err != nil {
return nil, nil, fmt.Errorf("error creating CreateProject request: %w", err)
}
resp, err := s.Client.Do(req)
if err != nil {
return nil, nil, fmt.Errorf("error performing client request during UpdateProject call: %w", err)
}
target := new(Project)
apiErr, err := resp.Decode(target)
if err != nil {
return nil, nil, fmt.Errorf("error decoding UpdateProject repsonse: %w", err)
}
target.Client = s.Client.Clone()
target.Client.SetProject(target.Id)
return target, apiErr, nil
}

@ -51,6 +51,7 @@ require (
go.mongodb.org/mongo-driver v1.3.2 // indirect
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1
golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380
google.golang.org/grpc v1.29.1
google.golang.org/protobuf v1.22.0

@ -568,8 +568,6 @@ github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyN
github.com/hashicorp/consul/sdk v0.2.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-alpnmux v0.0.0-20200323180452-dee08f00df54 h1:WhMHPQosFuXFjt2wpRzX2eqR1WpnS+35MFzmG3trg3Y=
github.com/hashicorp/go-alpnmux v0.0.0-20200323180452-dee08f00df54/go.mod h1:KvpteZzIafT4tRAuQ9vVRBgZyqeVCS2B2177fNAyEZc=
github.com/hashicorp/go-alpnmux v0.0.0-20200513011953-0293f5d23c31 h1:pxqI71/0R1WIASjQEJ9W9skCKYiREEkRoXFvHCZH1pg=
github.com/hashicorp/go-alpnmux v0.0.0-20200513011953-0293f5d23c31/go.mod h1:KvpteZzIafT4tRAuQ9vVRBgZyqeVCS2B2177fNAyEZc=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.21.0
// protoc-gen-go v1.22.0
// protoc v3.11.4
// source: controller/storage/db/db_test/v1/db_test.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.21.0
// protoc-gen-go v1.22.0
// protoc v3.11.4
// source: controller/api/v1/error.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.21.0
// protoc-gen-go v1.22.0
// protoc v3.11.4
// source: controller/api/resources/hosts/v1/host.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.21.0
// protoc-gen-go v1.22.0
// protoc v3.11.4
// source: controller/api/resources/hosts/v1/host_catalog.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.21.0
// protoc-gen-go v1.22.0
// protoc v3.11.4
// source: controller/api/resources/hosts/v1/host_set.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.21.0
// protoc-gen-go v1.22.0
// protoc v3.11.4
// source: controller/api/resources/scopes/v1/organization.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.21.0
// protoc-gen-go v1.22.0
// protoc v3.11.4
// source: controller/api/resources/scopes/v1/project.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.21.0
// protoc-gen-go v1.22.0
// protoc v3.11.4
// source: controller/api/services/v1/host_catalog_service.proto

@ -135,7 +135,10 @@ func local_request_HostCatalogService_GetHostCatalog_0(ctx context.Context, mars
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostCatalogService_GetHostCatalog_0); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostCatalogService_GetHostCatalog_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -226,7 +229,10 @@ func local_request_HostCatalogService_GetHostCatalog_1(ctx context.Context, mars
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostCatalogService_GetHostCatalog_1); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostCatalogService_GetHostCatalog_1); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -317,7 +323,10 @@ func local_request_HostCatalogService_ListHostCatalogs_0(ctx context.Context, ma
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "project_id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostCatalogService_ListHostCatalogs_0); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostCatalogService_ListHostCatalogs_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -386,7 +395,10 @@ func local_request_HostCatalogService_ListHostCatalogs_1(ctx context.Context, ma
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "org_id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostCatalogService_ListHostCatalogs_1); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostCatalogService_ListHostCatalogs_1); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -563,7 +575,10 @@ func local_request_HostCatalogService_CreateHostCatalog_1(ctx context.Context, m
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "org_id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostCatalogService_CreateHostCatalog_1); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostCatalogService_CreateHostCatalog_1); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -708,7 +723,10 @@ func local_request_HostCatalogService_UpdateHostCatalog_0(ctx context.Context, m
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostCatalogService_UpdateHostCatalog_0); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostCatalogService_UpdateHostCatalog_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -831,7 +849,10 @@ func local_request_HostCatalogService_UpdateHostCatalog_1(ctx context.Context, m
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostCatalogService_UpdateHostCatalog_1); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostCatalogService_UpdateHostCatalog_1); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -1020,7 +1041,10 @@ func local_request_HostCatalogService_DeleteHostCatalog_1(ctx context.Context, m
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostCatalogService_DeleteHostCatalog_1); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostCatalogService_DeleteHostCatalog_1); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.21.0
// protoc-gen-go v1.22.0
// protoc v3.11.4
// source: controller/api/services/v1/host_service.proto

@ -157,7 +157,10 @@ func local_request_HostService_GetHost_0(ctx context.Context, marshaler runtime.
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostService_GetHost_0); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostService_GetHost_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -270,7 +273,10 @@ func local_request_HostService_GetHost_1(ctx context.Context, marshaler runtime.
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostService_GetHost_1); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostService_GetHost_1); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -383,7 +389,10 @@ func local_request_HostService_ListHosts_0(ctx context.Context, marshaler runtim
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "host_catalog_id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostService_ListHosts_0); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostService_ListHosts_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -452,7 +461,10 @@ func local_request_HostService_ListHosts_1(ctx context.Context, marshaler runtim
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "org_id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostService_ListHosts_1); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostService_ListHosts_1); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -673,7 +685,10 @@ func local_request_HostService_CreateHost_1(ctx context.Context, marshaler runti
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "host_catalog_id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostService_CreateHost_1); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostService_CreateHost_1); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -840,7 +855,10 @@ func local_request_HostService_UpdateHost_0(ctx context.Context, marshaler runti
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostService_UpdateHost_0); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostService_UpdateHost_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -985,7 +1003,10 @@ func local_request_HostService_UpdateHost_1(ctx context.Context, marshaler runti
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostService_UpdateHost_1); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostService_UpdateHost_1); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -1218,7 +1239,10 @@ func local_request_HostService_DeleteHost_1(ctx context.Context, marshaler runti
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostService_DeleteHost_1); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostService_DeleteHost_1); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.21.0
// protoc-gen-go v1.22.0
// protoc v3.11.4
// source: controller/api/services/v1/host_set_service.proto

@ -157,7 +157,10 @@ func local_request_HostSetService_GetHostSet_0(ctx context.Context, marshaler ru
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostSetService_GetHostSet_0); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostSetService_GetHostSet_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -270,7 +273,10 @@ func local_request_HostSetService_GetHostSet_1(ctx context.Context, marshaler ru
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostSetService_GetHostSet_1); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostSetService_GetHostSet_1); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -383,7 +389,10 @@ func local_request_HostSetService_ListHostSets_0(ctx context.Context, marshaler
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "host_catalog_id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostSetService_ListHostSets_0); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostSetService_ListHostSets_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -474,7 +483,10 @@ func local_request_HostSetService_ListHostSets_1(ctx context.Context, marshaler
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "host_catalog_id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostSetService_ListHostSets_1); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostSetService_ListHostSets_1); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -695,7 +707,10 @@ func local_request_HostSetService_CreateHostSet_1(ctx context.Context, marshaler
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "host_catalog_id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostSetService_CreateHostSet_1); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostSetService_CreateHostSet_1); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -862,7 +877,10 @@ func local_request_HostSetService_UpdateHostSet_0(ctx context.Context, marshaler
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostSetService_UpdateHostSet_0); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostSetService_UpdateHostSet_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -1007,7 +1025,10 @@ func local_request_HostSetService_UpdateHostSet_1(ctx context.Context, marshaler
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostSetService_UpdateHostSet_1); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostSetService_UpdateHostSet_1); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -1240,7 +1261,10 @@ func local_request_HostSetService_DeleteHostSet_1(ctx context.Context, marshaler
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostSetService_DeleteHostSet_1); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostSetService_DeleteHostSet_1); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -1375,7 +1399,10 @@ func local_request_HostSetService_AddToHostSet_0(ctx context.Context, marshaler
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostSetService_AddToHostSet_0); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostSetService_AddToHostSet_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -1488,7 +1515,10 @@ func local_request_HostSetService_AddToHostSet_1(ctx context.Context, marshaler
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostSetService_AddToHostSet_1); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostSetService_AddToHostSet_1); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -1623,7 +1653,10 @@ func local_request_HostSetService_RemoveFromHostSet_0(ctx context.Context, marsh
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostSetService_RemoveFromHostSet_0); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostSetService_RemoveFromHostSet_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -1736,7 +1769,10 @@ func local_request_HostSetService_RemoveFromHostSet_1(ctx context.Context, marsh
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_HostSetService_RemoveFromHostSet_1); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HostSetService_RemoveFromHostSet_1); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.21.0
// protoc-gen-go v1.22.0
// protoc v3.11.4
// source: controller/api/services/v1/project_service.proto

@ -113,7 +113,10 @@ func local_request_ProjectService_GetProject_0(ctx context.Context, marshaler ru
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_ProjectService_GetProject_0); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ProjectService_GetProject_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -182,7 +185,10 @@ func local_request_ProjectService_ListProjects_0(ctx context.Context, marshaler
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "org_id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_ProjectService_ListProjects_0); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ProjectService_ListProjects_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -375,7 +381,10 @@ func local_request_ProjectService_UpdateProject_0(ctx context.Context, marshaler
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_ProjectService_UpdateProject_0); err != nil {
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ProjectService_UpdateProject_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.21.0
// protoc-gen-go v1.22.0
// protoc v3.11.4
// source: controller/storage/v1/placeholder.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.21.0
// protoc-gen-go v1.22.0
// protoc v3.11.4
// source: controller/storage/iam/store/v1/scope.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.21.0
// protoc-gen-go v1.22.0
// protoc v3.11.4
// source: controller/storage/iam/store/v1/timestamp.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.21.0
// protoc-gen-go v1.22.0
// protoc v3.11.4
// source: controller/storage/oplog/v1/any_operation.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.21.0
// protoc-gen-go v1.22.0
// protoc v3.11.4
// source: controller/storage/oplog/test/v1/oplog_test.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.21.0
// protoc-gen-go v1.22.0
// protoc v3.11.4
// source: controller/storage/oplog/store/v1/oplog.proto

@ -11,11 +11,19 @@ import (
pb "github.com/hashicorp/watchtower/internal/gen/controller/api/resources/scopes"
pbs "github.com/hashicorp/watchtower/internal/gen/controller/api/services"
"github.com/hashicorp/watchtower/internal/iam"
"google.golang.org/genproto/protobuf/field_mask"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
var reInvalidID = regexp.MustCompile("[^A-Za-z0-9]")
var (
reInvalidID = regexp.MustCompile("[^A-Za-z0-9]")
// TODO(ICU-28): Find a way to auto update these names and enforce the mappings between wire and storage.
wireToStorageMask = map[string]string{
"name": "Name",
"description": "Description",
}
)
type Service struct {
pbs.UnimplementedProjectServiceServer
@ -107,22 +115,25 @@ func (s Service) createInRepo(ctx context.Context, req *pbs.CreateProjectRequest
func (s Service) updateInRepo(ctx context.Context, req *pbs.UpdateProjectRequest) (*pb.Project, error) {
item := req.GetItem()
// TODO: convert field masks from API field masks with snake_case to db field masks casing.
madeUp := []string{}
opts := []iam.Option{iam.WithPublicId(req.GetId())}
if desc := item.GetDescription(); desc != nil {
madeUp = append(madeUp, "Description")
opts = append(opts, iam.WithDescription(desc.GetValue()))
}
if name := item.GetName(); name != nil {
madeUp = append(madeUp, "Name")
opts = append(opts, iam.WithName(name.GetValue()))
}
p, err := iam.NewProject(req.GetOrgId(), opts...)
if err != nil {
return nil, err
}
out, _, err := s.repo.UpdateScope(ctx, p, madeUp)
dbMask, err := toDbUpdateMask(req.GetUpdateMask())
if err != nil {
return nil, err
}
if len(dbMask) == 0 {
return nil, status.Errorf(codes.InvalidArgument, "No valid fields included in the update mask.")
}
out, _, err := s.repo.UpdateScope(ctx, p, dbMask)
if err != nil {
return nil, err
}
@ -132,6 +143,25 @@ func (s Service) updateInRepo(ctx context.Context, req *pbs.UpdateProjectRequest
return toProto(out), nil
}
// toDbUpdateMask converts the wire format's FieldMask into a list of strings containing FieldMask paths used
func toDbUpdateMask(fm *field_mask.FieldMask) ([]string, error) {
dbPaths := []string{}
invalid := []string{}
for _, p := range fm.GetPaths() {
for _, f := range strings.Split(p, ",") {
if dbField, ok := wireToStorageMask[strings.TrimSpace(f)]; ok {
dbPaths = append(dbPaths, dbField)
} else {
invalid = append(invalid, f)
}
}
}
if len(invalid) > 0 {
return nil, status.Errorf(codes.InvalidArgument, "Invalid fields passed in update_update mask: %v", invalid)
}
return dbPaths, nil
}
func toProto(in *iam.Scope) *pb.Project {
out := pb.Project{Id: in.GetPublicId()}
if in.GetDescription() != "" {
@ -194,7 +224,9 @@ func validateUpdateProjectRequest(req *pbs.UpdateProjectRequest) error {
if err := validateID(req.GetOrgId(), "o_"); err != nil {
return err
}
// TODO: Either require mask to be set or document in API that an unset mask updates all fields.
if req.GetUpdateMask() == nil {
return status.Errorf(codes.InvalidArgument, "UpdateMask not provided but is required to update a project.")
}
item := req.GetItem()
if item == nil {
// It is legitimate for no item to be specified in an update request as it indicates all fields provided in

@ -14,6 +14,7 @@ import (
pbs "github.com/hashicorp/watchtower/internal/gen/controller/api/services"
"github.com/hashicorp/watchtower/internal/iam"
"github.com/hashicorp/watchtower/internal/servers/controller/handlers/projects"
"google.golang.org/genproto/protobuf/field_mask"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
@ -42,7 +43,7 @@ func createDefaultProjectAndRepo(t *testing.T) (*iam.Scope, *iam.Repository) {
t.Fatalf("Could not create org scope: %v", err)
}
p, err := iam.NewProject(oRes.GetPublicId(), iam.WithName("default"))
p, err := iam.NewProject(oRes.GetPublicId(), iam.WithName("default"), iam.WithDescription("default"))
if err != nil {
t.Fatalf("Could not get new project: %v", err)
}
@ -64,6 +65,7 @@ func TestGet(t *testing.T) {
pProject := &pb.Project{
Id: proj.GetPublicId(),
Name: &wrappers.StringValue{Value: proj.GetName()},
Description: &wrappers.StringValue{Value: proj.GetDescription()},
CreatedTime: proj.CreateTime.GetTimestamp(),
UpdatedTime: proj.UpdateTime.GetTimestamp(),
}
@ -210,6 +212,15 @@ func TestCreate(t *testing.T) {
func TestUpdate(t *testing.T) {
proj, repo := createDefaultProjectAndRepo(t)
tested := projects.NewService(repo)
var err error
resetProject := func() {
if proj, _, err = repo.UpdateScope(context.Background(), proj, []string{"Name", "Description"}); err != nil {
t.Fatalf("Failed to reset the project")
}
}
projCreated, err := ptypes.Timestamp(proj.GetCreateTime().GetTimestamp())
if err != nil {
t.Fatalf("Error converting proto to timestamp: %v", err)
@ -227,10 +238,36 @@ func TestUpdate(t *testing.T) {
}{
{
name: "Update an Existing Project",
req: &pbs.UpdateProjectRequest{Item: &pb.Project{
Name: &wrappers.StringValue{Value: "new"},
Description: &wrappers.StringValue{Value: "desc"},
}},
req: &pbs.UpdateProjectRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"name", "description"},
},
Item: &pb.Project{
Name: &wrappers.StringValue{Value: "new"},
Description: &wrappers.StringValue{Value: "desc"},
},
},
res: &pbs.UpdateProjectResponse{
Item: &pb.Project{
Id: proj.GetPublicId(),
Name: &wrappers.StringValue{Value: "new"},
Description: &wrappers.StringValue{Value: "desc"},
CreatedTime: proj.GetCreateTime().GetTimestamp(),
},
},
errCode: codes.OK,
},
{
name: "Multiple Paths in single string",
req: &pbs.UpdateProjectRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"name,description"},
},
Item: &pb.Project{
Name: &wrappers.StringValue{Value: "new"},
Description: &wrappers.StringValue{Value: "desc"},
},
},
res: &pbs.UpdateProjectResponse{
Item: &pb.Project{
Id: proj.GetPublicId(),
@ -241,24 +278,122 @@ func TestUpdate(t *testing.T) {
},
errCode: codes.OK,
},
{
name: "No Update Mask Is Invalid Argument",
req: &pbs.UpdateProjectRequest{
Item: &pb.Project{
Name: &wrappers.StringValue{Value: "updated name"},
Description: &wrappers.StringValue{Value: "updated desc"},
},
},
errCode: codes.InvalidArgument,
},
{
name: "No Paths in Mask Is Invalid Argument",
req: &pbs.UpdateProjectRequest{
UpdateMask: &field_mask.FieldMask{Paths: []string{}},
Item: &pb.Project{
Name: &wrappers.StringValue{Value: "updated name"},
Description: &wrappers.StringValue{Value: "updated desc"},
},
},
errCode: codes.InvalidArgument,
},
{
name: "Only non-existant paths in Mask Is Invalid Argument",
req: &pbs.UpdateProjectRequest{
UpdateMask: &field_mask.FieldMask{Paths: []string{"nonexistant_field"}},
Item: &pb.Project{
Name: &wrappers.StringValue{Value: "updated name"},
Description: &wrappers.StringValue{Value: "updated desc"},
},
},
errCode: codes.InvalidArgument,
},
{
name: "Unset Name",
req: &pbs.UpdateProjectRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"name"},
},
Item: &pb.Project{
Description: &wrappers.StringValue{Value: "ignored"},
},
},
res: &pbs.UpdateProjectResponse{
Item: &pb.Project{
Id: proj.GetPublicId(),
Description: &wrappers.StringValue{Value: "default"},
CreatedTime: proj.GetCreateTime().GetTimestamp(),
},
},
errCode: codes.OK,
},
{
name: "Update Only Name",
req: &pbs.UpdateProjectRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"name"},
},
Item: &pb.Project{
Name: &wrappers.StringValue{Value: "updated"},
Description: &wrappers.StringValue{Value: "ignored"},
},
},
res: &pbs.UpdateProjectResponse{
Item: &pb.Project{
Id: proj.GetPublicId(),
Name: &wrappers.StringValue{Value: "updated"},
Description: &wrappers.StringValue{Value: "default"},
CreatedTime: proj.GetCreateTime().GetTimestamp(),
},
},
errCode: codes.OK,
},
{
name: "Update Only Description",
req: &pbs.UpdateProjectRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"description"},
},
Item: &pb.Project{
Name: &wrappers.StringValue{Value: "ignored"},
Description: &wrappers.StringValue{Value: "notignored"},
},
},
res: &pbs.UpdateProjectResponse{
Item: &pb.Project{
Id: proj.GetPublicId(),
Name: &wrappers.StringValue{Value: "default"},
Description: &wrappers.StringValue{Value: "notignored"},
CreatedTime: proj.GetCreateTime().GetTimestamp(),
},
},
errCode: codes.OK,
},
{
name: "Update a Non Existing Project",
req: &pbs.UpdateProjectRequest{
Id: "p_DoesntExis",
UpdateMask: &field_mask.FieldMask{
Paths: []string{"description"},
},
Item: &pb.Project{
Name: &wrappers.StringValue{Value: "new"},
Description: &wrappers.StringValue{Value: "desc"},
},
},
// TODO: Update this to be NotFound.
errCode: codes.Unknown,
},
{
name: "Cant change Id",
req: &pbs.UpdateProjectRequest{
Id: "p_1234567890",
Id: proj.GetPublicId(),
UpdateMask: &field_mask.FieldMask{
Paths: []string{"id"},
},
Item: &pb.Project{
Id: "p_0987654321",
Id: "p_somethinge",
Name: &wrappers.StringValue{Value: "new"},
Description: &wrappers.StringValue{Value: "new desc"},
}},
@ -267,33 +402,43 @@ func TestUpdate(t *testing.T) {
},
{
name: "Cant specify Created Time",
req: &pbs.UpdateProjectRequest{Item: &pb.Project{
CreatedTime: ptypes.TimestampNow(),
}},
req: &pbs.UpdateProjectRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"created_time"},
},
Item: &pb.Project{
CreatedTime: ptypes.TimestampNow(),
},
},
res: nil,
errCode: codes.InvalidArgument,
},
{
name: "Cant specify Updated Time",
req: &pbs.UpdateProjectRequest{Item: &pb.Project{
UpdatedTime: ptypes.TimestampNow(),
}},
req: &pbs.UpdateProjectRequest{
UpdateMask: &field_mask.FieldMask{
Paths: []string{"updated_time"},
},
Item: &pb.Project{
UpdatedTime: ptypes.TimestampNow(),
},
},
res: nil,
errCode: codes.InvalidArgument,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
defer resetProject()
assert := assert.New(t)
req := proto.Clone(toMerge).(*pbs.UpdateProjectRequest)
proto.Merge(req, tc.req)
s := projects.NewService(repo)
got, gErr := s.UpdateProject(context.Background(), req)
got, gErr := tested.UpdateProject(context.Background(), req)
assert.Equal(tc.errCode, status.Code(gErr), "UpdateProject(%+v) got error %v, wanted %v", req, gErr, tc.errCode)
if got != nil {
assert.NotNilf(tc.res, "Expected UpdateProject response to be nil, but was %v", got)
gotUpdateTime, err := ptypes.Timestamp(got.GetItem().GetUpdatedTime())
if err != nil {
t.Fatalf("Error converting proto to timestamp: %v", err)
@ -305,7 +450,7 @@ func TestUpdate(t *testing.T) {
_ = projCreated
// Clear all values which are hard to compare against.
got.Item.CreatedTime, got.Item.UpdatedTime, tc.res.Item.CreatedTime, tc.res.Item.UpdatedTime = nil, nil, nil, nil
got.Item.UpdatedTime, tc.res.Item.UpdatedTime = nil, nil
}
assert.True(proto.Equal(got, tc.res), "UpdateProject(%q) got response %q, wanted %q", req, got, tc.res)
})

@ -10,23 +10,28 @@
package tools
//go:generate go install github.com/bufbuild/buf/cmd/buf
import _ "github.com/bufbuild/buf/cmd/buf"
import (
//go:generate go install github.com/bufbuild/buf/cmd/buf
_ "github.com/bufbuild/buf/cmd/buf"
//go:generate go install github.com/go-swagger/go-swagger/cmd/swagger
import _ "github.com/go-swagger/go-swagger/cmd/swagger"
//go:generate go install github.com/favadi/protoc-go-inject-tag
_ "github.com/favadi/protoc-go-inject-tag"
//go:generate go install github.com/favadi/protoc-go-inject-tag
import _ "github.com/favadi/protoc-go-inject-tag"
//go:generate go install github.com/go-swagger/go-swagger/cmd/swagger
_ "github.com/go-swagger/go-swagger/cmd/swagger"
// use this instead of google.golang.org/protobuf/cmd/protoc-gen-go since this supports grpc plugin while the other does not.
// see https://github.com/golang/protobuf/releases#v1.4-generated-code and
// https://github.com/protocolbuffers/protobuf-go/releases/tag/v1.20.0#v1.20-grpc-support
//go:generate go install github.com/golang/protobuf/protoc-gen-go
import _ "github.com/golang/protobuf/protoc-gen-go"
// use this instead of google.golang.org/protobuf/cmd/protoc-gen-go since this supports grpc plugin while the other does not.
// see https://github.com/golang/protobuf/releases#v1.4-generated-code and
// https://github.com/protocolbuffers/protobuf-go/releases/tag/v1.20.0#v1.20-grpc-support
//go:generate go install github.com/golang/protobuf/protoc-gen-go
_ "github.com/golang/protobuf/protoc-gen-go"
//go:generate go install github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
import _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway"
//go:generate go install github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
_ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway"
//go:generate go install github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
import _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger"
//go:generate go install github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
_ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger"
//go:generate go install golang.org/x/tools/cmd/goimports
_ "golang.org/x/tools/cmd/goimports"
)

Loading…
Cancel
Save