Skip to content

Commit

Permalink
CA-948 project per workspace db columns and plumbing (#1289)
Browse files Browse the repository at this point in the history
  • Loading branch information
dvoet authored Oct 20, 2020
1 parent 505fda0 commit 18b1c01
Show file tree
Hide file tree
Showing 12 changed files with 120 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,5 @@
<include file="changesets/20200311_workspace_requester_pays.xml" relativeToChangelogFile="true"/>
<include file="changesets/20200629_method_config_data_ref_name.xml" relativeToChangelogFile="true"/>
<include file="changesets/20200718_external_workflow_entities.xml" relativeToChangelogFile="true"/>
<include file="changesets/20201013_project_per_workspace.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<databaseChangeLog logicalFilePath="dummy" xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd">

<changeSet logicalFilePath="dummy" author="dvoet" id="INVALID_BILLING_ACCT">
<addColumn tableName="BILLING_PROJECT">
<column name="INVALID_BILLING_ACCT" type="boolean" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
</addColumn>
</changeSet>
<changeSet logicalFilePath="dummy" author="dvoet" id="workspace_version">
<addColumn tableName="WORKSPACE">
<column name="WORKSPACE_VERSION" type="VARCHAR(10)" defaultValue="v1">
<constraints nullable="false"/>
</column>
<column name="GOOGLE_PROJECT" type="VARCHAR(254)">
<constraints nullable="true"/>
</column>
</addColumn>
</changeSet>
<changeSet logicalFilePath="dummy" author="dvoet" id="populate_google_project">
<sql>
update WORKSPACE set google_project = namespace;
</sql>
<addNotNullConstraint tableName="WORKSPACE" columnName="GOOGLE_PROJECT" columnDataType="VARCHAR(254)"/>
</changeSet>

</databaseChangeLog>
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import org.broadinstitute.dsde.rawls.dataaccess.GoogleOperationNames.GoogleOpera
import org.broadinstitute.dsde.rawls.model.CreationStatuses.CreationStatus
import org.broadinstitute.dsde.rawls.model._

case class RawlsBillingProjectRecord(projectName: String, cromwellAuthBucketUrl: String, creationStatus: String, billingAccount: Option[String], message: Option[String], cromwellBackend: Option[String], servicePerimeter: Option[String], googleProjectNumber: Option[String])
case class RawlsBillingProjectRecord(projectName: String, cromwellAuthBucketUrl: String, creationStatus: String, billingAccount: Option[String], message: Option[String], cromwellBackend: Option[String], servicePerimeter: Option[String], googleProjectNumber: Option[String], invalidBillingAccount: Boolean)
case class RawlsBillingProjectOperationRecord(projectName: String, operationName: GoogleOperationName, operationId: String, done: Boolean, errorMessage: Option[String], api: GoogleApiType)

trait RawlsBillingProjectComponent {
Expand All @@ -24,8 +24,9 @@ trait RawlsBillingProjectComponent {
def cromwellBackend = column[Option[String]]("CROMWELL_BACKEND")
def servicePerimeter = column[Option[String]]("SERVICE_PERIMETER")
def googleProjectNumber = column[Option[String]]("GOOGLE_PROJECT_NUMBER")
def invalidBillingAccount = column[Boolean]("INVALID_BILLING_ACCT")

def * = (projectName, cromwellAuthBucketUrl, creationStatus, billingAccount, message, cromwellBackend, servicePerimeter, googleProjectNumber) <> (RawlsBillingProjectRecord.tupled, RawlsBillingProjectRecord.unapply)
def * = (projectName, cromwellAuthBucketUrl, creationStatus, billingAccount, message, cromwellBackend, servicePerimeter, googleProjectNumber, invalidBillingAccount) <> (RawlsBillingProjectRecord.tupled, RawlsBillingProjectRecord.unapply)
}

// these 2 implicits are lazy because there is a timing problem initializing MappedColumnType, if they are not lazy
Expand Down Expand Up @@ -134,11 +135,11 @@ trait RawlsBillingProjectComponent {
}

private def marshalBillingProject(billingProject: RawlsBillingProject): RawlsBillingProjectRecord = {
RawlsBillingProjectRecord(billingProject.projectName.value, billingProject.cromwellAuthBucketUrl, billingProject.status.toString, billingProject.billingAccount.map(_.value), billingProject.message, billingProject.cromwellBackend.map(_.value), billingProject.servicePerimeter.map(_.value), billingProject.googleProjectNumber.map(_.value))
RawlsBillingProjectRecord(billingProject.projectName.value, billingProject.cromwellAuthBucketUrl, billingProject.status.toString, billingProject.billingAccount.map(_.value), billingProject.message, billingProject.cromwellBackend.map(_.value), billingProject.servicePerimeter.map(_.value), billingProject.googleProjectNumber.map(_.value), billingProject.invalidBillingAccount)
}

private def unmarshalBillingProject(projectRecord: RawlsBillingProjectRecord): RawlsBillingProject = {
RawlsBillingProject(RawlsBillingProjectName(projectRecord.projectName), projectRecord.cromwellAuthBucketUrl, CreationStatuses.withName(projectRecord.creationStatus), projectRecord.billingAccount.map(RawlsBillingAccountName), projectRecord.message, projectRecord.cromwellBackend.map(CromwellBackend), projectRecord.servicePerimeter.map(ServicePerimeterName), projectRecord.googleProjectNumber.map(GoogleProjectNumber))
RawlsBillingProject(RawlsBillingProjectName(projectRecord.projectName), projectRecord.cromwellAuthBucketUrl, CreationStatuses.withName(projectRecord.creationStatus), projectRecord.billingAccount.map(RawlsBillingAccountName), projectRecord.message, projectRecord.cromwellBackend.map(CromwellBackend), projectRecord.servicePerimeter.map(ServicePerimeterName), projectRecord.googleProjectNumber.map(GoogleProjectNumber), projectRecord.invalidBillingAccount)
}

private def findBillingProjectByName(name: RawlsBillingProjectName): RawlsBillingProjectQuery = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ case class WorkspaceRecord(
lastModified: Timestamp,
createdBy: String,
isLocked: Boolean,
recordVersion: Long) {
recordVersion: Long,
workspaceVersion: String,
googleProject: String) {
def toWorkspaceName: WorkspaceName = WorkspaceName(namespace, name)
}

Expand All @@ -53,10 +55,12 @@ trait WorkspaceComponent {
def createdBy = column[String]("created_by", O.Length(254))
def isLocked = column[Boolean]("is_locked")
def recordVersion = column[Long]("record_version")
def workspaceVersion = column[String]("workspace_version")
def googleProject = column[String]("google_project")

def uniqueNamespaceName = index("IDX_WS_UNIQUE_NAMESPACE_NAME", (namespace, name), unique = true)

def * = (namespace, name, id, bucketName, workflowCollection, createdDate, lastModified, createdBy, isLocked, recordVersion) <> (WorkspaceRecord.tupled, WorkspaceRecord.unapply)
def * = (namespace, name, id, bucketName, workflowCollection, createdDate, lastModified, createdBy, isLocked, recordVersion, workspaceVersion, googleProject) <> (WorkspaceRecord.tupled, WorkspaceRecord.unapply)
}

/** raw/optimized SQL queries for working with workspace attributes
Expand Down Expand Up @@ -386,11 +390,11 @@ trait WorkspaceComponent {
}

private def marshalNewWorkspace(workspace: Workspace) = {
WorkspaceRecord(workspace.namespace, workspace.name, UUID.fromString(workspace.workspaceId), workspace.bucketName, workspace.workflowCollectionName, new Timestamp(workspace.createdDate.getMillis), new Timestamp(workspace.lastModified.getMillis), workspace.createdBy, workspace.isLocked, 0)
WorkspaceRecord(workspace.namespace, workspace.name, UUID.fromString(workspace.workspaceId), workspace.bucketName, workspace.workflowCollectionName, new Timestamp(workspace.createdDate.getMillis), new Timestamp(workspace.lastModified.getMillis), workspace.createdBy, workspace.isLocked, 0, workspace.workspaceVersion.value, workspace.googleProject)
}

private def unmarshalWorkspace(workspaceRec: WorkspaceRecord, attributes: AttributeMap): Workspace = {
Workspace(workspaceRec.namespace, workspaceRec.name, workspaceRec.id.toString, workspaceRec.bucketName, workspaceRec.workflowCollection, new DateTime(workspaceRec.createdDate), new DateTime(workspaceRec.lastModified), workspaceRec.createdBy, attributes, workspaceRec.isLocked)
Workspace(workspaceRec.namespace, workspaceRec.name, workspaceRec.id.toString, workspaceRec.bucketName, workspaceRec.workflowCollection, new DateTime(workspaceRec.createdDate), new DateTime(workspaceRec.lastModified), workspaceRec.createdBy, attributes, workspaceRec.isLocked, WorkspaceVersions.fromString(workspaceRec.workspaceVersion).getOrElse(throw new RawlsException(s"unexpected version string ${workspaceRec.workspaceVersion}")), workspaceRec.googleProject)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ object ManagedGroup {
case class ManagedGroup(membersGroup: RawlsGroup, adminsGroup: RawlsGroup) extends Managed

case class RawlsBillingAccount(accountName: RawlsBillingAccountName, firecloudHasAccess: Boolean, displayName: String)
case class RawlsBillingProject(projectName: RawlsBillingProjectName, cromwellAuthBucketUrl: String, status: CreationStatuses.CreationStatus, billingAccount: Option[RawlsBillingAccountName], message: Option[String], cromwellBackend: Option[CromwellBackend] = None, servicePerimeter: Option[ServicePerimeterName] = None, googleProjectNumber: Option[GoogleProjectNumber] = None)
case class RawlsBillingProject(projectName: RawlsBillingProjectName, cromwellAuthBucketUrl: String, status: CreationStatuses.CreationStatus, billingAccount: Option[RawlsBillingAccountName], message: Option[String], cromwellBackend: Option[CromwellBackend] = None, servicePerimeter: Option[ServicePerimeterName] = None, googleProjectNumber: Option[GoogleProjectNumber] = None, invalidBillingAccount: Boolean = false)

case class RawlsBillingProjectTransfer(project: String, bucket: String, newOwnerEmail: String, newOwnerToken: String)

Expand Down Expand Up @@ -140,7 +140,7 @@ class UserAuthJsonSupport extends JsonSupport {

implicit val RawlsGroupMemberListFormat = jsonFormat4(RawlsGroupMemberList)

implicit val RawlsBillingProjectFormat = jsonFormat8(RawlsBillingProject)
implicit val RawlsBillingProjectFormat = jsonFormat9(RawlsBillingProject)

implicit val RawlsBillingAccountFormat = jsonFormat3(RawlsBillingAccount)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ trait WorkspaceSupport {
response <- userHasAction match {
case true =>
traceDBIOWithParent("loadBillingProject", parentSpan)( _ => dataAccess.rawlsBillingProjectQuery.load(projectName)).flatMap {
case Some(RawlsBillingProject(_, _, CreationStatuses.Ready, _, _, _, _, _)) => op //Sam will check to make sure the Auth Domain selection is valid
case Some(RawlsBillingProject(RawlsBillingProjectName(name), _, CreationStatuses.Creating, _, _, _, _, _)) =>
case Some(RawlsBillingProject(_, _, CreationStatuses.Ready, _, _, _, _, _, _)) => op //Sam will check to make sure the Auth Domain selection is valid
case Some(RawlsBillingProject(RawlsBillingProjectName(name), _, CreationStatuses.Creating, _, _, _, _, _, _)) =>
DBIO.failed(new RawlsExceptionWithErrorReport(errorReport = ErrorReport(StatusCodes.BadRequest, s"${name} is still being created")))

case Some(RawlsBillingProject(RawlsBillingProjectName(name), _, CreationStatuses.Error, _, messageOp, _, _, _)) =>
case Some(RawlsBillingProject(RawlsBillingProjectName(name), _, CreationStatuses.Error, _, messageOp, _, _, _, _)) =>
DBIO.failed(new RawlsExceptionWithErrorReport(errorReport = ErrorReport(StatusCodes.BadRequest, s"Error creating ${name}: ${messageOp.getOrElse("no message")}")))
case Some(_) | None =>
// this can't happen with the current code but a 404 would be the correct response
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ class AttributeComponentSpec extends TestDriverComponentWithFlatSpecAndMatchers
}

it should "insert entity reference attribute" in withEmptyTestDatabase {
runAndWait(workspaceQuery += WorkspaceRecord("testns", "testname1", workspaceId, "bucket", Some("workflow-collection"), defaultTimeStamp, defaultTimeStamp, "me", false, 0))
runAndWait(workspaceQuery += WorkspaceRecord("testns", "testname1", workspaceId, "bucket", Some("workflow-collection"), defaultTimeStamp, defaultTimeStamp, "me", false, 0, WorkspaceVersions.V1.value, "gp"))
val entityId = runAndWait((entityQuery returning entityQuery.map(_.id)) += EntityRecord(0, "name", "type", workspaceId, 0, deleted = false, None))
val testAttribute = AttributeEntityReference("type", "name")
runAndWait(insertWorkspaceAttributeRecords(workspaceId, AttributeName.withDefaultNS("test"), testAttribute))
Expand All @@ -170,7 +170,7 @@ class AttributeComponentSpec extends TestDriverComponentWithFlatSpecAndMatchers
}

it should "insert entity reference attribute list" in withEmptyTestDatabase {
runAndWait(workspaceQuery += WorkspaceRecord("testns", "testname2", workspaceId, "bucket", Some("workflow-collection"), defaultTimeStamp, defaultTimeStamp, "me", false, 0))
runAndWait(workspaceQuery += WorkspaceRecord("testns", "testname2", workspaceId, "bucket", Some("workflow-collection"), defaultTimeStamp, defaultTimeStamp, "me", false, 0, WorkspaceVersions.V1.value, "gp"))
val entityId1 = runAndWait((entityQuery returning entityQuery.map(_.id)) += EntityRecord(0, "name1", "type", workspaceId, 0, deleted = false, None))
val entityId2 = runAndWait((entityQuery returning entityQuery.map(_.id)) += EntityRecord(0, "name2", "type", workspaceId, 0, deleted = false, None))
val entityId3 = runAndWait((entityQuery returning entityQuery.map(_.id)) += EntityRecord(0, "name3", "type", workspaceId, 0, deleted = false, None))
Expand All @@ -188,7 +188,7 @@ class AttributeComponentSpec extends TestDriverComponentWithFlatSpecAndMatchers
}

it should "throw exception inserting ref to nonexistent entity" in withEmptyTestDatabase {
runAndWait(workspaceQuery += WorkspaceRecord("testns", "testname3", workspaceId, "bucket", Some("workflow-collection"), defaultTimeStamp, defaultTimeStamp, "me", false, 0))
runAndWait(workspaceQuery += WorkspaceRecord("testns", "testname3", workspaceId, "bucket", Some("workflow-collection"), defaultTimeStamp, defaultTimeStamp, "me", false, 0, WorkspaceVersions.V1.value, "gp"))
val testAttribute = AttributeEntityReference("type", "name")
intercept[RawlsException] {
runAndWait(insertWorkspaceAttributeRecords(workspaceId, AttributeName.withDefaultNS("test"), testAttribute))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ class EntityComponentSpec extends TestDriverComponentWithFlatSpecAndMatchers wit

class BugTestData extends TestData {
val wsName = WorkspaceName("myNamespace2", "myWorkspace2")
val workspace = new Workspace(wsName.namespace, wsName.name, UUID.randomUUID.toString, "aBucket", Some("workflow-collection"), currentTime(), currentTime(), "testUser", Map.empty)
val workspace = Workspace(wsName.namespace, wsName.name, UUID.randomUUID.toString, "aBucket", Some("workflow-collection"), currentTime(), currentTime(), "testUser", Map.empty)

val sample1 = new Entity("sample1", "Sample",
Map(AttributeName.withDefaultNS("aliquot") -> AttributeEntityReference("Aliquot", "aliquot1")))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ class WorkspaceComponentSpec extends TestDriverComponentWithFlatSpecAndMatchers
AttributeName.withDefaultNS("attributeString") -> AttributeString("value"),
AttributeName.withDefaultNS("attributeBool") -> AttributeBoolean(true),
AttributeName.withDefaultNS("attributeNum") -> AttributeNumber(3.14159)),
false)
false,
WorkspaceVersions.V2,
"test_google_project"
)

assertResult(None) {
runAndWait(workspaceQuery.findById(workspaceId.toString))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ trait DataRepoEntityProviderSpecSupport {
val snapshot: String = UUID.randomUUID().toString

// default Workspace object, mostly irrelevant for DataRepoEntityProviderSpec but necessary to exist
val workspace = new Workspace("namespace", "name", wsId.toString, "bucketName", None,
val workspace = Workspace("namespace", "name", wsId.toString, "bucketName", None,
DateTime.now(), DateTime.now(), "createdBy", Map.empty, false)

// defaults for DataRepoEntityProviderConfig
Expand Down
Loading

0 comments on commit 18b1c01

Please sign in to comment.