Skip to content
Merged
20 changes: 20 additions & 0 deletions apps/customer-portal/backend/modules/entity/entity.bal
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,23 @@ public isolated function searchAttachments(string idToken, ReferenceSearchPayloa

return csEntityClient->/attachments/search.post(payload, generateHeaders(idToken));
}

# Get products of a deployment.
#
# + idToken - ID token for authorization
# + deploymentId - ID of the deployment
# + return - Products response or error
public isolated function getDeployedProducts(string idToken, string deploymentId)
returns DeployedProductsResponse|error {

return csEntityClient->/deployed\-products/search.post({deploymentId}, generateHeaders(idToken));
}

# Get deployments of a project.
#
# + idToken - ID token for authorization
# + projectId - ID of the project
# + return - Deployments response or error
public isolated function getDeployments(string idToken, string projectId) returns DeploymentsResponse|error {
return csEntityClient->/deployments/search.post({filters: {projectIds: [projectId]}}, generateHeaders(idToken));
}
50 changes: 50 additions & 0 deletions apps/customer-portal/backend/modules/entity/types.bal
Original file line number Diff line number Diff line change
Expand Up @@ -441,3 +441,53 @@ public type AttachmentsResponse record {|
int totalRecords;
*Pagination;
|};

# Deployed product data.
public type DeployedProduct record {|
# ID
string id;
# Created date and time
string createdOn;
# Updated date and time
string updatedOn;
# Description
string? description;
# Associated deployment
ReferenceTableItem? deployment;
# Product information
ReferenceTableItem? product;
json...;
|};

# Deployed products response.
public type DeployedProductsResponse record {|
# List of deployed products
DeployedProduct[] deployedProducts;
|};

# Deployment data.
public type Deployment record {|
# ID
string id;
# Name
string name;
# Created date and time
string createdOn;
# Updated date and time
string updatedOn;
# Description
string? description;
# URL
string? url;
# Associated project
ReferenceTableItem? project;
# Type
ChoiceListItem? 'type;
json...;
|};

# Deployments response.
public type DeploymentsResponse record {|
# List of deployments
Deployment[] deployments;
|};
95 changes: 94 additions & 1 deletion apps/customer-portal/backend/service.bal
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,52 @@ service http:InterceptableService / on new http:Listener(9090) {
return projectResponse;
}

# Get deployments of a project by ID.
#
# + id - ID of the project
# + return - Deployments response or error response
resource function get projects/[string id]/deployments(http:RequestContext ctx)
returns Deployment[]|http:BadRequest|http:Forbidden|http:InternalServerError {

authorization:UserInfoPayload|error userInfo = ctx.getWithType(authorization:HEADER_USER_INFO);
if userInfo is error {
return <http:InternalServerError>{
body: {
message: ERR_MSG_USER_INFO_HEADER_NOT_FOUND
}
};
}

if isEmptyId(id) {
return <http:BadRequest>{
Comment thread
shayanmalinda marked this conversation as resolved.
body: {
message: ERR_MSG_PROJECT_ID_EMPTY
}
};
}

entity:DeploymentsResponse|error deploymentsResponse = entity:getDeployments(userInfo.idToken, id);
if deploymentsResponse is error {
if getStatusCode(deploymentsResponse) == http:STATUS_FORBIDDEN {
logForbiddenProjectAccess(id, userInfo.userId);
return <http:Forbidden>{
body: {
message: ERR_MSG_PROJECT_ACCESS_FORBIDDEN
}
};
}

string customError = "Failed to retrieve deployments for the project.";
log:printError(customError, deploymentsResponse);
return <http:InternalServerError>{
body: {
message: customError
}
};
}
return mapDeployments(deploymentsResponse);
}
Comment thread
Rashmika998 marked this conversation as resolved.

# Get overall project statistics by ID.
#
# + id - ID of the project
Expand Down Expand Up @@ -827,7 +873,7 @@ service http:InterceptableService / on new http:Listener(9090) {
}
};
}

if getStatusCode(caseDetails) == http:STATUS_FORBIDDEN {
logForbiddenCaseAccess(id, userInfo.userId);
return <http:Forbidden>{
Expand Down Expand Up @@ -927,4 +973,51 @@ service http:InterceptableService / on new http:Listener(9090) {
}
return mapAttachmentsResponse(attachmentResponse);
}

# Get products of a deployment by deployment ID.
#
# + id - ID of the deployment
# + return - Deployed products response or error response
resource function get deployments/[string id]/products(http:RequestContext ctx)
returns DeployedProduct[]|http:BadRequest|http:Forbidden|http:InternalServerError {

authorization:UserInfoPayload|error userInfo = ctx.getWithType(authorization:HEADER_USER_INFO);
if userInfo is error {
return <http:InternalServerError>{
body: {
message: ERR_MSG_USER_INFO_HEADER_NOT_FOUND
}
};
}

if isEmptyId(id) {
return <http:BadRequest>{
body: {
message: "Deployment ID cannot be empty!"
}
};
}

entity:DeployedProductsResponse|error productsResponse =
entity:getDeployedProducts(userInfo.idToken, id);
if productsResponse is error {
if getStatusCode(productsResponse) == http:STATUS_FORBIDDEN {
log:printWarn(string `Access to deployment ID: ${id} is forbidden for user: ${userInfo.userId}`);
return <http:Forbidden>{
body: {
message: "Access to the requested deployment is forbidden!"
}
};
}

string customError = "Failed to retrieve products for the deployment.";
log:printError(customError, productsResponse);
return <http:InternalServerError>{
body: {
message: customError
}
};
}
return mapDeployedProducts(productsResponse);
}
}
38 changes: 37 additions & 1 deletion apps/customer-portal/backend/types.bal
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ public type RecentActivity record {|
# Last deployment date
string? lastDeploymentOn;
# System health status
string systemHealth;
string systemHealth;
|};

# Project statistics response.
Expand Down Expand Up @@ -247,3 +247,39 @@ public type AttachmentsResponse record {|
int totalRecords;
*entity:Pagination;
|};

# Deployment information.
public type Deployment record {|
# ID
string id;
# Name
string name;
# Created date and time
string createdOn;
# Updated date and time
string updatedOn;
# Description
string? description;
# URL
string? url;
# Associated project
ReferenceItem? project;
# Type
ReferenceItem? 'type;
|};

# Deployed product data.
public type DeployedProduct record {|
# ID
string id;
# Created date and time
string createdOn;
# Updated date and time
string updatedOn;
# Description
string? description;
# Associated product
ReferenceItem? product;
# Deployment
ReferenceItem? deployment;
|};
42 changes: 41 additions & 1 deletion apps/customer-portal/backend/utils.bal
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ public function isInvalidLimitOffset(int? 'limit, int? offset) returns boolean =
('limit != () && ('limit < 1 || 'limit > 50)) || (offset != () && offset < 0);

# Map attachments response to map to desired structure.
#
#
# + response - Attachments response from the entity service
# + return - Mapped attachments response
public isolated function mapAttachmentsResponse(entity:AttachmentsResponse response) returns AttachmentsResponse {
Expand All @@ -184,3 +184,43 @@ public isolated function mapAttachmentsResponse(entity:AttachmentsResponse respo
offset: response.offset
};
}

# Map deployments response to the desired structure.
#
# + response - Deployments response from the entity service
# + return - Mapped deployments response
public isolated function mapDeployments(entity:DeploymentsResponse response) returns Deployment[] {
return from entity:Deployment deployment in response.deployments
let entity:ReferenceTableItem? project = deployment.project
let entity:ChoiceListItem? 'type = deployment.'type
select {
id: deployment.id,
name: deployment.name,
createdOn: deployment.createdOn,
updatedOn: deployment.updatedOn,
description: deployment.description,
url: deployment.url,
project: project != () ? {id: project.id, label: project.name} : (),
'type: 'type != () ? {id: 'type.id.toString(), label: 'type.label} : ()
};
}

# Map deployed products response to the desired structure.
#
# + response - Deployed products response from the entity service
# + return - Mapped deployed products response
public isolated function mapDeployedProducts(entity:DeployedProductsResponse response)
returns DeployedProduct[] {

return from entity:DeployedProduct product in response.deployedProducts
let entity:ReferenceTableItem? associatedProduct = product.product
let entity:ReferenceTableItem? deployment = product.deployment
select {
id: product.id,
createdOn: product.createdOn,
updatedOn: product.updatedOn,
description: product.description,
product: associatedProduct != () ? {id: associatedProduct.id, label: associatedProduct.name} : (),
deployment: deployment != () ? {id: deployment.id, label: deployment.name} : ()
};
}
Comment thread
Rashmika998 marked this conversation as resolved.