Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
9 changes: 9 additions & 0 deletions apps/customer-portal/backend/modules/entity/entity.bal
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,15 @@ public isolated function getProjectTimeCardStats(string idToken, string projectI
return csEntityClient->/projects/[projectId]/time\-cards/stats.get(generateHeaders(idToken));
}

# Get change request by ID.
#
# + idToken - ID token for authorization
# + changeRequestId - Unique ID of the change request to be retrieved
# + return - Change request response containing details of the retrieved change request or error
public isolated function getChangeRequestDetails(string idToken, string changeRequestId) returns ChangeRequestResponse|error {
return csEntityClient->/change\-requests/[changeRequestId].get(generateHeaders(idToken));
}

# Search change requests of a project.
#
# + idToken - ID token for authorization
Expand Down
36 changes: 33 additions & 3 deletions apps/customer-portal/backend/modules/entity/types.bal
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ public type Case record {|
# Choice list item information.
public type ChoiceListItem record {|
# Choice list item value
int id;
int|string id;
Comment thread
Rashmika998 marked this conversation as resolved.
Resolved
# Choice list item label
string label;
# Count
Expand Down Expand Up @@ -688,6 +688,8 @@ public type DeployedProductUpdatePayload record {|
int? cores?;
# TPS allocated for the product
decimal? tps?;
# Active status (can only be set to false to deactivate deployed product)
boolean active?;
|};

# Response from updating a deployed product.
Expand Down Expand Up @@ -1411,10 +1413,12 @@ public type ChangeRequest record {|
ReferenceTableItem? deployment;
# Deployed product information
ReferenceTableItem? deployedProduct;
# Product information
ReferenceTableItem? product;
# Planned start date and time
string? startDate;
Date? plannedStartOn;
# Planned end date and time
string? endDate;
Date? plannedEndOn;
# Duration
string? duration;
# Indicates if the change request has a service outage
Expand Down Expand Up @@ -1487,3 +1491,29 @@ public type CatalogItemVariablesResponse record {|
# List of catalog item variables
CatalogItemVariable[] variables;
|};

# Change request details information.
public type ChangeRequestResponse record {|
*ChangeRequest;
# Change request description
string? description;
# User who created the change request
string createdBy;
# Justification for the change request
string? justification;
# Impact description
string? impactDescription;
# Service outage details
string? serviceOutage;
# Communication plan
string? communicationPlan;
# Rollback plan
string? rollbackPlan;
# Test plan
string? testPlan;
# Indicates if the customer has approved
boolean hasCustomerApproved;
# Indicates if the customer has reviewed
boolean hasCustomerReviewed;
json...;
|};
21 changes: 21 additions & 0 deletions apps/customer-portal/backend/modules/entity/utils.bal
Original file line number Diff line number Diff line change
Expand Up @@ -225,3 +225,24 @@ public isolated function validateCaseCreatePayload(CaseCreatePayload payload) re
}
return;
}

# Validate deployed product update payload.
#
# + payload - Deployed product update payload
# + return - Error message if validation fails, () otherwise
public isolated function validateDeployedProductUpdatePayload(DeployedProductUpdatePayload payload) returns string? {
boolean? active = payload.active;
int? cores = payload?.cores;
decimal? tps = payload?.tps;
if active is boolean {
if active {
return "Invalid value for active field. When updating cores or tps, active field should be set to false.";
}
if cores !is () || tps !is () {
return "When deactivating, cores and tps fields should not be provided.";
}
} else if cores is () && tps is () {
return "At least one of cores or tps should be provided when updating deployed product details.";
}
return;
}
33 changes: 31 additions & 2 deletions apps/customer-portal/backend/modules/types/types.bal
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ public type CaseSearchFilters record {|
int severityId?;
# Deployment ID
string deploymentId?;
# Case created by the logged in user
boolean createdByMe?;
|};

# Payload for case search.
Expand Down Expand Up @@ -1052,10 +1054,12 @@ public type ChangeRequest record {|
ReferenceItem? deployment;
# Deployed product information
ReferenceItem? deployedProduct;
# Product information
ReferenceItem? product;
# Planned start date and time
string? startDate;
entity:Date? startDate;
# Planned end date and time
string? endDate;
entity:Date? endDate;
# Duration
string? duration;
# Indicates if the change request has a service outage
Expand Down Expand Up @@ -1122,3 +1126,28 @@ public type CatalogSearchPayload record {|
# Pagination details
entity:Pagination pagination?;
|};

# Change request details information.
public type ChangeRequestResponse record {|
*ChangeRequest;
# Change request description
string? description;
# User who created the change request
string createdBy;
# Justification for the change request
string? justification;
# Impact description
string? impactDescription;
# Service outage details
string? serviceOutage;
# Communication plan
string? communicationPlan;
# Rollback plan
string? rollbackPlan;
# Test plan
string? testPlan;
# Indicates if the customer has approved
boolean hasCustomerApproved;
# Indicates if the customer has reviewed
boolean hasCustomerReviewed;
|};
75 changes: 69 additions & 6 deletions apps/customer-portal/backend/service.bal
Original file line number Diff line number Diff line change
Expand Up @@ -718,10 +718,10 @@ service http:InterceptableService / on new http:Listener(9090, listenerConf) {
};
}

types:OverallConversationStats { openCount, resolvedCount, activeCount, abandonedCount} =
types:OverallConversationStats {openCount, resolvedCount, activeCount, abandonedCount} =
getConversationStats(conversationStats);

return { openCount, resolvedCount, activeCount, abandonedCount };
return {openCount, resolvedCount, activeCount, abandonedCount};
}

# Get project support statistics by ID.
Expand Down Expand Up @@ -1824,6 +1824,15 @@ service http:InterceptableService / on new http:Listener(9090, listenerConf) {
};
}

string? validateDeployedProductUpdatePayload = entity:validateDeployedProductUpdatePayload(payload);
if validateDeployedProductUpdatePayload is string {
return <http:BadRequest>{
body: {
message: validateDeployedProductUpdatePayload
}
};
}

entity:DeployedProductUpdateResponse|error response =
entity:updateDeployedProduct(userInfo.idToken, productId, payload);
if response is error {
Expand Down Expand Up @@ -1869,7 +1878,7 @@ service http:InterceptableService / on new http:Listener(9090, listenerConf) {
}

# Search catalogs for a specific deployed product with filters and pagination.
#
#
# + id - ID of the deployed product
# + payload - Catalog search request body
# + return - Paginated catalogs or error
Expand All @@ -1894,7 +1903,7 @@ service http:InterceptableService / on new http:Listener(9090, listenerConf) {
if searchResponse is error {
if getStatusCode(searchResponse) == http:STATUS_FORBIDDEN {
log:printWarn(string `User: ${
userInfo.userId} is forbidden to search catalogs for deployed product with ID: ${id}!`);
userInfo.userId} is forbidden to search catalogs for deployed product with ID: ${id}!`);
return <http:Forbidden>{
body: {
message: "You're not authorized to search catalogs for the deployed product. " +
Expand Down Expand Up @@ -1922,11 +1931,11 @@ service http:InterceptableService / on new http:Listener(9090, listenerConf) {
}

# Get catalog item variables by catalog ID and item ID.
#
#
# + catalogId - ID of the catalog
# + itemId - ID of the catalog item
# + return - Catalog item details or error
resource function get catalogs/[entity:IdString catalogId]/items/[entity:IdString itemId](http:RequestContext ctx)
resource function get catalogs/[entity:IdString catalogId]/items/[entity:IdString itemId](http:RequestContext ctx)
returns http:Ok|http:Unauthorized|http:Forbidden|http:InternalServerError {

authorization:UserInfoPayload|error userInfo = ctx.getWithType(authorization:HEADER_USER_INFO);
Expand Down Expand Up @@ -3074,4 +3083,58 @@ service http:InterceptableService / on new http:Listener(9090, listenerConf) {
body: mapChangeRequestSearchResponse(response)
};
}

# Get change request details by change request ID.
#
# + id - ID of the change request
# + return - Change request details or an error
resource function get change\-requests/[entity:IdString id](http:RequestContext ctx)
returns types:ChangeRequestResponse|http:Unauthorized|http:Forbidden|http:NotFound|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
}
};
}

entity:ChangeRequestResponse|error response = entity:getChangeRequestDetails(userInfo.idToken, id);
if response is error {
if getStatusCode(response) == http:STATUS_UNAUTHORIZED {
log:printWarn(string `User: ${
userInfo.userId} is not authorized to access change request information!`);
return <http:Unauthorized>{
body: {
message: ERR_MSG_UNAUTHORIZED_ACCESS
}
};
}
if getStatusCode(response) == http:STATUS_FORBIDDEN {
log:printWarn(string `Access to change request information is forbidden for user: ${userInfo.userId}`);
return <http:Forbidden>{
body: {
message: "Access to change request information is forbidden for the user!"
}
};
}
if getStatusCode(response) == http:STATUS_NOT_FOUND {
return <http:NotFound>{
body: {
message: "The requested change request is not found!"
}
};
}

string customError = "Failed to retrieve change request details.";
log:printError(customError, response);
return <http:InternalServerError>{
body: {
message: customError
}
};
}
return mapChangeRequestResponse(response);
}
}
58 changes: 55 additions & 3 deletions apps/customer-portal/backend/utils.bal
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ public isolated function searchCases(string idToken, string projectId, types:Cas
severityKey: payload.filters?.severityId,
caseTypes: payload.filters?.caseTypes,
stateKeys: payload.filters?.statusIds,
deploymentId: payload.filters?.deploymentId
deploymentId: payload.filters?.deploymentId,
createdByMe: payload.filters?.createdByMe
},
pagination: payload.pagination,
sortBy: payload.sortBy
Expand Down Expand Up @@ -653,15 +654,16 @@ public isolated function mapChangeRequestSearchResponse(entity:ChangeRequestSear
let entity:ReferenceTableItem? case = changeRequest.case
let entity:ReferenceTableItem? deployment = changeRequest.deployment
let entity:ReferenceTableItem? deployedProduct = changeRequest.deployedProduct
let entity:ReferenceTableItem? product = changeRequest.product
let entity:ChoiceListItem? state = changeRequest.state
let entity:ChoiceListItem? impact = changeRequest.impact
let entity:ChoiceListItem? 'type = changeRequest.'type
select {
id: changeRequest.id,
number: changeRequest.number,
title: changeRequest.title,
startDate: changeRequest.startDate,
endDate: changeRequest.endDate,
startDate: changeRequest.plannedStartOn,
endDate: changeRequest.plannedEndOn,
duration: changeRequest.duration,
hasServiceOutage: changeRequest.hasServiceOutage,
createdOn: changeRequest.createdOn,
Expand All @@ -671,6 +673,8 @@ public isolated function mapChangeRequestSearchResponse(entity:ChangeRequestSear
deployment: deployment != () ? {id: deployment.id, label: deployment.name, number: deployment?.number} : (),
deployedProduct: deployedProduct != () ?
{id: deployedProduct.id, label: deployedProduct.name, number: deployedProduct?.number} : (),
product: product != () ?
{id: product.id, label: product.name, number: product?.number} : (),
state: state != () ? {id: state.id.toString(), label: state.label} : (),
impact: impact != () ? {id: impact.id.toString(), label: impact.label} : (),
'type: 'type != () ? {id: 'type.id.toString(), label: 'type.label} : ()
Expand Down Expand Up @@ -702,3 +706,51 @@ public isolated function mapCatalogSearchResponse(entity:CatalogSearchResponse r

return {catalogs, totalRecords: response.totalRecords, 'limit: response.'limit, offset: response.offset};
}

# Map change request response to the desired structure.
#
# + response - Change request response from the entity service
# + return - Mapped change request response
public isolated function mapChangeRequestResponse(entity:ChangeRequestResponse response)
returns types:ChangeRequestResponse {

entity:ReferenceTableItem? project = response.project;
entity:ReferenceTableItem? case = response.case;
entity:ReferenceTableItem? deployment = response.deployment;
entity:ReferenceTableItem? deployedProduct = response.deployedProduct;
entity:ReferenceTableItem? product = response.product;
entity:ChoiceListItem? state = response.state;
entity:ChoiceListItem? impact = response.impact;
entity:ChoiceListItem? 'type = response.'type;
return {
id: response.id,
number: response.number,
title: response.title,
startDate: response.plannedStartOn,
endDate: response.plannedEndOn,
duration: response.duration,
hasServiceOutage: response.hasServiceOutage,
createdOn: response.createdOn,
updatedOn: response.updatedOn,
project: project != () ? {id: project.id, label: project.name, number: project?.number} : (),
case: case != () ? {id: case.id, label: case.name, number: case?.number} : (),
deployment: deployment != () ? {id: deployment.id, label: deployment.name, number: deployment?.number} : (),
deployedProduct: deployedProduct != () ?
{id: deployedProduct.id, label: deployedProduct.name, number: deployedProduct?.number} : (),
product: product != () ?
{id: product.id, label: product.name, number: product?.number} : (),
state: state != () ? {id: state.id.toString(), label: state.label} : (),
impact: impact != () ? {id: impact.id.toString(), label: impact.label} : (),
'type: 'type != () ? {id: 'type.id.toString(), label: 'type.label} : (),
description: response.description,
createdBy: response.createdBy,
justification: response.justification,
impactDescription: response.impactDescription,
serviceOutage: response.serviceOutage,
communicationPlan: response.communicationPlan,
rollbackPlan: response.rollbackPlan,
testPlan: response.testPlan,
hasCustomerApproved: response.hasCustomerApproved,
hasCustomerReviewed: response.hasCustomerReviewed
};
}