Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: enhance support for plan unique identifier in entitlements #953

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/resources/subaccount_entitlement.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ resource "btp_subaccount_entitlement" "uas_reporting" {
### Optional

- `amount` (Number) The quota assigned to the subaccount.
- `plan_unique_identifier` (String) The name of the unique identifier for the plan

### Read-Only

Expand Down
8 changes: 6 additions & 2 deletions internal/btpcli/facade_accounts_entitlement.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func (f *accountsEntitlementFacade) AssignToSubaccount(ctx context.Context, dire
return res, err
}

func (f *accountsEntitlementFacade) EnableInSubaccount(ctx context.Context, directoryId string, subaccountId string, serviceName string, servicePlanName string) (CommandResponse, error) {
func (f *accountsEntitlementFacade) EnableInSubaccount(ctx context.Context, directoryId string, subaccountId string, serviceName string, servicePlanName string, planUniqueIdentifier string, enable bool) (CommandResponse, error) {

params := map[string]string{
"globalAccount": f.cliClient.GetGlobalAccountSubdomain(),
Expand All @@ -94,8 +94,12 @@ func (f *accountsEntitlementFacade) EnableInSubaccount(ctx context.Context, dire
if len(directoryId) > 0 {
params["directoryID"] = directoryId
}
_, res, err := doExecute[cis_entitlements.EntitlementAssignmentResponseObject](f.cliClient, ctx, NewAssignRequest(f.getCommand(), params))

if planUniqueIdentifier != "" {
params["planUniqueIdentifier"] = planUniqueIdentifier
}

_, res, err := doExecute[cis_entitlements.EntitlementAssignmentResponseObject](f.cliClient, ctx, NewAssignRequest(f.getCommand(), params))
return res, err
}

Expand Down
16 changes: 9 additions & 7 deletions internal/btpcli/facade_accounts_entitlement_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ func TestAccountsEntitlementFacade_EnableInSubaccount(t *testing.T) {
subaccountId := "6aa64c2f-38c1-49a9-b2e8-cf9fea769b7f"
serviceName := "alert-notification"
planName := "free"
planUniqueIdentifier := "my-unique-id"

t.Run("constructs the CLI params correctly", func(t *testing.T) {
var srvCalled bool
Expand All @@ -154,17 +155,18 @@ func TestAccountsEntitlementFacade_EnableInSubaccount(t *testing.T) {
srvCalled = true

assertCall(t, r, command, ActionAssign, map[string]string{
"globalAccount": "795b53bb-a3f0-4769-adf0-26173282a975",
"directoryID": directoryId,
"subaccount": subaccountId,
"serviceName": serviceName,
"servicePlanName": planName,
"enable": "true",
"globalAccount": "795b53bb-a3f0-4769-adf0-26173282a975",
"directoryID": directoryId,
"subaccount": subaccountId,
"serviceName": serviceName,
"servicePlanName": planName,
"planUniqueIdentifier": planUniqueIdentifier,
"enable": "true",
})
}))
defer srv.Close()

res, err := uut.Accounts.Entitlement.EnableInSubaccount(context.TODO(), directoryId, subaccountId, serviceName, planName)
res, err := uut.Accounts.Entitlement.EnableInSubaccount(context.TODO(), directoryId, subaccountId, serviceName, planName, planUniqueIdentifier, true)

if assert.True(t, srvCalled) && assert.NoError(t, err) {
assert.Equal(t, 200, res.StatusCode)
Expand Down
15 changes: 11 additions & 4 deletions internal/provider/resource_subaccount_entitlement.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,14 @@ __Further documentation:__
int64planmodifier.UseStateForUnknown(),
},
},
"plan_unique_identifier": schema.StringAttribute{
MarkdownDescription: "The name of the unique identifier for the plan",
Optional: true,
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"state": schema.StringAttribute{
MarkdownDescription: "The current state of the entitlement. Possible values are: \n " +
getFormattedValueAsTableRow("state", "description") +
Expand Down Expand Up @@ -227,7 +235,7 @@ func (rs *subaccountEntitlementResource) createOrUpdate(ctx context.Context, req

// Determine the parent of the subaccount
subaccountData, _, _ := rs.cli.Accounts.Subaccount.Get(ctx, plan.SubaccountId.ValueString())
//Determine if the parent of the subaccount is a directory and if it has authoization enabled
// Determine if the parent of the subaccount is a directory and if it has authorization enabled
parentId, isParentGlobalAccount := determineParentIdForAuthorization(rs.cli, ctx, subaccountData.ParentGUID)

var directoryId string
Expand All @@ -240,12 +248,11 @@ func (rs *subaccountEntitlementResource) createOrUpdate(ctx context.Context, req
Pending: []string{entitlementCallRetryPending},
Target: []string{entitlementCallRetryFailed, entitlementCallRetrySucceeded},
Refresh: func() (interface{}, string, error) {

var callResult btpcli.CommandResponse
var err error

if !hasPlanQuota(plan) {
callResult, err = rs.cli.Accounts.Entitlement.EnableInSubaccount(ctx, directoryId, plan.SubaccountId.ValueString(), plan.ServiceName.ValueString(), plan.PlanName.ValueString())
callResult, err = rs.cli.Accounts.Entitlement.EnableInSubaccount(ctx, directoryId, plan.SubaccountId.ValueString(), plan.ServiceName.ValueString(), plan.PlanName.ValueString(), plan.PlanUniqueIdentifier.ValueString(), true)
} else {
callResult, err = rs.cli.Accounts.Entitlement.AssignToSubaccount(ctx, directoryId, plan.SubaccountId.ValueString(), plan.ServiceName.ValueString(), plan.PlanName.ValueString(), int(plan.Amount.ValueInt64()))
}
Expand Down Expand Up @@ -284,7 +291,6 @@ func (rs *subaccountEntitlementResource) createOrUpdate(ctx context.Context, req
Pending: []string{cis_entitlements.StateStarted, cis_entitlements.StateProcessing},
Target: []string{cis_entitlements.StateOK},
Refresh: func() (interface{}, string, error) {

entitlement, _, err := rs.cli.Accounts.Entitlement.GetAssignedBySubaccount(ctx, plan.SubaccountId.ValueString(), plan.ServiceName.ValueString(), plan.PlanName.ValueString(), isParentGlobalAccount, parentId)

if isRetriableError(err) {
Expand All @@ -298,6 +304,7 @@ func (rs *subaccountEntitlementResource) createOrUpdate(ctx context.Context, req
if entitlement == nil {
return nil, cis_entitlements.StateProcessing, nil
}

// No error returned even if operation failed
if entitlement.Assignment.EntityState == cis_entitlements.StateProcessingFailed {
return *entitlement, entitlement.Assignment.EntityState, errors.New("undefined API error during entitlement processing")
Expand Down
85 changes: 58 additions & 27 deletions internal/provider/resource_subaccount_entitlement_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,25 +85,19 @@ func TestResourceSubaccountEntitlement(t *testing.T) {
ProtoV6ProviderFactories: getProviders(rec.GetDefaultClient()),
Steps: []resource.TestStep{
{
Config: hclProviderFor(user) + hclResourceSubaccountEntitlementWithAmountBySubaccount("uut", "integration-test-acc-static", "data-privacy-integration-service", "standard", "3"),
Config: hclProviderFor(user) + hclResourceSubaccountEntitlementWithAmountBySubaccount("uut", "integration-test-acc-static", "uas", "reporting-directory", "3"),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestMatchResourceAttr("btp_subaccount_entitlement.uut", "subaccount_id", regexpValidUUID),
resource.TestMatchResourceAttr("btp_subaccount_entitlement.uut", "created_date", regexpValidRFC3999Format),
resource.TestMatchResourceAttr("btp_subaccount_entitlement.uut", "last_modified", regexpValidRFC3999Format),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "id", "data-privacy-integration-service-standard"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "plan_name", "standard"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "plan_id", "data-privacy-integration-service-standard"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "service_name", "data-privacy-integration-service"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "id", "uas-reporting-directory"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "plan_name", "reporting-directory"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "plan_id", "uas-reporting-directory"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "service_name", "uas"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "amount", "3"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "state", "OK"),
),
},
{
ResourceName: "btp_subaccount_entitlement.uut",
ImportStateIdFunc: getImportStateIdForSubaccountEntitlement("btp_subaccount_entitlement.uut", "data-privacy-integration-service", "standard"),
ImportState: true,
ImportStateVerify: true,
},
},
})
})
Expand All @@ -117,43 +111,43 @@ func TestResourceSubaccountEntitlement(t *testing.T) {
ProtoV6ProviderFactories: getProviders(rec.GetDefaultClient()),
Steps: []resource.TestStep{
{
Config: hclProviderFor(user) + hclResourceSubaccountEntitlementWithAmountBySubaccount("uut", "integration-test-acc-static", "data-privacy-integration-service", "standard", "1"),
Config: hclProviderFor(user) + hclResourceSubaccountEntitlementWithAmountBySubaccount("uut", "integration-test-acc-static", "uas", "reporting-directory", "1"),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestMatchResourceAttr("btp_subaccount_entitlement.uut", "subaccount_id", regexpValidUUID),
resource.TestMatchResourceAttr("btp_subaccount_entitlement.uut", "created_date", regexpValidRFC3999Format),
resource.TestMatchResourceAttr("btp_subaccount_entitlement.uut", "last_modified", regexpValidRFC3999Format),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "id", "data-privacy-integration-service-standard"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "plan_name", "standard"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "plan_id", "data-privacy-integration-service-standard"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "service_name", "data-privacy-integration-service"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "id", "uas-reporting-directory"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "plan_name", "reporting-directory"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "plan_id", "uas-reporting-directory"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "service_name", "uas"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "amount", "1"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "state", "OK"),
),
},
{
Config: hclProviderFor(user) + hclResourceSubaccountEntitlementWithAmountBySubaccount("uut", "integration-test-acc-static", "data-privacy-integration-service", "standard", "2"),
Config: hclProviderFor(user) + hclResourceSubaccountEntitlementWithAmountBySubaccount("uut", "integration-test-acc-static", "uas", "reporting-directory", "2"),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestMatchResourceAttr("btp_subaccount_entitlement.uut", "subaccount_id", regexpValidUUID),
resource.TestMatchResourceAttr("btp_subaccount_entitlement.uut", "created_date", regexpValidRFC3999Format),
resource.TestMatchResourceAttr("btp_subaccount_entitlement.uut", "last_modified", regexpValidRFC3999Format),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "id", "data-privacy-integration-service-standard"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "plan_name", "standard"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "plan_id", "data-privacy-integration-service-standard"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "service_name", "data-privacy-integration-service"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "id", "uas-reporting-directory"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "plan_name", "reporting-directory"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "plan_id", "uas-reporting-directory"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "service_name", "uas"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "amount", "2"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "state", "OK"),
),
},
{
Config: hclProviderFor(user) + hclResourceSubaccountEntitlementBySubaccount("uut", "integration-test-acc-static", "data-privacy-integration-service", "standard"),
Config: hclProviderFor(user) + hclResourceSubaccountEntitlementBySubaccount("uut", "integration-test-acc-static", "uas", "reporting-directory"),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestMatchResourceAttr("btp_subaccount_entitlement.uut", "subaccount_id", regexpValidUUID),
resource.TestMatchResourceAttr("btp_subaccount_entitlement.uut", "created_date", regexpValidRFC3999Format),
resource.TestMatchResourceAttr("btp_subaccount_entitlement.uut", "last_modified", regexpValidRFC3999Format),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "id", "data-privacy-integration-service-standard"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "plan_name", "standard"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "plan_id", "data-privacy-integration-service-standard"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "service_name", "data-privacy-integration-service"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "id", "uas-reporting-directory"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "plan_name", "reporting-directory"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "plan_id", "uas-reporting-directory"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "service_name", "uas"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "amount", "2"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "state", "OK"),
),
Expand All @@ -162,13 +156,39 @@ func TestResourceSubaccountEntitlement(t *testing.T) {
})
})

t.Run("happy path - plan unique identifier", func(t *testing.T) {
rec, _ := setupVCR(t, "fixtures/resource_subaccount_entitlement.plan_unique_identifier")
defer stopQuietly(rec)

resource.Test(t, resource.TestCase{
IsUnitTest: true,
ProtoV6ProviderFactories: getProviders(rec.GetDefaultClient()),
Steps: []resource.TestStep{
{
Config: hclResourceSubaccountEntitlementWithPlanUniqueIdentifierBySubaccount("uut", "integration-test-acc-static", "hana-cloud", "hana", "unique-id-hana"),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestMatchResourceAttr("btp_subaccount_entitlement.uut", "subaccount_id", regexpValidUUID),
resource.TestMatchResourceAttr("btp_subaccount_entitlement.uut", "created_date", regexpValidRFC3999Format),
resource.TestMatchResourceAttr("btp_subaccount_entitlement.uut", "last_modified", regexpValidRFC3999Format),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "id", "hana-cloud-hana"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "plan_name", "hana"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "plan_id", "hana-cloud-hana"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "service_name", "hana-cloud"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "plan_unique_identifier", "unique-id-hana"),
resource.TestCheckResourceAttr("btp_subaccount_entitlement.uut", "state", "OK"),
),
},
},
})
})

t.Run("error path - zero amount", func(t *testing.T) {
resource.Test(t, resource.TestCase{
IsUnitTest: true,
ProtoV6ProviderFactories: getProviders(nil),
Steps: []resource.TestStep{
{
Config: hclResourceSubaccountEntitlementWithAmountBySubaccount("uut", "integration-test-acc-static", "data-privacy-integration-service", "standard", "0"),
Config: hclResourceSubaccountEntitlementWithAmountBySubaccount("uut", "integration-test-acc-static", "uas", "reporting-directory", "0"),
ExpectError: regexp.MustCompile(`Attribute amount value must be between 1 and 2000000000, got: 0`),
},
},
Expand Down Expand Up @@ -208,3 +228,14 @@ func getImportStateIdForSubaccountEntitlement(resourceName string, serviceName s
return fmt.Sprintf("%s,%s,%s", rs.Primary.Attributes["subaccount_id"], serviceName, planName), nil
}
}

func hclResourceSubaccountEntitlementWithPlanUniqueIdentifierBySubaccount(resourceName string, subaccountName string, serviceName string, planName string, planUniqueIdentifier string) string {
return fmt.Sprintf(`
data "btp_subaccounts" "all" {}
resource "btp_subaccount_entitlement" "%s" {
subaccount_id = [for sa in data.btp_subaccounts.all.values : sa.id if sa.name == "%s"][0]
service_name = "%s"
plan_name = "%s"
plan_unique_identifier = "%s"
}`, resourceName, subaccountName, serviceName, planName, planUniqueIdentifier)
}
42 changes: 22 additions & 20 deletions internal/provider/type_subaccount_entitlement.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,31 @@ import (
)

type subaccountEntitlementType struct {
SubaccountId types.String `tfsdk:"subaccount_id"`
Id types.String `tfsdk:"id"`
ServiceName types.String `tfsdk:"service_name"`
PlanName types.String `tfsdk:"plan_name"`
Category types.String `tfsdk:"category"`
PlanId types.String `tfsdk:"plan_id"`
Amount types.Int64 `tfsdk:"amount"`
State types.String `tfsdk:"state"`
CreatedDate types.String `tfsdk:"created_date"`
LastModified types.String `tfsdk:"last_modified"`
SubaccountId types.String `tfsdk:"subaccount_id"`
Id types.String `tfsdk:"id"`
ServiceName types.String `tfsdk:"service_name"`
PlanName types.String `tfsdk:"plan_name"`
Category types.String `tfsdk:"category"`
PlanId types.String `tfsdk:"plan_id"`
Amount types.Int64 `tfsdk:"amount"`
PlanUniqueIdentifier types.String `tfsdk:"plan_unique_identifier"`
State types.String `tfsdk:"state"`
CreatedDate types.String `tfsdk:"created_date"`
LastModified types.String `tfsdk:"last_modified"`
}

func subaccountEntitlementValueFrom(ctx context.Context, value btpcli.UnfoldedAssignment) (subaccountEntitlementType, diag.Diagnostics) {
return subaccountEntitlementType{
SubaccountId: types.StringValue(value.Assignment.EntityId),
Id: types.StringValue(value.Plan.UniqueIdentifier),
ServiceName: types.StringValue(value.Service.Name),
PlanName: types.StringValue(value.Plan.Name),
Category: types.StringValue(value.Plan.Category),
PlanId: types.StringValue(value.Plan.UniqueIdentifier),
Amount: types.Int64Value(int64(value.Assignment.Amount)),
State: types.StringValue(value.Assignment.EntityState),
LastModified: timeToValue(value.Assignment.ModifiedDate.Time()),
CreatedDate: timeToValue(value.Assignment.CreatedDate.Time()),
SubaccountId: types.StringValue(value.Assignment.EntityId),
Id: types.StringValue(value.Plan.UniqueIdentifier),
ServiceName: types.StringValue(value.Service.Name),
PlanName: types.StringValue(value.Plan.Name),
Category: types.StringValue(value.Plan.Category),
PlanId: types.StringValue(value.Plan.UniqueIdentifier),
PlanUniqueIdentifier: types.StringValue(value.Plan.UniqueIdentifier),
Amount: types.Int64Value(int64(value.Assignment.Amount)),
State: types.StringValue(value.Assignment.EntityState),
LastModified: timeToValue(value.Assignment.ModifiedDate.Time()),
CreatedDate: timeToValue(value.Assignment.CreatedDate.Time()),
}, diag.Diagnostics{}
}