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

APIGOV-29633 - updates for application profile provisioning #887

Merged
merged 8 commits into from
Feb 11, 2025
2 changes: 2 additions & 0 deletions pkg/agent/handler/accessrequest.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ func (h *accessRequestHandler) onPending(ctx context.Context, ar *management.Acc
return ar
}

updateDataFromEnumMap(ar.Spec.Data, ard.Spec.Schema)

data := map[string]interface{}{}
status, accessData := h.prov.AccessRequestProvision(req)

Expand Down
11 changes: 8 additions & 3 deletions pkg/agent/handler/accessrequest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,9 @@ func TestAccessRequestHandler(t *testing.T) {

c := &mockClient{
expectedStatus: tc.outboundStatus,
getErr: tc.getErr,
getManAppErr: tc.getErr,
getARDErr: tc.getARDErr,
getRI: mApp,
manApp: mApp,
subError: tc.subError,
t: t,
ard: ardRI,
Expand Down Expand Up @@ -246,7 +246,7 @@ func TestAccessRequestHandler_deleting(t *testing.T) {

c := &mockClient{
expectedStatus: tc.outboundStatus.String(),
getRI: mApp,
manApp: mApp,
isDeleting: true,
t: t,
}
Expand Down Expand Up @@ -329,6 +329,8 @@ type mockClient struct {
getARDErr error
getRI *v1.ResourceInstance
ard *v1.ResourceInstance
manApp *v1.ResourceInstance
getManAppErr error
isDeleting bool
subError error
t *testing.T
Expand All @@ -338,6 +340,9 @@ func (m *mockClient) GetResource(url string) (*v1.ResourceInstance, error) {
if strings.Contains(url, "/accessrequestdefinitions") {
return m.ard, m.getARDErr
}
if strings.Contains(url, "/managedapplication") {
return m.manApp, m.getManAppErr
}
return m.getRI, m.getErr
}

Expand Down
2 changes: 2 additions & 0 deletions pkg/agent/handler/credential.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,8 @@ func (h *credentials) provisionPreProcess(ctx context.Context, cred *management.
return nil, nil, true
}

updateDataFromEnumMap(cred.Spec.Data, crd.Spec.Schema)

return app, crd, false
}

Expand Down
75 changes: 61 additions & 14 deletions pkg/agent/handler/managedapplicationprofile.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,29 @@ package handler

import (
"context"
"fmt"

apiv1 "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/api/v1"
v1 "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/api/v1"
management "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/management/v1alpha1"
defs "github.com/Axway/agent-sdk/pkg/apic/definitions"
prov "github.com/Axway/agent-sdk/pkg/apic/provisioning"
"github.com/Axway/agent-sdk/pkg/util"
"github.com/Axway/agent-sdk/pkg/util/log"
"github.com/Axway/agent-sdk/pkg/watchmanager/proto"
)

type getTeamByID interface {
GetTeamByID(id string) *defs.PlatformTeam
}

type getManagedAppByName interface {
GetManagedApplicationByName(name string) *apiv1.ResourceInstance
}

type managedApplicationProfileCache interface {
getTeamByID
getManagedAppByName
GetApplicationProfileDefinitionByName(name string) (*v1.ResourceInstance, error)
}

type managedApplicationProfile struct {
marketplaceHandler
logger log.FieldLogger
prov prov.ApplicationProfileProvisioner
cache managedApplicationProfileCache
client client
Expand All @@ -35,6 +33,7 @@ type managedApplicationProfile struct {
// NewManagedApplicationProfileHandler creates a Handler for Credentials
func NewManagedApplicationProfileHandler(prov prov.ApplicationProfileProvisioner, cache managedApplicationProfileCache, client client) Handler {
return &managedApplicationProfile{
logger: log.NewFieldLogger().WithComponent("managedApplicationProfile").WithPackage("agent.handler"),
prov: prov,
cache: cache,
client: client,
Expand Down Expand Up @@ -81,13 +80,15 @@ func (h *managedApplicationProfile) onPending(ctx context.Context, profile *mana
}
}()

app, err := getManagedApp(h.cache, profile.Spec.ManagedApplication)
app, err := h.getManagedApp(ctx, profile)
if err != nil {
log.WithError(err).Error("error getting managed app")
h.onError(ctx, profile, err)
return err
}

h.checkForEnumValueMap(ctx, profile.Spec.Data, profile.Spec.ApplicationProfileDefinition)

pma := provManagedAppProfile{
attributes: profile.Spec.Data,
profileDefinition: profile.Spec.ApplicationProfileDefinition,
Expand Down Expand Up @@ -117,6 +118,40 @@ func (h *managedApplicationProfile) onPending(ctx context.Context, profile *mana
return err
}

func (h *managedApplicationProfile) checkForEnumValueMap(_ context.Context, data map[string]interface{}, profileDef string) {
log := h.logger.WithField("applicationProfileDefinition", profileDef)

// get application profile definition
ri, err := h.cache.GetApplicationProfileDefinitionByName(profileDef)
if err != nil {
log.WithError(err).Error("error getting application profile definition from cache")
return
}
if ri == nil {
log.Debug("could not find application profile definition in cache")
return
}
appProfDef := management.ApplicationProfileDefinition{}
if err := appProfDef.FromInstance(ri); err != nil {
log.WithError(err).Error("error reading application profile definition from cache")
return
}

updateDataFromEnumMap(data, appProfDef.Spec.Schema)
}

func (h *managedApplicationProfile) getManagedApp(_ context.Context, profile *management.ManagedApplicationProfile) (*management.ManagedApplication, error) {
app := management.NewManagedApplication(profile.Spec.ManagedApplication, profile.Metadata.Scope.Name)
ri, err := h.client.GetResource(app.GetSelfLink())
if err != nil {
return nil, err
}

app = &management.ManagedApplication{}
err = app.FromInstance(ri)
return app, err
}

// onError updates the managed app with an error status
func (h *managedApplicationProfile) onError(_ context.Context, profile *management.ManagedApplicationProfile, err error) {
ps := prov.NewRequestStatusBuilder()
Expand Down Expand Up @@ -176,12 +211,24 @@ func (a provManagedAppProfile) GetID() string {
return a.id
}

func getManagedApp(cache getManagedAppByName, name string) (*management.ManagedApplication, error) {
ri := cache.GetManagedApplicationByName(name)
if ri == nil {
return nil, fmt.Errorf("could not retrieved managed application")
func updateDataFromEnumMap(data map[string]interface{}, schema map[string]interface{}) {
enumPropMap := prov.GetEnumValueMapsFromSchema(schema)
if len(enumPropMap) == 0 {
return
}
for k, l := range data {
enumMap, ok := enumPropMap[k]
if !ok {
continue
}
label, ok := l.(string)
if !ok {
continue
}
value, ok := enumMap[label]
if !ok {
continue
}
data[k] = value
}
app := &management.ManagedApplication{}
err := app.FromInstance(ri)
return app, err
}
90 changes: 51 additions & 39 deletions pkg/agent/handler/managedapplicationprofile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,77 +17,92 @@ import (
)

type mockCache struct {
team *defs.PlatformTeam
manApp *apiv1.ResourceInstance
team *defs.PlatformTeam
profDef *apiv1.ResourceInstance
}

func (m mockCache) GetTeamByID(id string) *defs.PlatformTeam {
return m.team
}

func (m mockCache) GetManagedApplicationByName(name string) *apiv1.ResourceInstance {
return m.manApp
func (m mockCache) GetApplicationProfileDefinitionByName(name string) (*apiv1.ResourceInstance, error) {
return m.profDef, nil
}

func TestManagedApplicationProfileHandler(t *testing.T) {
//setup test cache
appRI, _ := managedAppForTest.AsInstance()

tests := []struct {
apd, _ := prov.NewApplicationProfileBuilder(
func(a *v1alpha1.ApplicationProfileDefinition) (*v1alpha1.ApplicationProfileDefinition, error) {
return a, nil
}).
SetName("APD").
SetTitle("APD Title").
SetRequestSchema(prov.NewSchemaBuilder().
AddProperty(prov.NewSchemaPropertyBuilder().
SetName("enumMapData").
IsString().
AddEnumValueMap(map[string]interface{}{
"label1": "val1",
"label2": "val2",
"label3": "val3",
"label4": "val4",
}),
),
).
Register()
apdRI, _ := apd.AsInstance()

manAppRI, _ := managedAppForTest.AsInstance()

tests := map[string]struct {
skip bool
action proto.Event_Type
createErr error
getErr error
hasError bool
skipManAppLoad bool
name string
expectedProvType string
inboundStatus string
subError error
teamName string
outboundStatus string
}{
{
name: "should handle a create event for a ManagedApplicationProfile when status is pending",
"should handle a create event for a ManagedApplicationProfile when status is pending": {
skip: false,
action: proto.Event_CREATED,
teamName: teamName,
expectedProvType: provision,
inboundStatus: prov.Pending.String(),
outboundStatus: prov.Success.String(),
},
{
name: "should return err when get man app fails",
action: proto.Event_CREATED,
skipManAppLoad: true,
hasError: true,
inboundStatus: prov.Pending.String(),
outboundStatus: prov.Error.String(),
},
{
name: "should return nil when the event type is for updating",
"should return nil when the event type is for updating": {
skip: false,
action: proto.Event_UPDATED,
},
{
name: "should return nil when the event is for subresources",
"should return nil when the event is for subresources": {
skip: false,
action: proto.Event_SUBRESOURCEUPDATED,
},
{
name: "should return nil when status field is empty",
"should return nil when status field is empty": {
skip: false,
action: proto.Event_CREATED,
},
{
name: "should return nil when status field is Success",
"should return nil when status field is Success": {
skip: false,
action: proto.Event_CREATED,
inboundStatus: prov.Success.String(),
},
{
name: "should return nil when status field is Error",
"should return nil when status field is Error": {
skip: false,
action: proto.Event_CREATED,
inboundStatus: prov.Error.String(),
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
if tc.skip {
return
}
profile := managedAppForProfTest
profile.Status.Level = tc.inboundStatus

Expand All @@ -111,16 +126,12 @@ func TestManagedApplicationProfileHandler(t *testing.T) {
subError: tc.subError,
expectedStatus: tc.outboundStatus,
t: t,
manApp: manAppRI,
}

testCache := mockCache{
team: team,
manApp: appRI,
}
if tc.skipManAppLoad {
testCache = mockCache{
team: team,
}
team: team,
profDef: apdRI,
}

handler := NewManagedApplicationProfileHandler(p, testCache, c)
Expand Down Expand Up @@ -225,7 +236,8 @@ var managedAppForProfTest = management.ManagedApplicationProfile{
ManagedApplication: "app-test",
ApplicationProfileDefinition: "app-prof-def",
Data: map[string]interface{}{
"data": "value",
"enumMapData": "label2",
"data2": "value2",
},
},
Status: &apiv1.ResourceStatus{
Expand Down
39 changes: 39 additions & 0 deletions pkg/apic/provisioning/mock/provisioning.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,45 @@ func (m MockApplicationRequest) GetConsumerOrgID() string {
return m.ConsumerOrgID
}

type MockApplicationProfileRequest struct {
provisioning.ApplicationProfileRequest
ID string
AppProfileName string
Details map[string]interface{}
AppName string
AppDetails map[string]string
TeamName string
ConsumerOrgID string
}

func (m MockApplicationProfileRequest) GetApplicationProfileData() map[string]interface{} {
return m.Details
}

func (m MockApplicationProfileRequest) GetApplicationDetailsValue(key string) string {
return m.AppDetails[key]
}

func (m MockApplicationProfileRequest) GetApplicationProfileDefinitionName() string {
return m.AppProfileName
}

func (m MockApplicationProfileRequest) GetManagedApplicationName() string {
return m.AppName
}

func (m MockApplicationProfileRequest) GetTeamName() string {
return m.TeamName
}

func (m MockApplicationProfileRequest) GetConsumerOrgID() string {
return m.ConsumerOrgID
}

func (m MockApplicationProfileRequest) GetID() string {
return m.ID
}

type MockCredentialRequest struct {
provisioning.CredentialRequest
ID string
Expand Down
Loading