Skip to content

Commit 4c22546

Browse files
authored
Merge 690aadf into 2bca5ad
2 parents 2bca5ad + 690aadf commit 4c22546

11 files changed

Lines changed: 438 additions & 66 deletions

File tree

openapi/openapi-component_catalog-v1.0.0.yaml

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,56 @@ tags:
3535
- name: ProvisionerActions
3636
description: Provisioning notifications from AWX/Provisioner
3737
paths:
38+
/project/components:
39+
get:
40+
operationId: getAllProjectComponents
41+
parameters:
42+
- name: page
43+
in: query
44+
description: Page number (from 0 until limit)
45+
required: false
46+
schema:
47+
type: integer
48+
default: 0
49+
50+
- name: size
51+
in: query
52+
description: Page size
53+
required: false
54+
schema:
55+
type: integer
56+
default: 20
57+
responses:
58+
"200":
59+
content:
60+
application/json:
61+
schema:
62+
$ref: "#/components/schemas/ProjectComponentListResponse"
63+
description: A paginated list of all the provisioned project components
64+
"401":
65+
content:
66+
application/json:
67+
schema:
68+
$ref: "#/components/schemas/RestErrorMessage"
69+
description: Invalid client token on the request.
70+
"403":
71+
content:
72+
application/json:
73+
schema:
74+
$ref: "#/components/schemas/RestErrorMessage"
75+
description: Insufficient permissions for the client to access the resource.
76+
"500":
77+
content:
78+
application/json:
79+
schema:
80+
$ref: "#/components/schemas/RestErrorMessage"
81+
description: Server error.
82+
summary: Returns the information of the project's components in the Bitbucket
83+
repository.
84+
tags:
85+
- Project-components
86+
x-accepts:
87+
- application/json
3888
/project/{projectKey}/components:
3989
get:
4090
tags:
@@ -1350,3 +1400,111 @@ components:
13501400
example:
13511401
- "production"
13521402
- "staging"
1403+
1404+
ProjectComponentListItem:
1405+
type: object
1406+
example:
1407+
projectKey: SOMEPROJECT
1408+
componentId: any-component-id-from-backend
1409+
caller: some-person@email.com
1410+
catalogItemSlug: some_technology-name
1411+
createdAt: 1707043200000
1412+
updatedAt: 1707043200000
1413+
properties:
1414+
projectKey:
1415+
description: The projectKey which the component is provisioned for.
1416+
example: SOMEPROJECT
1417+
type: string
1418+
componentId:
1419+
description: The componentId set by the user.
1420+
example: any-component-id-from-backend
1421+
minLength: 1
1422+
pattern: ^(?!\s*$).+
1423+
type: string
1424+
caller:
1425+
description: The email of who provisioned the component.
1426+
example: some-person@email.com
1427+
type: string
1428+
catalogItemSlug:
1429+
description: The provisioned catalog item slug.
1430+
example: some_technology-name
1431+
type: string
1432+
createdAt:
1433+
description: The timestamp of the provision action.
1434+
example: 1707043200000
1435+
type: number
1436+
updatedAt:
1437+
description: The timestamp of the last change of the provisioned component.
1438+
example: 1707043200000
1439+
type: number
1440+
1441+
Pagination:
1442+
type: object
1443+
example:
1444+
page: 0
1445+
size: 20
1446+
totalElements: 117
1447+
totalPages: 6
1448+
next: "https://api.example.com/resources?page=1&size=20"
1449+
previous: null
1450+
properties:
1451+
page:
1452+
type: integer
1453+
example: 0
1454+
description: Current page of the response.
1455+
size:
1456+
type: integer
1457+
example: 20
1458+
description: Current page size of the response.
1459+
totalElements:
1460+
type: integer
1461+
format: integer
1462+
example: 117
1463+
description: Total number of elements.
1464+
totalPages:
1465+
type: integer
1466+
example: 6
1467+
description: Total number of pages of this exact size.
1468+
next:
1469+
type: string
1470+
format: uri
1471+
nullable: true
1472+
example: "https://api.example.com/resources?page=1&size=20"
1473+
description: URL of the next page (or null if the current is the last one)
1474+
previous:
1475+
type: string
1476+
format: uri
1477+
nullable: true
1478+
example: "https://api.example.com/resources?page=0&size=20"
1479+
description: URL of the previous page (or null if the current is the first one)
1480+
1481+
ProjectComponentListResponse:
1482+
type: object
1483+
example:
1484+
data:
1485+
- projectKey: SOMEPROJECT
1486+
componentId: any-component-id-from-backend
1487+
caller: some-person@email.com
1488+
catalogItemSlug: some_technology-name
1489+
createdAt: 1707043200000
1490+
updatedAt: 1707043200000
1491+
- projectKey: ANOTHERPROJECT
1492+
componentId: another-component-id-from-backend
1493+
caller: some-person2@email.com
1494+
catalogItemSlug: another_technology-name
1495+
createdAt: 1707043200001
1496+
updatedAt: 1707043200002
1497+
pagination:
1498+
page: 0
1499+
size: 20
1500+
totalElements: 117
1501+
totalPages: 6
1502+
next: https://api.example.com/resources?page=1&size=20
1503+
previous: null
1504+
properties:
1505+
data:
1506+
type: array
1507+
items:
1508+
$ref: '#/components/schemas/ProjectComponentListItem'
1509+
pagination:
1510+
$ref: '#/components/schemas/Pagination'

openapi/openapi-component_provisioner-v1.0.0.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -667,7 +667,7 @@ components:
667667
example: 1707043200000
668668
type: number
669669
updatedAt:
670-
description: The timestamp of the last change of the catalog item.
670+
description: The timestamp of the last change of the provisioned component.
671671
example: 1707043200000
672672
type: number
673673
Pagination:

src/main/java/org/opendevstack/component_provisioner/config/ApplicationPropertiesConfiguration.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ public static class ComponentCatalogServiceProps {
118118
@Data
119119
public static class OdsApiServerServiceProps {
120120
private URL baseRestUrl;
121+
private String oid;
121122
}
122123

123124
@Builder // useful for unit testing
Lines changed: 2 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,14 @@
11
package org.opendevstack.component_provisioner.server.controllers;
22

33
import lombok.AllArgsConstructor;
4-
import lombok.Generated;
54
import lombok.extern.slf4j.Slf4j;
65
import org.opendevstack.component_provisioner.server.api.ProjectComponentsWithProvisionStatusApi;
76
import org.opendevstack.component_provisioner.server.facade.ProjectComponentsApiFacade;
8-
import org.opendevstack.component_provisioner.server.model.Pagination;
9-
import org.opendevstack.component_provisioner.server.model.ProjectComponentListItem;
107
import org.opendevstack.component_provisioner.server.model.ProjectComponentListResponse;
118
import org.opendevstack.component_provisioner.server.model.ProjectComponentProvisionStatus;
129
import org.springframework.http.ResponseEntity;
1310
import org.springframework.stereotype.Controller;
1411
import org.springframework.web.bind.annotation.RequestMapping;
15-
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
16-
17-
import java.math.BigDecimal;
18-
import java.net.URI;
19-
import java.util.ArrayList;
20-
import java.util.List;
21-
import java.util.Optional;
2212

2313
@Controller
2414
@RequestMapping("${openapi.componentProvisionerREST.base-path:/v1}")
@@ -41,65 +31,12 @@ public ResponseEntity<ProjectComponentProvisionStatus> getProjectComponentProvis
4131
return ResponseEntity.ok(projectComponentExtendedInfo);
4232
}
4333

44-
// MOCKED RESULT
45-
@Generated
4634
@Override
4735
public ResponseEntity<ProjectComponentListResponse> getAllProjectComponents(Integer page, Integer size) {
4836
log.debug("getAllProjectComponents with page {} and page size of {}", page, size);
4937

50-
int currentPage = Optional.ofNullable(page).orElse(0);
51-
int pageSize = Optional.ofNullable(size).orElse(20);
52-
53-
int totalElements = 40;
54-
55-
List<ProjectComponentListItem> allItems = new ArrayList<>();
56-
57-
for (int i = 0; i < totalElements; i++) {
58-
allItems.add(ProjectComponentListItem.builder()
59-
.projectKey("PROJECT_" + i)
60-
.componentId("component-" + i)
61-
.caller("user" + i + "@email.com")
62-
.catalogItemSlug("tech-" + i)
63-
.createdAt(BigDecimal.valueOf(1707043200000L + i))
64-
.updatedAt(BigDecimal.valueOf(1707043200000L + i))
65-
.build());
66-
}
67-
68-
int fromIndex = Math.min(currentPage * pageSize, totalElements);
69-
int toIndex = Math.min(fromIndex + pageSize, totalElements);
70-
71-
List<ProjectComponentListItem> pageItems = allItems.subList(fromIndex, toIndex);
72-
73-
int totalPages = (int) Math.ceil((double) totalElements / pageSize);
74-
75-
String next = currentPage < totalPages - 1
76-
? ServletUriComponentsBuilder.fromCurrentRequest()
77-
.replaceQueryParam("page", currentPage + 1)
78-
.replaceQueryParam("size", pageSize)
79-
.toUriString()
80-
: null;
81-
82-
String previous = currentPage > 0
83-
? ServletUriComponentsBuilder.fromCurrentRequest()
84-
.replaceQueryParam("page", currentPage - 1)
85-
.replaceQueryParam("size", pageSize)
86-
.toUriString()
87-
: null;
88-
89-
Pagination pagination = Pagination.builder()
90-
.page(currentPage)
91-
.size(pageSize)
92-
.totalElements(totalElements)
93-
.totalPages(totalPages)
94-
.next(next != null ? URI.create(next) : null)
95-
.previous(previous != null ? URI.create(previous) : null)
96-
.build();
97-
98-
ProjectComponentListResponse response = ProjectComponentListResponse.builder()
99-
.data(pageItems)
100-
.pagination(pagination)
101-
.build();
38+
var paginatedProjectComponents = projectComponentsApiFacade.getPaginatedProjectComponents(page, size);
10239

103-
return ResponseEntity.ok(response);
40+
return ResponseEntity.ok(paginatedProjectComponents);
10441
}
10542
}

src/main/java/org/opendevstack/component_provisioner/server/facade/ProjectComponentsApiFacade.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,18 @@
44
import lombok.extern.slf4j.Slf4j;
55
import org.opendevstack.component_provisioner.client.awx.v2.model.JobDetail;
66
import org.opendevstack.component_provisioner.client.component_catalog.v1.model.ProjectComponentExtendedInfo;
7+
import org.opendevstack.component_provisioner.config.ApplicationPropertiesConfiguration.OdsApiServerServiceProps;
78
import org.opendevstack.component_provisioner.server.controllers.exceptions.InvalidRestEntityException;
9+
import org.opendevstack.component_provisioner.server.controllers.exceptions.UserNotAllowedException;
810
import org.opendevstack.component_provisioner.server.mappers.EntitiesMapper;
11+
import org.opendevstack.component_provisioner.server.mappers.ProjectComponentListResponseMapper;
12+
import org.opendevstack.component_provisioner.server.model.ProjectComponentListResponse;
913
import org.opendevstack.component_provisioner.server.model.ProjectComponentProvisionStatus;
14+
import org.opendevstack.component_provisioner.server.services.ApplicationAuthenticationProvider;
1015
import org.opendevstack.component_provisioner.server.services.AuthenticationProvider;
1116
import org.opendevstack.component_provisioner.server.services.AwxService;
1217
import org.opendevstack.component_provisioner.server.services.ComponentCatalogService;
18+
import org.opendevstack.component_provisioner.util.JwtUtils;
1319
import org.springframework.stereotype.Service;
1420

1521
@Service
@@ -21,6 +27,9 @@ public class ProjectComponentsApiFacade {
2127
private final ComponentCatalogService componentCatalogService;
2228
private final AwxService awxService;
2329
private final EntitiesMapper entitiesMapper;
30+
private final OdsApiServerServiceProps odsApiServerServiceProps;
31+
private final ApplicationAuthenticationProvider applicationAuthenticationProvider;
32+
private final ProjectComponentListResponseMapper projectComponentListResponseMapper;
2433

2534
public ProjectComponentExtendedInfo getProjectComponentById(String projectKey, String componentId) {
2635
var accessToken = authenticationProvider.getAccessToken();
@@ -48,4 +57,21 @@ public ProjectComponentProvisionStatus enrichWithAapInfo(String projectKey, Proj
4857

4958
return provisionStatus;
5059
}
60+
61+
public ProjectComponentListResponse getPaginatedProjectComponents(Integer page, Integer size) {
62+
String accessToken = authenticationProvider.getAccessToken();
63+
if (!validateTokenBelongsToOdsApiService(accessToken)) {
64+
throw new UserNotAllowedException("Invalid caller. Please, provide a valid token within the request.");
65+
}
66+
67+
String marketplaceAccessToken = applicationAuthenticationProvider.getAccessToken();
68+
69+
var response = componentCatalogService.getPaginatedProjectComponents(marketplaceAccessToken, page, size);
70+
return projectComponentListResponseMapper.map(response);
71+
}
72+
73+
private boolean validateTokenBelongsToOdsApiService(String accessToken) {
74+
var oid = JwtUtils.extractClaim(accessToken, "oid");
75+
return oid.map(odsApiServerServiceProps.getOid()::equals).orElse(false);
76+
}
5177
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package org.opendevstack.component_provisioner.server.mappers;
2+
3+
import org.mapstruct.Mapper;
4+
import org.openapitools.jackson.nullable.JsonNullable;
5+
import org.opendevstack.component_provisioner.server.model.Pagination;
6+
import org.opendevstack.component_provisioner.server.model.ProjectComponentListItem;
7+
import org.opendevstack.component_provisioner.server.model.ProjectComponentListResponse;
8+
9+
import java.net.URI;
10+
import java.util.List;
11+
12+
@Mapper(componentModel = "spring")
13+
public interface ProjectComponentListResponseMapper {
14+
15+
ProjectComponentListResponse map(
16+
org.opendevstack.component_provisioner.client.component_catalog.v1.model.ProjectComponentListResponse source
17+
);
18+
19+
List<ProjectComponentListItem> mapItems(
20+
List<org.opendevstack.component_provisioner.client.component_catalog.v1.model.ProjectComponentListItem> source
21+
);
22+
23+
ProjectComponentListItem mapItem(
24+
org.opendevstack.component_provisioner.client.component_catalog.v1.model.ProjectComponentListItem source
25+
);
26+
27+
Pagination mapPagination(
28+
org.opendevstack.component_provisioner.client.component_catalog.v1.model.Pagination source
29+
);
30+
31+
default JsonNullable<URI> map(URI value) {
32+
return JsonNullable.of(value);
33+
}
34+
}

src/main/java/org/opendevstack/component_provisioner/server/services/ComponentCatalogService.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,4 +227,10 @@ private Pair<HttpStatusCode, Optional<CatalogItemUserActionMessageDefinition>> g
227227
throw new CatalogClientException(e);
228228
}
229229
}
230+
231+
public ProjectComponentListResponse getPaginatedProjectComponents(String accessToken, Integer page, Integer size) {
232+
var apiClient = apiClientsBuilder.componentCatalogApiClient(accessToken, componentCatalogServiceProps.getBaseRestUrl().toString());
233+
var componentsApi = apiClientsBuilder.projectComponentsApi(apiClient);
234+
return componentsApi.getAllProjectComponents(page, size);
235+
}
230236
}

src/main/resources/application.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ component-provisioner:
8282
enabled: ${ODS_API_SERVICE_ENABLED}
8383
service:
8484
base-rest-url: ${ODS_API_SERVICE_SERVICE_BASE_REST_URL}
85+
oid: ${ODS_API_SERVICE_OID}
8586
params:
8687
client_id: ${DEVSTACK_MARKETPLACE_API_CLIENT_ID}
8788
client_secret: ${DEVSTACK_MARKETPLACE_API_CLIENT_SECRET}

0 commit comments

Comments
 (0)