Skip to content

Commit 0d07477

Browse files
committed
List artifacts that are deployed in a Gateway
1 parent 6702374 commit 0d07477

File tree

10 files changed

+326
-5
lines changed

10 files changed

+326
-5
lines changed

platform-api/spec/impls/gateway-management/gateway-management.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Gateway Management provides APIs for registering, managing, and deleting API gat
2929
| PUT | `/api/v1/gateways/{id}` | Update gateway | ✅ Implemented |
3030
| DELETE | `/api/v1/gateways/{id}` | Delete gateway | ✅ Implemented |
3131
| POST | `/api/v1/gateways/{id}/tokens` | Rotate gateway token | ✅ Implemented |
32+
| GET | `/api/v1/gateways/{id}/artifacts` | Get deployed artifacts | ✅ Implemented |
3233
| DELETE | `/api/v1/gateways/{id}/tokens/{tokenId}` | Revoke token | ⏳ Planned |
3334

3435
## Authentication & Authorization

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,39 @@ var ValidGatewayFunctionalityType = map[string]bool{
6464

6565
// DefaultGatewayFunctionalityType Default gateway functionality type for new gateways
6666
const DefaultGatewayFunctionalityType = GatewayFunctionalityTypeRegular
67+
68+
// API Type Constants
69+
const (
70+
APITypeHTTP = "HTTP"
71+
APITypeWS = "WS"
72+
APITypeSOAPToREST = "SOAPTOREST"
73+
APITypeSOAP = "SOAP"
74+
APITypeGraphQL = "GRAPHQL"
75+
APITypeWebSub = "WEBSUB"
76+
APITypeSSE = "SSE"
77+
APITypeWebhook = "WEBHOOK"
78+
APITypeAsync = "ASYNC"
79+
)
80+
81+
// API SubType Constants
82+
const (
83+
APISubTypeHTTP = "REST"
84+
APISubTypeGraphQL = "GQL"
85+
APISubTypeAsync = "ASYNC"
86+
APISubTypeWebSocket = "WEBSOCKET"
87+
APISubTypeSOAP = "SOAP"
88+
)
89+
90+
// Artifact Type Constants
91+
const (
92+
ArtifactTypeAPI = "API"
93+
ArtifactTypeMCP = "MCP"
94+
ArtifactTypeAPIProduct = "API_PRODUCT"
95+
)
96+
97+
// ValidArtifactTypes Valid artifact types deployed to gateways
98+
var ValidArtifactTypes = map[string]bool{
99+
ArtifactTypeAPI: true,
100+
ArtifactTypeMCP: true,
101+
ArtifactTypeAPIProduct: true,
102+
}

platform-api/src/internal/dto/gateway.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,21 @@ type GatewayStatusListResponse struct {
9090
List []GatewayStatusResponse `json:"list"`
9191
Pagination Pagination `json:"pagination"`
9292
}
93+
94+
// GatewayArtifact represents an artifact (API, MCP, API Product) deployed to a gateway
95+
type GatewayArtifact struct {
96+
ID string `json:"id"`
97+
Name string `json:"name"`
98+
DisplayName string `json:"displayName,omitempty"`
99+
Type string `json:"type"` // "API", "MCP", "API_PRODUCT"
100+
SubType string `json:"subType,omitempty"` // For APIs: "REST", "ASYNC", "GQL"
101+
CreatedAt time.Time `json:"createdAt"`
102+
UpdatedAt time.Time `json:"updatedAt"`
103+
}
104+
105+
// GatewayArtifactListResponse represents a paginated list of artifacts deployed to a gateway
106+
type GatewayArtifactListResponse struct {
107+
Count int `json:"count"`
108+
List []GatewayArtifact `json:"list"`
109+
Pagination Pagination `json:"pagination"`
110+
}

platform-api/src/internal/handler/gateway.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,51 @@ func (h *GatewayHandler) RotateToken(c *gin.Context) {
321321
c.JSON(http.StatusCreated, response)
322322
}
323323

324+
// GetGatewayArtifacts handles GET /api/v1/gateways/{gatewayId}/artifacts
325+
func (h *GatewayHandler) GetGatewayArtifacts(c *gin.Context) {
326+
orgId, exists := middleware.GetOrganizationFromContext(c)
327+
if !exists {
328+
c.JSON(http.StatusUnauthorized, utils.NewErrorResponse(401, "Unauthorized",
329+
"Organization claim not found in token"))
330+
return
331+
}
332+
333+
gatewayId := c.Param("gatewayId")
334+
if gatewayId == "" {
335+
c.JSON(http.StatusBadRequest, utils.NewErrorResponse(400, "Bad Request",
336+
"Gateway ID is required"))
337+
return
338+
}
339+
340+
// Parse artifact type filter parameter
341+
artifactType := c.Query("artifactType")
342+
// Validate artifactType if provided
343+
if artifactType != "" {
344+
if !constants.ValidArtifactTypes[artifactType] {
345+
c.JSON(http.StatusBadRequest, utils.NewErrorResponse(400, "Bad Request",
346+
"Invalid artifact type. Valid values are: "+constants.ArtifactTypeAPI+", "+constants.ArtifactTypeMCP+
347+
", "+constants.ArtifactTypeAPIProduct))
348+
return
349+
}
350+
}
351+
352+
// Get paginated artifacts for the gateway
353+
artifactListResponse, err := h.gatewayService.GetGatewayArtifacts(gatewayId, orgId, artifactType)
354+
if err != nil {
355+
if errors.Is(err, constants.ErrGatewayNotFound) {
356+
c.JSON(http.StatusNotFound, utils.NewErrorResponse(404, "Not Found",
357+
"Gateway not found"))
358+
return
359+
}
360+
c.JSON(http.StatusInternalServerError, utils.NewErrorResponse(500, "Internal Server Error",
361+
"Failed to get gateway artifacts"))
362+
return
363+
}
364+
365+
// Return paginated artifact list
366+
c.JSON(http.StatusOK, artifactListResponse)
367+
}
368+
324369
// RegisterRoutes registers gateway routes with the router
325370
func (h *GatewayHandler) RegisterRoutes(r *gin.Engine) {
326371
gatewayGroup := r.Group("/api/v1/gateways")
@@ -331,6 +376,7 @@ func (h *GatewayHandler) RegisterRoutes(r *gin.Engine) {
331376
gatewayGroup.PUT("/:gatewayId", h.UpdateGateway)
332377
gatewayGroup.DELETE("/:gatewayId", h.DeleteGateway)
333378
gatewayGroup.POST("/:gatewayId/tokens", h.RotateToken)
379+
gatewayGroup.GET("/:gatewayId/artifacts", h.GetGatewayArtifacts)
334380
}
335381

336382
gatewayStatusGroup := r.Group("/api/v1/status")

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,37 @@ func (r *APIRepo) GetAPIsByOrganizationID(orgID string, projectID *string) ([]*m
266266
return apis, rows.Err()
267267
}
268268

269+
// GetAPIsByGatewayID retrieves all APIs deployed to a specific gateway
270+
func (r *APIRepo) GetAPIsByGatewayID(gatewayID, organizationID string) ([]*model.API, error) {
271+
query := `
272+
SELECT a.uuid, a.name, a.display_name, a.type, a.created_at, a.updated_at
273+
FROM apis a
274+
INNER JOIN api_deployments ad ON a.uuid = ad.api_uuid
275+
WHERE ad.gateway_uuid = ? AND a.organization_uuid = ?
276+
ORDER BY a.created_at DESC
277+
`
278+
279+
rows, err := r.db.Query(query, gatewayID, organizationID)
280+
if err != nil {
281+
return nil, err
282+
}
283+
defer rows.Close()
284+
285+
var apis []*model.API
286+
for rows.Next() {
287+
api := &model.API{}
288+
err := rows.Scan(
289+
&api.ID, &api.Name, &api.DisplayName, &api.Type, &api.CreatedAt, &api.UpdatedAt,
290+
)
291+
if err != nil {
292+
return nil, err
293+
}
294+
apis = append(apis, api)
295+
}
296+
297+
return apis, nil
298+
}
299+
269300
// UpdateAPI modifies an existing API
270301
func (r *APIRepo) UpdateAPI(api *model.API) error {
271302
tx, err := r.db.Begin()

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ type APIRepository interface {
4848
GetAPIByUUID(apiId string) (*model.API, error)
4949
GetAPIsByProjectID(projectID string) ([]*model.API, error)
5050
GetAPIsByOrganizationID(orgID string, projectID *string) ([]*model.API, error)
51+
GetAPIsByGatewayID(gatewayID, organizationID string) ([]*model.API, error)
5152
UpdateAPI(api *model.API) error
5253
DeleteAPI(apiId string) error
5354
CreateDeployment(deployment *model.APIDeployment) error

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ func StartPlatformAPIServer(cfg *config.Server) (*Server, error) {
8484
projectService := service.NewProjectService(projectRepo, orgRepo, apiRepo)
8585
gatewayEventsService := service.NewGatewayEventsService(wsManager)
8686
apiService := service.NewAPIService(apiRepo, projectRepo, gatewayRepo, gatewayEventsService)
87-
gatewayService := service.NewGatewayService(gatewayRepo, orgRepo)
87+
gatewayService := service.NewGatewayService(gatewayRepo, orgRepo, apiRepo)
8888
internalGatewayService := service.NewGatewayInternalAPIService(apiRepo, gatewayRepo, orgRepo)
8989

9090
// Initialize handlers

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

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,27 +26,31 @@ import (
2626
"errors"
2727
"fmt"
2828
"platform-api/src/internal/constants"
29+
"platform-api/src/internal/dto"
30+
"platform-api/src/internal/model"
31+
"platform-api/src/internal/repository"
32+
"platform-api/src/internal/utils"
2933
"regexp"
3034
"strings"
3135
"time"
3236

3337
"github.com/google/uuid"
34-
"platform-api/src/internal/dto"
35-
"platform-api/src/internal/model"
36-
"platform-api/src/internal/repository"
3738
)
3839

3940
// GatewayService handles gateway business logic
4041
type GatewayService struct {
4142
gatewayRepo repository.GatewayRepository
4243
orgRepo repository.OrganizationRepository
44+
apiRepo repository.APIRepository
4345
}
4446

4547
// NewGatewayService creates a new gateway service
46-
func NewGatewayService(gatewayRepo repository.GatewayRepository, orgRepo repository.OrganizationRepository) *GatewayService {
48+
func NewGatewayService(gatewayRepo repository.GatewayRepository, orgRepo repository.OrganizationRepository,
49+
apiRepo repository.APIRepository) *GatewayService {
4750
return &GatewayService{
4851
gatewayRepo: gatewayRepo,
4952
orgRepo: orgRepo,
53+
apiRepo: apiRepo,
5054
}
5155
}
5256

@@ -469,6 +473,69 @@ func (s *GatewayService) UpdateGatewayActiveStatus(gatewayId string, isActive bo
469473
return s.gatewayRepo.UpdateActiveStatus(gatewayId, isActive)
470474
}
471475

476+
// GetGatewayArtifacts retrieves all artifacts (APIs) deployed to a specific gateway with pagination and optional type filtering
477+
func (s *GatewayService) GetGatewayArtifacts(gatewayID, orgID, artifactType string) (*dto.GatewayArtifactListResponse, error) {
478+
// First validate that the gateway exists and belongs to the organization
479+
gateway, err := s.gatewayRepo.GetByUUID(gatewayID)
480+
if err != nil {
481+
return nil, err
482+
}
483+
if gateway == nil {
484+
return nil, constants.ErrGatewayNotFound
485+
}
486+
if gateway.OrganizationID != orgID {
487+
return nil, constants.ErrGatewayNotFound
488+
}
489+
490+
// Get all APIs deployed to this gateway
491+
apis, err := s.apiRepo.GetAPIsByGatewayID(gatewayID, orgID)
492+
if err != nil {
493+
return nil, err
494+
}
495+
496+
// Convert APIs to GatewayArtifact DTOs and apply type filtering
497+
allArtifacts := make([]dto.GatewayArtifact, 0)
498+
for _, api := range apis {
499+
// Skip if artifactType filter is specified and doesn't match "API"
500+
if artifactType != "" && artifactType != "API" {
501+
continue
502+
}
503+
504+
// Determine API subtype based on the type field using APIUtil
505+
apiUtil := &utils.APIUtil{}
506+
subType := apiUtil.GetAPISubType(api.Type)
507+
508+
artifact := dto.GatewayArtifact{
509+
ID: api.ID,
510+
Name: api.Name,
511+
DisplayName: api.DisplayName,
512+
Type: "API",
513+
SubType: subType,
514+
CreatedAt: api.CreatedAt,
515+
UpdatedAt: api.UpdatedAt,
516+
}
517+
allArtifacts = append(allArtifacts, artifact)
518+
}
519+
520+
// If filtering by MCP or API_PRODUCT, return empty list for now (future implementation)
521+
if artifactType != "" && (artifactType == constants.ArtifactTypeMCP || artifactType == constants.ArtifactTypeAPIProduct) {
522+
// For future implementation when MCP and API_PRODUCT are supported
523+
allArtifacts = []dto.GatewayArtifact{}
524+
}
525+
526+
listResponse := &dto.GatewayArtifactListResponse{
527+
Count: len(allArtifacts),
528+
List: allArtifacts,
529+
Pagination: dto.Pagination{
530+
Total: len(allArtifacts),
531+
Offset: 0,
532+
Limit: len(allArtifacts),
533+
},
534+
}
535+
536+
return listResponse, nil
537+
}
538+
472539
// validateGatewayInput validates gateway registration inputs
473540
func (s *GatewayService) validateGatewayInput(orgID, name, displayName, vhost, functionalityType string) error {
474541
// Organization ID validation

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ package utils
2020
import (
2121
"fmt"
2222
"gopkg.in/yaml.v3"
23+
"platform-api/src/internal/constants"
2324
"platform-api/src/internal/dto"
2425
"platform-api/src/internal/model"
2526
)
@@ -715,6 +716,24 @@ func (u *APIUtil) policyModelToDTO(model *model.Policy) *dto.Policy {
715716
}
716717
}
717718

719+
// GetAPISubType determines the API subtype based on the API type using constants
720+
func (u *APIUtil) GetAPISubType(apiType string) string {
721+
switch apiType {
722+
case constants.APITypeHTTP:
723+
return constants.APISubTypeHTTP
724+
case constants.APITypeGraphQL:
725+
return constants.APISubTypeGraphQL
726+
case constants.APITypeAsync, constants.APITypeWebSub, constants.APITypeSSE, constants.APITypeWebhook:
727+
return constants.APISubTypeAsync
728+
case constants.APITypeWS:
729+
return constants.APISubTypeWebSocket
730+
case constants.APITypeSOAP, constants.APITypeSOAPToREST:
731+
return constants.APISubTypeSOAP
732+
default:
733+
return constants.APISubTypeHTTP // Default to HTTP for unknown types
734+
}
735+
}
736+
718737
// GenerateAPIDeploymentYAML creates the deployment YAML from API data
719738
func (u *APIUtil) GenerateAPIDeploymentYAML(api *dto.API) (string, error) {
720739
operationList := make([]dto.OperationRequest, 0)

0 commit comments

Comments
 (0)