Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Simplify the secrets API via an enum for the id's entity #271

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
16 changes: 9 additions & 7 deletions core/src/main/kotlin/api/OrganizationsRoute.kt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import org.eclipse.apoapsis.ortserver.core.utils.pagingOptions
import org.eclipse.apoapsis.ortserver.core.utils.requireIdParameter
import org.eclipse.apoapsis.ortserver.core.utils.requireParameter
import org.eclipse.apoapsis.ortserver.model.authorization.OrganizationPermission
import org.eclipse.apoapsis.ortserver.model.repositories.SecretRepository.Entity
import org.eclipse.apoapsis.ortserver.services.InfrastructureServiceService
import org.eclipse.apoapsis.ortserver.services.OrganizationService
import org.eclipse.apoapsis.ortserver.services.SecretService
Expand Down Expand Up @@ -175,7 +176,8 @@ fun Route.organizations() = route("organizations") {
val orgId = call.requireIdParameter("organizationId")
val pagingOptions = call.pagingOptions(SortProperty("name", SortDirection.ASCENDING))

val secretsForOrganization = secretService.listForOrganization(orgId, pagingOptions.mapToModel())
val secretsForOrganization = secretService
.listSecrets(Entity.ORGANIZATION, orgId, pagingOptions.mapToModel())
val pagedResponse = PagedResponse(
secretsForOrganization.map { it.mapToApi() },
pagingOptions
Expand All @@ -191,7 +193,7 @@ fun Route.organizations() = route("organizations") {
val organizationId = call.requireIdParameter("organizationId")
val secretName = call.requireParameter("secretName")

secretService.getSecretByOrganizationIdAndName(organizationId, secretName)
secretService.getSecret(Entity.ORGANIZATION, organizationId, secretName)
?.let { call.respond(HttpStatusCode.OK, it.mapToApi()) }
?: call.respond(HttpStatusCode.NotFound)
}
Expand All @@ -205,7 +207,8 @@ fun Route.organizations() = route("organizations") {

call.respond(
HttpStatusCode.OK,
secretService.updateSecretByOrganizationAndName(
secretService.updateSecret(
Entity.ORGANIZATION,
organizationId,
secretName,
updateSecret.value.mapToModel(),
Expand All @@ -220,7 +223,7 @@ fun Route.organizations() = route("organizations") {
val organizationId = call.requireIdParameter("organizationId")
val secretName = call.requireParameter("secretName")

secretService.deleteSecretByOrganizationAndName(organizationId, secretName)
secretService.deleteSecret(Entity.ORGANIZATION, organizationId, secretName)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the comment on the first commit, it would be nice to have convenience functions like getRepositorySecret, deleteRepositorySecret, or listRepositorySecrets.


call.respond(HttpStatusCode.NoContent)
}
Expand All @@ -238,9 +241,8 @@ fun Route.organizations() = route("organizations") {
createSecret.name,
createSecret.value,
createSecret.description,
organizationId,
null,
null
Entity.ORGANIZATION,
organizationId
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For most callers it would be nicer to have convenience functions like createRepositorySecret or createProductSecret (probably with default implementations in the interface as implementations would be identical).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where's the convenient in using these functions that basically just encode the entity parameter as part of the function name? They are hardly shorter, and create a maintenance burden if more entities (like business units, groups of organizations etc.) would be added in the future.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it's a matter of taste as well, personally I'd rather call getOrganizationSecrets(orgId) than getSecrets(Entity.ORGANIZATION, orgId) because it has less parameters.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, not a must to address? Because I'd prefer not to.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, not a must, but as this is still a draft maybe there is some time to get an opinion from another contributor (@oheger-bosch @MarcelBochtler @bs-ondem)?

).mapToApi()
)
}
Expand Down
15 changes: 8 additions & 7 deletions core/src/main/kotlin/api/ProductsRoute.kt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import org.eclipse.apoapsis.ortserver.core.utils.pagingOptions
import org.eclipse.apoapsis.ortserver.core.utils.requireIdParameter
import org.eclipse.apoapsis.ortserver.core.utils.requireParameter
import org.eclipse.apoapsis.ortserver.model.authorization.ProductPermission
import org.eclipse.apoapsis.ortserver.model.repositories.SecretRepository.Entity
import org.eclipse.apoapsis.ortserver.services.ProductService
import org.eclipse.apoapsis.ortserver.services.SecretService

Expand Down Expand Up @@ -138,7 +139,7 @@ fun Route.products() = route("products/{productId}") {
val productId = call.requireIdParameter("productId")
val pagingOptions = call.pagingOptions(SortProperty("name", SortDirection.ASCENDING))

val secretsForProduct = secretService.listForProduct(productId, pagingOptions.mapToModel())
val secretsForProduct = secretService.listSecrets(Entity.PRODUCT, productId, pagingOptions.mapToModel())
val pagedResponse = PagedResponse(
secretsForProduct.map { it.mapToApi() },
pagingOptions
Expand All @@ -154,7 +155,7 @@ fun Route.products() = route("products/{productId}") {
val productId = call.requireIdParameter("productId")
val secretName = call.requireParameter("secretName")

secretService.getSecretByProductIdAndName(productId, secretName)
secretService.getSecret(Entity.PRODUCT, productId, secretName)
?.let { call.respond(HttpStatusCode.OK, it.mapToApi()) }
?: call.respond(HttpStatusCode.NotFound)
}
Expand All @@ -168,7 +169,8 @@ fun Route.products() = route("products/{productId}") {

call.respond(
HttpStatusCode.OK,
secretService.updateSecretByProductAndName(
secretService.updateSecret(
Entity.PRODUCT,
productId,
secretName,
updateSecret.value.mapToModel(),
Expand All @@ -183,7 +185,7 @@ fun Route.products() = route("products/{productId}") {
val productId = call.requireIdParameter("productId")
val secretName = call.requireParameter("secretName")

secretService.deleteSecretByProductAndName(productId, secretName)
secretService.deleteSecret(Entity.PRODUCT, productId, secretName)

call.respond(HttpStatusCode.NoContent)
}
Expand All @@ -201,9 +203,8 @@ fun Route.products() = route("products/{productId}") {
createSecret.name,
createSecret.value,
createSecret.description,
null,
productId,
null
Entity.PRODUCT,
productId
).mapToApi()
)
}
Expand Down
14 changes: 8 additions & 6 deletions core/src/main/kotlin/api/RepositoriesRoute.kt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import org.eclipse.apoapsis.ortserver.core.utils.pagingOptions
import org.eclipse.apoapsis.ortserver.core.utils.requireIdParameter
import org.eclipse.apoapsis.ortserver.core.utils.requireParameter
import org.eclipse.apoapsis.ortserver.model.authorization.RepositoryPermission
import org.eclipse.apoapsis.ortserver.model.repositories.SecretRepository.Entity
import org.eclipse.apoapsis.ortserver.services.RepositoryService
import org.eclipse.apoapsis.ortserver.services.SecretService

Expand Down Expand Up @@ -167,7 +168,8 @@ fun Route.repositories() = route("repositories/{repositoryId}") {
val repositoryId = call.requireIdParameter("repositoryId")
val pagingOptions = call.pagingOptions(SortProperty("name", SortDirection.ASCENDING))

val secretsForRepository = secretService.listForRepository(repositoryId, pagingOptions.mapToModel())
val secretsForRepository = secretService
.listSecrets(Entity.REPOSITORY, repositoryId, pagingOptions.mapToModel())
val pagedResponse = PagedResponse(
secretsForRepository.map { it.mapToApi() },
pagingOptions
Expand All @@ -183,7 +185,7 @@ fun Route.repositories() = route("repositories/{repositoryId}") {
val repositoryId = call.requireIdParameter("repositoryId")
val secretName = call.requireParameter("secretName")

secretService.getSecretByRepositoryIdAndName(repositoryId, secretName)
secretService.getSecret(Entity.REPOSITORY, repositoryId, secretName)
?.let { call.respond(HttpStatusCode.OK, it.mapToApi()) }
?: call.respond(HttpStatusCode.NotFound)
}
Expand All @@ -197,7 +199,8 @@ fun Route.repositories() = route("repositories/{repositoryId}") {

call.respond(
HttpStatusCode.OK,
secretService.updateSecretByRepositoryAndName(
secretService.updateSecret(
Entity.REPOSITORY,
repositoryId,
secretName,
updateSecret.value.mapToModel(),
Expand All @@ -212,7 +215,7 @@ fun Route.repositories() = route("repositories/{repositoryId}") {
val repositoryId = call.requireIdParameter("repositoryId")
val secretName = call.requireParameter("secretName")

secretService.deleteSecretByRepositoryAndName(repositoryId, secretName)
secretService.deleteSecret(Entity.REPOSITORY, repositoryId, secretName)

call.respond(HttpStatusCode.NoContent)
}
Expand All @@ -230,8 +233,7 @@ fun Route.repositories() = route("repositories/{repositoryId}") {
createSecret.name,
createSecret.value,
createSecret.description,
null,
null,
Entity.REPOSITORY,
repositoryId
).mapToApi()
)
Expand Down
15 changes: 8 additions & 7 deletions core/src/test/kotlin/api/OrganizationsRouteIntegrationTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ import org.eclipse.apoapsis.ortserver.model.authorization.ProductRole
import org.eclipse.apoapsis.ortserver.model.authorization.Superuser
import org.eclipse.apoapsis.ortserver.model.repositories.InfrastructureServiceRepository
import org.eclipse.apoapsis.ortserver.model.repositories.SecretRepository
import org.eclipse.apoapsis.ortserver.model.repositories.SecretRepository.Entity
import org.eclipse.apoapsis.ortserver.model.util.ListQueryParameters.Companion.DEFAULT_LIMIT
import org.eclipse.apoapsis.ortserver.secrets.Path
import org.eclipse.apoapsis.ortserver.secrets.SecretsProviderFactoryForTesting
Expand Down Expand Up @@ -133,7 +134,7 @@ class OrganizationsRouteIntegrationTest : AbstractIntegrationTest({
path: String = secretPath,
name: String = secretName,
description: String = secretDescription,
) = secretRepository.create(path, name, description, organizationId, null, null)
) = secretRepository.create(path, name, description, Entity.ORGANIZATION, organizationId)

"GET /organizations" should {
"return all existing organizations for the superuser" {
Expand Down Expand Up @@ -693,7 +694,7 @@ class OrganizationsRouteIntegrationTest : AbstractIntegrationTest({
response shouldHaveStatus HttpStatusCode.Created
response shouldHaveBody Secret(secret.name, secret.description)

secretRepository.getByOrganizationIdAndName(organizationId, secret.name)?.mapToApi() shouldBe
secretRepository.get(Entity.ORGANIZATION, organizationId, secret.name)?.mapToApi() shouldBe
Secret(secret.name, secret.description)

val provider = SecretsProviderFactoryForTesting.instance()
Expand Down Expand Up @@ -731,7 +732,7 @@ class OrganizationsRouteIntegrationTest : AbstractIntegrationTest({
body.message shouldBe "Request validation has failed."
body.cause shouldContain "Validation failed for CreateSecret"

secretRepository.getByOrganizationIdAndName(organizationId, secret.name)?.mapToApi().shouldBeNull()
secretRepository.get(Entity.ORGANIZATION, organizationId, secret.name)?.mapToApi().shouldBeNull()

val provider = SecretsProviderFactoryForTesting.instance()
provider.readSecret(Path("organization_${organizationId}_${secret.name}"))?.value.shouldBeNull()
Expand Down Expand Up @@ -766,7 +767,7 @@ class OrganizationsRouteIntegrationTest : AbstractIntegrationTest({
response shouldHaveStatus HttpStatusCode.OK
response shouldHaveBody Secret(secret.name, updatedDescription)

secretRepository.getByOrganizationIdAndName(organizationId, updateSecret.name.valueOrThrow)
secretRepository.get(Entity.ORGANIZATION, organizationId, updateSecret.name.valueOrThrow)
?.mapToApi() shouldBe Secret(secret.name, updatedDescription)
}
}
Expand Down Expand Up @@ -799,7 +800,7 @@ class OrganizationsRouteIntegrationTest : AbstractIntegrationTest({
setBody(updateSecret)
} shouldHaveStatus HttpStatusCode.InternalServerError

secretRepository.getByOrganizationIdAndName(organizationId, secret.name) shouldBe secret
secretRepository.get(Entity.ORGANIZATION, organizationId, secret.name) shouldBe secret
}
}

Expand All @@ -824,7 +825,7 @@ class OrganizationsRouteIntegrationTest : AbstractIntegrationTest({
superuserClient.delete("/api/v1/organizations/$organizationId/secrets/${secret.name}") shouldHaveStatus
HttpStatusCode.NoContent

secretRepository.listForOrganization(organizationId) shouldBe emptyList()
secretRepository.list(Entity.ORGANIZATION, organizationId) shouldBe emptyList()

val provider = SecretsProviderFactoryForTesting.instance()
provider.readSecret(Path(secret.path)) should beNull()
Expand Down Expand Up @@ -867,7 +868,7 @@ class OrganizationsRouteIntegrationTest : AbstractIntegrationTest({
superuserClient.delete("/api/v1/organizations/$organizationId/secrets/${secret.name}") shouldHaveStatus
HttpStatusCode.InternalServerError

secretRepository.getByOrganizationIdAndName(organizationId, secret.name) shouldBe secret
secretRepository.get(Entity.ORGANIZATION, organizationId, secret.name) shouldBe secret
}
}

Expand Down
15 changes: 8 additions & 7 deletions core/src/test/kotlin/api/ProductsRouteIntegrationTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import org.eclipse.apoapsis.ortserver.model.authorization.RepositoryPermission
import org.eclipse.apoapsis.ortserver.model.authorization.RepositoryRole
import org.eclipse.apoapsis.ortserver.model.repositories.InfrastructureServiceRepository
import org.eclipse.apoapsis.ortserver.model.repositories.SecretRepository
import org.eclipse.apoapsis.ortserver.model.repositories.SecretRepository.Entity
import org.eclipse.apoapsis.ortserver.model.util.ListQueryParameters.Companion.DEFAULT_LIMIT
import org.eclipse.apoapsis.ortserver.secrets.Path
import org.eclipse.apoapsis.ortserver.secrets.SecretsProviderFactoryForTesting
Expand Down Expand Up @@ -128,7 +129,7 @@ class ProductsRouteIntegrationTest : AbstractIntegrationTest({
path: String = secretPath,
name: String = secretName,
description: String = secretDescription,
) = secretRepository.create(path, name, description, null, productId, null)
) = secretRepository.create(path, name, description, Entity.PRODUCT, productId)

"GET /products/{productId}" should {
"return a single product" {
Expand Down Expand Up @@ -481,7 +482,7 @@ class ProductsRouteIntegrationTest : AbstractIntegrationTest({
response shouldHaveStatus HttpStatusCode.Created
response shouldHaveBody Secret(secret.name, secret.description)

secretRepository.getByProductIdAndName(productId, secret.name)?.mapToApi() shouldBe
secretRepository.get(Entity.PRODUCT, productId, secret.name)?.mapToApi() shouldBe
Secret(secret.name, secret.description)

val provider = SecretsProviderFactoryForTesting.instance()
Expand Down Expand Up @@ -519,7 +520,7 @@ class ProductsRouteIntegrationTest : AbstractIntegrationTest({
body.message shouldBe "Request validation has failed."
body.cause shouldContain "Validation failed for CreateSecret"

secretRepository.getByProductIdAndName(productId, secret.name)?.mapToApi().shouldBeNull()
secretRepository.get(Entity.PRODUCT, productId, secret.name)?.mapToApi().shouldBeNull()

val provider = SecretsProviderFactoryForTesting.instance()
provider.readSecret(Path("product_${productId}_${secret.name}"))?.value shouldBe null
Expand Down Expand Up @@ -554,7 +555,7 @@ class ProductsRouteIntegrationTest : AbstractIntegrationTest({
response shouldHaveStatus HttpStatusCode.OK
response shouldHaveBody Secret(secret.name, updatedDescription)

secretRepository.getByProductIdAndName(orgId, updateSecret.name.valueOrThrow)?.mapToApi() shouldBe
secretRepository.get(Entity.PRODUCT, productId, updateSecret.name.valueOrThrow)?.mapToApi() shouldBe
Secret(secret.name, updatedDescription)
}
}
Expand Down Expand Up @@ -587,7 +588,7 @@ class ProductsRouteIntegrationTest : AbstractIntegrationTest({
setBody(updateSecret)
} shouldHaveStatus HttpStatusCode.InternalServerError

secretRepository.getByProductIdAndName(orgId, secret.name) shouldBe secret
secretRepository.get(Entity.PRODUCT, productId, secret.name) shouldBe secret
}
}

Expand All @@ -612,7 +613,7 @@ class ProductsRouteIntegrationTest : AbstractIntegrationTest({
superuserClient.delete("/api/v1/products/$productId/secrets/${secret.name}") shouldHaveStatus
HttpStatusCode.NoContent

secretRepository.listForProduct(productId) shouldBe emptyList()
secretRepository.list(Entity.PRODUCT, productId) shouldBe emptyList()

val provider = SecretsProviderFactoryForTesting.instance()
provider.readSecret(Path(secret.path)) should beNull()
Expand Down Expand Up @@ -654,7 +655,7 @@ class ProductsRouteIntegrationTest : AbstractIntegrationTest({
superuserClient.delete("/api/v1/products/$productId/secrets/${secret.name}") shouldHaveStatus
HttpStatusCode.InternalServerError

secretRepository.getByProductIdAndName(productId, secret.name) shouldBe secret
secretRepository.get(Entity.PRODUCT, productId, secret.name) shouldBe secret
}
}

Expand Down
Loading
Loading