@@ -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