Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions platform-api/api/generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

54 changes: 27 additions & 27 deletions platform-api/internal/handler/subscription_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func NewSubscriptionHandler(subscriptionService *service.SubscriptionService, su

// CreateSubscriptionRequest is the body for POST /api/v0.9/subscriptions
type CreateSubscriptionRequest struct {
APIID string `json:"apiId" binding:"required"`
ArtifactID string `json:"artifactId" binding:"required"`
Kind string `json:"kind" binding:"required"`
SubscriberID string `json:"subscriberId" binding:"required"`
ApplicationID *string `json:"applicationId,omitempty"`
Expand All @@ -78,8 +78,8 @@ func (h *SubscriptionHandler) CreateSubscription(w http.ResponseWriter, r *http.
return apperror.ValidationFailed.Wrap(err, "Invalid request body").
WithLogMessage(fmt.Sprintf("invalid create subscription request body for org %s", orgId))
}
if req.APIID == "" {
return apperror.ValidationFailed.New("API ID is required")
if req.ArtifactID == "" {
return apperror.ValidationFailed.New("artifactId is required")
}
if req.SubscriberID == "" {
return apperror.ValidationFailed.New("subscriberId is required")
Expand All @@ -99,17 +99,17 @@ func (h *SubscriptionHandler) CreateSubscription(w http.ResponseWriter, r *http.
if err != nil {
return err
}
sub, err := h.subscriptionService.CreateSubscription(req.APIID, req.Kind, orgId, req.SubscriberID, req.ApplicationID, req.SubscriptionPlanID, "", req.Status, actor)
sub, err := h.subscriptionService.CreateSubscription(req.ArtifactID, req.Kind, orgId, req.SubscriberID, req.ApplicationID, req.SubscriptionPlanID, "", req.Status, actor)
if err != nil {
var appErr *apperror.Error
if errors.As(err, &appErr) {
return err
}
return serviceError(err, fmt.Sprintf("failed to create subscription for api %s in org %s", req.APIID, orgId))
return serviceError(err, fmt.Sprintf("failed to create subscription for artifact %s in org %s", req.ArtifactID, orgId))
}
resp, err := h.toSubscriptionResponse(sub, orgId)
if err != nil {
return serviceError(err, fmt.Sprintf("failed to resolve subscription identity for api %s in org %s", req.APIID, orgId))
return serviceError(err, fmt.Sprintf("failed to resolve subscription identity for artifact %s in org %s", req.ArtifactID, orgId))
}
setLocation(w, "subscriptions", sub.UUID)
httputil.WriteJSON(w, http.StatusCreated, resp)
Expand All @@ -118,7 +118,7 @@ func (h *SubscriptionHandler) CreateSubscription(w http.ResponseWriter, r *http.

// ListSubscriptions handles GET /api/v0.9/subscriptions
func (h *SubscriptionHandler) ListSubscriptions(w http.ResponseWriter, r *http.Request) error {
apiId := r.URL.Query().Get("apiId")
artifactId := r.URL.Query().Get("artifactId")
subscriberID := r.URL.Query().Get("subscriberId")
applicationID := r.URL.Query().Get("applicationId")
status := r.URL.Query().Get("status")
Expand All @@ -129,9 +129,9 @@ func (h *SubscriptionHandler) ListSubscriptions(w http.ResponseWriter, r *http.R
WithLogMessage("organization claim not found in token")
}

var apiIDPtr, subscriberIDPtr, appIDPtr, statusPtr *string
if apiId != "" {
apiIDPtr = &apiId
var artifactIDPtr, subscriberIDPtr, appIDPtr, statusPtr *string
if artifactId != "" {
artifactIDPtr = &artifactId
}
if subscriberID != "" {
subscriberIDPtr = &subscriberID
Expand All @@ -148,34 +148,34 @@ func (h *SubscriptionHandler) ListSubscriptions(w http.ResponseWriter, r *http.R
statusPtr = &status
}
limit, offset := parsePagination(r)
list, total, err := h.subscriptionService.ListSubscriptionsByFilters(orgId, apiIDPtr, subscriberIDPtr, appIDPtr, statusPtr, limit, offset)
list, total, err := h.subscriptionService.ListSubscriptionsByFilters(orgId, artifactIDPtr, subscriberIDPtr, appIDPtr, statusPtr, limit, offset)
if err != nil {
var appErr *apperror.Error
if errors.As(err, &appErr) {
return err
}
return serviceError(err, fmt.Sprintf("failed to list subscriptions for api %s in org %s", apiId, orgId))
return serviceError(err, fmt.Sprintf("failed to list subscriptions for artifact %s in org %s", artifactId, orgId))
}
// Bulk fetch API handles and plan names to avoid N+1 queries
apiUUIDSet := make(map[string]struct{})
// Bulk fetch artifact handles and plan names to avoid N+1 queries
artifactUUIDSet := make(map[string]struct{})
planIDSet := make(map[string]struct{})
for _, sub := range list {
if sub.ArtifactUUID != "" {
apiUUIDSet[sub.ArtifactUUID] = struct{}{}
artifactUUIDSet[sub.ArtifactUUID] = struct{}{}
}
if sub.SubscriptionPlanID != nil && *sub.SubscriptionPlanID != "" {
planIDSet[*sub.SubscriptionPlanID] = struct{}{}
}
}
apiUUIDs := make([]string, 0, len(apiUUIDSet))
for u := range apiUUIDSet {
apiUUIDs = append(apiUUIDs, u)
artifactUUIDs := make([]string, 0, len(artifactUUIDSet))
for u := range artifactUUIDSet {
artifactUUIDs = append(artifactUUIDs, u)
}
planIDs := make([]string, 0, len(planIDSet))
for id := range planIDSet {
planIDs = append(planIDs, id)
}
artifactMetaMap, err := h.subscriptionService.GetArtifactMetadataMap(apiUUIDs, orgId)
artifactMetaMap, err := h.subscriptionService.GetArtifactMetadataMap(artifactUUIDs, orgId)
if err != nil {
return serviceError(err, fmt.Sprintf("failed to bulk fetch artifact metadata for list in org %s", orgId))
}
Expand Down Expand Up @@ -326,10 +326,10 @@ func (h *SubscriptionHandler) RegisterRoutes(mux *http.ServeMux) {
}

func (h *SubscriptionHandler) toSubscriptionResponse(sub *model.Subscription, orgId string) (map[string]any, error) {
// apiId in response should be the handle (e.g. "samp1"), not the internal UUID
apiIdForResponse, kind := h.subscriptionService.ResolveArtifactHandleAndKind(sub.ArtifactUUID, orgId)
if apiIdForResponse == "" {
apiIdForResponse = sub.ArtifactUUID // fallback to UUID
// artifactId in response should be the handle (e.g. "samp1"), not the internal UUID
artifactIdForResponse, kind := h.subscriptionService.ResolveArtifactHandleAndKind(sub.ArtifactUUID, orgId)
if artifactIdForResponse == "" {
artifactIdForResponse = sub.ArtifactUUID // fallback to UUID
}
createdBy, err := h.identity.SubForUUID(sub.CreatedBy)
if err != nil {
Expand All @@ -341,7 +341,7 @@ func (h *SubscriptionHandler) toSubscriptionResponse(sub *model.Subscription, or
}
resp := map[string]any{
"id": sub.UUID,
"apiId": apiIdForResponse,
"artifactId": artifactIdForResponse,
"subscriberId": sub.SubscriberID,
"organizationId": h.subscriptionService.ResolveOrgHandle(sub.OrganizationUUID),
"status": string(sub.Status),
Expand Down Expand Up @@ -375,11 +375,11 @@ func (h *SubscriptionHandler) toSubscriptionResponse(sub *model.Subscription, or
// toSubscriptionResponseWithMaps builds a subscription response using pre-fetched lookup maps.
// Used by ListSubscriptions to avoid N+1 queries.
func (h *SubscriptionHandler) toSubscriptionResponseWithMaps(sub *model.Subscription, orgId string, artifactMetaMap map[string]*model.APIMetadata, planNameMap map[string]string, createdByMap map[string]string) map[string]any {
apiIdForResponse := sub.ArtifactUUID // fallback to UUID
artifactIdForResponse := sub.ArtifactUUID // fallback to UUID
var kind string
if meta := artifactMetaMap[sub.ArtifactUUID]; meta != nil {
if meta.Handle != "" {
apiIdForResponse = meta.Handle
artifactIdForResponse = meta.Handle
}
kind = meta.Kind
}
Expand All @@ -389,7 +389,7 @@ func (h *SubscriptionHandler) toSubscriptionResponseWithMaps(sub *model.Subscrip
}
resp := map[string]any{
"id": sub.UUID,
"apiId": apiIdForResponse,
"artifactId": artifactIdForResponse,
"subscriberId": sub.SubscriberID,
"organizationId": h.subscriptionService.ResolveOrgHandle(sub.OrganizationUUID),
"status": string(sub.Status),
Expand Down
20 changes: 10 additions & 10 deletions platform-api/resources/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4160,8 +4160,8 @@ paths:
post:
summary: Create subscription
description: |
Creates a subscription for the specified API.
`subscriberId` identifies the unique subscriber for this API, allowing multiple subscribers to create subscriptions for the same API.
Creates a subscription for the specified artifact.
`subscriberId` identifies the unique subscriber for this artifact, allowing multiple subscribers to create subscriptions for the same artifact.
operationId: CreateSubscription
security:
- OAuth2Security:
Expand Down Expand Up @@ -4200,8 +4200,8 @@ paths:
get:
summary: List subscriptions
description: |
Returns subscriptions filtered by API and/or application.
Optional query parameters apiId, subscriberId, applicationId and status filter the list.
Returns subscriptions filtered by artifact and/or application.
Optional query parameters artifactId, subscriberId, applicationId and status filter the list.
Supports pagination via limit and offset.
operationId: ListSubscriptions
security:
Expand All @@ -4211,10 +4211,10 @@ paths:
tags:
- Subscriptions
parameters:
- name: apiId
- name: artifactId
in: query
required: false
description: Filter by API ID (UUID or handle)
description: Filter by artifact ID (UUID or handle)
schema:
type: string
- name: subscriberId
Expand Down Expand Up @@ -6398,18 +6398,18 @@ components:
CreateSubscriptionRequest:
type: object
required:
- apiId
- artifactId
- kind
- subscriberId
properties:
apiId:
artifactId:
type: string
description: Handle (ID) of the artifact to subscribe to. Resolved against the table for the given kind.
example: "my-rest-api"
kind:
type: string
enum: [RestApi, LlmProvider, LlmProxy, Mcp]
description: Type of the artifact identified by apiId. Determines which artifact table apiId is resolved against.
description: Type of the artifact identified by artifactId. Determines which artifact table artifactId is resolved against.
example: "RestApi"
subscriberId:
type: string
Expand Down Expand Up @@ -6437,7 +6437,7 @@ components:
type: string
format: uuid
description: Subscription ID
apiId:
artifactId:
type: string
description: Handle (ID) of the subscribed artifact
example: "my-rest-api"
Expand Down
2 changes: 1 addition & 1 deletion tests/integration-e2e/steps_devportal_lifecycle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ func (w *world) verifySubPlan() error {
deadline := time.Now().Add(pollTimeout)
var last string
for time.Now().Before(deadline) {
st, body, err := apiCall(http.MethodGet, "/subscriptions?apiId="+w.apiID, suite.token, nil)
st, body, err := apiCall(http.MethodGet, "/subscriptions?artifactId="+w.apiID, suite.token, nil)
if err == nil && st == http.StatusOK {
var env struct {
List []map[string]any `json:"list"`
Expand Down
2 changes: 1 addition & 1 deletion tests/integration-e2e/steps_secured_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ func createApplication(name, projectID string) (string, error) {

func createSubscription(apiID, appID, planID string) (string, error) {
st, body, err := apiCall(http.MethodPost, "/subscriptions", suite.token, map[string]any{
"apiId": apiID,
"artifactId": apiID,
"kind": "RestApi",
"subscriberId": "e2e-subscriber",
"applicationId": appID,
Expand Down
Loading