Skip to content

Commit 6ea2df9

Browse files
authored
add repoisotry permission resource (#563)
1 parent 1698038 commit 6ea2df9

8 files changed

Lines changed: 748 additions & 0 deletions

File tree

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
package client
2+
3+
import (
4+
"context"
5+
"fmt"
6+
neturl "net/url"
7+
8+
"github.com/hashicorp/terraform-plugin-log/tflog"
9+
)
10+
11+
// VCRRepositoryPermission represents a permission on a Vercel Container
12+
// Registry repository that grants another team read (pull) access to its
13+
// images.
14+
type VCRRepositoryPermission struct {
15+
RepositoryID string `json:"repositoryId"`
16+
GrantedTeamID string `json:"teamId"`
17+
GrantedTeamSlug string `json:"teamSlug"`
18+
CreatedAt string `json:"createdAt"`
19+
TeamID string `json:"-"`
20+
}
21+
22+
func (c *Client) vcrRepositoryPermissionsURL(teamID, projectID, idOrName, suffix string) string {
23+
url := fmt.Sprintf("%s/v1/vcr/repository/%s/permissions%s?projectId=%s", c.baseURL, idOrName, suffix, projectID)
24+
if c.TeamID(teamID) != "" {
25+
url = fmt.Sprintf("%s&teamId=%s", url, c.TeamID(teamID))
26+
}
27+
return url
28+
}
29+
30+
type vcrRepositoryPermissionResponse struct {
31+
Permission VCRRepositoryPermission `json:"permission"`
32+
}
33+
34+
type CreateVCRRepositoryPermissionRequest struct {
35+
TeamID string `json:"-"`
36+
ProjectID string `json:"-"`
37+
IDOrName string `json:"-"`
38+
// Exactly one of GrantedTeamID or GrantedTeamSlug must be set.
39+
GrantedTeamID string `json:"teamId,omitempty"`
40+
GrantedTeamSlug string `json:"teamSlug,omitempty"`
41+
}
42+
43+
func (c *Client) CreateVCRRepositoryPermission(ctx context.Context, request CreateVCRRepositoryPermissionRequest) (res VCRRepositoryPermission, err error) {
44+
url := c.vcrRepositoryPermissionsURL(request.TeamID, request.ProjectID, request.IDOrName, "")
45+
payload := string(mustMarshal(request))
46+
tflog.Info(ctx, "creating vcr repository permission", map[string]any{
47+
"url": url,
48+
"payload": payload,
49+
})
50+
var out vcrRepositoryPermissionResponse
51+
err = c.doRequest(clientRequest{
52+
ctx: ctx,
53+
method: "POST",
54+
url: url,
55+
body: payload,
56+
}, &out)
57+
if err != nil {
58+
return res, err
59+
}
60+
res = out.Permission
61+
res.TeamID = c.TeamID(request.TeamID)
62+
return res, nil
63+
}
64+
65+
type ListVCRRepositoryPermissionsRequest struct {
66+
TeamID string
67+
ProjectID string
68+
IDOrName string
69+
}
70+
71+
type listVCRRepositoryPermissionsResponse struct {
72+
Permissions []VCRRepositoryPermission `json:"permissions"`
73+
NextCursor string `json:"nextCursor"`
74+
}
75+
76+
func (c *Client) ListVCRRepositoryPermissions(ctx context.Context, request ListVCRRepositoryPermissionsRequest) (res []VCRRepositoryPermission, err error) {
77+
baseURL := c.vcrRepositoryPermissionsURL(request.TeamID, request.ProjectID, request.IDOrName, "") + "&limit=100"
78+
cursor := ""
79+
for {
80+
url := baseURL
81+
if cursor != "" {
82+
url = fmt.Sprintf("%s&cursor=%s", url, cursor)
83+
}
84+
tflog.Info(ctx, "listing vcr repository permissions", map[string]any{
85+
"url": url,
86+
})
87+
var out listVCRRepositoryPermissionsResponse
88+
err = c.doRequest(clientRequest{
89+
ctx: ctx,
90+
method: "GET",
91+
url: url,
92+
}, &out)
93+
if err != nil {
94+
return res, err
95+
}
96+
for _, permission := range out.Permissions {
97+
permission.TeamID = c.TeamID(request.TeamID)
98+
res = append(res, permission)
99+
}
100+
if out.NextCursor == "" {
101+
return res, nil
102+
}
103+
cursor = neturl.QueryEscape(out.NextCursor)
104+
}
105+
}
106+
107+
type GetVCRRepositoryPermissionRequest struct {
108+
TeamID string
109+
ProjectID string
110+
IDOrName string
111+
// A permission matches if either GrantedTeamID or GrantedTeamSlug does.
112+
GrantedTeamID string
113+
GrantedTeamSlug string
114+
}
115+
116+
// GetVCRRepositoryPermission finds a single repository permission by the team
117+
// it was granted to. The API has no individual GET, so this lists and filters.
118+
func (c *Client) GetVCRRepositoryPermission(ctx context.Context, request GetVCRRepositoryPermissionRequest) (res VCRRepositoryPermission, err error) {
119+
permissions, err := c.ListVCRRepositoryPermissions(ctx, ListVCRRepositoryPermissionsRequest{
120+
TeamID: request.TeamID,
121+
ProjectID: request.ProjectID,
122+
IDOrName: request.IDOrName,
123+
})
124+
if err != nil {
125+
return res, err
126+
}
127+
for _, permission := range permissions {
128+
if (request.GrantedTeamID != "" && permission.GrantedTeamID == request.GrantedTeamID) ||
129+
(request.GrantedTeamSlug != "" && permission.GrantedTeamSlug == request.GrantedTeamSlug) {
130+
return permission, nil
131+
}
132+
}
133+
grantedTeam := request.GrantedTeamID
134+
if grantedTeam == "" {
135+
grantedTeam = request.GrantedTeamSlug
136+
}
137+
return res, APIError{
138+
StatusCode: 404,
139+
Code: "not_found",
140+
Message: fmt.Sprintf("The repository %s is not shared with team %s.", request.IDOrName, grantedTeam),
141+
}
142+
}
143+
144+
type DeleteVCRRepositoryPermissionRequest struct {
145+
TeamID string `json:"-"`
146+
ProjectID string `json:"-"`
147+
IDOrName string `json:"-"`
148+
// Exactly one of GrantedTeamID or GrantedTeamSlug must be set.
149+
GrantedTeamID string `json:"teamId,omitempty"`
150+
GrantedTeamSlug string `json:"teamSlug,omitempty"`
151+
}
152+
153+
func (c *Client) DeleteVCRRepositoryPermission(ctx context.Context, request DeleteVCRRepositoryPermissionRequest) error {
154+
url := c.vcrRepositoryPermissionsURL(request.TeamID, request.ProjectID, request.IDOrName, "")
155+
payload := string(mustMarshal(request))
156+
tflog.Info(ctx, "deleting vcr repository permission", map[string]any{
157+
"url": url,
158+
"payload": payload,
159+
})
160+
return c.doRequest(clientRequest{
161+
ctx: ctx,
162+
method: "DELETE",
163+
url: url,
164+
body: payload,
165+
}, nil)
166+
}
167+
168+
type DeleteAllVCRRepositoryPermissionsRequest struct {
169+
TeamID string
170+
ProjectID string
171+
IDOrName string
172+
}
173+
174+
// DeleteAllVCRRepositoryPermissions removes every permission from a
175+
// repository, so it is no longer shared with any team.
176+
func (c *Client) DeleteAllVCRRepositoryPermissions(ctx context.Context, request DeleteAllVCRRepositoryPermissionsRequest) error {
177+
url := c.vcrRepositoryPermissionsURL(request.TeamID, request.ProjectID, request.IDOrName, "/all")
178+
tflog.Info(ctx, "deleting all vcr repository permissions", map[string]any{
179+
"url": url,
180+
})
181+
return c.doRequest(clientRequest{
182+
ctx: ctx,
183+
method: "DELETE",
184+
url: url,
185+
}, nil)
186+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "vercel_vcr_repository_permission Resource - terraform-provider-vercel"
4+
subcategory: ""
5+
description: |-
6+
Provides a Vercel Container Registry (VCR) Repository Permission resource.
7+
A VCR Repository Permission shares a VCR Repository with another Vercel Team, granting
8+
it read (pull) access to the repository's images. One resource manages a single grant,
9+
identified by the repository and the team the permission is granted to.
10+
---
11+
12+
# vercel_vcr_repository_permission (Resource)
13+
14+
Provides a Vercel Container Registry (VCR) Repository Permission resource.
15+
16+
A VCR Repository Permission shares a VCR Repository with another Vercel Team, granting
17+
it read (pull) access to the repository's images. One resource manages a single grant,
18+
identified by the repository and the team the permission is granted to.
19+
20+
## Example Usage
21+
22+
```terraform
23+
resource "vercel_project" "example" {
24+
name = "example-project-with-vcr-repository"
25+
}
26+
27+
resource "vercel_vcr_repository" "example" {
28+
project_id = vercel_project.example.id
29+
name = "my-repository"
30+
}
31+
32+
# Share the repository with another team, granting it
33+
# read (pull) access to the repository's images.
34+
resource "vercel_vcr_repository_permission" "example" {
35+
project_id = vercel_project.example.id
36+
repository = vercel_vcr_repository.example.name
37+
granted_team_id = "team_xxxxxxxxxxxxxxxxxxxxxxxx"
38+
}
39+
40+
# The granted team can alternatively be referenced by its slug.
41+
resource "vercel_vcr_repository_permission" "example_by_slug" {
42+
project_id = vercel_project.example.id
43+
repository = vercel_vcr_repository.example.name
44+
granted_team_slug = "my-other-team"
45+
}
46+
```
47+
48+
<!-- schema generated by tfplugindocs -->
49+
## Schema
50+
51+
### Required
52+
53+
- `project_id` (String) The ID of the Vercel Project the repository belongs to.
54+
- `repository` (String) The ID or name of the VCR Repository to share.
55+
56+
### Optional
57+
58+
- `granted_team_id` (String) The ID of the team to grant pull access to. Must specify one of granted_team_id or granted_team_slug.
59+
- `granted_team_slug` (String) The slug of the team to grant pull access to. Must specify one of granted_team_id or granted_team_slug.
60+
- `team_id` (String) The ID of the team that owns the repository. Required when configuring a team resource if a default team has not been set in the provider.
61+
62+
### Read-Only
63+
64+
- `id` (String) The ID of this resource. Format: `repository_id/granted_team_id`.
65+
- `repository_id` (String) The ID of the VCR Repository the permission is granted on.
66+
67+
## Import
68+
69+
Import is supported using the following syntax:
70+
71+
The [`terraform import` command](https://developer.hashicorp.com/terraform/cli/commands/import) can be used, for example:
72+
73+
```shell
74+
# If importing into a personal account, or with a team configured on
75+
# the provider, use the project_id, repository name and granted team ID.
76+
# - project_id can be found in the project `settings` tab in the Vercel UI.
77+
# - granted_team_id is the ID of the team the repository is shared with.
78+
terraform import vercel_vcr_repository_permission.example prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx/my-repository/team_xxxxxxxxxxxxxxxxxxxxxxxx
79+
80+
# Alternatively, you can import via the owning team_id, project_id,
81+
# repository name and granted team ID.
82+
# - team_id can be found in the team `settings` tab in the Vercel UI.
83+
# - project_id can be found in the project `settings` tab in the Vercel UI.
84+
# - granted_team_id is the ID of the team the repository is shared with.
85+
terraform import vercel_vcr_repository_permission.example team_xxxxxxxxxxxxxxxxxxxxxxxx/prj_xxxxxxxxxxxxxxxxxxxxxxxx/my-repository/team_yyyyyyyyyyyyyyyyyyyyyyyy
86+
```
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# If importing into a personal account, or with a team configured on
2+
# the provider, use the project_id, repository name and granted team ID.
3+
# - project_id can be found in the project `settings` tab in the Vercel UI.
4+
# - granted_team_id is the ID of the team the repository is shared with.
5+
terraform import vercel_vcr_repository_permission.example prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx/my-repository/team_xxxxxxxxxxxxxxxxxxxxxxxx
6+
7+
# Alternatively, you can import via the owning team_id, project_id,
8+
# repository name and granted team ID.
9+
# - team_id can be found in the team `settings` tab in the Vercel UI.
10+
# - project_id can be found in the project `settings` tab in the Vercel UI.
11+
# - granted_team_id is the ID of the team the repository is shared with.
12+
terraform import vercel_vcr_repository_permission.example team_xxxxxxxxxxxxxxxxxxxxxxxx/prj_xxxxxxxxxxxxxxxxxxxxxxxx/my-repository/team_yyyyyyyyyyyyyyyyyyyyyyyy
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
resource "vercel_project" "example" {
2+
name = "example-project-with-vcr-repository"
3+
}
4+
5+
resource "vercel_vcr_repository" "example" {
6+
project_id = vercel_project.example.id
7+
name = "my-repository"
8+
}
9+
10+
# Share the repository with another team, granting it
11+
# read (pull) access to the repository's images.
12+
resource "vercel_vcr_repository_permission" "example" {
13+
project_id = vercel_project.example.id
14+
repository = vercel_vcr_repository.example.name
15+
granted_team_id = "team_xxxxxxxxxxxxxxxxxxxxxxxx"
16+
}
17+
18+
# The granted team can alternatively be referenced by its slug.
19+
resource "vercel_vcr_repository_permission" "example_by_slug" {
20+
project_id = vercel_project.example.id
21+
repository = vercel_vcr_repository.example.name
22+
granted_team_slug = "my-other-team"
23+
}

vercel/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ func (p *vercelProvider) Resources(_ context.Context) []func() resource.Resource
9797
newTraceDrainResource,
9898
newUserTokenResource,
9999
newVCRRepositoryResource,
100+
newVCRRepositoryPermissionResource,
100101
newWebhookResource,
101102
newProjectRollingReleaseResource,
102103
}

0 commit comments

Comments
 (0)