Skip to content

Commit 766131f

Browse files
authored
Merge pull request #1469 from slahirucd7/apiPolicy
Platform API Custom Policy handling support for APIs
2 parents fdcfaf4 + 8596bb6 commit 766131f

4 files changed

Lines changed: 125 additions & 1 deletion

File tree

platform-api/src/internal/repository/custom_policy.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,39 @@ func (r *CustomPolicyRepo) CountCustomPolicyUsages(policyUUID string) (int, erro
189189
return count, err
190190
}
191191

192+
// GetCustomPolicyUsagesByAPIUUID returns all policy UUIDs currently used with the given API.
193+
func (r *CustomPolicyRepo) GetCustomPolicyUsagesByAPIUUID(apiUUID string) ([]string, error) {
194+
query := `SELECT policy_uuid FROM gateway_custom_policy_usages WHERE api_uuid = ?`
195+
rows, err := r.db.Query(r.db.Rebind(query), apiUUID)
196+
if err != nil {
197+
return nil, err
198+
}
199+
defer rows.Close()
200+
var uuids []string
201+
for rows.Next() {
202+
var u string
203+
if err := rows.Scan(&u); err != nil {
204+
return nil, err
205+
}
206+
uuids = append(uuids, u)
207+
}
208+
return uuids, rows.Err()
209+
}
210+
211+
// InsertCustomPolicyUsage adds a custom policy usage entry for the given API.
212+
func (r *CustomPolicyRepo) InsertCustomPolicyUsage(policyUUID, apiUUID string) error {
213+
query := `INSERT INTO gateway_custom_policy_usages (policy_uuid, api_uuid) VALUES (?, ?)`
214+
_, err := r.db.Exec(r.db.Rebind(query), policyUUID, apiUUID)
215+
return err
216+
}
217+
218+
// DeleteCustomPolicyUsage removes the usage entry.
219+
func (r *CustomPolicyRepo) DeleteCustomPolicyUsage(policyUUID, apiUUID string) error {
220+
query := `DELETE FROM gateway_custom_policy_usages WHERE policy_uuid = ? AND api_uuid = ?`
221+
_, err := r.db.Exec(r.db.Rebind(query), policyUUID, apiUUID)
222+
return err
223+
}
224+
192225
// DeleteCustomPolicyIfUnused atomically deletes the policy only when it has no active usages.
193226
func (r *CustomPolicyRepo) DeleteCustomPolicyIfUnused(orgUUID, policyUUID string) error {
194227
deleteQuery := `

platform-api/src/internal/repository/interfaces.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,4 +285,8 @@ type CustomPolicyRepository interface {
285285
CountCustomPolicyUsages(policyUUID string) (int, error)
286286
// DeleteCustomPolicyIfUnused atomically deletes the policy only when it has no active usages.
287287
DeleteCustomPolicyIfUnused(orgUUID, policyUUID string) error
288+
// Gateway Custom Policy usage tracking methods.
289+
GetCustomPolicyUsagesByAPIUUID(apiUUID string) ([]string, error)
290+
InsertCustomPolicyUsage(policyUUID, apiUUID string) error
291+
DeleteCustomPolicyUsage(policyUUID, apiUUID string) error
288292
}

platform-api/src/internal/server/server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ func StartPlatformAPIServer(cfg *config.Server, slogger *slog.Logger) (*Server,
171171
gatewayEventsService := service.NewGatewayEventsService(wsManager, slogger)
172172
appService := service.NewApplicationService(appRepo, projectRepo, orgRepo, apiRepo, gatewayEventsService, slogger)
173173
apiService := service.NewAPIService(apiRepo, projectRepo, orgRepo, gatewayRepo, deploymentRepo, devPortalRepo, publicationRepo,
174-
subscriptionPlanRepo, gatewayEventsService, devPortalService, apiUtil, slogger)
174+
subscriptionPlanRepo, customPolicyRepo, gatewayEventsService, devPortalService, apiUtil, slogger)
175175
gatewayService := service.NewGatewayService(gatewayRepo, orgRepo, apiRepo, customPolicyRepo, gatewayEventsService, slogger)
176176
subscriptionService := service.NewSubscriptionService(apiRepo, subscriptionRepo, gatewayEventsService, slogger)
177177
subscriptionPlanService := service.NewSubscriptionPlanService(subscriptionPlanRepo, gatewayRepo, gatewayEventsService, slogger)

platform-api/src/internal/service/api.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ type APIService struct {
5050
devPortalRepo repository.DevPortalRepository
5151
publicationRepo repository.APIPublicationRepository
5252
subscriptionPlanRepo repository.SubscriptionPlanRepository
53+
customPolicyRepo repository.CustomPolicyRepository
5354
gatewayEventsService *GatewayEventsService
5455
devPortalService *DevPortalService
5556
apiUtil *utils.APIUtil
@@ -62,6 +63,7 @@ func NewAPIService(apiRepo repository.APIRepository, projectRepo repository.Proj
6263
deploymentRepo repository.DeploymentRepository,
6364
devPortalRepo repository.DevPortalRepository, publicationRepo repository.APIPublicationRepository,
6465
subscriptionPlanRepo repository.SubscriptionPlanRepository,
66+
customPolicyRepo repository.CustomPolicyRepository,
6567
gatewayEventsService *GatewayEventsService, devPortalService *DevPortalService, apiUtil *utils.APIUtil,
6668
slogger *slog.Logger) *APIService {
6769
return &APIService{
@@ -73,6 +75,7 @@ func NewAPIService(apiRepo repository.APIRepository, projectRepo repository.Proj
7375
devPortalRepo: devPortalRepo,
7476
publicationRepo: publicationRepo,
7577
subscriptionPlanRepo: subscriptionPlanRepo,
78+
customPolicyRepo: customPolicyRepo,
7679
gatewayEventsService: gatewayEventsService,
7780
devPortalService: devPortalService,
7881
apiUtil: apiUtil,
@@ -154,6 +157,8 @@ func (s *APIService) CreateAPI(req *api.CreateRESTAPIRequest, orgUUID string) (*
154157
s.slogger.Error("Failed to create default DevPortal association for API", "apiUUID", apiUUID, "error", err)
155158
}
156159

160+
s.refreshCustomPolicyUsages(apiUUID, orgUUID, apiModel)
161+
157162
return s.apiUtil.ModelToRESTAPI(apiModel)
158163
}
159164

@@ -282,6 +287,8 @@ func (s *APIService) UpdateAPI(apiUUID string, req *api.UpdateRESTAPIRequest, or
282287
return nil, err
283288
}
284289

290+
s.refreshCustomPolicyUsages(apiUUID, orgUUID, updatedAPIModel)
291+
285292
return s.apiUtil.ModelToRESTAPI(updatedAPIModel)
286293
}
287294

@@ -1768,3 +1775,83 @@ func restAPIGatewayFunctionalityTypePtr(value string) *api.RESTAPIGatewayRespons
17681775
converted := api.RESTAPIGatewayResponseFunctionalityType(value)
17691776
return &converted
17701777
}
1778+
1779+
// refreshCustomPolicyUsages evaluates gateway_custom_policy_usages for an API.
1780+
// 1. Collects all policy (name, version) references across all three levels (API-level, operation-level, channel-level).
1781+
// 2. Resolves which ones are custom policies for the org via gateway_custom_policies.
1782+
// 3. Diffs against the current usage rows — inserting new ones and deleting removed ones.
1783+
func (s *APIService) refreshCustomPolicyUsages(apiUUID, orgUUID string, apiModel *model.API) {
1784+
if s.customPolicyRepo == nil {
1785+
return
1786+
}
1787+
1788+
// Step 1: collect all unique (name, version) pairs across all policy levels
1789+
type policyRef struct{ name, version string }
1790+
seen := make(map[policyRef]bool)
1791+
var refs []policyRef
1792+
1793+
collect := func(policies []model.Policy) {
1794+
for _, p := range policies {
1795+
ref := policyRef{p.Name, p.Version}
1796+
if !seen[ref] {
1797+
seen[ref] = true
1798+
refs = append(refs, ref)
1799+
}
1800+
}
1801+
}
1802+
1803+
// API-level policies
1804+
collect(apiModel.Configuration.Policies)
1805+
// Operation-level policies
1806+
for _, op := range apiModel.Configuration.Operations {
1807+
if op.Request != nil {
1808+
collect(op.Request.Policies)
1809+
}
1810+
}
1811+
// Channel-level policies
1812+
for _, ch := range apiModel.Channels {
1813+
if ch.Request != nil {
1814+
collect(ch.Request.Policies)
1815+
}
1816+
}
1817+
1818+
// Step 2: resolve which refs are synced custom policies for this org
1819+
newSet := make(map[string]bool)
1820+
for _, ref := range refs {
1821+
cp, err := s.customPolicyRepo.GetCustomPolicyByNameAndVersion(orgUUID, ref.name, ref.version)
1822+
if err != nil {
1823+
s.slogger.Warn("Failed to lookup custom policy during usage refresh", "name", ref.name, "version", ref.version, "orgUUID", orgUUID, "error", err)
1824+
continue
1825+
}
1826+
if cp != nil {
1827+
newSet[cp.UUID] = true
1828+
}
1829+
}
1830+
1831+
// Step 3: fetch current usages from the join table
1832+
currentUUIDs, err := s.customPolicyRepo.GetCustomPolicyUsagesByAPIUUID(apiUUID)
1833+
if err != nil {
1834+
s.slogger.Warn("Failed to fetch custom policy usages", "apiUUID", apiUUID, "error", err)
1835+
return
1836+
}
1837+
currentSet := make(map[string]bool, len(currentUUIDs))
1838+
for _, u := range currentUUIDs {
1839+
currentSet[u] = true
1840+
}
1841+
1842+
// Step 4: insert new, delete removed, skip unchanged
1843+
for uuid := range newSet {
1844+
if !currentSet[uuid] {
1845+
if err := s.customPolicyRepo.InsertCustomPolicyUsage(uuid, apiUUID); err != nil {
1846+
s.slogger.Warn("Failed to insert custom policy usage", "policyUUID", uuid, "apiUUID", apiUUID, "error", err)
1847+
}
1848+
}
1849+
}
1850+
for uuid := range currentSet {
1851+
if !newSet[uuid] {
1852+
if err := s.customPolicyRepo.DeleteCustomPolicyUsage(uuid, apiUUID); err != nil {
1853+
s.slogger.Warn("Failed to delete custom policy usage", "policyUUID", uuid, "apiUUID", apiUUID, "error", err)
1854+
}
1855+
}
1856+
}
1857+
}

0 commit comments

Comments
 (0)