|
21 | 21 | import static java.util.Collections.emptyList; |
22 | 22 | import static java.util.Collections.singletonList; |
23 | 23 |
|
| 24 | +import io.gravitee.apim.core.api.domain_service.OAIDomainService; |
| 25 | +import io.gravitee.apim.core.api.exception.InvalidPathsException; |
24 | 26 | import io.gravitee.apim.core.api.model.UpdateNativeApi; |
25 | 27 | import io.gravitee.apim.core.api.model.crd.IDExportStrategy; |
| 28 | +import io.gravitee.apim.core.api.model.import_definition.ImportDefinition; |
26 | 29 | import io.gravitee.apim.core.api.model.utils.MigrationResult; |
27 | 30 | import io.gravitee.apim.core.api.use_case.DetachAutomatedApiUseCase; |
28 | 31 | import io.gravitee.apim.core.api.use_case.ExportApiCRDUseCase; |
|
31 | 34 | import io.gravitee.apim.core.api.use_case.GetExposedEntrypointsUseCase; |
32 | 35 | import io.gravitee.apim.core.api.use_case.MigrateApiUseCase; |
33 | 36 | import io.gravitee.apim.core.api.use_case.RollbackApiUseCase; |
| 37 | +import io.gravitee.apim.core.api.use_case.UpdateApiDefinitionUseCase; |
34 | 38 | import io.gravitee.apim.core.api.use_case.UpdateFederatedApiUseCase; |
35 | 39 | import io.gravitee.apim.core.api.use_case.UpdateNativeApiUseCase; |
36 | 40 | import io.gravitee.apim.core.audit.model.AuditActor; |
|
59 | 63 | import io.gravitee.rest.api.management.v2.rest.model.ApiTransferOwnership; |
60 | 64 | import io.gravitee.rest.api.management.v2.rest.model.DuplicateApiOptions; |
61 | 65 | import io.gravitee.rest.api.management.v2.rest.model.Error; |
| 66 | +import io.gravitee.rest.api.management.v2.rest.model.ExportApiV4; |
| 67 | +import io.gravitee.rest.api.management.v2.rest.model.ImportSwaggerDescriptor; |
62 | 68 | import io.gravitee.rest.api.management.v2.rest.model.MigrationReportResponses; |
63 | 69 | import io.gravitee.rest.api.management.v2.rest.model.MigrationReportResponsesIssuesInner; |
64 | 70 | import io.gravitee.rest.api.management.v2.rest.model.MigrationStateType; |
|
84 | 90 | import io.gravitee.rest.api.management.v2.rest.resource.documentation.ApiPagesResource; |
85 | 91 | import io.gravitee.rest.api.management.v2.rest.resource.param.LifecycleAction; |
86 | 92 | import io.gravitee.rest.api.management.v2.rest.resource.param.PaginationParam; |
| 93 | +import io.gravitee.rest.api.model.ImportSwaggerDescriptorEntity; |
87 | 94 | import io.gravitee.rest.api.model.InlinePictureEntity; |
88 | 95 | import io.gravitee.rest.api.model.MembershipMemberType; |
89 | 96 | import io.gravitee.rest.api.model.RoleEntity; |
|
127 | 134 | import io.gravitee.rest.api.service.v4.ApiLicenseService; |
128 | 135 | import io.gravitee.rest.api.service.v4.ApiStateService; |
129 | 136 | import io.gravitee.rest.api.service.v4.ApiWorkflowStateService; |
| 137 | +import io.gravitee.rest.api.service.v4.exception.InvalidPathException; |
130 | 138 | import io.swagger.v3.oas.annotations.parameters.RequestBody; |
131 | 139 | import jakarta.annotation.Nullable; |
132 | 140 | import jakarta.inject.Inject; |
@@ -247,6 +255,12 @@ public class ApiResource extends AbstractResource { |
247 | 255 | @Inject |
248 | 256 | private DetachAutomatedApiUseCase detachAutomatedApiUseCase; |
249 | 257 |
|
| 258 | + @Inject |
| 259 | + private UpdateApiDefinitionUseCase updateApiDefinitionUseCase; |
| 260 | + |
| 261 | + @Inject |
| 262 | + private OAIDomainService oaiDomainService; |
| 263 | + |
250 | 264 | @Context |
251 | 265 | protected UriInfo uriInfo; |
252 | 266 |
|
@@ -322,6 +336,69 @@ public Response getApiById(@PathParam("apiId") String apiId) { |
322 | 336 | return apiResponse(apiEntity); |
323 | 337 | } |
324 | 338 |
|
| 339 | + @PUT |
| 340 | + @Path("/_import/definition") |
| 341 | + @Consumes(MediaType.APPLICATION_JSON) |
| 342 | + @Produces(MediaType.APPLICATION_JSON) |
| 343 | + @Permissions({ @Permission(value = RolePermission.API_DEFINITION, acls = RolePermissionAction.UPDATE) }) |
| 344 | + public Response updateApiWithDefinition(@PathParam("apiId") String apiId, @Valid ExportApiV4 apiToImport) { |
| 345 | + verifyApiImage(apiToImport.getApiPicture(), "picture"); |
| 346 | + verifyApiImage(apiToImport.getApiBackground(), "background"); |
| 347 | + |
| 348 | + ImportDefinition importDefinition = ImportExportApiMapper.INSTANCE.toImportDefinition(apiToImport); |
| 349 | + |
| 350 | + try { |
| 351 | + var audit = getAuditInfo(); |
| 352 | + UpdateApiDefinitionUseCase.Output output = updateApiDefinitionUseCase.execute( |
| 353 | + new UpdateApiDefinitionUseCase.Input(apiId, importDefinition, audit) |
| 354 | + ); |
| 355 | + |
| 356 | + boolean isSynchronized = apiStateService.isSynchronized( |
| 357 | + GraviteeContext.getExecutionContext(), |
| 358 | + getGenericApiEntityById(apiId, false) |
| 359 | + ); |
| 360 | + |
| 361 | + return Response.ok().entity(ApiMapper.INSTANCE.map(output.apiWithFlows(), uriInfo, isSynchronized)).build(); |
| 362 | + } catch (InvalidPathsException e) { |
| 363 | + throw new InvalidPathException("Cannot import API with invalid paths", e); |
| 364 | + } |
| 365 | + } |
| 366 | + |
| 367 | + @PUT |
| 368 | + @Path("/_import/swagger") |
| 369 | + @Consumes(MediaType.APPLICATION_JSON) |
| 370 | + @Produces(MediaType.APPLICATION_JSON) |
| 371 | + @Permissions({ @Permission(value = RolePermission.API_DEFINITION, acls = RolePermissionAction.UPDATE) }) |
| 372 | + public Response updateApiFromSwagger(@PathParam("apiId") String apiId, @Valid @NotNull ImportSwaggerDescriptor descriptor) { |
| 373 | + try { |
| 374 | + var audit = getAuditInfo(); |
| 375 | + var importSwaggerDescriptor = ImportSwaggerDescriptorEntity.builder() |
| 376 | + .payload(descriptor.getPayload()) |
| 377 | + .withDocumentation(Boolean.TRUE.equals(descriptor.getWithDocumentation())) |
| 378 | + .build(); |
| 379 | + |
| 380 | + var importDefinition = oaiDomainService.convert( |
| 381 | + audit.organizationId(), |
| 382 | + audit.environmentId(), |
| 383 | + importSwaggerDescriptor, |
| 384 | + Boolean.TRUE.equals(descriptor.getWithDocumentation()), |
| 385 | + Boolean.TRUE.equals(descriptor.getWithOASValidationPolicy()) |
| 386 | + ); |
| 387 | + |
| 388 | + UpdateApiDefinitionUseCase.Output output = updateApiDefinitionUseCase.execute( |
| 389 | + new UpdateApiDefinitionUseCase.Input(apiId, importDefinition, audit) |
| 390 | + ); |
| 391 | + |
| 392 | + boolean isSynchronized = apiStateService.isSynchronized( |
| 393 | + GraviteeContext.getExecutionContext(), |
| 394 | + getGenericApiEntityById(apiId, false) |
| 395 | + ); |
| 396 | + return Response.ok().entity(ApiMapper.INSTANCE.map(output.apiWithFlows(), uriInfo, isSynchronized)).build(); |
| 397 | + } catch (InvalidPathsException e) { |
| 398 | + throw new InvalidPathException("Cannot import API with invalid paths", e); |
| 399 | + } |
| 400 | + } |
| 401 | + |
325 | 402 | @PUT |
326 | 403 | @Consumes(MediaType.APPLICATION_JSON) |
327 | 404 | @Produces(MediaType.APPLICATION_JSON) |
@@ -1169,4 +1246,13 @@ private void assertNoPrimaryOwnerReassignment(String poRole) { |
1169 | 1246 | throw new TransferOwnershipNotAllowedException(poRole); |
1170 | 1247 | } |
1171 | 1248 | } |
| 1249 | + |
| 1250 | + private static void verifyApiImage(String imageContent, String imageUsage) { |
| 1251 | + try { |
| 1252 | + ImageUtils.verify(imageContent); |
| 1253 | + } catch (InvalidImageException e) { |
| 1254 | + log.warn("Error while parsing {} while importing api", imageUsage, e); |
| 1255 | + throw new BadRequestException("Invalid image format for api " + imageUsage); |
| 1256 | + } |
| 1257 | + } |
1172 | 1258 | } |
0 commit comments