From d632d6534507179a1fc47d4b8d173d1088080e0d Mon Sep 17 00:00:00 2001 From: Yurii Shynbuiev Date: Fri, 10 Jan 2025 18:09:05 +0700 Subject: [PATCH] fix: openAPI mutability #1115 Signed-off-by: Yurii Shynbuiev --- .../identus/api/http/ErrorResponse.scala | 8 +++ .../connect/controller/http/Connection.scala | 4 +- .../http/StatusListCredential.scala | 2 +- .../http/IssueCredentialRecord.scala | 6 +- .../controller/http/PresentationStatus.scala | 4 +- .../identus/api/util/Tapir2StaticOAS.scala | 69 ++++++++++--------- 6 files changed, 51 insertions(+), 42 deletions(-) diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/api/http/ErrorResponse.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/api/http/ErrorResponse.scala index 219e8fa58f..217acd2aa9 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/api/http/ErrorResponse.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/api/http/ErrorResponse.scala @@ -34,6 +34,14 @@ case class ErrorResponse( ) object ErrorResponse { + val example = ErrorResponse( + 404, + "NotFound", + "Not Found", + Some("The requested resource was not found"), + INSTANCE_URI_PREFIX + UUID.fromString("f47ac10b-58cc-4372-a567-0e02b2c3d479") + ) + given encoder: zio.json.JsonEncoder[ErrorResponse] = DeriveJsonEncoder.gen[ErrorResponse] given decoder: zio.json.JsonDecoder[ErrorResponse] = DeriveJsonDecoder.gen[ErrorResponse] diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/connect/controller/http/Connection.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/connect/controller/http/Connection.scala index b402ca0131..dbdbc57c6b 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/connect/controller/http/Connection.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/connect/controller/http/Connection.scala @@ -5,7 +5,6 @@ import org.hyperledger.identus.connect.controller.http.Connection.annotations import org.hyperledger.identus.connect.controller.http.Connection.annotations.goalcode import org.hyperledger.identus.connect.core.model import org.hyperledger.identus.connect.core.model.ConnectionRecord.Role -import org.hyperledger.identus.shared.models.{FailureInfo, StatusCode} import sttp.model.Uri import sttp.tapir.{Schema, Validator} import sttp.tapir.Schema.annotations.{description, encodedExample, validate} @@ -207,8 +206,7 @@ object Connection { object metaLastFailure extends Annotation[ErrorResponse]( description = "The last failure if any.", - example = - ErrorResponse.failureToErrorResponseConversion(FailureInfo("Error", StatusCode.NotFound, "Not Found")) + example = ErrorResponse.example ) object self diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/credentialstatus/controller/http/StatusListCredential.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/credentialstatus/controller/http/StatusListCredential.scala index f270b4500b..96e400f62a 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/credentialstatus/controller/http/StatusListCredential.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/credentialstatus/controller/http/StatusListCredential.scala @@ -84,7 +84,7 @@ object StatusListCredential { object issuanceDate extends Annotation[Instant]( description = "Issuance timestamp of status list credential", - example = Instant.now() + example = Instant.parse("2025-01-01T22:40:34.560891Z") ) object credentialSubject { diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/http/IssueCredentialRecord.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/http/IssueCredentialRecord.scala index b28d25e3d4..3e6b85f797 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/http/IssueCredentialRecord.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/http/IssueCredentialRecord.scala @@ -4,7 +4,6 @@ import org.hyperledger.identus.api.http.{Annotation, ErrorResponse} import org.hyperledger.identus.issue.controller.http.IssueCredentialRecord.annotations import org.hyperledger.identus.mercury.model.{AttachmentDescriptor, Base64} import org.hyperledger.identus.pollux.core.model.IssueCredentialRecord as PolluxIssueCredentialRecord -import org.hyperledger.identus.shared.models.{FailureInfo, StatusCode} import sttp.tapir.{Schema, Validator} import sttp.tapir.json.zio.schemaForZioJsonValue import sttp.tapir.Schema.annotations.{description, encodedExample, validate} @@ -195,7 +194,7 @@ object IssueCredentialRecord { object createdAt extends Annotation[OffsetDateTime]( description = "The date and time when the issue credential record was created.", - example = OffsetDateTime.now() + example = OffsetDateTime.parse("2023-01-01T00:00:00Z") ) object updatedAt @@ -295,8 +294,7 @@ object IssueCredentialRecord { object metaLastFailure extends Annotation[ErrorResponse]( description = "The last failure if any.", - example = - ErrorResponse.failureToErrorResponseConversion(FailureInfo("Error", StatusCode.NotFound, "Not Found")) + example = ErrorResponse.example ) } diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/presentproof/controller/http/PresentationStatus.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/presentproof/controller/http/PresentationStatus.scala index 301b6c8321..72cc0807ef 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/presentproof/controller/http/PresentationStatus.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/presentproof/controller/http/PresentationStatus.scala @@ -5,7 +5,6 @@ import org.hyperledger.identus.mercury.model.{AttachmentDescriptor, Base64, Json import org.hyperledger.identus.mercury.protocol.presentproof.{Presentation, RequestPresentation} import org.hyperledger.identus.pollux.core.model.PresentationRecord import org.hyperledger.identus.presentproof.controller.http.PresentationStatus.annotations -import org.hyperledger.identus.shared.models.{FailureInfo, StatusCode} import sttp.tapir.{Schema, Validator} import sttp.tapir.json.zio.schemaForZioJsonValue import sttp.tapir.Schema.annotations.{description, encodedExample, validate} @@ -196,8 +195,7 @@ object PresentationStatus { object metaLastFailure extends Annotation[ErrorResponse]( description = "The last failure if any.", - example = - ErrorResponse.failureToErrorResponseConversion(FailureInfo("Error", StatusCode.NotFound, "Not Found")) + example = ErrorResponse.example ) object goalcode diff --git a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/api/util/Tapir2StaticOAS.scala b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/api/util/Tapir2StaticOAS.scala index 9a8feb0a9c..1a7a891298 100644 --- a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/api/util/Tapir2StaticOAS.scala +++ b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/api/util/Tapir2StaticOAS.scala @@ -23,8 +23,10 @@ import org.hyperledger.identus.presentproof.controller.PresentProofController import org.hyperledger.identus.system.controller.SystemController import org.hyperledger.identus.verification.controller.VcVerificationController import org.scalatestplus.mockito.MockitoSugar.* +import sttp.apispec.openapi.circe.yaml.* +import sttp.apispec.openapi.OpenAPI import sttp.tapir.docs.openapi.OpenAPIDocsInterpreter -import zio.{Scope, ZIO, ZIOAppArgs, ZIOAppDefault, ZLayer} +import zio.{Config, IO, Scope, ZIO, ZIOAppArgs, ZIOAppDefault, ZLayer} import zio.config.typesafe.TypesafeConfigProvider import java.nio.charset.StandardCharsets @@ -34,43 +36,48 @@ import scala.util.Using object Tapir2StaticOAS extends ZIOAppDefault { @main override def run: ZIO[Any & ZIOAppArgs & Scope, Any, Any] = { - val effect = for { + for { args <- getArgs _ <- ZIO.when(args.length != 2)(ZIO.fail("Usage: Tapir2StaticOAS ")) - allEndpoints <- AgentHttpServer.agentRESTServiceEndpoints + model <- OpenAPISpecificationGenerator.generate } yield { - import sttp.apispec.openapi.circe.yaml.* - val model = DocModels.customiseDocsModel(OpenAPIDocsInterpreter().toOpenAPI(allEndpoints.map(_.endpoint), "", "")) val yaml = model.info(model.info.copy(version = args(1))).toYaml3_0_3 val path = Path.of(args.head) Using(Files.newBufferedWriter(path, StandardCharsets.UTF_8)) { writer => writer.write(yaml) } } - val configLayer = ZLayer.fromZIO( - TypesafeConfigProvider - .fromTypesafeConfig(ConfigFactory.load()) - .load(AppConfig.config) - ) - effect.provideSomeLayer( - ZLayer.succeed(mock[ConnectionController]) ++ - ZLayer.succeed(mock[CredentialDefinitionController]) ++ - ZLayer.succeed(mock[CredentialSchemaController]) ++ - ZLayer.succeed(mock[CredentialStatusController]) ++ - ZLayer.succeed(mock[VerificationPolicyController]) ++ - ZLayer.succeed(mock[DIDRegistrarController]) ++ - ZLayer.succeed(mock[PresentProofController]) ++ - ZLayer.succeed(mock[VcVerificationController]) ++ - ZLayer.succeed(mock[IssueController]) ++ - ZLayer.succeed(mock[DIDController]) ++ - ZLayer.succeed(mock[SystemController]) ++ - ZLayer.succeed(mock[EntityController]) ++ - ZLayer.succeed(mock[WalletManagementController]) ++ - ZLayer.succeed(mock[DefaultAuthenticator]) ++ - ZLayer.succeed(mock[EventController]) ++ - ZLayer.succeed(mock[CredentialIssuerController]) ++ - ZLayer.succeed(mock[PresentationExchangeController]) ++ - ZLayer.succeed(mock[Oid4vciAuthenticatorFactory]) ++ - configLayer - ) } +} + +object OpenAPISpecificationGenerator { + def generate: IO[Config.Error, OpenAPI] = + for { + allEndpoints <- AgentHttpServer.agentRESTServiceEndpoints provideSomeLayer layers + openApi = DocModels.customiseDocsModel(OpenAPIDocsInterpreter().toOpenAPI(allEndpoints.map(_.endpoint), "", "")) + } yield openApi + + private val configLayer = ZLayer.fromZIO( + TypesafeConfigProvider + .fromTypesafeConfig(ConfigFactory.load()) + .load(AppConfig.config) + ) + private def layers = ZLayer.succeed(mock[ConnectionController]) ++ + ZLayer.succeed(mock[CredentialDefinitionController]) ++ + ZLayer.succeed(mock[CredentialSchemaController]) ++ + ZLayer.succeed(mock[CredentialStatusController]) ++ + ZLayer.succeed(mock[VerificationPolicyController]) ++ + ZLayer.succeed(mock[DIDRegistrarController]) ++ + ZLayer.succeed(mock[PresentProofController]) ++ + ZLayer.succeed(mock[VcVerificationController]) ++ + ZLayer.succeed(mock[IssueController]) ++ + ZLayer.succeed(mock[DIDController]) ++ + ZLayer.succeed(mock[SystemController]) ++ + ZLayer.succeed(mock[EntityController]) ++ + ZLayer.succeed(mock[WalletManagementController]) ++ + ZLayer.succeed(mock[DefaultAuthenticator]) ++ + ZLayer.succeed(mock[EventController]) ++ + ZLayer.succeed(mock[CredentialIssuerController]) ++ + ZLayer.succeed(mock[PresentationExchangeController]) ++ + ZLayer.succeed(mock[Oid4vciAuthenticatorFactory]) ++ + configLayer }