From 5553fd989f85e8a9a043ad5b23d3797f3dcc17a0 Mon Sep 17 00:00:00 2001 From: Todd Knight Date: Wed, 2 Sep 2020 12:31:30 -0700 Subject: [PATCH] Host Set and Target SDK Generation using new Pathing (#320) * Adding new style template starts. * Host-set crud changes generated and tested. * Add templates for every resource with the new pathing. Only host-sets has testing or the backed pathing. * Only generate new style API if requested in the input. Have disabling old be opt in. --- api/hostsets/host_set.gen.go | 419 ++++++++++++++++++ api/hostsets/host_set_test.go | 248 ++++++----- api/internal/genapi/input.go | 35 ++ api/internal/genapi/templates.go | 374 +++++++++++++++- api/targets/host_set.gen.go | 7 + api/targets/option.gen.go | 92 ++++ .../custom.go => targets/target.gen.go} | 170 ++++--- api/targets/target_test.go | 200 +++++++++ 8 files changed, 1353 insertions(+), 192 deletions(-) create mode 100644 api/targets/host_set.gen.go create mode 100644 api/targets/option.gen.go rename api/{hostsets/custom.go => targets/target.gen.go} (61%) create mode 100644 api/targets/target_test.go diff --git a/api/hostsets/host_set.gen.go b/api/hostsets/host_set.gen.go index ca34d4856c..d05f72b0b4 100644 --- a/api/hostsets/host_set.gen.go +++ b/api/hostsets/host_set.gen.go @@ -269,6 +269,233 @@ func (c *Client) List(ctx context.Context, hostCatalogId string, opt ...Option) return target.Items, apiErr, nil } +func (c *Client) Create2(ctx context.Context, hostCatalogId string, opt ...Option) (*HostSet, *api.Error, error) { + if hostCatalogId == "" { + return nil, nil, fmt.Errorf("empty hostCatalogId value passed into Create request") + } + opts, apiOpts := getOpts(opt...) + apiOpts = append(apiOpts, api.WithNewStyle()) + if c.client == nil { + return nil, nil, fmt.Errorf("nil client") + } + + opts.postMap["host_catalog_id"] = hostCatalogId + + req, err := c.client.NewRequest(ctx, "POST", "host-sets", opts.postMap, apiOpts...) + if err != nil { + return nil, nil, fmt.Errorf("error creating Create request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, nil, fmt.Errorf("error performing client request during Create call: %w", err) + } + + target := new(HostSet) + apiErr, err := resp.Decode(target) + if err != nil { + return nil, nil, fmt.Errorf("error decoding Create response: %w", err) + } + if apiErr != nil { + return nil, apiErr, nil + } + return target, apiErr, nil +} + +func (c *Client) Read2(ctx context.Context, hostSetId string, opt ...Option) (*HostSet, *api.Error, error) { + if hostSetId == "" { + return nil, nil, fmt.Errorf("empty hostSetId value passed into Read request") + } + if c.client == nil { + return nil, nil, fmt.Errorf("nil client") + } + + opts, apiOpts := getOpts(opt...) + apiOpts = append(apiOpts, api.WithNewStyle()) + + req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("host-sets/%s", hostSetId), nil, apiOpts...) + if err != nil { + return nil, nil, fmt.Errorf("error creating Read request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, nil, fmt.Errorf("error performing client request during Read call: %w", err) + } + + target := new(HostSet) + apiErr, err := resp.Decode(target) + if err != nil { + return nil, nil, fmt.Errorf("error decoding Read response: %w", err) + } + if apiErr != nil { + return nil, apiErr, nil + } + return target, apiErr, nil +} + +func (c *Client) Update2(ctx context.Context, hostSetId string, version uint32, opt ...Option) (*HostSet, *api.Error, error) { + if hostSetId == "" { + return nil, nil, fmt.Errorf("empty hostSetId value passed into Update request") + } + if c.client == nil { + return nil, nil, fmt.Errorf("nil client") + } + + opts, apiOpts := getOpts(opt...) + apiOpts = append(apiOpts, api.WithNewStyle()) + + if version == 0 { + if !opts.withAutomaticVersioning { + return nil, nil, errors.New("zero version number passed into Update request and automatic versioning not specified") + } + existingTarget, existingApiErr, existingErr := c.Read2(ctx, hostSetId, opt...) + if existingErr != nil { + return nil, nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) + } + if existingApiErr != nil { + return nil, nil, fmt.Errorf("error from controller when performing initial check-and-set read: %s", pretty.Sprint(existingApiErr)) + } + if existingTarget == nil { + return nil, nil, errors.New("nil resource found when performing initial check-and-set read") + } + version = existingTarget.Version + } + + opts.postMap["version"] = version + + req, err := c.client.NewRequest(ctx, "PATCH", fmt.Sprintf("host-sets/%s", hostSetId), opts.postMap, apiOpts...) + if err != nil { + return nil, nil, fmt.Errorf("error creating Update request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, nil, fmt.Errorf("error performing client request during Update call: %w", err) + } + + target := new(HostSet) + apiErr, err := resp.Decode(target) + if err != nil { + return nil, nil, fmt.Errorf("error decoding Update response: %w", err) + } + if apiErr != nil { + return nil, apiErr, nil + } + return target, apiErr, nil +} + +func (c *Client) Delete2(ctx context.Context, hostSetId string, opt ...Option) (bool, *api.Error, error) { + if hostSetId == "" { + return false, nil, fmt.Errorf("empty hostSetId value passed into Delete request") + } + if c.client == nil { + return false, nil, fmt.Errorf("nil client") + } + + opts, apiOpts := getOpts(opt...) + apiOpts = append(apiOpts, api.WithNewStyle()) + + req, err := c.client.NewRequest(ctx, "DELETE", fmt.Sprintf("host-sets/%s", hostSetId), nil, apiOpts...) + if err != nil { + return false, nil, fmt.Errorf("error creating Delete request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return false, nil, fmt.Errorf("error performing client request during Delete call: %w", err) + } + + type deleteResponse struct { + Existed bool + } + target := &deleteResponse{} + apiErr, err := resp.Decode(target) + if err != nil { + return false, nil, fmt.Errorf("error decoding Delete response: %w", err) + } + if apiErr != nil { + return false, apiErr, nil + } + return target.Existed, apiErr, nil +} + +func (c *Client) List2(ctx context.Context, hostCatalogId string, opt ...Option) ([]*HostSet, *api.Error, error) { + if hostCatalogId == "" { + return nil, nil, fmt.Errorf("empty hostCatalogId value passed into List request") + } + if c.client == nil { + return nil, nil, fmt.Errorf("nil client") + } + + opts, apiOpts := getOpts(opt...) + apiOpts = append(apiOpts, api.WithNewStyle()) + opts.queryMap["host_catalog_id"] = hostCatalogId + + req, err := c.client.NewRequest(ctx, "GET", "host-sets", nil, apiOpts...) + if err != nil { + return nil, nil, fmt.Errorf("error creating List request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, nil, fmt.Errorf("error performing client request during List call: %w", err) + } + + type listResponse struct { + Items []*HostSet + } + target := &listResponse{} + apiErr, err := resp.Decode(target) + if err != nil { + return nil, nil, fmt.Errorf("error decoding List response: %w", err) + } + if apiErr != nil { + return nil, apiErr, nil + } + return target.Items, apiErr, nil +} + func (c *Client) AddHosts(ctx context.Context, hostCatalogId string, hostSetId string, version uint32, hostIds []string, opt ...Option) (*HostSet, *api.Error, error) { if hostCatalogId == "" { return nil, nil, fmt.Errorf("empty hostCatalogId value passed into AddHosts request") @@ -466,3 +693,195 @@ func (c *Client) RemoveHosts(ctx context.Context, hostCatalogId string, hostSetI } return target, apiErr, nil } + +func (c *Client) AddHosts2(ctx context.Context, hostSetId string, version uint32, hostIds []string, opt ...Option) (*HostSet, *api.Error, error) { + if hostSetId == "" { + return nil, nil, fmt.Errorf("empty hostSetId value passed into AddHosts request") + } + if c.client == nil { + return nil, nil, fmt.Errorf("nil client") + } + + opts, apiOpts := getOpts(opt...) + apiOpts = append(apiOpts, api.WithNewStyle()) + + if version == 0 { + if !opts.withAutomaticVersioning { + return nil, nil, errors.New("zero version number passed into AddHosts request") + } + existingTarget, existingApiErr, existingErr := c.Read2(ctx, hostSetId, opt...) + if existingErr != nil { + return nil, nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) + } + if existingApiErr != nil { + return nil, nil, fmt.Errorf("error from controller when performing initial check-and-set read: %s", pretty.Sprint(existingApiErr)) + } + if existingTarget == nil { + return nil, nil, errors.New("nil resource found when performing initial check-and-set read") + } + version = existingTarget.Version + } + + opts.postMap["version"] = version + + if len(hostIds) > 0 { + opts.postMap["host_ids"] = hostIds + } + + req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("host-sets/%s:add-hosts", hostSetId), opts.postMap, apiOpts...) + if err != nil { + return nil, nil, fmt.Errorf("error creating AddHosts request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, nil, fmt.Errorf("error performing client request during AddHosts call: %w", err) + } + + target := new(HostSet) + apiErr, err := resp.Decode(target) + if err != nil { + return nil, nil, fmt.Errorf("error decoding AddHosts response: %w", err) + } + if apiErr != nil { + return nil, apiErr, nil + } + return target, apiErr, nil +} + +func (c *Client) SetHosts2(ctx context.Context, hostSetId string, version uint32, hostIds []string, opt ...Option) (*HostSet, *api.Error, error) { + if hostSetId == "" { + return nil, nil, fmt.Errorf("empty hostSetId value passed into SetHosts request") + } + if c.client == nil { + return nil, nil, fmt.Errorf("nil client") + } + + opts, apiOpts := getOpts(opt...) + apiOpts = append(apiOpts, api.WithNewStyle()) + + if version == 0 { + if !opts.withAutomaticVersioning { + return nil, nil, errors.New("zero version number passed into SetHosts request") + } + existingTarget, existingApiErr, existingErr := c.Read2(ctx, hostSetId, opt...) + if existingErr != nil { + return nil, nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) + } + if existingApiErr != nil { + return nil, nil, fmt.Errorf("error from controller when performing initial check-and-set read: %s", pretty.Sprint(existingApiErr)) + } + if existingTarget == nil { + return nil, nil, errors.New("nil resource found when performing initial check-and-set read") + } + version = existingTarget.Version + } + + opts.postMap["version"] = version + + if len(hostIds) > 0 { + opts.postMap["host_ids"] = hostIds + } else if hostIds != nil { + // In this function, a non-nil but empty list means clear out + opts.postMap["host_ids"] = nil + } + + req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("host-sets/%s:set-hosts", hostSetId), opts.postMap, apiOpts...) + if err != nil { + return nil, nil, fmt.Errorf("error creating SetHosts request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, nil, fmt.Errorf("error performing client request during SetHosts call: %w", err) + } + + target := new(HostSet) + apiErr, err := resp.Decode(target) + if err != nil { + return nil, nil, fmt.Errorf("error decoding SetHosts response: %w", err) + } + if apiErr != nil { + return nil, apiErr, nil + } + return target, apiErr, nil +} + +func (c *Client) RemoveHosts2(ctx context.Context, hostSetId string, version uint32, hostIds []string, opt ...Option) (*HostSet, *api.Error, error) { + if hostSetId == "" { + return nil, nil, fmt.Errorf("empty hostSetId value passed into RemoveHosts request") + } + if c.client == nil { + return nil, nil, fmt.Errorf("nil client") + } + + opts, apiOpts := getOpts(opt...) + apiOpts = append(apiOpts, api.WithNewStyle()) + + if version == 0 { + if !opts.withAutomaticVersioning { + return nil, nil, errors.New("zero version number passed into RemoveHosts request") + } + existingTarget, existingApiErr, existingErr := c.Read2(ctx, hostSetId, opt...) + if existingErr != nil { + return nil, nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) + } + if existingApiErr != nil { + return nil, nil, fmt.Errorf("error from controller when performing initial check-and-set read: %s", pretty.Sprint(existingApiErr)) + } + if existingTarget == nil { + return nil, nil, errors.New("nil resource found when performing initial check-and-set read") + } + version = existingTarget.Version + } + + opts.postMap["version"] = version + + if len(hostIds) > 0 { + opts.postMap["host_ids"] = hostIds + } + + req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("host-sets/%s:remove-hosts", hostSetId), opts.postMap, apiOpts...) + if err != nil { + return nil, nil, fmt.Errorf("error creating RemoveHosts request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, nil, fmt.Errorf("error performing client request during RemoveHosts call: %w", err) + } + + target := new(HostSet) + apiErr, err := resp.Decode(target) + if err != nil { + return nil, nil, fmt.Errorf("error decoding RemoveHosts response: %w", err) + } + if apiErr != nil { + return nil, apiErr, nil + } + return target, apiErr, nil +} diff --git a/api/hostsets/host_set_test.go b/api/hostsets/host_set_test.go index d529cf718e..38a19505c6 100644 --- a/api/hostsets/host_set_test.go +++ b/api/hostsets/host_set_test.go @@ -18,64 +18,66 @@ import ( ) func TestCustom(t *testing.T) { - for _, newStyle := range []bool{false} { - assert, require := assert.New(t), require.New(t) - tc := controller.NewTestController(t, nil) - defer tc.Shutdown() + for _, newStyle := range []bool{false, true} { + t.Run(fmt.Sprintf("custom_%t", newStyle), func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + tc := controller.NewTestController(t, nil) + defer tc.Shutdown() - token := tc.Token() - _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) - client := tc.Client().Clone() - client.SetScopeId(proj.GetPublicId()) + token := tc.Token() + _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) + client := tc.Client().Clone() + client.SetScopeId(proj.GetPublicId()) - hc, apiErr, err := hostcatalogs.NewClient(client).Create(tc.Context(), "static") - require.NoError(err) - require.Nil(apiErr) + hc, apiErr, err := hostcatalogs.NewClient(client).Create(tc.Context(), "static") + require.NoError(err) + require.Nil(apiErr) - hClient := hosts.NewClient(client) - h1, apiErr, err := hClient.Create(tc.Context(), hc.Id, hosts.WithStaticHostAddress("someaddress")) - require.NoError(err) - require.Nil(apiErr) - h2, apiErr, err := hClient.Create(tc.Context(), hc.Id, hosts.WithStaticHostAddress("someaddress")) - require.NoError(err) - require.Nil(apiErr) + hClient := hosts.NewClient(client) + h1, apiErr, err := hClient.Create(tc.Context(), hc.Id, hosts.WithStaticHostAddress("someaddress")) + require.NoError(err) + require.Nil(apiErr) + h2, apiErr, err := hClient.Create(tc.Context(), hc.Id, hosts.WithStaticHostAddress("someaddress")) + require.NoError(err) + require.Nil(apiErr) - hSetClient := hostsets.NewClient(client) - var hSet *hostsets.HostSet - if newStyle { - hSet, apiErr, err = hSetClient.Create2(tc.Context(), hc.Id) - } else { - hSet, apiErr, err = hSetClient.Create(tc.Context(), hc.Id) - } - require.NoError(err) - require.Nil(apiErr) + hSetClient := hostsets.NewClient(client) + var hSet *hostsets.HostSet + if newStyle { + hSet, apiErr, err = hSetClient.Create2(tc.Context(), hc.Id) + } else { + hSet, apiErr, err = hSetClient.Create(tc.Context(), hc.Id) + } + require.NoError(err) + require.Nil(apiErr) - if newStyle { - hSet, apiErr, err = hSetClient.AddHosts(tc.Context(), hc.Id, hSet.Id, hSet.Version, []string{h1.Id, h2.Id}) - } else { - hSet, apiErr, err = hSetClient.AddHosts2(tc.Context(), hSet.Id, hSet.Version, []string{h1.Id, h2.Id}) - } - require.NoError(err) - require.Nil(apiErr) - assert.Contains(hSet.HostIds, h1.Id, h2.Id) + if newStyle { + hSet, apiErr, err = hSetClient.AddHosts2(tc.Context(), hSet.Id, hSet.Version, []string{h1.Id, h2.Id}) + } else { + hSet, apiErr, err = hSetClient.AddHosts(tc.Context(), hc.Id, hSet.Id, hSet.Version, []string{h1.Id, h2.Id}) + } + require.NoError(err) + require.Nil(apiErr) + assert.Contains(hSet.HostIds, h1.Id, h2.Id) - if newStyle { - hSet, apiErr, err = hSetClient.SetHosts(tc.Context(), hc.Id, hSet.Id, hSet.Version, []string{h1.Id}) - } else { - hSet, apiErr, err = hSetClient.SetHosts2(tc.Context(), hSet.Id, hSet.Version, []string{h1.Id}) - } - require.NoError(err) - require.Nil(apiErr, pretty.Sprint(apiErr)) - assert.ElementsMatch([]string{h1.Id}, hSet.HostIds) + if newStyle { + hSet, apiErr, err = hSetClient.SetHosts2(tc.Context(), hSet.Id, hSet.Version, []string{h1.Id}) + } else { + hSet, apiErr, err = hSetClient.SetHosts(tc.Context(), hc.Id, hSet.Id, hSet.Version, []string{h1.Id}) + } + require.NoError(err) + require.Nil(apiErr, pretty.Sprint(apiErr)) + assert.ElementsMatch([]string{h1.Id}, hSet.HostIds) - if newStyle { - hSet, apiErr, err = hSetClient.RemoveHosts(tc.Context(), hc.Id, hSet.Id, hSet.Version, []string{h1.Id}) - } else { - hSet, apiErr, err = hSetClient.RemoveHosts2(tc.Context(), hSet.Id, hSet.Version, []string{h1.Id}) - } - require.NoError(err) - require.Nil(apiErr) - assert.Empty(hSet.HostIds) + if newStyle { + hSet, apiErr, err = hSetClient.RemoveHosts2(tc.Context(), hSet.Id, hSet.Version, []string{h1.Id}) + } else { + hSet, apiErr, err = hSetClient.RemoveHosts(tc.Context(), hc.Id, hSet.Id, hSet.Version, []string{h1.Id}) + } + require.NoError(err) + require.Nil(apiErr) + assert.Empty(hSet.HostIds) + }) } } @@ -157,85 +159,87 @@ func comparableSetSlice(in []*hostsets.HostSet) []hostsets.HostSet { } func TestSet_Crud(t *testing.T) { - for _, newStyle := range []bool{true} { - assert, require := assert.New(t), require.New(t) - tc := controller.NewTestController(t, nil) - defer tc.Shutdown() - - client := tc.Client() - token := tc.Token() - org, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) - client.SetScopeId(org.GetPublicId()) - projClient := client.Clone() - projClient.SetScopeId(proj.GetPublicId()) - - hc, apiErr, err := hostcatalogs.NewClient(projClient).Create(tc.Context(), "static") - require.NoError(err) - require.Nil(apiErr) - require.NotNil(hc) - - checkHost := func(t *testing.T, step string, h *hostsets.HostSet, apiErr *api.Error, err error, wantedName string, wantVersion uint32) { - t.Helper() - require.NoError(err, step) - if !assert.Nil(apiErr, step) && apiErr.Message != "" { - t.Errorf("ApiError message: %q", apiErr.Message) - } - assert.NotNil(h, "returned no resource", step) - gotName := "" - if h.Name != "" { - gotName = h.Name + for _, newStyle := range []bool{false, true} { + t.Run(fmt.Sprintf("crud_%t", newStyle), func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + tc := controller.NewTestController(t, nil) + defer tc.Shutdown() + + client := tc.Client() + token := tc.Token() + org, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) + client.SetScopeId(org.GetPublicId()) + projClient := client.Clone() + projClient.SetScopeId(proj.GetPublicId()) + + hc, apiErr, err := hostcatalogs.NewClient(projClient).Create(tc.Context(), "static") + require.NoError(err) + require.Nil(apiErr) + require.NotNil(hc) + + checkHost := func(t *testing.T, step string, h *hostsets.HostSet, apiErr *api.Error, err error, wantedName string, wantVersion uint32) { + t.Helper() + require.NoError(err, step) + if !assert.Nil(apiErr, step) && apiErr.Message != "" { + t.Errorf("ApiError message: %q", apiErr.Message) + } + assert.NotNil(h, "returned no resource", step) + gotName := "" + if h.Name != "" { + gotName = h.Name + } + assert.Equal(wantedName, gotName, step) + assert.Equal(wantVersion, h.Version) } - assert.Equal(wantedName, gotName, step) - assert.Equal(wantVersion, h.Version) - } - hClient := hostsets.NewClient(projClient) + hClient := hostsets.NewClient(projClient) - var h *hostsets.HostSet - if newStyle { - h, apiErr, err = hClient.Create2(tc.Context(), hc.Id, hostsets.WithName("foo")) - } else { - h, apiErr, err = hClient.Create(tc.Context(), hc.Id, hostsets.WithName("foo")) - } - checkHost(t, "create", h, apiErr, err, "foo", 1) + var h *hostsets.HostSet + if newStyle { + h, apiErr, err = hClient.Create2(tc.Context(), hc.Id, hostsets.WithName("foo")) + } else { + h, apiErr, err = hClient.Create(tc.Context(), hc.Id, hostsets.WithName("foo")) + } + checkHost(t, "create", h, apiErr, err, "foo", 1) - if newStyle { - h, apiErr, err = hClient.Read2(tc.Context(), h.Id) - } else { - h, apiErr, err = hClient.Read(tc.Context(), hc.Id, h.Id) - } - checkHost(t, "read", h, apiErr, err, "foo", 1) + if newStyle { + h, apiErr, err = hClient.Read2(tc.Context(), h.Id) + } else { + h, apiErr, err = hClient.Read(tc.Context(), hc.Id, h.Id) + } + checkHost(t, "read", h, apiErr, err, "foo", 1) - if newStyle { - h, apiErr, err = hClient.Update2(tc.Context(), h.Id, h.Version, hostsets.WithName("bar")) - } else { - h, apiErr, err = hClient.Update(tc.Context(), hc.Id, h.Id, h.Version, hostsets.WithName("bar")) - } - checkHost(t, "update", h, apiErr, err, "bar", 2) + if newStyle { + h, apiErr, err = hClient.Update2(tc.Context(), h.Id, h.Version, hostsets.WithName("bar")) + } else { + h, apiErr, err = hClient.Update(tc.Context(), hc.Id, h.Id, h.Version, hostsets.WithName("bar")) + } + checkHost(t, "update", h, apiErr, err, "bar", 2) - if newStyle { - h, apiErr, err = hClient.Update2(tc.Context(), h.Id, h.Version, hostsets.DefaultName()) - } else { - h, apiErr, err = hClient.Update(tc.Context(), hc.Id, h.Id, h.Version, hostsets.DefaultName()) - } - checkHost(t, "update", h, apiErr, err, "", 3) + if newStyle { + h, apiErr, err = hClient.Update2(tc.Context(), h.Id, h.Version, hostsets.DefaultName()) + } else { + h, apiErr, err = hClient.Update(tc.Context(), hc.Id, h.Id, h.Version, hostsets.DefaultName()) + } + checkHost(t, "update", h, apiErr, err, "", 3) - var existed bool - if newStyle { - existed, apiErr, err = hClient.Delete2(tc.Context(), h.Id) - } else { - existed, apiErr, err = hClient.Delete(tc.Context(), hc.Id, h.Id) - } - assert.NoError(err) - assert.True(existed, "Expected existing catalog when deleted, but it wasn't.") + var existed bool + if newStyle { + existed, apiErr, err = hClient.Delete2(tc.Context(), h.Id) + } else { + existed, apiErr, err = hClient.Delete(tc.Context(), hc.Id, h.Id) + } + assert.NoError(err) + assert.True(existed, "Expected existing catalog when deleted, but it wasn't.") - if newStyle { - existed, apiErr, err = hClient.Delete2(tc.Context(), h.Id) - } else { - existed, apiErr, err = hClient.Delete(tc.Context(), hc.Id, h.Id) - } - assert.NoError(err) - assert.False(existed, "Expected catalog to not exist when deleted, but it did.") + if newStyle { + existed, apiErr, err = hClient.Delete2(tc.Context(), h.Id) + } else { + existed, apiErr, err = hClient.Delete(tc.Context(), hc.Id, h.Id) + } + assert.NoError(err) + assert.False(existed, "Expected catalog to not exist when deleted, but it did.") + }) } } diff --git a/api/internal/genapi/input.go b/api/internal/genapi/input.go index ebc9b53dad..d0833710c8 100644 --- a/api/internal/genapi/input.go +++ b/api/internal/genapi/input.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/boundary/internal/gen/controller/api/resources/hostsets" "github.com/hashicorp/boundary/internal/gen/controller/api/resources/roles" "github.com/hashicorp/boundary/internal/gen/controller/api/resources/scopes" + "github.com/hashicorp/boundary/internal/gen/controller/api/resources/targets" "github.com/hashicorp/boundary/internal/gen/controller/api/resources/users" "google.golang.org/protobuf/proto" ) @@ -75,6 +76,10 @@ type structInfo struct { // given type, e.g. arguments only valid for one call or purpose and not // conveyed within the item itself extraOptions []fieldInfo + + useNewStyle bool + + disableOldStyle bool } var inputStructs = []*structInfo{ @@ -291,11 +296,41 @@ var inputStructs = []*structInfo{ updateTemplate, deleteTemplate, listTemplate, + createTemplate2, + readTemplate2, + updateTemplate2, + deleteTemplate2, + listTemplate2, }, pathArgs: []string{"host-catalog", "host-set"}, sliceSubTypes: map[string]string{ "Hosts": "hostIds", }, versionEnabled: true, + useNewStyle: true, + }, + { + inProto: &targets.HostSet{}, + outFile: "targets/host_set.gen.go", + }, + { + inProto: &targets.Target{}, + outFile: "targets/target.gen.go", + templates: []*template.Template{ + clientTemplate, + createTemplate2, + readTemplate2, + updateTemplate2, + deleteTemplate2, + listTemplate2, + }, + pathArgs: []string{"target"}, + sliceSubTypes: map[string]string{ + "HostSets": "hostSetIds", + }, + versionEnabled: true, + typeOnCreate: true, + useNewStyle: true, + disableOldStyle: true, }, } diff --git a/api/internal/genapi/templates.go b/api/internal/genapi/templates.go index 59936803ae..107c60969e 100644 --- a/api/internal/genapi/templates.go +++ b/api/internal/genapi/templates.go @@ -58,6 +58,22 @@ func getArgsAndPaths(in []string, action string) (colArgs, resArgs []string, col return } +func getArgsAndPathsNewStyle(in []string, action string) (colArg, resArg string, colPath, resPath string) { + resArg = fmt.Sprintf("%sId", strcase.ToLowerCamel(strings.ReplaceAll(in[len(in)-1], "-", "_"))) + if len(in) == 1 { + colArg = "scopeId" + } else { + colArg = fmt.Sprintf("%sId", strcase.ToLowerCamel(strings.ReplaceAll(in[len(in)-2], "-", "_"))) + } + colPath = fmt.Sprintf("%ss", in[len(in)-1]) + + if action != "" { + action = fmt.Sprintf(":%s", action) + } + resPath = fmt.Sprintf("fmt.Sprintf(\"%s/%%s%s\", %s)", colPath, action, resArg) + return +} + type templateInput struct { Name string Package string @@ -67,6 +83,10 @@ type templateInput struct { ResourceFunctionArgs []string CollectionPath string ResourcePath string + CollectionFunctionArg2 string + ResourceFunctionArg2 string + CollectionPath2 string + ResourcePath2 string SliceSubTypes map[string]string ExtraOptions []fieldInfo VersionEnabled bool @@ -89,6 +109,7 @@ func fillTemplates() { if len(in.pathArgs) > 0 { input.CollectionFunctionArgs, input.ResourceFunctionArgs, input.CollectionPath, input.ResourcePath = getArgsAndPaths(in.pathArgs, "") + input.CollectionFunctionArg2, input.ResourceFunctionArg2, input.CollectionPath2, input.ResourcePath2 = getArgsAndPathsNewStyle(in.pathArgs, "") } if err := structTemplate.Execute(outBuf, input); err != nil { @@ -98,7 +119,12 @@ func fillTemplates() { if len(in.sliceSubTypes) > 0 { input.SliceSubTypes = in.sliceSubTypes - in.templates = append(in.templates, sliceSubTypeTemplate) + if !in.disableOldStyle { + in.templates = append(in.templates, sliceSubTypeTemplate) + } + if in.useNewStyle { + in.templates = append(in.templates, sliceSubTypeTemplate2) + } } for _, t := range in.templates { @@ -197,6 +223,58 @@ func fillTemplates() { } } +var listTemplate2 = template.Must(template.New("").Funcs( + template.FuncMap{ + "snakeCase": snakeCase, + }, +).Parse(` +func (c *Client) List2(ctx context.Context, {{ .CollectionFunctionArg2 }} string, opt... Option) ([]*{{ .Name }}, *api.Error, error) { + if {{ .CollectionFunctionArg2 }} == "" { + return nil, nil, fmt.Errorf("empty {{ .CollectionFunctionArg2 }} value passed into List request") + } + if c.client == nil { + return nil, nil, fmt.Errorf("nil client") + } + + opts, apiOpts := getOpts(opt...) + apiOpts = append(apiOpts, api.WithNewStyle()) + opts.queryMap["{{ snakeCase .CollectionFunctionArg2 }}"] = {{ .CollectionFunctionArg2 }} + + req, err := c.client.NewRequest(ctx, "GET", "{{ .CollectionPath2 }}", nil, apiOpts...) + if err != nil { + return nil, nil, fmt.Errorf("error creating List request: %w", err) + } + {{ if ( eq .CollectionPath "\"scopes\"" ) }} + opts.queryMap["scope_id"] = scopeId + {{ end }} + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, nil, fmt.Errorf("error performing client request during List call: %w", err) + } + + type listResponse struct { + Items []*{{ .Name }} + } + target := &listResponse{} + apiErr, err := resp.Decode(target) + if err != nil { + return nil, nil, fmt.Errorf("error decoding List response: %w", err) + } + if apiErr != nil { + return nil, apiErr, nil + } + return target.Items, apiErr, nil +} +`)) + var listTemplate = template.Must(template.New("").Parse(` func (c *Client) List(ctx context.Context, {{ range .CollectionFunctionArgs }} {{ . }} string, {{ end }}opt... Option) ([]*{{ .Name }}, *api.Error, error) { {{ range .CollectionFunctionArgs }} if {{ . }} == "" { @@ -244,6 +322,48 @@ func (c *Client) List(ctx context.Context, {{ range .CollectionFunctionArgs }} { } `)) +var readTemplate2 = template.Must(template.New("").Parse(` +func (c *Client) Read2(ctx context.Context, {{ .ResourceFunctionArg2 }} string, opt... Option) (*{{ .Name }}, *api.Error, error) { + if {{ .ResourceFunctionArg2 }} == "" { + return nil, nil, fmt.Errorf("empty {{ .ResourceFunctionArg2 }} value passed into Read request") + } + if c.client == nil { + return nil, nil, fmt.Errorf("nil client") + } + + opts, apiOpts := getOpts(opt...) + apiOpts = append(apiOpts, api.WithNewStyle()) + + req, err := c.client.NewRequest(ctx, "GET", {{ .ResourcePath2 }}, nil, apiOpts...) + if err != nil { + return nil, nil, fmt.Errorf("error creating Read request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, nil, fmt.Errorf("error performing client request during Read call: %w", err) + } + + target := new({{ .Name }}) + apiErr, err := resp.Decode(target) + if err != nil { + return nil, nil, fmt.Errorf("error decoding Read response: %w", err) + } + if apiErr != nil { + return nil, apiErr, nil + } + return target, apiErr, nil +} +`)) + var readTemplate = template.Must(template.New("").Parse(` func (c *Client) Read(ctx context.Context, {{ range .ResourceFunctionArgs }} {{ . }} string, {{ end }} opt... Option) (*{{ .Name }}, *api.Error, error) { {{ range .ResourceFunctionArgs }} if {{ . }} == "" { @@ -286,6 +406,51 @@ func (c *Client) Read(ctx context.Context, {{ range .ResourceFunctionArgs }} {{ } `)) +var deleteTemplate2 = template.Must(template.New("").Parse(` +func (c *Client) Delete2(ctx context.Context, {{ .ResourceFunctionArg2 }} string, opt... Option) (bool, *api.Error, error) { + if {{ .ResourceFunctionArg2 }} == "" { + return false, nil, fmt.Errorf("empty {{ .ResourceFunctionArg2 }} value passed into Delete request") + } + if c.client == nil { + return false, nil, fmt.Errorf("nil client") + } + + opts, apiOpts := getOpts(opt...) + apiOpts = append(apiOpts, api.WithNewStyle()) + + req, err := c.client.NewRequest(ctx, "DELETE", {{ .ResourcePath2 }}, nil, apiOpts...) + if err != nil { + return false, nil, fmt.Errorf("error creating Delete request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return false, nil, fmt.Errorf("error performing client request during Delete call: %w", err) + } + + type deleteResponse struct { + Existed bool + } + target := &deleteResponse{} + apiErr, err := resp.Decode(target) + if err != nil { + return false, nil, fmt.Errorf("error decoding Delete response: %w", err) + } + if apiErr != nil { + return false, apiErr, nil + } + return target.Existed, apiErr, nil +} +`)) + var deleteTemplate = template.Must(template.New("").Parse(` func (c *Client) Delete(ctx context.Context, {{ range .ResourceFunctionArgs }} {{ . }} string, {{ end }} opt... Option) (bool, *api.Error, error) { {{ range .ResourceFunctionArgs }} if {{ . }} == "" { @@ -331,6 +496,60 @@ func (c *Client) Delete(ctx context.Context, {{ range .ResourceFunctionArgs }} { } `)) +var createTemplate2 = template.Must(template.New("").Funcs( + template.FuncMap{ + "snakeCase": snakeCase, + }, +).Parse(` +func (c *Client) Create2(ctx context.Context, {{ if .TypeOnCreate }} resourceType string, {{ end }} {{ .CollectionFunctionArg2 }} string, opt... Option) (*{{ .Name }}, *api.Error, error) { + if {{ .CollectionFunctionArg2 }} == "" { + return nil, nil, fmt.Errorf("empty {{ .CollectionFunctionArg2 }} value passed into Create request") + } + opts, apiOpts := getOpts(opt...) + apiOpts = append(apiOpts, api.WithNewStyle()) + if c.client == nil { + return nil, nil, fmt.Errorf("nil client") + } + {{ if .TypeOnCreate }} if resourceType == "" { + return nil, nil, fmt.Errorf("empty resourceType value passed into Create request") + } else { + opts.postMap["type"] = resourceType + }{{ end }} + + opts.postMap["{{ snakeCase .CollectionFunctionArg2 }}"] = {{ .CollectionFunctionArg2 }} + + req, err := c.client.NewRequest(ctx, "POST", "{{ .CollectionPath2 }}", opts.postMap, apiOpts...) + if err != nil { + return nil, nil, fmt.Errorf("error creating Create request: %w", err) + } + {{ if ( eq .CollectionPath "\"scopes\"" ) }} + opts.queryMap["scope_id"] = scopeId + {{ end }} + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, nil, fmt.Errorf("error performing client request during Create call: %w", err) + } + + target := new({{ .Name }}) + apiErr, err := resp.Decode(target) + if err != nil { + return nil, nil, fmt.Errorf("error decoding Create response: %w", err) + } + if apiErr != nil { + return nil, apiErr, nil + } + return target, apiErr, nil +} +`)) + var createTemplate = template.Must(template.New("").Parse(` func (c *Client) Create(ctx context.Context, {{ if .TypeOnCreate }} resourceType string, {{ end }} {{ range .CollectionFunctionArgs }} {{ . }} string, {{ end }} opt... Option) (*{{ .Name }}, *api.Error, error) { {{ range .CollectionFunctionArgs }} if {{ . }} == "" { @@ -378,6 +597,69 @@ func (c *Client) Create(ctx context.Context, {{ if .TypeOnCreate }} resourceType } `)) +var updateTemplate2 = template.Must(template.New("").Parse(` +func (c *Client) Update2(ctx context.Context, {{ .ResourceFunctionArg2 }} string, version uint32, opt... Option) (*{{ .Name }}, *api.Error, error) { + if {{ .ResourceFunctionArg2 }} == "" { + return nil, nil, fmt.Errorf("empty {{ .ResourceFunctionArg2 }} value passed into Update request") + } + if c.client == nil { + return nil, nil, fmt.Errorf("nil client") + } + + opts, apiOpts := getOpts(opt...) + apiOpts = append(apiOpts, api.WithNewStyle()) + + {{ if .VersionEnabled }} + if version == 0 { + if !opts.withAutomaticVersioning { + return nil, nil, errors.New("zero version number passed into Update request and automatic versioning not specified") + } + existingTarget, existingApiErr, existingErr := c.Read2(ctx, {{ .ResourceFunctionArg2 }}, opt...) + if existingErr != nil { + return nil, nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) + } + if existingApiErr != nil { + return nil, nil, fmt.Errorf("error from controller when performing initial check-and-set read: %s", pretty.Sprint(existingApiErr)) + } + if existingTarget == nil { + return nil, nil, errors.New("nil resource found when performing initial check-and-set read") + } + version = existingTarget.Version + } + {{ end }} + + opts.postMap["version"] = version + + req, err := c.client.NewRequest(ctx, "PATCH", {{ .ResourcePath2 }}, opts.postMap, apiOpts...) + if err != nil { + return nil, nil, fmt.Errorf("error creating Update request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, nil, fmt.Errorf("error performing client request during Update call: %w", err) + } + + target := new({{ .Name }}) + apiErr, err := resp.Decode(target) + if err != nil { + return nil, nil, fmt.Errorf("error decoding Update response: %w", err) + } + if apiErr != nil { + return nil, apiErr, nil + } + return target, apiErr, nil +} +`)) + var updateTemplate = template.Must(template.New("").Parse(` func (c *Client) Update(ctx context.Context, {{ range .ResourceFunctionArgs }} {{ . }} string, {{ end }}version uint32, opt... Option) (*{{ .Name }}, *api.Error, error) { {{ range .ResourceFunctionArgs }} if {{ . }} == "" { @@ -524,6 +806,91 @@ func (c *Client) {{ $fullName }}(ctx context.Context, {{ range $input.ResourceFu {{ end }} `)) +var sliceSubTypeTemplate2 = template.Must(template.New("").Funcs( + template.FuncMap{ + "makeSlice": makeSlice, + "snakeCase": snakeCase, + "kebabCase": kebabCase, + "getPathWithActionNewStyle": getPathWithActionNewStyle, + }, +).Parse(` +{{ $input := . }} +{{ range $index, $op := makeSlice "Add" "Set" "Remove" }} +{{ range $key, $value := $input.SliceSubTypes }} +{{ $fullName := print $op $key }} +{{ $actionName := kebabCase $fullName }} +{{ $resPath := getPathWithActionNewStyle $input.PathArgs $actionName }} +func (c *Client) {{ $fullName }}2(ctx context.Context, {{ $input.ResourceFunctionArg2 }} string, version uint32, {{ $value }} []string, opt... Option) (*{{ $input.Name }}, *api.Error, error) { + if {{ $input.ResourceFunctionArg2 }} == "" { + return nil, nil, fmt.Errorf("empty {{ $input.ResourceFunctionArg2 }} value passed into {{ $fullName }} request") + } + if c.client == nil { + return nil, nil, fmt.Errorf("nil client") + } + + opts, apiOpts := getOpts(opt...) + apiOpts = append(apiOpts, api.WithNewStyle()) + + {{ if $input.VersionEnabled }} + if version == 0 { + if !opts.withAutomaticVersioning { + return nil, nil, errors.New("zero version number passed into {{ $fullName }} request") + } + existingTarget, existingApiErr, existingErr := c.Read2(ctx, {{ $input.ResourceFunctionArg2 }}, opt...) + if existingErr != nil { + return nil, nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) + } + if existingApiErr != nil { + return nil, nil, fmt.Errorf("error from controller when performing initial check-and-set read: %s", pretty.Sprint(existingApiErr)) + } + if existingTarget == nil { + return nil, nil, errors.New("nil resource found when performing initial check-and-set read") + } + version = existingTarget.Version + } + {{ end }} + opts.postMap["version"] = version + + if len({{ $value }}) > 0 { + opts.postMap["{{ snakeCase $value }}"] = {{ $value }} + }{{ if ( eq $op "Set" ) }} else if {{ $value }} != nil { + // In this function, a non-nil but empty list means clear out + opts.postMap["{{ snakeCase $value }}"] = nil + } + {{ end }} + + req, err := c.client.NewRequest(ctx, "POST", {{ $resPath }}, opts.postMap, apiOpts...) + if err != nil { + return nil, nil, fmt.Errorf("error creating {{ $fullName }} request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, nil, fmt.Errorf("error performing client request during {{ $fullName }} call: %w", err) + } + + target := new({{ $input.Name }}) + apiErr, err := resp.Decode(target) + if err != nil { + return nil, nil, fmt.Errorf("error decoding {{ $fullName }} response: %w", err) + } + if apiErr != nil { + return nil, apiErr, nil + } + return target, apiErr, nil +} +{{ end }} +{{ end }} +`)) + var structTemplate = template.Must(template.New("").Parse( fmt.Sprint(`// Code generated by "make api"; DO NOT EDIT. package {{ .Package }} @@ -664,3 +1031,8 @@ func getPathWithAction(resArgs []string, action string) string { _, _, _, resPath := getArgsAndPaths(resArgs, action) return resPath } + +func getPathWithActionNewStyle(resArgs []string, action string) string { + _, _, _, resPath := getArgsAndPathsNewStyle(resArgs, action) + return resPath +} diff --git a/api/targets/host_set.gen.go b/api/targets/host_set.gen.go new file mode 100644 index 0000000000..611e11c3fb --- /dev/null +++ b/api/targets/host_set.gen.go @@ -0,0 +1,7 @@ +// Code generated by "make api"; DO NOT EDIT. +package targets + +type HostSet struct { + Id string `json:"id,omitempty"` + HostCatalogId string `json:"host_catalog_id,omitempty"` +} diff --git a/api/targets/option.gen.go b/api/targets/option.gen.go new file mode 100644 index 0000000000..1b1dbee6a1 --- /dev/null +++ b/api/targets/option.gen.go @@ -0,0 +1,92 @@ +package targets + +import ( + "github.com/hashicorp/boundary/api" +) + +// Option is a func that sets optional attributes for a call. This does not need +// to be used directly, but instead option arguments are built from the +// functions in this package. WithX options set a value to that given in the +// argument; DefaultX options indicate that the value should be set to its +// default. When an API call is made options are processed in ther order they +// appear in the function call, so for a given argument X, a succession of WithX +// or DefaultX calls will result in the last call taking effect. +type Option func(*options) + +type options struct { + postMap map[string]interface{} + queryMap map[string]string + withScopeId string + withAutomaticVersioning bool +} + +func getDefaultOptions() options { + return options{ + postMap: make(map[string]interface{}), + queryMap: make(map[string]string), + } +} + +func getOpts(opt ...Option) (options, []api.Option) { + opts := getDefaultOptions() + for _, o := range opt { + o(&opts) + } + var apiOpts []api.Option + if opts.withScopeId != "" { + apiOpts = append(apiOpts, api.WithScopeId(opts.withScopeId)) + } + return opts, apiOpts +} + +func WithScopeId(id string) Option { + return func(o *options) { + o.withScopeId = id + } +} + +// If set, and if the version is zero during an update, the API will perform a +// fetch to get the current version of the resource and populate it during the +// update call. This is convenient but opens up the possibility for subtle +// order-of-modification issues, so use carefully. +func WithAutomaticVersioning() Option { + return func(o *options) { + o.withAutomaticVersioning = true + } +} + +func WithDefaultPort(inDefaultPort uint32) Option { + return func(o *options) { + o.postMap["default_port"] = inDefaultPort + } +} + +func DefaultDefaultPort() Option { + return func(o *options) { + o.postMap["default_port"] = nil + } +} + +func WithDescription(inDescription string) Option { + return func(o *options) { + o.postMap["description"] = inDescription + } +} + +func DefaultDescription() Option { + return func(o *options) { + o.postMap["description"] = nil + } +} + +func WithName(inName string) Option { + return func(o *options) { + o.postMap["name"] = inName + } +} + +func DefaultName() Option { + return func(o *options) { + o.postMap["name"] = nil + } +} diff --git a/api/hostsets/custom.go b/api/targets/target.gen.go similarity index 61% rename from api/hostsets/custom.go rename to api/targets/target.gen.go index 3a01f60ed0..51ffa58ec0 100644 --- a/api/hostsets/custom.go +++ b/api/targets/target.gen.go @@ -1,28 +1,60 @@ -package hostsets +// Code generated by "make api"; DO NOT EDIT. +package targets import ( "context" "errors" "fmt" "net/url" + "time" - "github.com/hashicorp/boundary/api" "github.com/kr/pretty" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/scopes" ) -func (c *Client) Create2(ctx context.Context, hostCatalogId string, opt ...Option) (*HostSet, *api.Error, error) { - if hostCatalogId == "" { - return nil, nil, fmt.Errorf("empty hostCatalogId value passed into Create request") +type Target struct { + Id string `json:"id,omitempty"` + ScopeId string `json:"scope_id,omitempty"` + Scope *scopes.ScopeInfo `json:"scope,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + CreatedTime time.Time `json:"created_time,omitempty"` + UpdatedTime time.Time `json:"updated_time,omitempty"` + Version uint32 `json:"version,omitempty"` + Type string `json:"type,omitempty"` + HostSetIds []string `json:"host_set_ids,omitempty"` + HostSets []*HostSet `json:"host_sets,omitempty"` + DefaultPort uint32 `json:"default_port,omitempty"` +} + +type Client struct { + client *api.Client +} + +func NewClient(c *api.Client) *Client { + return &Client{client: c} +} + +func (c *Client) Create2(ctx context.Context, resourceType string, scopeId string, opt ...Option) (*Target, *api.Error, error) { + if scopeId == "" { + return nil, nil, fmt.Errorf("empty scopeId value passed into Create request") } opts, apiOpts := getOpts(opt...) apiOpts = append(apiOpts, api.WithNewStyle()) - if c.client == nil { return nil, nil, fmt.Errorf("nil client") } + if resourceType == "" { + return nil, nil, fmt.Errorf("empty resourceType value passed into Create request") + } else { + opts.postMap["type"] = resourceType + } + + opts.postMap["scope_id"] = scopeId - opts.postMap["host_catalog_id"] = hostCatalogId - req, err := c.client.NewRequest(ctx, "POST", "host-sets", opts.postMap, apiOpts...) + req, err := c.client.NewRequest(ctx, "POST", "targets", opts.postMap, apiOpts...) if err != nil { return nil, nil, fmt.Errorf("error creating Create request: %w", err) } @@ -40,7 +72,7 @@ func (c *Client) Create2(ctx context.Context, hostCatalogId string, opt ...Optio return nil, nil, fmt.Errorf("error performing client request during Create call: %w", err) } - target := new(HostSet) + target := new(Target) apiErr, err := resp.Decode(target) if err != nil { return nil, nil, fmt.Errorf("error decoding Create response: %w", err) @@ -51,11 +83,10 @@ func (c *Client) Create2(ctx context.Context, hostCatalogId string, opt ...Optio return target, apiErr, nil } -func (c *Client) Read2(ctx context.Context, hostSetId string, opt ...Option) (*HostSet, *api.Error, error) { - if hostSetId == "" { - return nil, nil, fmt.Errorf("empty hostSetId value passed into Read request") +func (c *Client) Read2(ctx context.Context, targetId string, opt ...Option) (*Target, *api.Error, error) { + if targetId == "" { + return nil, nil, fmt.Errorf("empty targetId value passed into Read request") } - if c.client == nil { return nil, nil, fmt.Errorf("nil client") } @@ -63,7 +94,7 @@ func (c *Client) Read2(ctx context.Context, hostSetId string, opt ...Option) (*H opts, apiOpts := getOpts(opt...) apiOpts = append(apiOpts, api.WithNewStyle()) - req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("host-sets/%s", hostSetId), nil, apiOpts...) + req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("targets/%s", targetId), nil, apiOpts...) if err != nil { return nil, nil, fmt.Errorf("error creating Read request: %w", err) } @@ -81,7 +112,7 @@ func (c *Client) Read2(ctx context.Context, hostSetId string, opt ...Option) (*H return nil, nil, fmt.Errorf("error performing client request during Read call: %w", err) } - target := new(HostSet) + target := new(Target) apiErr, err := resp.Decode(target) if err != nil { return nil, nil, fmt.Errorf("error decoding Read response: %w", err) @@ -92,9 +123,9 @@ func (c *Client) Read2(ctx context.Context, hostSetId string, opt ...Option) (*H return target, apiErr, nil } -func (c *Client) Update2(ctx context.Context, hostSetId string, version uint32, opt ...Option) (*HostSet, *api.Error, error) { - if hostSetId == "" { - return nil, nil, fmt.Errorf("empty hostSetId value passed into Update request") +func (c *Client) Update2(ctx context.Context, targetId string, version uint32, opt ...Option) (*Target, *api.Error, error) { + if targetId == "" { + return nil, nil, fmt.Errorf("empty targetId value passed into Update request") } if c.client == nil { return nil, nil, fmt.Errorf("nil client") @@ -107,7 +138,7 @@ func (c *Client) Update2(ctx context.Context, hostSetId string, version uint32, if !opts.withAutomaticVersioning { return nil, nil, errors.New("zero version number passed into Update request and automatic versioning not specified") } - existingTarget, existingApiErr, existingErr := c.Read2(ctx, hostSetId, opt...) + existingTarget, existingApiErr, existingErr := c.Read2(ctx, targetId, opt...) if existingErr != nil { return nil, nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) } @@ -122,7 +153,7 @@ func (c *Client) Update2(ctx context.Context, hostSetId string, version uint32, opts.postMap["version"] = version - req, err := c.client.NewRequest(ctx, "PATCH", fmt.Sprintf("host-sets/%s", hostSetId), opts.postMap, apiOpts...) + req, err := c.client.NewRequest(ctx, "PATCH", fmt.Sprintf("targets/%s", targetId), opts.postMap, apiOpts...) if err != nil { return nil, nil, fmt.Errorf("error creating Update request: %w", err) } @@ -140,7 +171,7 @@ func (c *Client) Update2(ctx context.Context, hostSetId string, version uint32, return nil, nil, fmt.Errorf("error performing client request during Update call: %w", err) } - target := new(HostSet) + target := new(Target) apiErr, err := resp.Decode(target) if err != nil { return nil, nil, fmt.Errorf("error decoding Update response: %w", err) @@ -151,11 +182,10 @@ func (c *Client) Update2(ctx context.Context, hostSetId string, version uint32, return target, apiErr, nil } -func (c *Client) Delete2(ctx context.Context, hostSetId string, opt ...Option) (bool, *api.Error, error) { - if hostSetId == "" { - return false, nil, fmt.Errorf("empty hostSetId value passed into Delete request") +func (c *Client) Delete2(ctx context.Context, targetId string, opt ...Option) (bool, *api.Error, error) { + if targetId == "" { + return false, nil, fmt.Errorf("empty targetId value passed into Delete request") } - if c.client == nil { return false, nil, fmt.Errorf("nil client") } @@ -163,7 +193,7 @@ func (c *Client) Delete2(ctx context.Context, hostSetId string, opt ...Option) ( opts, apiOpts := getOpts(opt...) apiOpts = append(apiOpts, api.WithNewStyle()) - req, err := c.client.NewRequest(ctx, "DELETE", fmt.Sprintf("host-sets/%s", hostSetId), nil, apiOpts...) + req, err := c.client.NewRequest(ctx, "DELETE", fmt.Sprintf("targets/%s", targetId), nil, apiOpts...) if err != nil { return false, nil, fmt.Errorf("error creating Delete request: %w", err) } @@ -195,21 +225,23 @@ func (c *Client) Delete2(ctx context.Context, hostSetId string, opt ...Option) ( return target.Existed, apiErr, nil } -func (c *Client) List2(ctx context.Context, hostCatalogId string, opt ...Option) ([]*HostSet, *api.Error, error) { +func (c *Client) List2(ctx context.Context, scopeId string, opt ...Option) ([]*Target, *api.Error, error) { + if scopeId == "" { + return nil, nil, fmt.Errorf("empty scopeId value passed into List request") + } if c.client == nil { return nil, nil, fmt.Errorf("nil client") } opts, apiOpts := getOpts(opt...) apiOpts = append(apiOpts, api.WithNewStyle()) + opts.queryMap["scope_id"] = scopeId - req, err := c.client.NewRequest(ctx, "GET", "host-sets", nil, apiOpts...) + req, err := c.client.NewRequest(ctx, "GET", "targets", nil, apiOpts...) if err != nil { return nil, nil, fmt.Errorf("error creating List request: %w", err) } - opts.queryMap["host_catalog_id"] = hostCatalogId - if len(opts.queryMap) > 0 { q := url.Values{} for k, v := range opts.queryMap { @@ -224,7 +256,7 @@ func (c *Client) List2(ctx context.Context, hostCatalogId string, opt ...Option) } type listResponse struct { - Items []*HostSet + Items []*Target } target := &listResponse{} apiErr, err := resp.Decode(target) @@ -237,9 +269,9 @@ func (c *Client) List2(ctx context.Context, hostCatalogId string, opt ...Option) return target.Items, apiErr, nil } -func (c *Client) AddHosts2(ctx context.Context, hostSetId string, version uint32, hostIds []string, opt ...Option) (*HostSet, *api.Error, error) { - if hostSetId == "" { - return nil, nil, fmt.Errorf("empty hostSetId value passed into AddHosts request") +func (c *Client) AddHostSets2(ctx context.Context, targetId string, version uint32, hostSetIds []string, opt ...Option) (*Target, *api.Error, error) { + if targetId == "" { + return nil, nil, fmt.Errorf("empty targetId value passed into AddHostSets request") } if c.client == nil { return nil, nil, fmt.Errorf("nil client") @@ -250,9 +282,9 @@ func (c *Client) AddHosts2(ctx context.Context, hostSetId string, version uint32 if version == 0 { if !opts.withAutomaticVersioning { - return nil, nil, errors.New("zero version number passed into AddHosts request") + return nil, nil, errors.New("zero version number passed into AddHostSets request") } - existingTarget, existingApiErr, existingErr := c.Read2(ctx, hostSetId, opt...) + existingTarget, existingApiErr, existingErr := c.Read2(ctx, targetId, opt...) if existingErr != nil { return nil, nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) } @@ -267,13 +299,13 @@ func (c *Client) AddHosts2(ctx context.Context, hostSetId string, version uint32 opts.postMap["version"] = version - if len(hostIds) > 0 { - opts.postMap["host_ids"] = hostIds + if len(hostSetIds) > 0 { + opts.postMap["host_set_ids"] = hostSetIds } - req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("host-sets/%s:add-hosts", hostSetId), opts.postMap, apiOpts...) + req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("targets/%s:add-host-sets", targetId), opts.postMap, apiOpts...) if err != nil { - return nil, nil, fmt.Errorf("error creating AddHosts request: %w", err) + return nil, nil, fmt.Errorf("error creating AddHostSets request: %w", err) } if len(opts.queryMap) > 0 { @@ -286,13 +318,13 @@ func (c *Client) AddHosts2(ctx context.Context, hostSetId string, version uint32 resp, err := c.client.Do(req) if err != nil { - return nil, nil, fmt.Errorf("error performing client request during AddHosts call: %w", err) + return nil, nil, fmt.Errorf("error performing client request during AddHostSets call: %w", err) } - target := new(HostSet) + target := new(Target) apiErr, err := resp.Decode(target) if err != nil { - return nil, nil, fmt.Errorf("error decoding AddHosts response: %w", err) + return nil, nil, fmt.Errorf("error decoding AddHostSets response: %w", err) } if apiErr != nil { return nil, apiErr, nil @@ -300,9 +332,9 @@ func (c *Client) AddHosts2(ctx context.Context, hostSetId string, version uint32 return target, apiErr, nil } -func (c *Client) SetHosts2(ctx context.Context, hostSetId string, version uint32, hostIds []string, opt ...Option) (*HostSet, *api.Error, error) { - if hostSetId == "" { - return nil, nil, fmt.Errorf("empty hostSetId value passed into SetHosts request") +func (c *Client) SetHostSets2(ctx context.Context, targetId string, version uint32, hostSetIds []string, opt ...Option) (*Target, *api.Error, error) { + if targetId == "" { + return nil, nil, fmt.Errorf("empty targetId value passed into SetHostSets request") } if c.client == nil { return nil, nil, fmt.Errorf("nil client") @@ -313,9 +345,9 @@ func (c *Client) SetHosts2(ctx context.Context, hostSetId string, version uint32 if version == 0 { if !opts.withAutomaticVersioning { - return nil, nil, errors.New("zero version number passed into SetHosts request") + return nil, nil, errors.New("zero version number passed into SetHostSets request") } - existingTarget, existingApiErr, existingErr := c.Read2(ctx, hostSetId, opt...) + existingTarget, existingApiErr, existingErr := c.Read2(ctx, targetId, opt...) if existingErr != nil { return nil, nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) } @@ -330,16 +362,16 @@ func (c *Client) SetHosts2(ctx context.Context, hostSetId string, version uint32 opts.postMap["version"] = version - if len(hostIds) > 0 { - opts.postMap["host_ids"] = hostIds - } else if hostIds != nil { + if len(hostSetIds) > 0 { + opts.postMap["host_set_ids"] = hostSetIds + } else if hostSetIds != nil { // In this function, a non-nil but empty list means clear out - opts.postMap["host_ids"] = nil + opts.postMap["host_set_ids"] = nil } - req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("host-sets/%s:set-hosts", hostSetId), opts.postMap, apiOpts...) + req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("targets/%s:set-host-sets", targetId), opts.postMap, apiOpts...) if err != nil { - return nil, nil, fmt.Errorf("error creating SetHosts request: %w", err) + return nil, nil, fmt.Errorf("error creating SetHostSets request: %w", err) } if len(opts.queryMap) > 0 { @@ -352,13 +384,13 @@ func (c *Client) SetHosts2(ctx context.Context, hostSetId string, version uint32 resp, err := c.client.Do(req) if err != nil { - return nil, nil, fmt.Errorf("error performing client request during SetHosts call: %w", err) + return nil, nil, fmt.Errorf("error performing client request during SetHostSets call: %w", err) } - target := new(HostSet) + target := new(Target) apiErr, err := resp.Decode(target) if err != nil { - return nil, nil, fmt.Errorf("error decoding SetHosts response: %w", err) + return nil, nil, fmt.Errorf("error decoding SetHostSets response: %w", err) } if apiErr != nil { return nil, apiErr, nil @@ -366,9 +398,9 @@ func (c *Client) SetHosts2(ctx context.Context, hostSetId string, version uint32 return target, apiErr, nil } -func (c *Client) RemoveHosts2(ctx context.Context, hostSetId string, version uint32, hostIds []string, opt ...Option) (*HostSet, *api.Error, error) { - if hostSetId == "" { - return nil, nil, fmt.Errorf("empty hostSetId value passed into RemoveHosts request") +func (c *Client) RemoveHostSets2(ctx context.Context, targetId string, version uint32, hostSetIds []string, opt ...Option) (*Target, *api.Error, error) { + if targetId == "" { + return nil, nil, fmt.Errorf("empty targetId value passed into RemoveHostSets request") } if c.client == nil { return nil, nil, fmt.Errorf("nil client") @@ -379,9 +411,9 @@ func (c *Client) RemoveHosts2(ctx context.Context, hostSetId string, version uin if version == 0 { if !opts.withAutomaticVersioning { - return nil, nil, errors.New("zero version number passed into RemoveHosts request") + return nil, nil, errors.New("zero version number passed into RemoveHostSets request") } - existingTarget, existingApiErr, existingErr := c.Read2(ctx, hostSetId, opt...) + existingTarget, existingApiErr, existingErr := c.Read2(ctx, targetId, opt...) if existingErr != nil { return nil, nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr) } @@ -396,13 +428,13 @@ func (c *Client) RemoveHosts2(ctx context.Context, hostSetId string, version uin opts.postMap["version"] = version - if len(hostIds) > 0 { - opts.postMap["host_ids"] = hostIds + if len(hostSetIds) > 0 { + opts.postMap["host_set_ids"] = hostSetIds } - req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("host-sets/%s:remove-hosts", hostSetId), opts.postMap, apiOpts...) + req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("targets/%s:remove-host-sets", targetId), opts.postMap, apiOpts...) if err != nil { - return nil, nil, fmt.Errorf("error creating RemoveHosts request: %w", err) + return nil, nil, fmt.Errorf("error creating RemoveHostSets request: %w", err) } if len(opts.queryMap) > 0 { @@ -415,13 +447,13 @@ func (c *Client) RemoveHosts2(ctx context.Context, hostSetId string, version uin resp, err := c.client.Do(req) if err != nil { - return nil, nil, fmt.Errorf("error performing client request during RemoveHosts call: %w", err) + return nil, nil, fmt.Errorf("error performing client request during RemoveHostSets call: %w", err) } - target := new(HostSet) + target := new(Target) apiErr, err := resp.Decode(target) if err != nil { - return nil, nil, fmt.Errorf("error decoding RemoveHosts response: %w", err) + return nil, nil, fmt.Errorf("error decoding RemoveHostSets response: %w", err) } if apiErr != nil { return nil, apiErr, nil diff --git a/api/targets/target_test.go b/api/targets/target_test.go new file mode 100644 index 0000000000..b1febb7286 --- /dev/null +++ b/api/targets/target_test.go @@ -0,0 +1,200 @@ +package targets_test + +import ( + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/hostcatalogs" + "github.com/hashicorp/boundary/api/hostsets" + "github.com/hashicorp/boundary/api/targets" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/servers/controller" + "github.com/hashicorp/boundary/internal/target" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestCustom(t *testing.T) { + assert, require := assert.New(t), require.New(t) + tc := controller.NewTestController(t, nil) + defer tc.Shutdown() + + token := tc.Token() + _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) + client := tc.Client().Clone() + client.SetScopeId(proj.GetPublicId()) + + hc, apiErr, err := hostcatalogs.NewClient(client).Create(tc.Context(), "static") + require.NoError(err) + require.Nil(apiErr) + + hSetClient := hostsets.NewClient(client) + hSet, apiErr, err := hSetClient.Create2(tc.Context(), hc.Id) + require.NoError(err) + require.Nil(apiErr) + require.NotNil(hSet) + hSet2, apiErr, err := hSetClient.Create2(tc.Context(), hc.Id) + require.NoError(err) + require.Nil(apiErr) + require.NotNil(hSet2) + + tarClient := targets.NewClient(client) + tar, apiErr, err := tarClient.Create2(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("foo")) + require.NoError(err) + require.Nil(apiErr) + require.NotNil(tar) + assert.Empty(tar.HostSetIds) + + tar, apiErr, err = tarClient.AddHostSets2(tc.Context(), tar.Id, tar.Version, []string{hSet.Id}) + require.NoError(err) + require.Nil(apiErr) + require.NotNil(tar) + assert.ElementsMatch(tar.HostSetIds, []string{hSet.Id}) + + tar, apiErr, err = tarClient.SetHostSets2(tc.Context(), tar.Id, tar.Version, []string{hSet2.Id}) + require.NoError(err) + require.Nil(apiErr) + require.NotNil(tar) + assert.ElementsMatch(tar.HostSetIds, []string{hSet2.Id}) + + tar, apiErr, err = tarClient.RemoveHostSets2(tc.Context(), tar.Id, tar.Version, []string{hSet2.Id}) + require.NoError(err) + require.Nil(apiErr) + require.NotNil(tar) + assert.Empty(tar.HostSetIds) +} + +func TestList(t *testing.T) { + assert, require := assert.New(t), require.New(t) + tc := controller.NewTestController(t, nil) + defer tc.Shutdown() + + client := tc.Client() + token := tc.Token() + _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) + client.SetScopeId(proj.GetPublicId()) + + tarClient := targets.NewClient(client) + ul, apiErr, err := tarClient.List2(tc.Context(), proj.GetPublicId()) + require.NoError(err) + require.Nil(apiErr) + assert.Empty(ul) + + var expected []*targets.Target + for i := 0; i < 10; i++ { + expected = append(expected, &targets.Target{Name: fmt.Sprint(i)}) + } + + expected[0], apiErr, err = tarClient.Create2(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName(expected[0].Name)) + require.NoError(err) + require.Nil(apiErr) + + ul, apiErr, err = tarClient.List2(tc.Context(), proj.GetPublicId()) + require.NoError(err) + require.Nil(apiErr) + assert.ElementsMatch(comparableSlice(expected[:1]), comparableSlice(ul)) + + for i := 1; i < 10; i++ { + expected[i], apiErr, err = tarClient.Create2(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName(expected[i].Name)) + require.NoError(err) + require.Nil(apiErr) + } + ul, apiErr, err = tarClient.List2(tc.Context(), proj.GetPublicId()) + require.NoError(err) + require.Nil(apiErr) + assert.ElementsMatch(comparableSlice(expected), comparableSlice(ul)) +} + +func comparableSlice(in []*targets.Target) []targets.Target { + var filtered []targets.Target + for _, i := range in { + p := targets.Target{ + Id: i.Id, + Name: i.Name, + Description: i.Description, + CreatedTime: i.CreatedTime, + UpdatedTime: i.UpdatedTime, + } + filtered = append(filtered, p) + } + return filtered +} + +func TestCrud(t *testing.T) { + assert, require := assert.New(t), require.New(t) + tc := controller.NewTestController(t, nil) + defer tc.Shutdown() + + client := tc.Client() + token := tc.Token() + _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) + + checkResource := func(t *testing.T, step string, h *targets.Target, apiErr *api.Error, err error, wantedName string, wantVersion uint32) { + t.Helper() + require.NoError(err, step) + if !assert.Nil(apiErr, step) && apiErr.Message != "" { + t.Errorf("ApiError message: %q", apiErr.Message) + } + assert.NotNil(h, "returned no resource", step) + gotName := "" + if h.Name != "" { + gotName = h.Name + } + assert.Equal(wantedName, gotName, step) + assert.Equal(wantVersion, h.Version) + } + + tarClient := targets.NewClient(client) + + tar, apiErr, err := tarClient.Create2(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("foo")) + checkResource(t, "create", tar, apiErr, err, "foo", 1) + + tar, apiErr, err = tarClient.Read2(tc.Context(), tar.Id) + checkResource(t, "read", tar, apiErr, err, "foo", 1) + + tar, apiErr, err = tarClient.Update2(tc.Context(), tar.Id, tar.Version, targets.WithName("bar")) + checkResource(t, "update", tar, apiErr, err, "bar", 2) + + existed, apiErr, err := tarClient.Delete2(tc.Context(), tar.Id) + assert.NoError(err) + assert.True(existed, "Expected existing target when deleted, but it wasn't.") + + existed, apiErr, err = tarClient.Delete2(tc.Context(), tar.Id) + assert.NoError(err) + assert.NotNil(apiErr) + assert.EqualValues(apiErr.Status, http.StatusForbidden) +} + +// TODO: Get better coverage for expected errors and error formats. +func TestSet_Errors(t *testing.T) { + assert, require := assert.New(t), require.New(t) + tc := controller.NewTestController(t, nil) + defer tc.Shutdown() + + client := tc.Client() + token := tc.Token() + _, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId)) + + tarClient := targets.NewClient(client) + + tar, apiErr, err := tarClient.Create2(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("foo")) + require.NoError(err) + require.Nil(apiErr) + assert.NotNil(tar) + tar, apiErr, err = tarClient.Create2(tc.Context(), "tcp", proj.GetPublicId(), targets.WithName("foo")) + require.NoError(err) + assert.NotNil(apiErr) + assert.Nil(tar) + + _, apiErr, err = tarClient.Read2(tc.Context(), target.TcpTargetPrefix+"_doesntexis") + require.NoError(err) + assert.NotNil(apiErr) + assert.EqualValues(http.StatusForbidden, apiErr.Status) + + _, apiErr, err = tarClient.Read2(tc.Context(), "invalid id") + require.NoError(err) + assert.NotNil(apiErr) + assert.EqualValues(http.StatusBadRequest, apiErr.Status) +}