Repository: apigee/registry
Pull request #718 (2022-09-01 18:18:07)

prevent deletion of the only deployment revision
addresses #717, follows the pattern in #698

codecov[bot]: # [Codecov](https://codecov.io/gh/apigee/registry/pull/718?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee) Report
> Merging [#718](https://codecov.io/gh/apigee/registry/pull/718?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee) (544c3c6) into [main](https://codecov.io/gh/apigee/registry/commit/952753d6a29ec0334b116bf48a7fe101d294cd52?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee) (952753d) will **increase** coverage by `0.11%`.
> The diff coverage is `n/a`.

> :exclamation: Current head 544c3c6 differs from pull request most recent head c17fa81. Consider uploading reports for the commit c17fa81 to get more accurate results

```diff
@@            Coverage Diff             @@
##             main     #718      +/-   ##
==========================================
+ Coverage   56.93%   57.04%   +0.11%     
==========================================
  Files          93       93              
  Lines        8008     8008              
==========================================
+ Hits         4559     4568       +9     
+ Misses       3016     3009       -7     
+ Partials      433      431       -2     
```


| [Impacted Files](https://codecov.io/gh/apigee/registry/pull/718?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee) | Coverage Δ | |
|---|---|---|
| [server/registry/actions\_deployments.go](https://codecov.io/gh/apigee/registry/pull/718/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee#diff-c2VydmVyL3JlZ2lzdHJ5L2FjdGlvbnNfZGVwbG95bWVudHMuZ28=) | `76.33% <0.00%> (+1.77%)` | :arrow_up: |
| [server/registry/actions\_deployment\_revisions.go](https://codecov.io/gh/apigee/registry/pull/718/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee#diff-c2VydmVyL3JlZ2lzdHJ5L2FjdGlvbnNfZGVwbG95bWVudF9yZXZpc2lvbnMuZ28=) | `47.44% <0.00%> (+4.37%)` | :arrow_up: |

:mega: We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee)


Patch:
diff --git a/server/registry/internal/storage/delete.go b/server/registry/internal/storage/delete.go
index f48f3ca..ec596d8 100644
--- a/server/registry/internal/storage/delete.go
+++ b/server/registry/internal/storage/delete.go
@@ -285,6 +285,19 @@ func (c *Client) DeleteDeploymentRevision(ctx context.Context, name names.Deploy
 		}
 	}
 
+	// if we deleted the last revision, return an error to cancel the transaction
+	op := c.db.WithContext(ctx).
+		Where("project_id = ?", name.ProjectID).
+		Where("api_id = ?", name.ApiID).
+		Where("deployment_id = ?", name.DeploymentID).
+		Order("revision_create_time desc")
+	v := new(models.Deployment)
+	if err := op.First(v).Error; err == gorm.ErrRecordNotFound {
+		return status.Errorf(codes.FailedPrecondition, "cannot delete the only revision: %s", name)
+	} else if err != nil {
+		return grpcErrorForDBError(ctx, err)
+	}
+
 	return nil
 }
 


Test patch:
diff --git a/server/registry/actions_deployment_revisions_test.go b/server/registry/actions_deployment_revisions_test.go
index f848fef..ec02f82 100644
--- a/server/registry/actions_deployment_revisions_test.go
+++ b/server/registry/actions_deployment_revisions_test.go
@@ -239,11 +239,17 @@ func TestDeleteApiDeploymentRevision(t *testing.T) {
 		t.Fatalf("Setup/Seeding: Failed to seed registry: %s", err)
 	}
 
-	t.Run("only remaining revision", func(t *testing.T) {
-		t.Skip("not yet supported")
+	getReq := &rpc.GetApiDeploymentRequest{
+		Name: "projects/my-project/locations/global/apis/my-api/deployments/d",
+	}
+	firstRevision, err := server.GetApiDeployment(ctx, getReq)
+	if err != nil {
+		t.Fatalf("Setup: GetApiSpecRequest(%+v) returned error: %s", getReq, err)
+	}
 
+	t.Run("only remaining revision", func(t *testing.T) {
 		req := &rpc.DeleteApiDeploymentRevisionRequest{
-			Name: "projects/my-project/locations/global/apis/my-api/deployments/d",
+			Name: fmt.Sprintf("projects/my-project/locations/global/apis/my-api/deployments/d@%s", firstRevision.GetRevisionId()),
 		}
 
 		if _, err := server.DeleteApiDeploymentRevision(ctx, req); status.Code(err) != codes.FailedPrecondition {