Skip to content

Commit

Permalink
feat: client apis to interact with the konnect applications (#490)
Browse files Browse the repository at this point in the history
* feat: konnect application auth client apis

* fix dependency issue

* update test to only run on the ee version of the kong

* delete endpoint

* update kong version in test

* update create test to run only on the enterprise versions

* add missing function comments

* Update kong/konnect_application_service_test.go

Co-authored-by: Patryk Małek <patryk.malek@konghq.com>

* Update kong/konnect_application_service.go

Co-authored-by: Patryk Małek <patryk.malek@konghq.com>

* Update kong/konnect_application.go

Co-authored-by: Patryk Małek <patryk.malek@konghq.com>

* Update kong/konnect_application_service_test.go

Co-authored-by: Patryk Małek <patryk.malek@konghq.com>

* fix syntax errors

* add test for list

* add changelog

* update to require instead of assert

* check for the status code when deleting

---------

Co-authored-by: Patryk Małek <patryk.malek@konghq.com>
  • Loading branch information
reversearrow and pmalek authored Dec 12, 2024
1 parent 2ca5c65 commit c87a2a7
Show file tree
Hide file tree
Showing 8 changed files with 439 additions and 0 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@
- [0.2.0](#020)
- [0.1.0](#010)

## [v0.62.0]

> Release date: 2024/12/11
- Added Client APIs (Create, List, ListALL, Delete) to interact
with the Konnect Application Auth resources.
[#490](https://github.com/Kong/go-kong/pull/490)

## [v0.61.0]

> Release date: 2024/12/06
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/samber/lo v1.47.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc=
github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
Expand Down
2 changes: 2 additions & 0 deletions kong/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ type Client struct {
Vaults AbstractVaultService
Keys AbstractKeyService
KeySets AbstractKeySetService
KonnectApplication AbstractKonnectApplicationService
Licenses AbstractLicenseService
FilterChains AbstractFilterChainService

Expand Down Expand Up @@ -165,6 +166,7 @@ func NewClient(baseURL *string, client *http.Client) (*Client, error) {
kong.Vaults = (*VaultService)(&kong.common)
kong.Keys = (*KeyService)(&kong.common)
kong.KeySets = (*KeySetService)(&kong.common)
kong.KonnectApplication = (*KonnectApplicationService)(&kong.common)
kong.Licenses = (*LicenseService)(&kong.common)
kong.FilterChains = (*FilterChainService)(&kong.common)

Expand Down
27 changes: 27 additions & 0 deletions kong/konnect_application.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package kong

// KonnectApplication represents Konnect-Application-Auth
// in Kong.
// Read https://docs.konghq.com/konnect/dev-portal/applications/application-overview/
// +k8s:deepcopy-gen=true
type KonnectApplication struct {
ID *string `json:"id"`
CreatedAt int64 `json:"created_at"`
ClientID string `json:"client_id"`
ConsumerGroups []string `json:"consumer_groups"`
Scopes []string `json:"scopes"`
AuthStrategyID *string `json:"auth_strategy_id"`
ApplicationContext *ApplicationContext `json:"application_context"`
ExhaustedScopes []string `json:"exhausted_scopes"`
Tags *[]string `json:"tags"`
}

// ApplicationContext represents the application context inside the
// Konnenct-Application-Auth.
// +k8s:deepcopy-gen=true
type ApplicationContext struct {
PortalID *string `json:"portal_id"`
ApplicationID *string `json:"application_id"`
DeveloperID *string `json:"developer_id"`
OrganizationID *string `json:"organization_id"`
}
105 changes: 105 additions & 0 deletions kong/konnect_application_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package kong

import (
"context"
"encoding/json"
"fmt"
"net/http"
)

var _ AbstractKonnectApplicationService = &KonnectApplicationService{}

// AbstractKonnectApplicationService handles Konnect applications in Kong.
type AbstractKonnectApplicationService interface {
// Create creates a Konnect Application in Kong.
Create(ctx context.Context, key *KonnectApplication) (*KonnectApplication, error)
// List fetches list of Konnect Applications in Kong.
List(ctx context.Context, opt *ListOpt) ([]*KonnectApplication, *ListOpt, error)
// ListAll fetches all Konnect Applications in Kong.
ListAll(ctx context.Context) ([]*KonnectApplication, error)
// Delete deletes a Konnect Application in Kong by ID.
Delete(ctx context.Context, ID *string) error
}

type KonnectApplicationService service

// Create creates a Konnect Application in Kong.
func (k *KonnectApplicationService) Create(ctx context.Context, key *KonnectApplication) (*KonnectApplication, error) {
queryPath := "/konnect_applications"
method := "POST"
if key.ID != nil {
queryPath = queryPath + "/" + *key.ID
method = "PUT"
}
req, err := k.client.NewRequest(method, queryPath, nil, key)
if err != nil {
return nil, err
}

var createdKey KonnectApplication
_, err = k.client.Do(ctx, req, &createdKey)
if err != nil {
return nil, err
}
return &createdKey, nil
}

// List fetches list of Konnect Applications in Kong.
func (k *KonnectApplicationService) List(ctx context.Context, opt *ListOpt) ([]*KonnectApplication, *ListOpt, error) {
data, next, err := k.client.list(ctx, "/konnect_applications", opt)
if err != nil {
return nil, nil, err
}
var kaas []*KonnectApplication

for _, object := range data {
b, err := object.MarshalJSON()
if err != nil {
return nil, nil, err
}
var kaa KonnectApplication
err = json.Unmarshal(b, &kaa)
if err != nil {
return nil, nil, err
}
kaas = append(kaas, &kaa)
}

return kaas, next, nil
}

// ListAll fetches all Konnect Applications in Kong.
func (k *KonnectApplicationService) ListAll(ctx context.Context) ([]*KonnectApplication, error) {
var kaa, data []*KonnectApplication
var err error
opt := &ListOpt{Size: pageSize}

for opt != nil {
data, opt, err = k.List(ctx, opt)
if err != nil {
return nil, err
}
kaa = append(kaa, data...)
}
return kaa, nil
}

// Delete deletes a Konnect Application in Kong by ID.
func (k *KonnectApplicationService) Delete(ctx context.Context, ID *string) error {
if isEmptyString(ID) {
return fmt.Errorf("ID cannot be nil for Delete operation")
}

req, err := k.client.NewRequest("DELETE", fmt.Sprintf("/konnect_applications/%s", *ID), nil, nil)
if err != nil {
return err
}

resp, err := k.client.Do(ctx, req, nil)
if resp.StatusCode != http.StatusNoContent {
return fmt.Errorf("failed to delete Konnect Application: %s: "+
"expected status %v, but received %v", *ID, http.StatusNoContent, resp.Status)
}

return err
}
203 changes: 203 additions & 0 deletions kong/konnect_application_service_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
package kong

import (
"context"
"testing"
"time"

"github.com/google/uuid"
"github.com/samber/lo"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestKonnectApplicationService_Create(T *testing.T) {
RunWhenDBMode(T, "postgres")
RunWhenEnterprise(T, ">=3.6.0", RequiredFeatures{})

assert := assert.New(T)
require := require.New(T)

client, err := NewTestClient(nil, nil)
require.NoError(err)
require.NotNil(client)

var (
clientID = uuid.NewString()
consumerGroup = []string{uuid.NewString()}
scopes = []string{"/auth"}
authStrategyID = uuid.NewString()
exhaustedScopes = []string{"/eauth"}
orgID = uuid.NewString()
developerID = uuid.NewString()
createdAt = time.Now().Unix()
)

kaa := &KonnectApplication{
ID: &clientID,
ClientID: clientID,
ConsumerGroups: consumerGroup,
Scopes: scopes,
AuthStrategyID: &authStrategyID,
ExhaustedScopes: exhaustedScopes,
ApplicationContext: &ApplicationContext{
OrganizationID: &orgID,
DeveloperID: &developerID,
},
CreatedAt: createdAt,
}
createResponse, err := client.KonnectApplication.Create(defaultCtx, kaa)
require.NoError(err)
require.NotNil(createResponse)

T.Cleanup(func() {
assert.NoError(client.KonnectApplication.Delete(context.Background(), createResponse.ID))
})

require.Equal(createResponse.ClientID, clientID)
require.Equal(createResponse.CreatedAt, createdAt)
require.Equal(createResponse.ConsumerGroups, consumerGroup)
require.Equal(createResponse.Scopes, scopes)
require.Equal(createResponse.ExhaustedScopes, exhaustedScopes)
}

func TestKonnectApplicationService_ListAll(T *testing.T) {
RunWhenDBMode(T, "postgres")
RunWhenEnterprise(T, ">=3.6.0", RequiredFeatures{})

assert := assert.New(T)
require := require.New(T)

client, err := NewTestClient(nil, nil)
require.NoError(err)
require.NotNil(client)

var (
expectedKonnectApplications = 10
consumerGroup = []string{uuid.NewString()}
scopes = []string{"/auth"}
authStrategyID = uuid.NewString()
exhaustedScopes = []string{"/eauth"}
orgID = uuid.NewString()
developerID = uuid.NewString()
createdAt = time.Now().Unix()
)

kaa := &KonnectApplication{
ConsumerGroups: consumerGroup,
Scopes: scopes,
AuthStrategyID: &authStrategyID,
ExhaustedScopes: exhaustedScopes,
ApplicationContext: &ApplicationContext{
OrganizationID: &orgID,
DeveloperID: &developerID,
},
CreatedAt: createdAt,
}

for i := 0; i < expectedKonnectApplications; i++ {
clientID := uuid.NewString()
kaa.ID = &clientID
kaa.ClientID = clientID
createResponse, err := client.KonnectApplication.Create(defaultCtx, kaa)
require.NoError(err)
require.NotNil(createResponse)

T.Cleanup(func() {
assert.NoError(client.KonnectApplication.Delete(context.Background(), createResponse.ID))
})
}

listKonnectApplicationResponse, err := client.KonnectApplication.ListAll(defaultCtx)
require.NoError(err)
require.Len(listKonnectApplicationResponse, expectedKonnectApplications)
}

func TestKonnectApplicationService_List(T *testing.T) {
RunWhenDBMode(T, "postgres")
RunWhenEnterprise(T, ">=3.6.0", RequiredFeatures{})

assert := assert.New(T)
require := require.New(T)

client, err := NewTestClient(nil, nil)
require.NoError(err)
require.NotNil(client)

var (
expectedKonnectApplications = 10
consumerGroup = []string{uuid.NewString()}
scopes = []string{"/auth"}
authStrategyID = uuid.NewString()
exhaustedScopes = []string{"/eauth"}
orgID = uuid.NewString()
developerID = uuid.NewString()
createdAt = time.Now().Unix()
tagA = "list1"
tagB = "list2"
)

kaa := &KonnectApplication{
ConsumerGroups: consumerGroup,
Scopes: scopes,
AuthStrategyID: &authStrategyID,
ExhaustedScopes: exhaustedScopes,
ApplicationContext: &ApplicationContext{
OrganizationID: &orgID,
DeveloperID: &developerID,
},
CreatedAt: createdAt,
}

for i := 0; i < expectedKonnectApplications/2; i++ {
clientID := uuid.NewString()
kaa.ID = &clientID
kaa.ClientID = clientID
kaa.Tags = lo.ToPtr([]string{tagA})
createResponse, err := client.KonnectApplication.Create(defaultCtx, kaa)
require.NoError(err)
require.NotNil(createResponse)

T.Cleanup(func() {
assert.NoError(client.KonnectApplication.Delete(context.Background(), createResponse.ID))
})
}

for i := 0; i < expectedKonnectApplications/2; i++ {
clientID := uuid.NewString()
kaa.ID = &clientID
kaa.ClientID = clientID
kaa.Tags = lo.ToPtr([]string{tagB})
createResponse, err := client.KonnectApplication.Create(defaultCtx, kaa)
require.NoError(err)
require.NotNil(createResponse)

T.Cleanup(func() {
assert.NoError(client.KonnectApplication.Delete(context.Background(), createResponse.ID))
})
}

// Filter by tag listA
listKonnectApplicationResponseByTagA, _, err := client.KonnectApplication.List(defaultCtx, &ListOpt{
Size: 10,
Tags: []*string{lo.ToPtr(tagA)},
})
require.NoError(err)
require.Len(listKonnectApplicationResponseByTagA, 5)

// Filter by tag listB
listKonnectApplicationResponseByTagB, _, err := client.KonnectApplication.List(defaultCtx, &ListOpt{
Size: 10,
Tags: []*string{lo.ToPtr(tagB)},
})
require.NoError(err)
require.Len(listKonnectApplicationResponseByTagB, 5)

size := 2
// Filter by size
listKonnectApplicationResponseBySize, _, err := client.KonnectApplication.List(defaultCtx, &ListOpt{
Size: size,
})
require.NoError(err)
require.Len(listKonnectApplicationResponseBySize, size)
}
Loading

0 comments on commit c87a2a7

Please sign in to comment.