Skip to content

Commit

Permalink
add new spendreport api
Browse files Browse the repository at this point in the history
  • Loading branch information
calypsomatic committed Nov 7, 2024
1 parent 414207c commit c5c736a
Show file tree
Hide file tree
Showing 2 changed files with 201 additions and 134 deletions.
49 changes: 49 additions & 0 deletions core/src/main/resources/swagger/api-docs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,55 @@ paths:
$ref: '#/components/schemas/ErrorReport'
500:
$ref: '#/components/responses/RawlsInternalError'
/api/billing/v2/spendReport:
get:
tags:
- billing_v2
summary: get spend report for all workspaces user has owner access to
description: get spend report for all workspaces user has owner access to
operationId: getSpendReportAllWorkspaces
parameters:
- name: startDate
in: query
description: start date of report (YYYY-MM-DD). Data included in report will start at 12 AM UTC on this date.
required: true
schema:
type: string
format: date
- name: endDate
in: query
description: end date of report (YYYY-MM-DD). Data included in report will end at 11:59 PM UTC on this date.
required: true
schema:
type: string
format: date
responses:
200:
description: Success
content:
'application/json':
schema:
$ref: '#/components/schemas/SpendReport'
400:
description: invalid spend report parameters
content:
'application/json':
schema:
$ref: '#/components/schemas/ErrorReport'
403:
description: You must be a project owner to view the spend report of a project
content:
'application/json':
schema:
$ref: '#/components/schemas/ErrorReport'
404:
description: The specified billing project could not be found
content:
'application/json':
schema:
$ref: '#/components/schemas/ErrorReport'
500:
$ref: '#/components/responses/RawlsInternalError'
/api/billing/v2/{projectId}/spendReportConfiguration:
get:
tags:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,177 +59,195 @@ trait BillingApiServiceV2 extends UserInfoDirectives {
requireUserInfo(Option(otelContext)) { userInfo =>
val ctx = RawlsRequestContext(userInfo, Option(otelContext))
pathPrefix("billing" / "v2") {

pathPrefix(Segment) { projectId =>
pathEnd {
pathPrefix("spendReport") {
pathEndOrSingleSlash {
get {
complete {
import spray.json._
userServiceConstructor(ctx).getBillingProject(RawlsBillingProjectName(projectId)).map {
case Some(projectResponse) => StatusCodes.OK -> Option(projectResponse).toJson
case None => StatusCodes.NotFound -> Option(StatusCodes.NotFound.defaultMessage).toJson
parameters(
"startDate".as[DateTime],
"endDate".as[DateTime]
) { (startDate, endDate) =>
complete {
spendReportingConstructor(ctx).getSpendForAllWorkspaces(
startDate,
endDate.plusDays(1).minusMillis(1)
)
}
}
} ~
delete {
}
}
} ~
pathPrefix(Segment) { projectId =>
pathEnd {
get {
complete {
billingProjectOrchestratorConstructor(ctx)
.deleteBillingProjectV2(RawlsBillingProjectName(projectId))
.map(_ => StatusCodes.NoContent)
import spray.json._
userServiceConstructor(ctx).getBillingProject(RawlsBillingProjectName(projectId)).map {
case Some(projectResponse) => StatusCodes.OK -> Option(projectResponse).toJson
case None => StatusCodes.NotFound -> Option(StatusCodes.NotFound.defaultMessage).toJson
}
}
}
} ~
pathPrefix("spendReport") {
pathEndOrSingleSlash {
get {
parameters(
"startDate".as[DateTime],
"endDate".as[DateTime],
"aggregationKey"
.as[SpendReportingAggregationKeyWithSub](aggregationKeyParameterUnmarshaller)
.repeated
) { (startDate, endDate, aggregationKeyParameters) =>
complete {
spendReportingConstructor(ctx).getSpendForBillingProject(
RawlsBillingProjectName(projectId),
startDate,
endDate.plusDays(1).minusMillis(1),
aggregationKeyParameters.toSet
)
}
} ~
delete {
complete {
billingProjectOrchestratorConstructor(ctx)
.deleteBillingProjectV2(RawlsBillingProjectName(projectId))
.map(_ => StatusCodes.NoContent)
}
}
}
} ~
pathPrefix("spendReportConfiguration") {
pathEnd {
put {
entity(as[BillingProjectSpendConfiguration]) { spendConfiguration =>
complete {
userServiceConstructor(ctx)
.setBillingProjectSpendConfiguration(RawlsBillingProjectName(projectId), spendConfiguration)
.map(_ => StatusCodes.NoContent)
pathPrefix("spendReport") {
pathEndOrSingleSlash {
get {
parameters(
"startDate".as[DateTime],
"endDate".as[DateTime],
"aggregationKey"
.as[SpendReportingAggregationKeyWithSub](aggregationKeyParameterUnmarshaller)
.repeated
) { (startDate, endDate, aggregationKeyParameters) =>
complete {
spendReportingConstructor(ctx).getSpendForBillingProject(
RawlsBillingProjectName(projectId),
startDate,
endDate.plusDays(1).minusMillis(1),
aggregationKeyParameters.toSet
)
}
}
}
} ~
delete {
complete {
userServiceConstructor(ctx)
.clearBillingProjectSpendConfiguration(RawlsBillingProjectName(projectId))
.map(_ => StatusCodes.NoContent)
}
} ~
pathPrefix("spendReportConfiguration") {
pathEnd {
put {
entity(as[BillingProjectSpendConfiguration]) { spendConfiguration =>
complete {
userServiceConstructor(ctx)
.setBillingProjectSpendConfiguration(RawlsBillingProjectName(projectId), spendConfiguration)
.map(_ => StatusCodes.NoContent)
}
}
} ~
get {
complete {
userServiceConstructor(ctx)
.getBillingProjectSpendConfiguration(RawlsBillingProjectName(projectId))
.map {
case Some(config) => StatusCodes.OK -> Option(config)
case None => StatusCodes.NoContent -> None
}
delete {
complete {
userServiceConstructor(ctx)
.clearBillingProjectSpendConfiguration(RawlsBillingProjectName(projectId))
.map(_ => StatusCodes.NoContent)
}
} ~
get {
complete {
userServiceConstructor(ctx)
.getBillingProjectSpendConfiguration(RawlsBillingProjectName(projectId))
.map {
case Some(config) => StatusCodes.OK -> Option(config)
case None => StatusCodes.NoContent -> None
}
}
}
}
}
} ~
pathPrefix("billingAccount") {
pathEnd {
put {
entity(as[UpdateRawlsBillingAccountRequest]) { updateProjectRequest =>
complete {
userServiceConstructor(ctx)
.updateBillingProjectBillingAccount(RawlsBillingProjectName(projectId), updateProjectRequest)
.map {
}
} ~
pathPrefix("billingAccount") {
pathEnd {
put {
entity(as[UpdateRawlsBillingAccountRequest]) { updateProjectRequest =>
complete {
userServiceConstructor(ctx)
.updateBillingProjectBillingAccount(RawlsBillingProjectName(projectId), updateProjectRequest)
.map {
case Some(billingProject) => StatusCodes.OK -> Option(billingProject)
case None => StatusCodes.NoContent -> None
}
}
}
} ~
delete {
complete {
userServiceConstructor(ctx).deleteBillingAccount(RawlsBillingProjectName(projectId)).map {
case Some(billingProject) => StatusCodes.OK -> Option(billingProject)
case None => StatusCodes.NoContent -> None
}
}
}
}
} ~
delete {
}
} ~
pathPrefix("members") {
pathEnd {
get {
complete {
userServiceConstructor(ctx).deleteBillingAccount(RawlsBillingProjectName(projectId)).map {
case Some(billingProject) => StatusCodes.OK -> Option(billingProject)
case None => StatusCodes.NoContent -> None
userServiceConstructor(ctx).getBillingProjectMembers(RawlsBillingProjectName(projectId))
}
} ~
patch {
parameter(Symbol("inviteUsersNotFound").?) { inviteUsersNotFound =>
entity(as[BatchProjectAccessUpdate]) { batchProjectAccessUpdate =>
complete {
userServiceConstructor(ctx)
.batchUpdateBillingProjectMembers(RawlsBillingProjectName(projectId),
batchProjectAccessUpdate,
inviteUsersNotFound.getOrElse("false").toBoolean
)
.map(_ => StatusCodes.NoContent -> None)
}
}
}
}
}
}
} ~
pathPrefix("members") {
pathEnd {
get {
complete {
userServiceConstructor(ctx).getBillingProjectMembers(RawlsBillingProjectName(projectId))
}
} ~
patch {
parameter(Symbol("inviteUsersNotFound").?) { inviteUsersNotFound =>
entity(as[BatchProjectAccessUpdate]) { batchProjectAccessUpdate =>
// these routes are for adding/removing users from projects
path(Segment / Segment) { (workbenchRole, userEmail) =>
put {
complete {
userServiceConstructor(ctx)
.addUserToBillingProjectV2(RawlsBillingProjectName(projectId),
ProjectAccessUpdate(userEmail,
ProjectRoles.withName(workbenchRole)
)
)
.map(_ => StatusCodes.OK)
}
} ~
delete {
complete {
userServiceConstructor(ctx)
.batchUpdateBillingProjectMembers(RawlsBillingProjectName(projectId),
batchProjectAccessUpdate,
inviteUsersNotFound.getOrElse("false").toBoolean
.removeUserFromBillingProjectV2(RawlsBillingProjectName(projectId),
ProjectAccessUpdate(userEmail,
ProjectRoles.withName(workbenchRole)
)
)
.map(_ => StatusCodes.NoContent -> None)
.map(_ => StatusCodes.OK)
}
}
}
}
} ~
// these routes are for adding/removing users from projects
path(Segment / Segment) { (workbenchRole, userEmail) =>
put {
pathPrefix("bucketMigration") {
val billingProjectName = RawlsBillingProjectName(projectId)
pathEndOrSingleSlash {
post {
complete {
userServiceConstructor(ctx)
.addUserToBillingProjectV2(RawlsBillingProjectName(projectId),
ProjectAccessUpdate(userEmail, ProjectRoles.withName(workbenchRole))
)
.map(_ => StatusCodes.OK)
bucketMigrationServiceConstructor(ctx)
.migrateWorkspaceBucketsInBillingProject(billingProjectName)
.map(StatusCodes.Created -> _)
}
} ~
delete {
get {
complete {
userServiceConstructor(ctx)
.removeUserFromBillingProjectV2(RawlsBillingProjectName(projectId),
ProjectAccessUpdate(userEmail,
ProjectRoles.withName(workbenchRole)
)
)
.map(_ => StatusCodes.OK)
bucketMigrationServiceConstructor(ctx)
.getBucketMigrationAttemptsForBillingProject(billingProjectName)
.map(ms => StatusCodes.OK -> ms)
}
}
}
} ~
pathPrefix("bucketMigration") {
val billingProjectName = RawlsBillingProjectName(projectId)
pathEndOrSingleSlash {
post {
complete {
bucketMigrationServiceConstructor(ctx)
.migrateWorkspaceBucketsInBillingProject(billingProjectName)
.map(StatusCodes.Created -> _)
}
} ~
get {
complete {
bucketMigrationServiceConstructor(ctx)
.getBucketMigrationAttemptsForBillingProject(billingProjectName)
.map(ms => StatusCodes.OK -> ms)
}
}
} ~
path("progress") {
get {
complete {
bucketMigrationServiceConstructor(ctx)
.getBucketMigrationProgressForBillingProject(billingProjectName)
.map(StatusCodes.OK -> _)
path("progress") {
get {
complete {
bucketMigrationServiceConstructor(ctx)
.getBucketMigrationProgressForBillingProject(billingProjectName)
.map(StatusCodes.OK -> _)
}
}
}
}
}
} ~
}
} ~
pathEnd {
get {
complete {
Expand Down

0 comments on commit c5c736a

Please sign in to comment.