Skip to content

Commit b4e1a9a

Browse files
feat: adapt DEPLOY_MODEL_COMMAND to allow v4 API update
1 parent a72da1f commit b4e1a9a

File tree

8 files changed

+723
-89
lines changed

8 files changed

+723
-89
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright © 2015 The Gravitee team (http://gravitee.io)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.gravitee.apim.core.api.use_case.cockpit;
17+
18+
import io.gravitee.apim.core.api.domain_service.ApiStateDomainService;
19+
import io.gravitee.apim.core.api.domain_service.UpdateApiDomainService;
20+
import io.gravitee.apim.core.api.model.Api;
21+
import io.gravitee.apim.core.audit.model.AuditInfo;
22+
import io.gravitee.definition.model.v4.plan.PlanMode;
23+
import io.gravitee.definition.model.v4.plan.PlanSecurity;
24+
import io.gravitee.definition.model.v4.plan.PlanStatus;
25+
import io.gravitee.rest.api.model.ImportSwaggerDescriptorEntity;
26+
import io.gravitee.rest.api.service.common.UuidString;
27+
import java.util.List;
28+
import lombok.extern.slf4j.Slf4j;
29+
30+
@Slf4j
31+
public abstract class AbstractDeployModelToApiUseCase {
32+
33+
protected final io.gravitee.definition.model.v4.plan.Plan defaultPlanDefinition = io.gravitee.definition.model.v4.plan.Plan.builder()
34+
.id(UuidString.generateRandom())
35+
.name("Default plan")
36+
.mode(PlanMode.STANDARD)
37+
.status(PlanStatus.PUBLISHED)
38+
.security(PlanSecurity.builder().type("key-less").build())
39+
.build();
40+
41+
protected final UpdateApiDomainService updateApiDomainService;
42+
protected final ApiStateDomainService apiStateDomainService;
43+
44+
protected AbstractDeployModelToApiUseCase(UpdateApiDomainService updateApiDomainService, ApiStateDomainService apiStateDomainService) {
45+
this.updateApiDomainService = updateApiDomainService;
46+
this.apiStateDomainService = apiStateDomainService;
47+
}
48+
49+
protected ImportSwaggerDescriptorEntity configure(DeployModelToApiUpdateUseCase.Mode mode, String swaggerDefinition) {
50+
var importSwaggerDescriptor = ImportSwaggerDescriptorEntity.builder().payload(swaggerDefinition);
51+
52+
log.debug("API will be Documented.");
53+
importSwaggerDescriptor.withDocumentation(true);
54+
55+
if (mode == DeployModelToApiUpdateUseCase.Mode.MOCKED || mode == DeployModelToApiUpdateUseCase.Mode.PUBLISHED) {
56+
log.debug("API will be Mocked.");
57+
importSwaggerDescriptor.withPolicyPaths(true);
58+
importSwaggerDescriptor.withPolicies(List.of("mock"));
59+
}
60+
61+
return importSwaggerDescriptor.build();
62+
}
63+
64+
protected Api manageApiState(Api api, AuditInfo audit, DeployModelToApiUpdateUseCase.Mode mode) {
65+
// API should be published
66+
if (mode == DeployModelToApiUpdateUseCase.Mode.PUBLISHED) {
67+
log.debug("Published v4 API.");
68+
69+
api.setVisibility(Api.Visibility.PUBLIC);
70+
api.setApiLifecycleState(Api.ApiLifecycleState.PUBLISHED);
71+
}
72+
73+
var updatedApi = updateApiDomainService.updateV4(api, audit);
74+
75+
// API should be started
76+
if (mode == DeployModelToApiUpdateUseCase.Mode.MOCKED || mode == DeployModelToApiUpdateUseCase.Mode.PUBLISHED) {
77+
log.debug("Started v4 API.");
78+
apiStateDomainService.start(api, audit);
79+
}
80+
81+
// Force API deployment if out of sync
82+
if (!apiStateDomainService.isSynchronized(api, audit)) {
83+
apiStateDomainService.deploy(api, "Managed by Gravitee Cloud", audit);
84+
}
85+
86+
return updatedApi;
87+
}
88+
89+
public enum Mode {
90+
DOCUMENTED,
91+
MOCKED,
92+
PUBLISHED,
93+
}
94+
95+
public record Output(Api api) {}
96+
}

gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/apim/core/api/use_case/cockpit/DeployModelToApiCreateUseCase.java

Lines changed: 9 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -25,53 +25,36 @@
2525
import io.gravitee.apim.core.audit.model.AuditInfo;
2626
import io.gravitee.apim.core.plan.model.Plan;
2727
import io.gravitee.apim.core.plan.model.PlanWithFlows;
28-
import io.gravitee.definition.model.v4.plan.PlanMode;
29-
import io.gravitee.definition.model.v4.plan.PlanSecurity;
30-
import io.gravitee.definition.model.v4.plan.PlanStatus;
31-
import io.gravitee.rest.api.model.ImportSwaggerDescriptorEntity;
32-
import io.gravitee.rest.api.service.common.UuidString;
3328
import java.util.List;
3429
import java.util.Set;
3530
import lombok.extern.slf4j.Slf4j;
3631

3732
@Slf4j
3833
@UseCase
39-
public class DeployModelToApiCreateUseCase {
34+
public class DeployModelToApiCreateUseCase extends AbstractDeployModelToApiUseCase {
4035

41-
public enum Mode {
42-
DOCUMENTED,
43-
MOCKED,
44-
PUBLISHED,
45-
}
46-
47-
private static final io.gravitee.definition.model.v4.plan.Plan defaultPlanDefinition =
48-
io.gravitee.definition.model.v4.plan.Plan.builder()
49-
.id(UuidString.generateRandom())
50-
.name("Default plan")
51-
.mode(PlanMode.STANDARD)
52-
.status(PlanStatus.PUBLISHED)
53-
.security(PlanSecurity.builder().type("key-less").build())
54-
.build();
55-
56-
public record Input(String swaggerDefinition, AuditInfo auditInfo, String apiCrossId, Mode mode, List<String> labels) {}
36+
public record Input(
37+
String swaggerDefinition,
38+
AuditInfo auditInfo,
39+
String apiCrossId,
40+
AbstractDeployModelToApiUseCase.Mode mode,
41+
List<String> labels
42+
) {}
5743

5844
public record Output(Api api) {}
5945

6046
private final OAIDomainService oaiDomainService;
6147
private final ImportDefinitionCreateDomainService importDefinitionCreateDomainService;
62-
private final UpdateApiDomainService updateApiDomainService;
63-
private final ApiStateDomainService apiStateDomainService;
6448

6549
public DeployModelToApiCreateUseCase(
6650
OAIDomainService oaiDomainService,
6751
ImportDefinitionCreateDomainService importDefinitionCreateDomainService,
6852
UpdateApiDomainService updateApiDomainService,
6953
ApiStateDomainService apiStateDomainService
7054
) {
55+
super(updateApiDomainService, apiStateDomainService);
7156
this.oaiDomainService = oaiDomainService;
7257
this.importDefinitionCreateDomainService = importDefinitionCreateDomainService;
73-
this.updateApiDomainService = updateApiDomainService;
74-
this.apiStateDomainService = apiStateDomainService;
7558
}
7659

7760
public DeployModelToApiCreateUseCase.Output execute(DeployModelToApiCreateUseCase.Input input) {
@@ -96,44 +79,4 @@ public DeployModelToApiCreateUseCase.Output execute(DeployModelToApiCreateUseCas
9679

9780
return new DeployModelToApiCreateUseCase.Output(api);
9881
}
99-
100-
private ImportSwaggerDescriptorEntity configure(Mode mode, String swaggerDefinition) {
101-
var importSwaggerDescriptor = ImportSwaggerDescriptorEntity.builder().payload(swaggerDefinition);
102-
103-
log.debug("API will be Documented.");
104-
importSwaggerDescriptor.withDocumentation(true);
105-
106-
if (mode == Mode.MOCKED || mode == Mode.PUBLISHED) {
107-
log.debug("API will be Mocked.");
108-
importSwaggerDescriptor.withPolicyPaths(true);
109-
importSwaggerDescriptor.withPolicies(List.of("mock"));
110-
}
111-
112-
return importSwaggerDescriptor.build();
113-
}
114-
115-
private Api manageApiState(Api api, AuditInfo audit, Mode mode) {
116-
// API should be published
117-
if (mode == Mode.PUBLISHED) {
118-
log.debug("Published v4 API.");
119-
120-
api.setVisibility(Api.Visibility.PUBLIC);
121-
api.setApiLifecycleState(Api.ApiLifecycleState.PUBLISHED);
122-
}
123-
124-
var updatedApi = updateApiDomainService.updateV4(api, audit);
125-
126-
// API should be started
127-
if (mode == Mode.MOCKED || mode == Mode.PUBLISHED) {
128-
log.debug("Started v4 API.");
129-
apiStateDomainService.start(api, audit);
130-
}
131-
132-
// Force API deployment if out of sync
133-
if (!apiStateDomainService.isSynchronized(api, audit)) {
134-
apiStateDomainService.deploy(api, "Managed by Gravitee Cloud", audit);
135-
}
136-
137-
return updatedApi;
138-
}
13982
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
* Copyright © 2015 The Gravitee team (http://gravitee.io)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.gravitee.apim.core.api.use_case.cockpit;
17+
18+
import io.gravitee.apim.core.UseCase;
19+
import io.gravitee.apim.core.api.domain_service.ApiStateDomainService;
20+
import io.gravitee.apim.core.api.domain_service.OAIDomainService;
21+
import io.gravitee.apim.core.api.domain_service.UpdateApiDomainService;
22+
import io.gravitee.apim.core.api.model.factory.ApiModelFactory;
23+
import io.gravitee.apim.core.audit.model.AuditInfo;
24+
import io.gravitee.apim.core.documentation.crud_service.PageCrudService;
25+
import io.gravitee.apim.core.documentation.model.Page;
26+
import io.gravitee.apim.core.plan.crud_service.PlanCrudService;
27+
import io.gravitee.apim.core.plan.model.Plan;
28+
import io.gravitee.common.utils.TimeProvider;
29+
import io.gravitee.definition.model.v4.plan.PlanStatus;
30+
import io.gravitee.rest.api.service.common.UuidString;
31+
import java.util.Date;
32+
import java.util.List;
33+
import lombok.extern.slf4j.Slf4j;
34+
35+
@Slf4j
36+
@UseCase
37+
public class DeployModelToApiUpdateUseCase extends AbstractDeployModelToApiUseCase {
38+
39+
private final OAIDomainService oaiDomainService;
40+
private final PlanCrudService planCrudService;
41+
private final PageCrudService pageCrudService;
42+
43+
public DeployModelToApiUpdateUseCase(
44+
OAIDomainService oaiDomainService,
45+
UpdateApiDomainService updateApiDomainService,
46+
ApiStateDomainService apiStateDomainService,
47+
PlanCrudService planCrudService,
48+
PageCrudService pageCrudService
49+
) {
50+
super(updateApiDomainService, apiStateDomainService);
51+
this.oaiDomainService = oaiDomainService;
52+
this.planCrudService = planCrudService;
53+
this.pageCrudService = pageCrudService;
54+
}
55+
56+
public DeployModelToApiUpdateUseCase.Output execute(DeployModelToApiUpdateUseCase.Input input) {
57+
var organizationId = input.auditInfo().organizationId();
58+
var environmentId = input.auditInfo().environmentId();
59+
var importSwaggerDescriptorEntity = configure(input.mode(), input.swaggerDefinition());
60+
var importDefinition = oaiDomainService.convert(organizationId, environmentId, importSwaggerDescriptorEntity, true, false);
61+
62+
managePlan(input);
63+
manageDocumentationPage(input, importDefinition.getPages().getFirst());
64+
65+
importDefinition.getApiExport().setId(input.apiId());
66+
importDefinition.getApiExport().setCrossId(input.apiCrossId());
67+
importDefinition.getApiExport().setLabels(input.labels());
68+
69+
var apiUpdated = updateApiDomainService.updateV4(
70+
ApiModelFactory.fromApiExport(importDefinition.getApiExport(), environmentId),
71+
input.auditInfo()
72+
);
73+
var api = manageApiState(apiUpdated, input.auditInfo(), input.mode());
74+
75+
return new DeployModelToApiUpdateUseCase.Output(api);
76+
}
77+
78+
private void managePlan(final Input input) {
79+
final var plans = planCrudService.findByApiId(input.apiId());
80+
81+
if (plans.stream().noneMatch(plan -> plan.getPlanStatus() == PlanStatus.PUBLISHED)) {
82+
planCrudService.create(
83+
new Plan(input.apiId(), defaultPlanDefinition)
84+
.toBuilder()
85+
.needRedeployAt(Date.from(TimeProvider.now().toInstant()))
86+
.validation(Plan.PlanValidationType.AUTO)
87+
.build()
88+
);
89+
}
90+
}
91+
92+
private void manageDocumentationPage(final Input input, final Page pageToCreate) {
93+
final var pages = pageCrudService.findByApiId(input.apiId());
94+
final var swaggerDocumentationPageCount = pages
95+
.stream()
96+
.filter(page -> page.getType() == Page.Type.SWAGGER)
97+
.count();
98+
99+
if (swaggerDocumentationPageCount > 1) {
100+
log.error("More than one Swagger documentation page already exists for this API.");
101+
return;
102+
}
103+
104+
if (swaggerDocumentationPageCount == 0) {
105+
log.error("No Swagger documentation page exists for this API.");
106+
pageToCreate.setId(UuidString.generateRandom());
107+
pageToCreate.setCreatedAt(Date.from(TimeProvider.now().toInstant()));
108+
pageToCreate.setUpdatedAt(Date.from(TimeProvider.now().toInstant()));
109+
pageToCreate.setReferenceId(input.apiId());
110+
pageCrudService.createDocumentationPage(pageToCreate);
111+
return;
112+
}
113+
114+
final var currentPage = pages.getFirst();
115+
currentPage.setContent(pageToCreate.getContent());
116+
currentPage.setUpdatedAt(Date.from(TimeProvider.now().toInstant()));
117+
pageCrudService.updateDocumentationPage(currentPage);
118+
}
119+
120+
public record Input(
121+
String swaggerDefinition,
122+
AuditInfo auditInfo,
123+
String apiId,
124+
String apiCrossId,
125+
AbstractDeployModelToApiUseCase.Mode mode,
126+
List<String> labels
127+
) {}
128+
}

gravitee-apim-rest-api/gravitee-apim-rest-api-service/src/main/java/io/gravitee/rest/api/service/cockpit/command/handler/DeployModelCommandHandler.java

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
import static io.gravitee.rest.api.service.common.SecurityContextHelper.authenticateAs;
1919

2020
import io.gravitee.apim.core.api.exception.InvalidPathsException;
21+
import io.gravitee.apim.core.api.use_case.cockpit.AbstractDeployModelToApiUseCase;
2122
import io.gravitee.apim.core.api.use_case.cockpit.DeployModelToApiCreateUseCase;
23+
import io.gravitee.apim.core.api.use_case.cockpit.DeployModelToApiUpdateUseCase;
2224
import io.gravitee.apim.core.audit.model.AuditActor;
2325
import io.gravitee.apim.core.audit.model.AuditInfo;
2426
import io.gravitee.cockpit.api.command.v1.CockpitCommandType;
@@ -61,6 +63,7 @@ public class DeployModelCommandHandler implements CommandHandler<DeployModelComm
6163
private final UserService userService;
6264
private final EnvironmentService environmentService;
6365
private final DeployModelToApiCreateUseCase deployModelToApiCreateUseCase;
66+
private final DeployModelToApiUpdateUseCase deployModelToApiUpdateUseCase;
6467

6568
@Override
6669
public String supportType() {
@@ -109,7 +112,17 @@ public Single<DeployModelReply> handle(DeployModelCommand command) {
109112
return updateV2Api(command, apiId, executionContext, user, mode, swaggerDefinition, labels);
110113
}
111114

112-
return Single.just(new DeployModelReply(command.getId(), "Not yet implemented"));
115+
deployModelToApiUpdateUseCase.execute(
116+
new DeployModelToApiUpdateUseCase.Input(
117+
swaggerDefinition,
118+
audit,
119+
apiId,
120+
apiCrossId,
121+
fromDeploymentModel(mode),
122+
labels
123+
)
124+
);
125+
return Single.just(new DeployModelReply(command.getId()));
113126
});
114127
} else {
115128
var message = permissionChecker.checkCreatePermission(
@@ -139,11 +152,11 @@ public Single<DeployModelReply> handle(DeployModelCommand command) {
139152
}
140153
}
141154

142-
private DeployModelToApiCreateUseCase.Mode fromDeploymentModel(DeploymentMode mode) {
155+
private AbstractDeployModelToApiUseCase.Mode fromDeploymentModel(DeploymentMode mode) {
143156
return switch (mode) {
144-
case API_DOCUMENTED -> DeployModelToApiCreateUseCase.Mode.DOCUMENTED;
145-
case API_MOCKED -> DeployModelToApiCreateUseCase.Mode.MOCKED;
146-
case API_PUBLISHED -> DeployModelToApiCreateUseCase.Mode.PUBLISHED;
157+
case API_DOCUMENTED -> AbstractDeployModelToApiUseCase.Mode.DOCUMENTED;
158+
case API_MOCKED -> AbstractDeployModelToApiUseCase.Mode.MOCKED;
159+
case API_PUBLISHED -> AbstractDeployModelToApiUseCase.Mode.PUBLISHED;
147160
};
148161
}
149162

@@ -171,7 +184,7 @@ private DeployModelToApiCreateUseCase.Mode fromDeploymentModel(DeploymentMode mo
171184
}
172185

173186
/**
174-
* In order to prepare future refactoring on cockpit which will always send apim reference, we first search by cockpit id then by apim id.
187+
* To prepare future refactoring on cockpit which will always send apim reference, we first search by cockpit id then by apim id.
175188
*
176189
* @param environmentId the env id which could be a cockpit id or apim environment id
177190
* @return {@link EnvironmentEntity} found

0 commit comments

Comments
 (0)