diff --git a/src/applications/drydock/query/DrydockBlueprintQuery.php b/src/applications/drydock/query/DrydockBlueprintQuery.php index abad714d8f..c8e53217c9 100644 --- a/src/applications/drydock/query/DrydockBlueprintQuery.php +++ b/src/applications/drydock/query/DrydockBlueprintQuery.php @@ -1,78 +1,105 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } + public function withBlueprintClasses(array $classes) { + $this->blueprintClasses = $classes; + return $this; + } + public function withDatasourceQuery($query) { $this->datasourceQuery = $query; return $this; } + public function newResultObject() { + return new DrydockBlueprint(); + } + protected function loadPage() { - $table = new DrydockBlueprint(); - $conn_r = $table->establishConnection('r'); - - $data = queryfx_all( - $conn_r, - 'SELECT blueprint.* FROM %T blueprint %Q %Q %Q', - $table->getTableName(), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); - - $blueprints = $table->loadAllFromArray($data); - - $implementations = - DrydockBlueprintImplementation::getAllBlueprintImplementations(); - - foreach ($blueprints as $blueprint) { - if (array_key_exists($blueprint->getClassName(), $implementations)) { - $blueprint->attachImplementation( - $implementations[$blueprint->getClassName()]); + return $this->loadStandardPage($this->newResultObject()); + } + + protected function willFilterPage(array $blueprints) { + $impls = DrydockBlueprintImplementation::getAllBlueprintImplementations(); + foreach ($blueprints as $key => $blueprint) { + $impl = idx($impls, $blueprint->getClassName()); + if (!$impl) { + $this->didRejectResult($blueprint); + unset($blueprints[$key]); + continue; } + $impl = clone $impl; + $blueprint->attachImplementation($impl); } return $blueprints; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { - $where = array(); + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'phid IN (%Ls)', $this->phids); } if ($this->datasourceQuery !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'blueprintName LIKE %>', $this->datasourceQuery); } - return $this->formatWhereClause($where); + if ($this->blueprintClasses !== null) { + $where[] = qsprintf( + $conn, + 'className IN (%Ls)', + $this->blueprintClasses); + } + + return $where; + } + + public function getOrderableColumns() { + // TODO: Blueprints implement CustomFields, but can not be ordered by + // custom field classes because the custom fields are not global. There + // is no graceful way to handle this in ApplicationSearch at the moment. + // Just brute force around it until we can clean this up. + + return array( + 'id' => array( + 'table' => $this->getPrimaryTableAlias(), + 'column' => 'id', + 'reverse' => false, + 'type' => 'int', + 'unique' => true, + ), + ); } } diff --git a/src/applications/drydock/query/DrydockLeaseQuery.php b/src/applications/drydock/query/DrydockLeaseQuery.php index 37f02bd748..f7adc07cce 100644 --- a/src/applications/drydock/query/DrydockLeaseQuery.php +++ b/src/applications/drydock/query/DrydockLeaseQuery.php @@ -1,112 +1,112 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withResourceIDs(array $ids) { $this->resourceIDs = $ids; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } - public function newResultObject() { - return new DrydockLease(); - } - public function withDatasourceQuery($query) { $this->datasourceQuery = $query; return $this; } + public function newResultObject() { + return new DrydockLease(); + } + protected function loadPage() { return $this->loadStandardPage($this->newResultObject()); } protected function willFilterPage(array $leases) { $resource_ids = array_filter(mpull($leases, 'getResourceID')); if ($resource_ids) { $resources = id(new DrydockResourceQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withIDs(array_unique($resource_ids)) ->execute(); } else { $resources = array(); } foreach ($leases as $key => $lease) { $resource = null; if ($lease->getResourceID()) { $resource = idx($resources, $lease->getResourceID()); if (!$resource) { unset($leases[$key]); continue; } } $lease->attachResource($resource); } return $leases; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->resourceIDs !== null) { $where[] = qsprintf( $conn, 'resourceID IN (%Ld)', $this->resourceIDs); } if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'status IN (%Ld)', $this->statuses); } if ($this->datasourceQuery !== null) { $where[] = qsprintf( $conn, 'id = %d', (int)$this->datasourceQuery); } return $where; } } diff --git a/src/applications/drydock/query/DrydockResourceQuery.php b/src/applications/drydock/query/DrydockResourceQuery.php index d07e729276..d15b737141 100644 --- a/src/applications/drydock/query/DrydockResourceQuery.php +++ b/src/applications/drydock/query/DrydockResourceQuery.php @@ -1,109 +1,120 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withTypes(array $types) { $this->types = $types; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function withBlueprintPHIDs(array $blueprint_phids) { $this->blueprintPHIDs = $blueprint_phids; return $this; } public function withDatasourceQuery($query) { $this->datasourceQuery = $query; return $this; } - protected function loadPage() { - $table = new DrydockResource(); - $conn_r = $table->establishConnection('r'); + public function newResultObject() { + return new DrydockResource(); + } - $data = queryfx_all( - $conn_r, - 'SELECT resource.* FROM %T resource %Q %Q %Q', - $table->getTableName(), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); + protected function loadPage() { + return $this->loadStandardPage($this->newResultObject()); + } - $resources = $table->loadAllFromArray($data); + protected function willFilterPage(array $resources) { + $blueprint_phids = mpull($resources, 'getBlueprintPHID'); + + $blueprints = id(new DrydockBlueprintQuery()) + ->setViewer($this->getViewer()) + ->withPHIDs($blueprint_phids) + ->execute(); + $blueprints = mpull($blueprints, null, 'getPHID'); + + foreach ($resources as $key => $resource) { + $blueprint = idx($blueprints, $resource->getBlueprintPHID()); + if (!$blueprint) { + $this->didRejectResult($resource); + unset($resources[$key]); + continue; + } + $resource->attachBlueprint($blueprint); + } return $resources; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { - $where = array(); + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'phid IN (%Ls)', $this->phids); } if ($this->types !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'type IN (%Ls)', $this->types); } if ($this->statuses !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'status IN (%Ls)', $this->statuses); } if ($this->blueprintPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'blueprintPHID IN (%Ls)', $this->blueprintPHIDs); } if ($this->datasourceQuery !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'name LIKE %>', $this->datasourceQuery); } - $where[] = $this->buildPagingClause($conn_r); - - return $this->formatWhereClause($where); + return $where; } } diff --git a/src/applications/drydock/storage/DrydockResource.php b/src/applications/drydock/storage/DrydockResource.php index d078c76c2f..87638d1ae0 100644 --- a/src/applications/drydock/storage/DrydockResource.php +++ b/src/applications/drydock/storage/DrydockResource.php @@ -1,134 +1,142 @@ true, self::CONFIG_SERIALIZATION => array( 'attributes' => self::SERIALIZATION_JSON, 'capabilities' => self::SERIALIZATION_JSON, ), self::CONFIG_COLUMN_SCHEMA => array( 'name' => 'text255', 'ownerPHID' => 'phid?', 'status' => 'uint32', 'type' => 'text64', ), self::CONFIG_KEY_SCHEMA => array( 'key_phid' => null, 'phid' => array( 'columns' => array('phid'), 'unique' => true, ), ), ) + parent::getConfiguration(); } public function generatePHID() { return PhabricatorPHID::generateNewPHID(DrydockResourcePHIDType::TYPECONST); } public function getAttribute($key, $default = null) { return idx($this->attributes, $key, $default); } public function getAttributesForTypeSpec(array $attribute_names) { return array_select_keys($this->attributes, $attribute_names); } public function setAttribute($key, $value) { $this->attributes[$key] = $value; return $this; } public function getCapability($key, $default = null) { return idx($this->capbilities, $key, $default); } public function getInterface(DrydockLease $lease, $type) { return $this->getBlueprint()->getInterface($this, $lease, $type); } public function getBlueprint() { - // TODO: Policy stuff. - if (empty($this->blueprint)) { - $blueprint = id(new DrydockBlueprint()) - ->loadOneWhere('phid = %s', $this->blueprintPHID); - $this->blueprint = $blueprint->getImplementation(); - } - return $this->blueprint; + return $this->assertAttached($this->blueprint); + } + + public function attachBlueprint(DrydockBlueprint $blueprint) { + $this->blueprint = $blueprint; + return $this; + } + + public function canAllocateLease(DrydockLease $lease) { + return $this->getBlueprint()->canAllocateLeaseOnResource( + $this, + $lease); } public function closeResource() { + + // TODO: This is super broken and will race other lease writers! + $this->openTransaction(); $statuses = array( DrydockLeaseStatus::STATUS_PENDING, DrydockLeaseStatus::STATUS_ACTIVE, ); $leases = id(new DrydockLeaseQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withResourceIDs(array($this->getID())) ->withStatuses($statuses) ->execute(); foreach ($leases as $lease) { switch ($lease->getStatus()) { case DrydockLeaseStatus::STATUS_PENDING: $message = pht('Breaking pending lease (resource closing).'); $lease->setStatus(DrydockLeaseStatus::STATUS_BROKEN); break; case DrydockLeaseStatus::STATUS_ACTIVE: $message = pht('Releasing active lease (resource closing).'); $lease->setStatus(DrydockLeaseStatus::STATUS_RELEASED); break; } DrydockBlueprintImplementation::writeLog($this, $lease, $message); $lease->save(); } $this->setStatus(DrydockResourceStatus::STATUS_CLOSED); $this->save(); $this->saveTransaction(); } /* -( PhabricatorPolicyInterface )----------------------------------------- */ public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, ); } public function getPolicy($capability) { switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: return PhabricatorPolicies::getMostOpenPolicy(); } } public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { return false; } public function describeAutomaticCapability($capability) { return null; } }