diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 13d71ece..43103c3f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,25 +22,25 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Unshallow run: git fetch --prune --unshallow - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: - go-version: 1.21 + go-version: 1.22 - name: Import GPG key id: import_gpg - uses: crazy-max/ghaction-import-gpg@v5.0.0 + uses: crazy-max/ghaction-import-gpg@v6 with: gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} passphrase: ${{ secrets.PASSPHRASE }} - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v4 + uses: goreleaser/goreleaser-action@v6 with: version: ${{ github.event.inputs.tag }} args: release --clean diff --git a/.goreleaser.yml b/.goreleaser.yml index c1f2fb39..858bde04 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -1,3 +1,5 @@ +version: 2 + # Visit https://goreleaser.com for documentation on how to customize this # behavior. before: diff --git a/CHANGELOG.md b/CHANGELOG.md index 12e87bc4..fe496222 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +## 1.7.0 (July 24, 2024). Tested on Artifactory 7.84.17 with Terraform 1.9.2 and OpenTofu 1.7.3 + +NOTES: + +* provider: `check_license` attribute is deprecated and provider no longer checks Artifactory license during initialization. It will be removed in the next major version release. + +FEATURES: + +* **New Resource:** `project_share_repository` - New resource to share repository with a project. +* **New Resource:** `project_share_repository_with_all` - New resource to share repository with all projects. + +Issue: [#80](https://github.com/jfrog/terraform-provider-project/issues/80) +PR: [#147](https://github.com/jfrog/terraform-provider-project/pull/147) + ## 1.6.2 (June 4, 2024). Tested on Artifactory 7.84.14 with Terraform and OpenTofu 1.7.2 IMPROVEMENTS: diff --git a/docs/index.md b/docs/index.md index 0f27e950..3fb55ddb 100644 --- a/docs/index.md +++ b/docs/index.md @@ -250,12 +250,9 @@ provider "project" { ## Schema -### Required - -- `url` (String) URL of Artifactory. This can also be sourced from the `PROJECT_URL` or `JFROG_URL` environment variable. Default to 'http://localhost:8081' if not set. - ### Optional - `access_token` (String, Sensitive) This is a Bearer token that can be given to you by your admin under `Identity and Access`. This can also be sourced from the `PROJECT_ACCESS_TOKEN` or `JFROG_ACCESS_TOKEN` environment variable. Defauult to empty string if not set. -- `check_license` (Boolean) Toggle for pre-flight checking of Artifactory Enterprise license. Default to `true`. +- `check_license` (Boolean, Deprecated) Toggle for pre-flight checking of Artifactory Enterprise license. Default to `true`. - `oidc_provider_name` (String) OIDC provider name. See [Configure an OIDC Integration](https://jfrog.com/help/r/jfrog-platform-administration-documentation/configure-an-oidc-integration) for more details. +- `url` (String) URL of Artifactory. This can also be sourced from the `PROJECT_URL` or `JFROG_URL` environment variable. Default to 'http://localhost:8081' if not set. diff --git a/docs/resources/share_repository.md b/docs/resources/share_repository.md new file mode 100644 index 00000000..7c2bbc49 --- /dev/null +++ b/docs/resources/share_repository.md @@ -0,0 +1,38 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "project_share_repository Resource - terraform-provider-project" +subcategory: "" +description: |- + Share a local or remote repository with a list of projects. Project Members of the target project are granted actions to the shared repository according to their Roles and Role actions assigned in the target Project. Requires a user assigned with the 'Administer the Platform' role. Only available for Artifactory 7.90.1 or later. +--- + +# project_share_repository (Resource) + +Share a local or remote repository with a list of projects. Project Members of the target project are granted actions to the shared repository according to their Roles and Role actions assigned in the target Project. Requires a user assigned with the 'Administer the Platform' role. + +->Only available for Artifactory 7.90.1 or later. + +## Example Usage + +```terraform +resource "project_share_repository" "myprojectsharerepo" { + repo_key = "myrepo-generic-local" + target_project_key = "myproj" +} +``` + + +## Schema + +### Required + +- `repo_key` (String) The key of the repository. +- `target_project_key` (String) The project key to which the repository should be shared with. + +## Import + +Import is supported using the following syntax: + +```shell +terraform import project_share_repository.myprojectsharerepo repo_key:project_key +``` diff --git a/docs/resources/share_repository_with_all.md b/docs/resources/share_repository_with_all.md new file mode 100644 index 00000000..d85e51b0 --- /dev/null +++ b/docs/resources/share_repository_with_all.md @@ -0,0 +1,36 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "project_share_repository_with_all Resource - terraform-provider-project" +subcategory: "" +description: |- + Share a local or remote repository with all projects. Project Members of the target project are granted actions to the shared repository according to their Roles and Role actions assigned in the target Project. Requires a user assigned with the 'Administer the Platform' role. Only available for Artifactory 7.90.1 or later. +--- + +# project_share_repository_with_all (Resource) + +Share a local or remote repository with all projects. Project Members of the target project are granted actions to the shared repository according to their Roles and Role actions assigned in the target Project. Requires a user assigned with the 'Administer the Platform' role. + +->Only available for Artifactory 7.90.1 or later. + +## Example Usage + +```terraform +resource "project_share_repository_with_all" "myprojectsharerepo" { + repo_key = "myrepo-generic-local" +} +``` + + +## Schema + +### Required + +- `repo_key` (String) The key of the repository. + +## Import + +Import is supported using the following syntax: + +```shell +terraform import project_share_repository_with_all.myprojectsharerepo repo_key +``` diff --git a/examples/resources/project_share_repository/import.sh b/examples/resources/project_share_repository/import.sh new file mode 100644 index 00000000..12fe8aac --- /dev/null +++ b/examples/resources/project_share_repository/import.sh @@ -0,0 +1 @@ +terraform import project_share_repository.myprojectsharerepo repo_key:project_key \ No newline at end of file diff --git a/examples/resources/project_share_repository/resource.tf b/examples/resources/project_share_repository/resource.tf new file mode 100644 index 00000000..c9f1bb0c --- /dev/null +++ b/examples/resources/project_share_repository/resource.tf @@ -0,0 +1,4 @@ +resource "project_share_repository" "myprojectsharerepo" { + repo_key = "myrepo-generic-local" + target_project_key = "myproj" +} \ No newline at end of file diff --git a/examples/resources/project_share_repository_with_all/import.sh b/examples/resources/project_share_repository_with_all/import.sh new file mode 100644 index 00000000..81a6b948 --- /dev/null +++ b/examples/resources/project_share_repository_with_all/import.sh @@ -0,0 +1 @@ +terraform import project_share_repository_with_all.myprojectsharerepo repo_key \ No newline at end of file diff --git a/examples/resources/project_share_repository_with_all/resource.tf b/examples/resources/project_share_repository_with_all/resource.tf new file mode 100644 index 00000000..da1e5668 --- /dev/null +++ b/examples/resources/project_share_repository_with_all/resource.tf @@ -0,0 +1,3 @@ +resource "project_share_repository_with_all" "myprojectsharerepo" { + repo_key = "myrepo-generic-local" +} \ No newline at end of file diff --git a/go.mod b/go.mod index 3117b91d..9c1d4787 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/jfrog/terraform-provider-project // if you need to do local dev, literally just uncomment the line below // replace github.com/jfrog/terraform-provider-shared => ../terraform-provider-shared -go 1.21.5 +go 1.22.5 require ( github.com/go-resty/resty/v2 v2.13.1 diff --git a/main.go b/main.go index fee77772..99ad8172 100644 --- a/main.go +++ b/main.go @@ -6,7 +6,7 @@ import ( "log" "github.com/hashicorp/terraform-plugin-framework/providerserver" - provider "github.com/jfrog/terraform-provider-project/pkg/project/provider" + "github.com/jfrog/terraform-provider-project/pkg/project" ) // Run the docs generation tool, check its repository for more information on how it works and how docs @@ -24,7 +24,7 @@ func main() { Address: "registry.terraform.io/jfrog/project", Debug: debug, } - err := providerserver.Serve(context.Background(), provider.Framework(), opts) + err := providerserver.Serve(context.Background(), project.NewProvider(), opts) if err != nil { log.Fatal(err.Error()) } diff --git a/pkg/project/acctest/test.go b/pkg/project/acctest/test.go index 2d8d78ad..8951966d 100644 --- a/pkg/project/acctest/test.go +++ b/pkg/project/acctest/test.go @@ -13,7 +13,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/providerserver" "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-testing/terraform" - project "github.com/jfrog/terraform-provider-project/pkg/project/provider" + "github.com/jfrog/terraform-provider-project/pkg/project" "github.com/jfrog/terraform-provider-shared/client" "github.com/jfrog/terraform-provider-shared/testutil" ) @@ -31,7 +31,7 @@ var Provider provider.Provider var ProtoV6ProviderFactories map[string]func() (tfprotov6.ProviderServer, error) func init() { - Provider = project.Framework()() + Provider = project.NewProvider()() ProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){ "project": providerserver.NewProtocol6WithError(Provider), diff --git a/pkg/project/provider/framework.go b/pkg/project/provider.go similarity index 91% rename from pkg/project/provider/framework.go rename to pkg/project/provider.go index b8bc4f25..3e4e7dfe 100644 --- a/pkg/project/provider/framework.go +++ b/pkg/project/provider.go @@ -1,4 +1,4 @@ -package provider +package project import ( "context" @@ -17,6 +17,11 @@ import ( validatorfw_string "github.com/jfrog/terraform-provider-shared/validator/fw/string" ) +var Version = "1.6.1" + +// needs to be exported so make file can update this +var productId = "terraform-provider-project/" + Version + // Ensure the implementation satisfies the provider.Provider interface. var _ provider.Provider = &ProjectProvider{} @@ -65,8 +70,9 @@ func (p *ProjectProvider) Schema(ctx context.Context, req provider.SchemaRequest Description: "OIDC provider name. See [Configure an OIDC Integration](https://jfrog.com/help/r/jfrog-platform-administration-documentation/configure-an-oidc-integration) for more details.", }, "check_license": schema.BoolAttribute{ - Optional: true, - Description: "Toggle for pre-flight checking of Artifactory Enterprise license. Default to `true`.", + Optional: true, + Description: "Toggle for pre-flight checking of Artifactory Enterprise license. Default to `true`.", + DeprecationMessage: "Remove this attribute from your provider configuration as it is no longer used and the attribute will be removed in the next major version of the provider.", }, }, } @@ -147,16 +153,6 @@ func (p *ProjectProvider) Configure(ctx context.Context, req provider.ConfigureR ) } - if config.CheckLicense.IsNull() || config.CheckLicense.ValueBool() { - if err := util.CheckArtifactoryLicense(restyClient, "Enterprise", "Commercial", "Edge"); err != nil { - resp.Diagnostics.AddError( - "Error checking Artifactory license", - err.Error(), - ) - return - } - } - version, err := util.GetArtifactoryVersion(restyClient) if err != nil { resp.Diagnostics.AddError( @@ -189,6 +185,8 @@ func (p *ProjectProvider) Resources(ctx context.Context) []func() resource.Resou project.NewProjectGroupResource, project.NewProjectRepositoryResource, project.NewProjectRoleResource, + project.NewProjectShareRepositoryResource, + project.NewProjectShareRepositoryWithAllResource, project.NewProjectUserResource, } } @@ -198,7 +196,7 @@ func (p *ProjectProvider) DataSources(_ context.Context) []func() datasource.Dat return []func() datasource.DataSource{} } -func Framework() func() provider.Provider { +func NewProvider() func() provider.Provider { return func() provider.Provider { return &ProjectProvider{} } diff --git a/pkg/project/provider/provider.go b/pkg/project/provider/provider.go deleted file mode 100644 index 793765f5..00000000 --- a/pkg/project/provider/provider.go +++ /dev/null @@ -1,6 +0,0 @@ -package provider - -var Version = "1.6.1" - -// needs to be exported so make file can update this -var productId = "terraform-provider-project/" + Version diff --git a/pkg/project/resource/membership_test.go b/pkg/project/resource/membership_test.go index 4e173f26..9eb311b8 100644 --- a/pkg/project/resource/membership_test.go +++ b/pkg/project/resource/membership_test.go @@ -15,8 +15,8 @@ func TestAccProject_membership(t *testing.T) { resourceName := "project." + name projectKey := strings.ToLower(acctest.RandSeq(10)) - username1 := "user1" - username2 := "user2" + username1 := fmt.Sprintf("user1%s", strings.ToLower(acctest.RandSeq(5))) + username2 := fmt.Sprintf("user2%s", strings.ToLower(acctest.RandSeq(5))) developeRole := "Developer" contributorRole := "Contributor" @@ -174,8 +174,9 @@ func TestAccProject_group(t *testing.T) { resourceName := "project." + name projectKey := strings.ToLower(acctest.RandSeq(10)) - group1 := "group1" - group2 := "group2" + group1 := fmt.Sprintf("group1%s", strings.ToLower(acctest.RandSeq(5))) + group2 := fmt.Sprintf("group2%s", strings.ToLower(acctest.RandSeq(5))) + developeRole := "Developer" contributorRole := "Contributor" diff --git a/pkg/project/resource/resource_project.go b/pkg/project/resource/resource_project.go index b8e20f96..bd3a940c 100644 --- a/pkg/project/resource/resource_project.go +++ b/pkg/project/resource/resource_project.go @@ -38,7 +38,9 @@ const ( var customRoleTypeRegex = regexp.MustCompile(fmt.Sprintf("^%s$", customRoleType)) func NewProjectResource() resource.Resource { - return &ProjectResource{} + return &ProjectResource{ + TypeName: "project", + } } type ProjectResource struct { @@ -401,7 +403,6 @@ type ProjectAPIModel struct { func (r *ProjectResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { resp.TypeName = req.ProviderTypeName - r.TypeName = resp.TypeName } var schemaV1 = schema.Schema{ diff --git a/pkg/project/resource/resource_project_environment.go b/pkg/project/resource/resource_project_environment.go index 72c212c6..f0f47546 100644 --- a/pkg/project/resource/resource_project_environment.go +++ b/pkg/project/resource/resource_project_environment.go @@ -24,7 +24,9 @@ import ( const ProjectEnvironmentUrl = "/access/api/v1/projects/{projectKey}/environments" func NewProjectEnvironmentResource() resource.Resource { - return &ProjectEnvironmentResource{} + return &ProjectEnvironmentResource{ + TypeName: "project_environment", + } } type ProjectEnvironmentResource struct { @@ -47,8 +49,7 @@ type ProjectEnvironmentUpdateAPIModel struct { } func (r *ProjectEnvironmentResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_environment" - r.TypeName = resp.TypeName + resp.TypeName = r.TypeName } func (r *ProjectEnvironmentResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { diff --git a/pkg/project/resource/resource_project_group.go b/pkg/project/resource/resource_project_group.go index bffe6a31..1f5779ea 100644 --- a/pkg/project/resource/resource_project_group.go +++ b/pkg/project/resource/resource_project_group.go @@ -23,7 +23,9 @@ import ( const ProjectGroupsUrl = "access/api/v1/projects/{projectKey}/groups/{name}" func NewProjectGroupResource() resource.Resource { - return &ProjectGroupResource{} + return &ProjectGroupResource{ + TypeName: "project_group", + } } type ProjectGroupResource struct { @@ -44,8 +46,7 @@ type ProjectGroupAPIModel struct { } func (r *ProjectGroupResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_group" - r.TypeName = resp.TypeName + resp.TypeName = r.TypeName } func (r *ProjectGroupResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { diff --git a/pkg/project/resource/resource_project_repository.go b/pkg/project/resource/resource_project_repository.go index 04fcb229..fd9ffe8b 100644 --- a/pkg/project/resource/resource_project_repository.go +++ b/pkg/project/resource/resource_project_repository.go @@ -24,7 +24,9 @@ import ( const repositoryEndpoint = "/artifactory/api/repositories/{key}" func NewProjectRepositoryResource() resource.Resource { - return &ProjectRepositoryResource{} + return &ProjectRepositoryResource{ + TypeName: "project_repository", + } } type ProjectRepositoryResource struct { @@ -44,8 +46,7 @@ type ProjectRepositoryAPIModel struct { } func (r *ProjectRepositoryResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_repository" - r.TypeName = resp.TypeName + resp.TypeName = r.TypeName } func (r *ProjectRepositoryResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { @@ -169,29 +170,73 @@ func (r *ProjectRepositoryResource) Read(ctx context.Context, req resource.ReadR projectKey := state.ProjectKey.ValueString() repoKey := state.Key.ValueString() - var repo ProjectRepositoryAPIModel - var projectError ProjectErrorsResponse - response, err := r.ProviderData.Client.R(). - SetResult(&repo). - SetPathParam("key", repoKey). - Get(repositoryEndpoint) + newAPIVersion, err := util.CheckVersion(r.ProviderData.ArtifactoryVersion, "7.90.1") if err != nil { - utilfw.UnableToRefreshResourceError(resp, err.Error()) - return + resp.Diagnostics.AddError( + "Failed to check Artifactory version", + err.Error(), + ) } - if response.StatusCode() == http.StatusBadRequest || response.StatusCode() == http.StatusNotFound { - resp.State.RemoveResource(ctx) - return - } - if response.IsError() { - utilfw.UnableToRefreshResourceError(resp, projectError.String()) - return - } - if repo.ProjectKey == "" { - tflog.Warn(ctx, "no project_key for repo", map[string]any{"repoKey": repoKey}) - resp.State.RemoveResource(ctx) - return + var projectError ProjectErrorsResponse + if newAPIVersion { + // use new project API + var status ProjectRepositoryStatusAPIModel + response, err := r.ProviderData.Client.R(). + SetResult(&status). + SetPathParam("repo_key", repoKey). + Get(ProjectRepositoryStatusEndpoint) + if err != nil { + utilfw.UnableToRefreshResourceError(resp, err.Error()) + return + } + + if response.StatusCode() == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } + + if response.IsError() { + utilfw.UnableToRefreshResourceError(resp, projectError.String()) + return + } + + if status.AssignedTo != projectKey { + tflog.Warn(ctx, "repo not assigned to project", map[string]any{ + "repoKey": repoKey, + "projectKey": projectKey, + }) + resp.State.RemoveResource(ctx) + return + } + } else { + // continue using old repo API for checking + var repo ProjectRepositoryAPIModel + response, err := r.ProviderData.Client.R(). + SetResult(&repo). + SetPathParam("key", repoKey). + Get(repositoryEndpoint) + if err != nil { + utilfw.UnableToRefreshResourceError(resp, err.Error()) + return + } + + if response.StatusCode() == http.StatusBadRequest || response.StatusCode() == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } + if response.IsError() { + utilfw.UnableToRefreshResourceError(resp, projectError.String()) + return + } + if repo.ProjectKey == "" { + tflog.Warn(ctx, "no project_key for repo", map[string]any{ + "repoKey": repoKey, + "projectKey": projectKey, + }) + resp.State.RemoveResource(ctx) + return + } } state.ID = types.StringValue(fmt.Sprintf("%s-%s", projectKey, repoKey)) diff --git a/pkg/project/resource/resource_project_role.go b/pkg/project/resource/resource_project_role.go index 76d1a5aa..ba8dc0d0 100644 --- a/pkg/project/resource/resource_project_role.go +++ b/pkg/project/resource/resource_project_role.go @@ -65,7 +65,9 @@ var validRoleActions = []string{ } func NewProjectRoleResource() resource.Resource { - return &ProjectRoleResource{} + return &ProjectRoleResource{ + TypeName: "project_role", + } } type ProjectRoleResource struct { @@ -90,8 +92,7 @@ type ProjectRoleAPIModel struct { } func (r *ProjectRoleResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_role" - r.TypeName = resp.TypeName + resp.TypeName = r.TypeName } func (r *ProjectRoleResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { diff --git a/pkg/project/resource/resource_project_share_repository.go b/pkg/project/resource/resource_project_share_repository.go new file mode 100644 index 00000000..f8639c0f --- /dev/null +++ b/pkg/project/resource/resource_project_share_repository.go @@ -0,0 +1,236 @@ +package project + +import ( + "context" + "fmt" + "net/http" + "slices" + "strings" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/jfrog/terraform-provider-shared/util" + utilfw "github.com/jfrog/terraform-provider-shared/util/fw" + validatorfw_string "github.com/jfrog/terraform-provider-shared/validator/fw/string" +) + +const shareWithTargetProject = "access/api/v1/projects/_/share/repositories/{repo_key}/{target_project_key}" + +func NewProjectShareRepositoryResource() resource.Resource { + return &ProjectShareRepositoryResource{ + TypeName: "project_share_repository", + } +} + +type ProjectShareRepositoryResource struct { + ProviderData util.ProviderMetadata + TypeName string +} + +type ProjectShareRepositoryResourceModel struct { + RepoKey types.String `tfsdk:"repo_key"` + TargetProjectKey types.String `tfsdk:"target_project_key"` +} + +func (r *ProjectShareRepositoryResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = r.TypeName +} + +func (r *ProjectShareRepositoryResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "repo_key": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + validatorfw_string.RepoKey(), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Description: "The key of the repository.", + }, + "target_project_key": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + validatorfw_string.ProjectKey(), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Description: "The project key to which the repository should be shared with.", + }, + }, + Description: "Share a local or remote repository with a list of projects. Project Members of the target project are granted actions to the shared repository according to their Roles and Role actions assigned in the target Project. Requires a user assigned with the 'Administer the Platform' role.\n\n" + + "->Only available for Artifactory 7.90.1 or later.", + } +} + +func (r *ProjectShareRepositoryResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + // Prevent panic if the provider has not been configured. + if req.ProviderData == nil { + return + } + + r.ProviderData = req.ProviderData.(util.ProviderMetadata) + + supported, err := util.CheckVersion(r.ProviderData.ArtifactoryVersion, "7.90.1") + if err != nil { + resp.Diagnostics.AddError( + "Failed to check Artifactory version", + err.Error(), + ) + return + } + + if !supported { + resp.Diagnostics.AddError( + "Unsupported Artifactory version", + fmt.Sprintf("This resource is supported by Artifactory version 7.90.1 or later. Current version: %s", r.ProviderData.ArtifactoryVersion), + ) + return + } +} + +func (r *ProjectShareRepositoryResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + go util.SendUsageResourceCreate(ctx, r.ProviderData.Client.R(), r.ProviderData.ProductId, r.TypeName) + + var plan ProjectShareRepositoryResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + var projectError ProjectErrorsResponse + + response, err := r.ProviderData.Client.R(). + SetPathParams(map[string]string{ + "repo_key": plan.RepoKey.ValueString(), + "target_project_key": plan.TargetProjectKey.ValueString(), + }). + SetError(&projectError). + Put(shareWithTargetProject) + + if err != nil { + utilfw.UnableToCreateResourceError(resp, err.Error()) + return + } + + if response.IsError() { + utilfw.UnableToCreateResourceError(resp, projectError.String()) + return + } + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *ProjectShareRepositoryResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + go util.SendUsageResourceRead(ctx, r.ProviderData.Client.R(), r.ProviderData.ProductId, r.TypeName) + + var state ProjectShareRepositoryResourceModel + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + repoKey := state.RepoKey.ValueString() + + var status ProjectRepositoryStatusAPIModel + var projectError ProjectErrorsResponse + response, err := r.ProviderData.Client.R(). + SetPathParam("repo_key", repoKey). + SetResult(&status). + SetError(&projectError). + Get(ProjectRepositoryStatusEndpoint) + + if err != nil { + utilfw.UnableToRefreshResourceError(resp, err.Error()) + return + } + + if response.StatusCode() == http.StatusNotFound { + resp.Diagnostics.AddWarning( + "repo not found", + repoKey, + ) + resp.State.RemoveResource(ctx) + return + } + + if response.IsError() { + utilfw.UnableToRefreshResourceError(resp, projectError.String()) + return + } + + if !slices.Contains(status.SharedWithProjects, state.TargetProjectKey.ValueString()) { + state.TargetProjectKey = types.StringNull() + } + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *ProjectShareRepositoryResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + resp.Diagnostics.AddWarning( + "Update not supported", + "Repository sharing with project cannnot be updated.", + ) +} + +func (r *ProjectShareRepositoryResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + go util.SendUsageResourceDelete(ctx, r.ProviderData.Client.R(), r.ProviderData.ProductId, r.TypeName) + + var state ProjectShareRepositoryResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + var projectError ProjectErrorsResponse + + response, err := r.ProviderData.Client.R(). + SetPathParams(map[string]string{ + "repo_key": state.RepoKey.ValueString(), + "target_project_key": state.TargetProjectKey.ValueString(), + }). + SetError(&projectError). + Delete(shareWithTargetProject) + + if err != nil { + utilfw.UnableToDeleteResourceError(resp, err.Error()) + return + } + + if response.IsError() { + utilfw.UnableToDeleteResourceError(resp, projectError.String()) + return + } + + // If the logic reaches here, it implicitly succeeded and will remove + // the resource from state if there are no other errors. +} + +// ImportState imports the resource into the Terraform state. +func (r *ProjectShareRepositoryResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + parts := strings.SplitN(req.ID, ":", 2) + if len(parts) != 2 || parts[0] == "" || parts[1] == "" { + resp.Diagnostics.AddError( + "Unexpected Import Identifier", + "Expected repo_key:target_project_key", + ) + return + } + + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("repo_key"), parts[0])...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("target_project_key"), parts[1])...) +} diff --git a/pkg/project/resource/resource_project_share_repository_test.go b/pkg/project/resource/resource_project_share_repository_test.go new file mode 100644 index 00000000..a000845a --- /dev/null +++ b/pkg/project/resource/resource_project_share_repository_test.go @@ -0,0 +1,144 @@ +package project_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + acctest "github.com/jfrog/terraform-provider-project/pkg/project/acctest" + "github.com/jfrog/terraform-provider-shared/testutil" + "github.com/jfrog/terraform-provider-shared/util" +) + +func TestAccProjectShareRepository_full(t *testing.T) { + client := acctest.GetTestResty(t) + version, err := util.GetArtifactoryVersion(client) + if err != nil { + t.Fatal(err) + } + valid, err := util.CheckVersion(version, "7.90.1") + if err != nil { + t.Fatal(err) + } + if !valid { + t.Skipf("Artifactory version %s is earlier than 7.90.1", version) + } + + projectKey1 := strings.ToLower(acctest.RandSeq(10)) + projectKey2 := strings.ToLower(acctest.RandSeq(10)) + projectName1 := fmt.Sprintf("tftestprojects%s", projectKey1) + projectName2 := fmt.Sprintf("tftestprojects%s", projectKey2) + + repoKey := fmt.Sprintf("repo%d", testutil.RandomInt()) + + _, fqrn, resourceName := testutil.MkNames("test-project-share-repo", "project_share_repository") + + params := map[string]string{ + "project_name_1": projectName1, + "project_key_1": projectKey1, + "repo_key": repoKey, + "resource_name": resourceName, + } + + temp := ` + resource "artifactory_local_generic_repository" "{{ .repo_key }}" { + key = "{{ .repo_key }}" + + lifecycle { + ignore_changes = ["project_key"] + } + } + + resource "project" "{{ .project_name_1 }}" { + key = "{{ .project_key_1 }}" + display_name = "{{ .project_name_1 }}" + description = "test description" + admin_privileges { + manage_members = true + manage_resources = true + index_resources = true + } + max_storage_in_gibibytes = 1 + block_deployments_on_limit = true + email_notification = false + } + + resource "project_share_repository" "{{ .resource_name }}" { + repo_key = artifactory_local_generic_repository.{{ .repo_key }}.key + target_project_key = project.{{ .project_name_1 }}.key + } + ` + + config := util.ExecuteTemplate("TestAccProjectShareRepository", temp, params) + + updatedTemp := ` + resource "artifactory_local_generic_repository" "{{ .repo_key }}" { + key = "{{ .repo_key }}" + + lifecycle { + ignore_changes = ["project_key"] + } + } + + resource "project" "{{ .project_name_2 }}" { + key = "{{ .project_key_2 }}" + display_name = "{{ .project_name_2 }}" + description = "test description" + admin_privileges { + manage_members = true + manage_resources = true + index_resources = true + } + max_storage_in_gibibytes = 1 + block_deployments_on_limit = true + email_notification = false + } + + resource "project_share_repository" "{{ .resource_name }}" { + repo_key = artifactory_local_generic_repository.{{ .repo_key }}.key + target_project_key = project.{{ .project_name_2 }}.key + } + ` + updateParams := map[string]string{ + "project_name_2": projectName2, + "project_key_2": projectKey2, + "repo_key": params["repo_key"], + "resource_name": resourceName, + } + + configUpdated := util.ExecuteTemplate("TestAccProjectShareRepository", updatedTemp, updateParams) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories, + ExternalProviders: map[string]resource.ExternalProvider{ + "artifactory": { + Source: "jfrog/artifactory", + }, + }, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(fqrn, "repo_key", params["repo_key"]), + resource.TestCheckResourceAttr(fqrn, "target_project_key", params["project_key_1"]), + ), + }, + { + Config: configUpdated, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(fqrn, "repo_key", updateParams["repo_key"]), + resource.TestCheckResourceAttr(fqrn, "target_project_key", updateParams["project_key_2"]), + ), + }, + { + ResourceName: fqrn, + ImportStateId: fmt.Sprintf("%s:%s", updateParams["repo_key"], updateParams["project_key_2"]), + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: "repo_key", + }, + }, + }) +} diff --git a/pkg/project/resource/resource_project_share_repository_with_all.go b/pkg/project/resource/resource_project_share_repository_with_all.go new file mode 100644 index 00000000..f6c7f15b --- /dev/null +++ b/pkg/project/resource/resource_project_share_repository_with_all.go @@ -0,0 +1,208 @@ +package project + +import ( + "context" + "fmt" + "net/http" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/jfrog/terraform-provider-shared/util" + utilfw "github.com/jfrog/terraform-provider-shared/util/fw" + validatorfw_string "github.com/jfrog/terraform-provider-shared/validator/fw/string" +) + +const shareWithAllProjectsEndpoint = "access/api/v1/projects/_/share/repositories/{repo_key}" + +func NewProjectShareRepositoryWithAllResource() resource.Resource { + return &ProjectShareRepositoryWithAllResource{ + TypeName: "project_share_repository_with_all", + } +} + +type ProjectShareRepositoryWithAllResource struct { + ProviderData util.ProviderMetadata + TypeName string +} + +type ProjectShareRepositoryWithAllResourceModel struct { + RepoKey types.String `tfsdk:"repo_key"` +} + +func (r *ProjectShareRepositoryWithAllResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = r.TypeName +} + +func (r *ProjectShareRepositoryWithAllResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "repo_key": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + validatorfw_string.RepoKey(), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Description: "The key of the repository.", + }, + }, + Description: "Share a local or remote repository with all projects. Project Members of the target project are granted actions to the shared repository according to their Roles and Role actions assigned in the target Project. Requires a user assigned with the 'Administer the Platform' role.\n\n" + + "->Only available for Artifactory 7.90.1 or later.", + } +} + +func (r *ProjectShareRepositoryWithAllResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + // Prevent panic if the provider has not been configured. + if req.ProviderData == nil { + return + } + + r.ProviderData = req.ProviderData.(util.ProviderMetadata) + + supported, err := util.CheckVersion(r.ProviderData.ArtifactoryVersion, "7.90.1") + if err != nil { + resp.Diagnostics.AddError( + "Failed to check Artifactory version", + err.Error(), + ) + return + } + + if !supported { + resp.Diagnostics.AddError( + "Unsupported Artifactory version", + fmt.Sprintf("This resource is supported by Artifactory version 7.90.1 or later. Current version: %s", r.ProviderData.ArtifactoryVersion), + ) + return + } +} + +func (r *ProjectShareRepositoryWithAllResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + go util.SendUsageResourceCreate(ctx, r.ProviderData.Client.R(), r.ProviderData.ProductId, r.TypeName) + + var plan ProjectShareRepositoryWithAllResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + var projectError ProjectErrorsResponse + + response, err := r.ProviderData.Client.R(). + SetPathParam("repo_key", plan.RepoKey.ValueString()). + SetError(&projectError). + Put(shareWithAllProjectsEndpoint) + + if err != nil { + utilfw.UnableToCreateResourceError(resp, err.Error()) + } + + if response.IsError() { + utilfw.UnableToCreateResourceError(resp, projectError.String()) + } + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *ProjectShareRepositoryWithAllResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + go util.SendUsageResourceRead(ctx, r.ProviderData.Client.R(), r.ProviderData.ProductId, r.TypeName) + + var state ProjectShareRepositoryWithAllResourceModel + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + repoKey := state.RepoKey.ValueString() + + var status ProjectRepositoryStatusAPIModel + var projectError ProjectErrorsResponse + response, err := r.ProviderData.Client.R(). + SetPathParam("repo_key", repoKey). + SetResult(&status). + SetError(&projectError). + Get("access/api/v1/projects/_/repositories/{repo_key}") + + if err != nil { + utilfw.UnableToRefreshResourceError(resp, err.Error()) + return + } + + if response.StatusCode() == http.StatusNotFound { + resp.Diagnostics.AddWarning( + "repo not found", + repoKey, + ) + resp.State.RemoveResource(ctx) + return + } + + if response.IsError() { + utilfw.UnableToRefreshResourceError(resp, projectError.String()) + return + } + + if !status.SharedWithAllProjects { + resp.Diagnostics.AddWarning( + "repo is not shared with all projects", + repoKey, + ) + resp.State.RemoveResource(ctx) + return + } + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *ProjectShareRepositoryWithAllResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + resp.Diagnostics.AddWarning( + "Update not supported", + "Repository sharing with all projects cannnot be updated.", + ) +} + +func (r *ProjectShareRepositoryWithAllResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + go util.SendUsageResourceDelete(ctx, r.ProviderData.Client.R(), r.ProviderData.ProductId, r.TypeName) + + var state ProjectShareRepositoryWithAllResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + var projectError ProjectErrorsResponse + + response, err := r.ProviderData.Client.R(). + SetPathParam("repo_key", state.RepoKey.ValueString()). + SetError(&projectError). + Delete(shareWithAllProjectsEndpoint) + + if err != nil { + utilfw.UnableToDeleteResourceError(resp, err.Error()) + } + + if response.IsError() { + utilfw.UnableToDeleteResourceError(resp, projectError.String()) + } + + // If the logic reaches here, it implicitly succeeded and will remove + // the resource from state if there are no other errors. +} + +// ImportState imports the resource into the Terraform state. +func (r *ProjectShareRepositoryWithAllResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("repo_key"), req, resp) +} diff --git a/pkg/project/resource/resource_project_share_repository_with_all_test.go b/pkg/project/resource/resource_project_share_repository_with_all_test.go new file mode 100644 index 00000000..465017b9 --- /dev/null +++ b/pkg/project/resource/resource_project_share_repository_with_all_test.go @@ -0,0 +1,101 @@ +package project_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + acctest "github.com/jfrog/terraform-provider-project/pkg/project/acctest" + "github.com/jfrog/terraform-provider-shared/testutil" + "github.com/jfrog/terraform-provider-shared/util" +) + +func TestAccProjectShareWithAllRepository_full(t *testing.T) { + client := acctest.GetTestResty(t) + version, err := util.GetArtifactoryVersion(client) + if err != nil { + t.Fatal(err) + } + valid, err := util.CheckVersion(version, "7.90.1") + if err != nil { + t.Fatal(err) + } + if !valid { + t.Skipf("Artifactory version %s is earlier than 7.90.1", version) + } + + projectKey := strings.ToLower(acctest.RandSeq(10)) + projectName := fmt.Sprintf("tftestprojects%s", projectKey) + + repoKey := fmt.Sprintf("repo%d", testutil.RandomInt()) + + _, fqrn, resourceName := testutil.MkNames("test-project-share-repo", "project_share_repository_with_all") + + params := map[string]interface{}{ + "project_name": projectName, + "project_key": projectKey, + "repo_key": repoKey, + "resource_name": resourceName, + "share_with_all": "true", + } + + temp := ` + resource "artifactory_local_generic_repository" "{{ .repo_key }}" { + key = "{{ .repo_key }}" + + lifecycle { + ignore_changes = ["project_key"] + } + } + + resource "project" "{{ .project_name }}" { + key = "{{ .project_key }}" + display_name = "{{ .project_name }}" + description = "test description" + admin_privileges { + manage_members = true + manage_resources = true + index_resources = true + } + max_storage_in_gibibytes = 1 + block_deployments_on_limit = true + email_notification = false + } + + resource "project_share_repository_with_all" "{{ .resource_name }}" { + repo_key = artifactory_local_generic_repository.{{ .repo_key }}.key + + depends_on = [ + project.{{ .project_name }} + ] + } + ` + + config := util.ExecuteTemplate("TestAccProjectShareRepository", temp, params) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories, + ExternalProviders: map[string]resource.ExternalProvider{ + "artifactory": { + Source: "jfrog/artifactory", + }, + }, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(fqrn, "repo_key", params["repo_key"].(string)), + ), + }, + { + ResourceName: fqrn, + ImportStateId: params["repo_key"].(string), + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: "repo_key", + }, + }, + }) +} diff --git a/pkg/project/resource/resource_project_test.go b/pkg/project/resource/resource_project_test.go index 3c24ec64..a1ca28c8 100644 --- a/pkg/project/resource/resource_project_test.go +++ b/pkg/project/resource/resource_project_test.go @@ -19,12 +19,12 @@ func TestAccProject_UpgradeFromSDKv2(t *testing.T) { name := fmt.Sprintf("tftestprojects%s", acctest.RandSeq(10)) resourceName := fmt.Sprintf("project.%s", name) - username1 := "user1" + username1 := fmt.Sprintf("user1%s", strings.ToLower(acctest.RandSeq(5))) email1 := username1 + "@tempurl.org" - username2 := "user2" + username2 := fmt.Sprintf("user2%s", strings.ToLower(acctest.RandSeq(5))) email2 := username2 + "@tempurl.org" - group1 := "group1" - group2 := "group2" + group1 := fmt.Sprintf("group1%s", strings.ToLower(acctest.RandSeq(5))) + group2 := fmt.Sprintf("group2%s", strings.ToLower(acctest.RandSeq(5))) repo1 := fmt.Sprintf("repo%s", strings.ToLower(acctest.RandSeq(6))) repo2 := fmt.Sprintf("repo%s", strings.ToLower(acctest.RandSeq(6))) @@ -158,8 +158,7 @@ func TestAccProject_UpgradeFromSDKv2(t *testing.T) { Source: "jfrog/project", }, "artifactory": { - Source: "jfrog/artifactory", - VersionConstraint: "10.3.3", + Source: "jfrog/artifactory", }, }, Config: config, @@ -199,8 +198,7 @@ func TestAccProject_UpgradeFromSDKv2(t *testing.T) { { ExternalProviders: map[string]resource.ExternalProvider{ "artifactory": { - Source: "jfrog/artifactory", - VersionConstraint: "10.3.3", + Source: "jfrog/artifactory", }, }, ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories, @@ -453,12 +451,12 @@ func TestAccProject_full(t *testing.T) { name := fmt.Sprintf("tftestprojects%s", acctest.RandSeq(10)) resourceName := fmt.Sprintf("project.%s", name) - username1 := "user1" + username1 := fmt.Sprintf("user1%s", strings.ToLower(acctest.RandSeq(5))) email1 := username1 + "@tempurl.org" - username2 := "user2" + username2 := fmt.Sprintf("user2%s", strings.ToLower(acctest.RandSeq(5))) email2 := username2 + "@tempurl.org" - group1 := "group1" - group2 := "group2" + group1 := fmt.Sprintf("group1%s", strings.ToLower(acctest.RandSeq(5))) + group2 := fmt.Sprintf("group2%s", strings.ToLower(acctest.RandSeq(5))) repo1 := fmt.Sprintf("repo%s", strings.ToLower(acctest.RandSeq(6))) repo2 := fmt.Sprintf("repo%s", strings.ToLower(acctest.RandSeq(6))) @@ -609,8 +607,7 @@ func TestAccProject_full(t *testing.T) { ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories, ExternalProviders: map[string]resource.ExternalProvider{ "artifactory": { - Source: "jfrog/artifactory", - VersionConstraint: "10.3.3", + Source: "jfrog/artifactory", }, }, Steps: []resource.TestStep{ diff --git a/pkg/project/resource/resource_project_user.go b/pkg/project/resource/resource_project_user.go index fc211fd4..ea1c230b 100644 --- a/pkg/project/resource/resource_project_user.go +++ b/pkg/project/resource/resource_project_user.go @@ -24,7 +24,9 @@ import ( const ProjectUsersUrl = "access/api/v1/projects/{projectKey}/users/{name}" func NewProjectUserResource() resource.Resource { - return &ProjectUserResource{} + return &ProjectUserResource{ + TypeName: "project_user", + } } type ProjectUserResource struct { @@ -46,8 +48,7 @@ type ProjectUserAPIModel struct { } func (r *ProjectUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_user" - r.TypeName = resp.TypeName + resp.TypeName = r.TypeName } func (r *ProjectUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { diff --git a/pkg/project/resource/resource_project_user_test.go b/pkg/project/resource/resource_project_user_test.go index a8bc932d..24922500 100644 --- a/pkg/project/resource/resource_project_user_test.go +++ b/pkg/project/resource/resource_project_user_test.go @@ -169,8 +169,7 @@ func TestAccProjectUser_full(t *testing.T) { ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories, ExternalProviders: map[string]resource.ExternalProvider{ "artifactory": { - Source: "jfrog/artifactory", - VersionConstraint: "10.3.3", + Source: "jfrog/artifactory", }, }, Steps: []resource.TestStep{ diff --git a/pkg/project/resource/util.go b/pkg/project/resource/util.go index 7d6eb3d7..c84dee67 100644 --- a/pkg/project/resource/util.go +++ b/pkg/project/resource/util.go @@ -43,3 +43,14 @@ func (r ProjectErrorsResponse) String() string { }, "") return errs } + +const ProjectRepositoryStatusEndpoint = "access/api/v1/projects/_/repositories/{repo_key}" + +type ProjectRepositoryStatusAPIModel struct { + ResourceName string `json:"resource_name"` + Environments []string `json:"environments"` + SharedWithProjects []string `json:"shared_with_projects"` + SharedWithAllProjects bool `json:"shared_with_all_projects"` + SharedReadOnly bool `json:"shared_read_only"` + AssignedTo string `json:"assigned_to"` +}