From 4c83f9b3f25fe87af8e984ec715f14ad4df96359 Mon Sep 17 00:00:00 2001 From: Fuochi Date: Thu, 8 Dec 2022 14:51:21 +0400 Subject: [PATCH] feat: add indexer nyaa resource --- docs/resources/indexer_nyaa.md | 61 +++ .../resources/radarr_indexer_nyaa/import.sh | 2 + .../resources/radarr_indexer_nyaa/resource.tf | 7 + internal/provider/indexer_nyaa_resource.go | 354 ++++++++++++++++++ .../provider/indexer_nyaa_resource_test.go | 51 +++ internal/provider/provider.go | 1 + 6 files changed, 476 insertions(+) create mode 100644 docs/resources/indexer_nyaa.md create mode 100644 examples/resources/radarr_indexer_nyaa/import.sh create mode 100644 examples/resources/radarr_indexer_nyaa/resource.tf create mode 100644 internal/provider/indexer_nyaa_resource.go create mode 100644 internal/provider/indexer_nyaa_resource_test.go diff --git a/docs/resources/indexer_nyaa.md b/docs/resources/indexer_nyaa.md new file mode 100644 index 00000000..7f596323 --- /dev/null +++ b/docs/resources/indexer_nyaa.md @@ -0,0 +1,61 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "radarr_indexer_nyaa Resource - terraform-provider-radarr" +subcategory: "Indexers" +description: |- + Indexer Nyaa resource. + For more information refer to Indexer https://wiki.servarr.com/radarr/settings#indexers and Nyaa https://wiki.servarr.com/radarr/supported#nyaa. +--- + +# radarr_indexer_nyaa (Resource) + +Indexer Nyaa resource. +For more information refer to [Indexer](https://wiki.servarr.com/radarr/settings#indexers) and [Nyaa](https://wiki.servarr.com/radarr/supported#nyaa). + +## Example Usage + +```terraform +resource "radarr_indexer_nyaa" "example" { + enable_automatic_search = true + name = "Example" + base_url = "https://nyaa.io" + additional_parameters = "&cats=1_0&filter=1" + minimum_seeders = 1 +} +``` + + +## Schema + +### Required + +- `base_url` (String) Base URL. +- `name` (String) IndexerNyaa name. + +### Optional + +- `additional_parameters` (String) Additional parameters. +- `download_client_id` (Number) Download client ID. +- `enable_automatic_search` (Boolean) Enable automatic search flag. +- `enable_interactive_search` (Boolean) Enable interactive search flag. +- `enable_rss` (Boolean) Enable RSS flag. +- `minimum_seeders` (Number) Minimum seeders. +- `multi_languages` (Set of Number) Languages list. +- `priority` (Number) Priority. +- `required_flags` (Set of Number) Flag list. +- `seed_ratio` (Number) Seed ratio. +- `seed_time` (Number) Seed time. +- `tags` (Set of Number) List of associated tags. + +### Read-Only + +- `id` (Number) IndexerNyaa ID. + +## Import + +Import is supported using the following syntax: + +```shell +# import using the API/UI ID +terraform import radarr_indexer_nyaa.example 1 +``` diff --git a/examples/resources/radarr_indexer_nyaa/import.sh b/examples/resources/radarr_indexer_nyaa/import.sh new file mode 100644 index 00000000..b0ed6105 --- /dev/null +++ b/examples/resources/radarr_indexer_nyaa/import.sh @@ -0,0 +1,2 @@ +# import using the API/UI ID +terraform import radarr_indexer_nyaa.example 1 \ No newline at end of file diff --git a/examples/resources/radarr_indexer_nyaa/resource.tf b/examples/resources/radarr_indexer_nyaa/resource.tf new file mode 100644 index 00000000..1ea28417 --- /dev/null +++ b/examples/resources/radarr_indexer_nyaa/resource.tf @@ -0,0 +1,7 @@ +resource "radarr_indexer_nyaa" "example" { + enable_automatic_search = true + name = "Example" + base_url = "https://nyaa.io" + additional_parameters = "&cats=1_0&filter=1" + minimum_seeders = 1 +} diff --git a/internal/provider/indexer_nyaa_resource.go b/internal/provider/indexer_nyaa_resource.go new file mode 100644 index 00000000..c166f22d --- /dev/null +++ b/internal/provider/indexer_nyaa_resource.go @@ -0,0 +1,354 @@ +package provider + +import ( + "context" + "fmt" + "strconv" + + "github.com/devopsarr/terraform-provider-sonarr/tools" + "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/int64planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "golift.io/starr/radarr" +) + +const ( + indexerNyaaResourceName = "indexer_nyaa" + IndexerNyaaImplementation = "Nyaa" + IndexerNyaaConfigContrat = "NyaaSettings" + IndexerNyaaProtocol = "torrent" +) + +// Ensure provider defined types fully satisfy framework interfaces. +var _ resource.Resource = &IndexerNyaaResource{} +var _ resource.ResourceWithImportState = &IndexerNyaaResource{} + +func NewIndexerNyaaResource() resource.Resource { + return &IndexerNyaaResource{} +} + +// IndexerNyaaResource defines the Nyaa indexer implementation. +type IndexerNyaaResource struct { + client *radarr.Radarr +} + +// IndexerNyaa describes the Nyaa indexer data model. +type IndexerNyaa struct { + Tags types.Set `tfsdk:"tags"` + MultiLanguages types.Set `tfsdk:"multi_languages"` + RequiredFlags types.Set `tfsdk:"required_flags"` + Name types.String `tfsdk:"name"` + BaseURL types.String `tfsdk:"base_url"` + AdditionalParameters types.String `tfsdk:"additional_parameters"` + Priority types.Int64 `tfsdk:"priority"` + ID types.Int64 `tfsdk:"id"` + DownloadClientID types.Int64 `tfsdk:"download_client_id"` + MinimumSeeders types.Int64 `tfsdk:"minimum_seeders"` + SeedTime types.Int64 `tfsdk:"seed_time"` + SeedRatio types.Float64 `tfsdk:"seed_ratio"` + EnableAutomaticSearch types.Bool `tfsdk:"enable_automatic_search"` + EnableRss types.Bool `tfsdk:"enable_rss"` + EnableInteractiveSearch types.Bool `tfsdk:"enable_interactive_search"` +} + +func (i IndexerNyaa) toIndexer() *Indexer { + return &Indexer{ + EnableAutomaticSearch: i.EnableAutomaticSearch, + EnableInteractiveSearch: i.EnableInteractiveSearch, + EnableRss: i.EnableRss, + Priority: i.Priority, + DownloadClientID: i.DownloadClientID, + ID: i.ID, + Name: i.Name, + AdditionalParameters: i.AdditionalParameters, + MinimumSeeders: i.MinimumSeeders, + SeedTime: i.SeedTime, + SeedRatio: i.SeedRatio, + BaseURL: i.BaseURL, + Tags: i.Tags, + MultiLanguages: i.MultiLanguages, + RequiredFlags: i.RequiredFlags, + } +} + +func (i *IndexerNyaa) fromIndexer(indexer *Indexer) { + i.EnableAutomaticSearch = indexer.EnableAutomaticSearch + i.EnableInteractiveSearch = indexer.EnableInteractiveSearch + i.EnableRss = indexer.EnableRss + i.Priority = indexer.Priority + i.DownloadClientID = indexer.DownloadClientID + i.ID = indexer.ID + i.Name = indexer.Name + i.AdditionalParameters = indexer.AdditionalParameters + i.MinimumSeeders = indexer.MinimumSeeders + i.SeedTime = indexer.SeedTime + i.SeedRatio = indexer.SeedRatio + i.BaseURL = indexer.BaseURL + i.Tags = indexer.Tags + i.MultiLanguages = indexer.MultiLanguages + i.RequiredFlags = indexer.RequiredFlags +} + +func (r *IndexerNyaaResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_" + indexerNyaaResourceName +} + +func (r *IndexerNyaaResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "Indexer Nyaa resource.\nFor more information refer to [Indexer](https://wiki.servarr.com/radarr/settings#indexers) and [Nyaa](https://wiki.servarr.com/radarr/supported#nyaa).", + Attributes: map[string]schema.Attribute{ + "enable_automatic_search": schema.BoolAttribute{ + MarkdownDescription: "Enable automatic search flag.", + Optional: true, + Computed: true, + }, + "enable_interactive_search": schema.BoolAttribute{ + MarkdownDescription: "Enable interactive search flag.", + Optional: true, + Computed: true, + }, + "enable_rss": schema.BoolAttribute{ + MarkdownDescription: "Enable RSS flag.", + Optional: true, + Computed: true, + }, + "priority": schema.Int64Attribute{ + MarkdownDescription: "Priority.", + Optional: true, + Computed: true, + }, + "download_client_id": schema.Int64Attribute{ + MarkdownDescription: "Download client ID.", + Optional: true, + Computed: true, + }, + "name": schema.StringAttribute{ + MarkdownDescription: "IndexerNyaa name.", + Required: true, + }, + "tags": schema.SetAttribute{ + MarkdownDescription: "List of associated tags.", + Optional: true, + Computed: true, + ElementType: types.Int64Type, + }, + "id": schema.Int64Attribute{ + MarkdownDescription: "IndexerNyaa ID.", + Computed: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.UseStateForUnknown(), + }, + }, + // Field values + "minimum_seeders": schema.Int64Attribute{ + MarkdownDescription: "Minimum seeders.", + Optional: true, + Computed: true, + }, + "seed_time": schema.Int64Attribute{ + MarkdownDescription: "Seed time.", + Optional: true, + Computed: true, + }, + "seed_ratio": schema.Float64Attribute{ + MarkdownDescription: "Seed ratio.", + Optional: true, + Computed: true, + }, + "additional_parameters": schema.StringAttribute{ + MarkdownDescription: "Additional parameters.", + Optional: true, + Computed: true, + }, + "base_url": schema.StringAttribute{ + MarkdownDescription: "Base URL.", + Required: true, + }, + "multi_languages": schema.SetAttribute{ + MarkdownDescription: "Languages list.", + Optional: true, + Computed: true, + ElementType: types.Int64Type, + }, + "required_flags": schema.SetAttribute{ + MarkdownDescription: "Flag list.", + Optional: true, + Computed: true, + ElementType: types.Int64Type, + }, + }, + } +} + +func (r *IndexerNyaaResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + // Prevent panic if the provider has not been configured. + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*radarr.Radarr) + if !ok { + resp.Diagnostics.AddError( + tools.UnexpectedResourceConfigureType, + fmt.Sprintf("Expected *radarr.Radarr, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + r.client = client +} + +func (r *IndexerNyaaResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + // Retrieve values from plan + var indexer *IndexerNyaa + + resp.Diagnostics.Append(req.Plan.Get(ctx, &indexer)...) + + if resp.Diagnostics.HasError() { + return + } + + // Create new IndexerNyaa + request := indexer.read(ctx) + + response, err := r.client.AddIndexerContext(ctx, request) + if err != nil { + resp.Diagnostics.AddError(tools.ClientError, fmt.Sprintf("Unable to create %s, got error: %s", indexerNyaaResourceName, err)) + + return + } + + tflog.Trace(ctx, "created "+indexerNyaaResourceName+": "+strconv.Itoa(int(response.ID))) + // Generate resource state struct + indexer.write(ctx, response) + resp.Diagnostics.Append(resp.State.Set(ctx, &indexer)...) +} + +func (r *IndexerNyaaResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + // Get current state + var indexer *IndexerNyaa + + resp.Diagnostics.Append(req.State.Get(ctx, &indexer)...) + + if resp.Diagnostics.HasError() { + return + } + + // Get IndexerNyaa current value + response, err := r.client.GetIndexerContext(ctx, indexer.ID.ValueInt64()) + if err != nil { + resp.Diagnostics.AddError(tools.ClientError, fmt.Sprintf("Unable to read %s, got error: %s", indexerNyaaResourceName, err)) + + return + } + + tflog.Trace(ctx, "read "+indexerNyaaResourceName+": "+strconv.Itoa(int(response.ID))) + // Map response body to resource schema attribute + indexer.write(ctx, response) + resp.Diagnostics.Append(resp.State.Set(ctx, &indexer)...) +} + +func (r *IndexerNyaaResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + // Get plan values + var indexer *IndexerNyaa + + resp.Diagnostics.Append(req.Plan.Get(ctx, &indexer)...) + + if resp.Diagnostics.HasError() { + return + } + + // Update IndexerNyaa + request := indexer.read(ctx) + + response, err := r.client.UpdateIndexerContext(ctx, request) + if err != nil { + resp.Diagnostics.AddError(tools.ClientError, fmt.Sprintf("Unable to update "+indexerNyaaResourceName+", got error: %s", err)) + + return + } + + tflog.Trace(ctx, "updated "+indexerNyaaResourceName+": "+strconv.Itoa(int(response.ID))) + // Generate resource state struct + indexer.write(ctx, response) + resp.Diagnostics.Append(resp.State.Set(ctx, &indexer)...) +} + +func (r *IndexerNyaaResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var indexer *IndexerNyaa + + resp.Diagnostics.Append(req.State.Get(ctx, &indexer)...) + + if resp.Diagnostics.HasError() { + return + } + + // Delete IndexerNyaa current value + err := r.client.DeleteIndexerContext(ctx, indexer.ID.ValueInt64()) + if err != nil { + resp.Diagnostics.AddError(tools.ClientError, fmt.Sprintf("Unable to read %s, got error: %s", indexerNyaaResourceName, err)) + + return + } + + tflog.Trace(ctx, "deleted "+indexerNyaaResourceName+": "+strconv.Itoa(int(indexer.ID.ValueInt64()))) + resp.State.RemoveResource(ctx) +} + +func (r *IndexerNyaaResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + // resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + id, err := strconv.Atoi(req.ID) + if err != nil { + resp.Diagnostics.AddError( + tools.UnexpectedImportIdentifier, + fmt.Sprintf("Expected import identifier with format: ID. Got: %q", req.ID), + ) + + return + } + + tflog.Trace(ctx, "imported "+indexerNyaaResourceName+": "+strconv.Itoa(id)) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), id)...) +} + +func (i *IndexerNyaa) write(ctx context.Context, indexer *radarr.IndexerOutput) { + genericIndexer := Indexer{ + EnableAutomaticSearch: types.BoolValue(indexer.EnableAutomaticSearch), + EnableInteractiveSearch: types.BoolValue(indexer.EnableInteractiveSearch), + EnableRss: types.BoolValue(indexer.EnableRss), + Priority: types.Int64Value(indexer.Priority), + DownloadClientID: types.Int64Value(indexer.DownloadClientID), + ID: types.Int64Value(indexer.ID), + Name: types.StringValue(indexer.Name), + } + genericIndexer.Tags, _ = types.SetValueFrom(ctx, types.Int64Type, indexer.Tags) + genericIndexer.writeFields(ctx, indexer.Fields) + i.fromIndexer(&genericIndexer) +} + +func (i *IndexerNyaa) read(ctx context.Context) *radarr.IndexerInput { + var tags []int + + tfsdk.ValueAs(ctx, i.Tags, &tags) + + return &radarr.IndexerInput{ + EnableAutomaticSearch: i.EnableAutomaticSearch.ValueBool(), + EnableInteractiveSearch: i.EnableInteractiveSearch.ValueBool(), + EnableRss: i.EnableRss.ValueBool(), + Priority: i.Priority.ValueInt64(), + DownloadClientID: i.DownloadClientID.ValueInt64(), + ID: i.ID.ValueInt64(), + ConfigContract: IndexerNyaaConfigContrat, + Implementation: IndexerNyaaImplementation, + Name: i.Name.ValueString(), + Protocol: IndexerNyaaProtocol, + Tags: tags, + Fields: i.toIndexer().readFields(ctx), + } +} diff --git a/internal/provider/indexer_nyaa_resource_test.go b/internal/provider/indexer_nyaa_resource_test.go new file mode 100644 index 00000000..2c9fa5c6 --- /dev/null +++ b/internal/provider/indexer_nyaa_resource_test.go @@ -0,0 +1,51 @@ +package provider + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccIndexerNyaaResource(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create and Read testing + { + Config: testAccIndexerNyaaResourceConfig("nyaaResourceTest", "https://nyaa.org"), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("radarr_indexer_nyaa.test", "base_url", "https://nyaa.org"), + resource.TestCheckResourceAttrSet("radarr_indexer_nyaa.test", "id"), + ), + }, + // Update and Read testing + { + Config: testAccIndexerNyaaResourceConfig("nyaaResourceTest", "https://nyaa.net"), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("radarr_indexer_nyaa.test", "base_url", "https://nyaa.net"), + ), + }, + // ImportState testing + { + ResourceName: "radarr_indexer_nyaa.test", + ImportState: true, + ImportStateVerify: true, + }, + // Delete testing automatically occurs in TestCase + }, + }) +} + +func testAccIndexerNyaaResourceConfig(name, url string) string { + return fmt.Sprintf(` + resource "radarr_indexer_nyaa" "test" { + enable_automatic_search = false + name = "%s" + base_url = "%s" + minimum_seeders = 1 + }`, name, url) +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 7119e41c..ff3715a1 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -158,6 +158,7 @@ func (p *RadarrProvider) Resources(ctx context.Context) []func() resource.Resour NewIndexerIptorrentsResource, NewIndexerHdbitsResource, NewIndexerNewznabResource, + NewIndexerNyaaResource, NewIndexerRarbgResource, NewRestrictionResource,