feat: add country setting resource support with unifi_setting_country resource (#31)
* feat: add country setting resource support with `unifi_setting_country` resource * linting
This commit is contained in:
committed by
GitHub
parent
ccac6edebe
commit
a36940b019
1
go.mod
1
go.mod
@@ -8,6 +8,7 @@ go 1.23.5
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/apparentlymart/go-cidr v1.1.0
|
github.com/apparentlymart/go-cidr v1.1.0
|
||||||
|
github.com/biter777/countries v1.7.5
|
||||||
github.com/deckarep/golang-set/v2 v2.7.0
|
github.com/deckarep/golang-set/v2 v2.7.0
|
||||||
github.com/filipowm/go-unifi v1.4.0
|
github.com/filipowm/go-unifi v1.4.0
|
||||||
github.com/golangci/golangci-lint v1.64.5
|
github.com/golangci/golangci-lint v1.64.5
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -122,6 +122,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
|||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/bgentry/speakeasy v0.2.0 h1:tgObeVOf8WAvtuAX6DhJ4xks4CFNwPDZiqzGqIHE51E=
|
github.com/bgentry/speakeasy v0.2.0 h1:tgObeVOf8WAvtuAX6DhJ4xks4CFNwPDZiqzGqIHE51E=
|
||||||
github.com/bgentry/speakeasy v0.2.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
github.com/bgentry/speakeasy v0.2.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
|
github.com/biter777/countries v1.7.5 h1:MJ+n3+rSxWQdqVJU8eBy9RqcdH6ePPn4PJHocVWUa+Q=
|
||||||
|
github.com/biter777/countries v1.7.5/go.mod h1:1HSpZ526mYqKJcpT5Ti1kcGQ0L0SrXWIaptUWjFfv2E=
|
||||||
github.com/bitly/go-hostpool v0.1.0/go.mod h1:4gOCgp6+NZnVqlKyZ/iBZFTAJKembaVENUpMkpg42fw=
|
github.com/bitly/go-hostpool v0.1.0/go.mod h1:4gOCgp6+NZnVqlKyZ/iBZFTAJKembaVENUpMkpg42fw=
|
||||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
||||||
github.com/bkielbasa/cyclop v1.2.3 h1:faIVMIGDIANuGPWH031CZJTi2ymOQBULs9H21HSMa5w=
|
github.com/bkielbasa/cyclop v1.2.3 h1:faIVMIGDIANuGPWH031CZJTi2ymOQBULs9H21HSMa5w=
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ func TestDNSRecord_Update(t *testing.T) {
|
|||||||
{
|
{
|
||||||
Config: testAccDnsRecordConfig(updated),
|
Config: testAccDnsRecordConfig(updated),
|
||||||
Check: testAccDnsRecordCheckAttrs(updated),
|
Check: testAccDnsRecordCheckAttrs(updated),
|
||||||
ConfigPlanChecks: pt.CheckResourceAction(testDnsRecordResourceName, plancheck.ResourceActionUpdate),
|
ConfigPlanChecks: pt.CheckResourceActions(testDnsRecordResourceName, plancheck.ResourceActionUpdate),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
69
internal/provider/acctest/resource_setting_country_test.go
Normal file
69
internal/provider/acctest/resource_setting_country_test.go
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package acctest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
pt "github.com/filipowm/terraform-provider-unifi/internal/provider/testing"
|
||||||
|
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform-plugin-testing/plancheck"
|
||||||
|
"regexp"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var settingCountryLock = &sync.Mutex{}
|
||||||
|
|
||||||
|
func TestAccSettingCountry(t *testing.T) {
|
||||||
|
AcceptanceTest(t, AcceptanceTestCase{
|
||||||
|
Lock: settingCountryLock,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: testAccSettingCountryConfig("US"),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
resource.TestCheckResourceAttr("unifi_setting_country.test", "code", "US"),
|
||||||
|
resource.TestCheckResourceAttr("unifi_setting_country.test", "code_numeric", "840"),
|
||||||
|
),
|
||||||
|
ConfigPlanChecks: pt.CheckResourceActions("unifi_setting_country.test", plancheck.ResourceActionCreate),
|
||||||
|
},
|
||||||
|
pt.ImportStepWithSite("unifi_setting_country.test"),
|
||||||
|
{
|
||||||
|
Config: testAccSettingCountryConfig("PL"),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
resource.TestCheckResourceAttr("unifi_setting_country.test", "code", "PL"),
|
||||||
|
resource.TestCheckResourceAttr("unifi_setting_country.test", "code_numeric", "616"),
|
||||||
|
),
|
||||||
|
ConfigPlanChecks: pt.CheckResourceActions("unifi_setting_country.test", plancheck.ResourceActionUpdate),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var invalidCountryCodeErrorRegex = regexp.MustCompile("ISO 3166-1 alpha-2")
|
||||||
|
var stringLengthExactly2Regex = regexp.MustCompile("string length must be exactly 2")
|
||||||
|
|
||||||
|
func TestAccSettingCountry_invalidCode(t *testing.T) {
|
||||||
|
AcceptanceTest(t, AcceptanceTestCase{
|
||||||
|
Lock: settingCountryLock,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: testAccSettingCountryConfig("WP"),
|
||||||
|
ExpectError: invalidCountryCodeErrorRegex,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Config: testAccSettingCountryConfig("Too long"),
|
||||||
|
ExpectError: stringLengthExactly2Regex,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Config: testAccSettingCountryConfig(""),
|
||||||
|
ExpectError: stringLengthExactly2Regex,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccSettingCountryConfig(code string) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
resource "unifi_setting_country" "test" {
|
||||||
|
code = %q
|
||||||
|
}
|
||||||
|
`, code)
|
||||||
|
}
|
||||||
@@ -4,12 +4,31 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BaseData interface {
|
type BaseData interface {
|
||||||
SetClient(client *Client)
|
SetClient(client *Client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Site struct {
|
||||||
|
Site types.String `tfsdk:"site"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSite(str string) Site {
|
||||||
|
return Site{
|
||||||
|
Site: types.StringValue(str),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Site) SetSite(site string) {
|
||||||
|
s.Site = types.StringValue(site)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Site) AsString() string {
|
||||||
|
return s.Site.ValueString()
|
||||||
|
}
|
||||||
|
|
||||||
func ConfigureDatasource(base BaseData, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
func ConfigureDatasource(base BaseData, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||||
if req.ProviderData == nil {
|
if req.ProviderData == nil {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -80,6 +80,13 @@ type Client struct {
|
|||||||
Version *version.Version
|
Version *version.Version
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) ResolveSite(site *Site) string {
|
||||||
|
if site == nil || IsEmptyString(site.Site) {
|
||||||
|
return c.Site
|
||||||
|
}
|
||||||
|
return site.AsString()
|
||||||
|
}
|
||||||
|
|
||||||
func CreateHttpTransport(insecure bool) http.RoundTripper {
|
func CreateHttpTransport(insecure bool) http.RoundTripper {
|
||||||
return &http.Transport{
|
return &http.Transport{
|
||||||
Proxy: http.ProxyFromEnvironment,
|
Proxy: http.ProxyFromEnvironment,
|
||||||
|
|||||||
36
internal/provider/base/importer.go
Normal file
36
internal/provider/base/importer.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||||
|
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ImportSiteAndID(_ context.Context, d *schema.ResourceData, _ interface{}) ([]*schema.ResourceData, error) {
|
||||||
|
if id := d.Id(); strings.Contains(id, ":") {
|
||||||
|
importParts := strings.SplitN(id, ":", 2)
|
||||||
|
d.SetId(importParts[1])
|
||||||
|
d.Set("site", importParts[0])
|
||||||
|
}
|
||||||
|
return []*schema.ResourceData{d}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ImportIDWithSite(req resource.ImportStateRequest, resp *resource.ImportStateResponse) (string, string) {
|
||||||
|
id := req.ID
|
||||||
|
if id == "" {
|
||||||
|
resp.Diagnostics.AddError("Invalid ID", "ID is required")
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(id, ":") {
|
||||||
|
importParts := strings.SplitN(id, ":", 2)
|
||||||
|
if len(importParts) == 2 {
|
||||||
|
return importParts[1], importParts[0]
|
||||||
|
}
|
||||||
|
resp.Diagnostics.AddError("Invalid ID", "ID contains too many colon-separated parts. Format should be 'site:id'")
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
resp.Diagnostics.AddError("Invalid ID", "ID does not contain site part. Format should be 'site:id'")
|
||||||
|
return id, ""
|
||||||
|
}
|
||||||
@@ -1,17 +1,18 @@
|
|||||||
package utils
|
package base
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
"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/planmodifier"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
|
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ID generates an attribute definition suitable for the always-present `id` attribute.
|
// ID generates an attribute definition suitable for the always-present `id` attribute.
|
||||||
func ID(desc ...string) schema.StringAttribute {
|
func ID(desc ...string) schema.StringAttribute {
|
||||||
a := schema.StringAttribute{
|
a := schema.StringAttribute{
|
||||||
Computed: true,
|
|
||||||
Description: "The unique identifier of this resource.",
|
Description: "The unique identifier of this resource.",
|
||||||
|
Computed: true,
|
||||||
PlanModifiers: []planmodifier.String{
|
PlanModifiers: []planmodifier.String{
|
||||||
stringplanmodifier.UseStateForUnknown(),
|
stringplanmodifier.UseStateForUnknown(),
|
||||||
},
|
},
|
||||||
@@ -24,6 +25,23 @@ func ID(desc ...string) schema.StringAttribute {
|
|||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SiteAttribute(desc ...string) schema.StringAttribute {
|
||||||
|
s := schema.StringAttribute{
|
||||||
|
MarkdownDescription: "The name of the UniFi site where this resource should be applied. If not specified, the default site will be used.",
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
PlanModifiers: []planmodifier.String{
|
||||||
|
stringplanmodifier.UseStateForUnknown(),
|
||||||
|
stringplanmodifier.RequiresReplace(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(desc) > 0 {
|
||||||
|
s.Description = desc[0]
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
// ShouldBeRemoved evaluates if an attribute should be removed from the plan during update.
|
// ShouldBeRemoved evaluates if an attribute should be removed from the plan during update.
|
||||||
func ShouldBeRemoved(plan attr.Value, state attr.Value, isClone bool) bool {
|
func ShouldBeRemoved(plan attr.Value, state attr.Value, isClone bool) bool {
|
||||||
return !IsDefined(plan) && IsDefined(state) && !isClone
|
return !IsDefined(plan) && IsDefined(state) && !isClone
|
||||||
@@ -33,3 +51,7 @@ func ShouldBeRemoved(plan attr.Value, state attr.Value, isClone bool) bool {
|
|||||||
func IsDefined(v attr.Value) bool {
|
func IsDefined(v attr.Value) bool {
|
||||||
return !v.IsNull() && !v.IsUnknown()
|
return !v.IsNull() && !v.IsUnknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsEmptyString(s types.String) bool {
|
||||||
|
return s.IsNull() || s.IsUnknown() || s.ValueString() == ""
|
||||||
|
}
|
||||||
@@ -29,7 +29,7 @@ func ResourcePortProfile() *schema.Resource {
|
|||||||
UpdateContext: resourcePortProfileUpdate,
|
UpdateContext: resourcePortProfileUpdate,
|
||||||
DeleteContext: resourcePortProfileDelete,
|
DeleteContext: resourcePortProfileDelete,
|
||||||
Importer: &schema.ResourceImporter{
|
Importer: &schema.ResourceImporter{
|
||||||
StateContext: utils.ImportSiteAndID,
|
StateContext: base.ImportSiteAndID,
|
||||||
},
|
},
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package dns
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/filipowm/go-unifi/unifi"
|
"github.com/filipowm/go-unifi/unifi"
|
||||||
"github.com/filipowm/terraform-provider-unifi/internal/utils"
|
"github.com/filipowm/terraform-provider-unifi/internal/provider/base"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
)
|
)
|
||||||
@@ -32,8 +32,8 @@ type dnsRecordsDatasourceModel struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var dnsRecordDatasourceAttributes = map[string]schema.Attribute{
|
var dnsRecordDatasourceAttributes = map[string]schema.Attribute{
|
||||||
"id": utils.ID(),
|
"id": base.ID(),
|
||||||
"site_id": utils.ID("The site ID where the DNS record is located."),
|
"site_id": base.ID("The site ID where the DNS record is located."),
|
||||||
"name": schema.StringAttribute{
|
"name": schema.StringAttribute{
|
||||||
Description: "DNS record name.",
|
Description: "DNS record name.",
|
||||||
Computed: true,
|
Computed: true,
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/filipowm/terraform-provider-unifi/internal/provider/base"
|
"github.com/filipowm/terraform-provider-unifi/internal/provider/base"
|
||||||
"github.com/filipowm/terraform-provider-unifi/internal/utils"
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework-validators/int32validator"
|
"github.com/hashicorp/terraform-plugin-framework-validators/int32validator"
|
||||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||||
@@ -53,8 +52,8 @@ func (d *dnsRecordResource) Schema(_ context.Context, _ resource.SchemaRequest,
|
|||||||
" * Adding TXT records for service verification\n\n",
|
" * Adding TXT records for service verification\n\n",
|
||||||
|
|
||||||
Attributes: map[string]schema.Attribute{
|
Attributes: map[string]schema.Attribute{
|
||||||
"id": utils.ID(),
|
"id": base.ID(),
|
||||||
"site_id": utils.ID("The site ID where the DNS record is located."),
|
"site_id": base.ID("The site ID where the DNS record is located."),
|
||||||
"name": schema.StringAttribute{
|
"name": schema.StringAttribute{
|
||||||
MarkdownDescription: "DNS record name.",
|
MarkdownDescription: "DNS record name.",
|
||||||
Required: true,
|
Required: true,
|
||||||
@@ -244,11 +243,6 @@ func (d *dnsRecordResource) ImportState(ctx context.Context, req resource.Import
|
|||||||
}
|
}
|
||||||
d.read(ctx, &state, &resp.Diagnostics)
|
d.read(ctx, &state, &resp.Diagnostics)
|
||||||
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
d.read(ctx, &state, &resp.Diagnostics)
|
|
||||||
|
|
||||||
if resp.Diagnostics.HasError() {
|
if resp.Diagnostics.HasError() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/filipowm/terraform-provider-unifi/internal/provider/base"
|
|
||||||
"github.com/filipowm/terraform-provider-unifi/internal/utils"
|
|
||||||
|
|
||||||
"github.com/filipowm/go-unifi/unifi"
|
"github.com/filipowm/go-unifi/unifi"
|
||||||
|
"github.com/filipowm/terraform-provider-unifi/internal/provider/base"
|
||||||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
|
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
|
||||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||||
)
|
)
|
||||||
@@ -31,7 +29,7 @@ func ResourceDynamicDNS() *schema.Resource {
|
|||||||
UpdateContext: resourceDynamicDNSUpdate,
|
UpdateContext: resourceDynamicDNSUpdate,
|
||||||
DeleteContext: resourceDynamicDNSDelete,
|
DeleteContext: resourceDynamicDNSDelete,
|
||||||
Importer: &schema.ResourceImporter{
|
Importer: &schema.ResourceImporter{
|
||||||
StateContext: utils.ImportSiteAndID,
|
StateContext: base.ImportSiteAndID,
|
||||||
},
|
},
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ func ResourceFirewallGroup() *schema.Resource {
|
|||||||
UpdateContext: resourceFirewallGroupUpdate,
|
UpdateContext: resourceFirewallGroupUpdate,
|
||||||
DeleteContext: resourceFirewallGroupDelete,
|
DeleteContext: resourceFirewallGroupDelete,
|
||||||
Importer: &schema.ResourceImporter{
|
Importer: &schema.ResourceImporter{
|
||||||
StateContext: utils.ImportSiteAndID,
|
StateContext: base.ImportSiteAndID,
|
||||||
},
|
},
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ func ResourceFirewallRule() *schema.Resource {
|
|||||||
UpdateContext: resourceFirewallRuleUpdate,
|
UpdateContext: resourceFirewallRuleUpdate,
|
||||||
DeleteContext: resourceFirewallRuleDelete,
|
DeleteContext: resourceFirewallRuleDelete,
|
||||||
Importer: &schema.ResourceImporter{
|
Importer: &schema.ResourceImporter{
|
||||||
StateContext: utils.ImportSiteAndID,
|
StateContext: base.ImportSiteAndID,
|
||||||
},
|
},
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ func ResourceWLAN() *schema.Resource {
|
|||||||
UpdateContext: resourceWLANUpdate,
|
UpdateContext: resourceWLANUpdate,
|
||||||
DeleteContext: resourceWLANDelete,
|
DeleteContext: resourceWLANDelete,
|
||||||
Importer: &schema.ResourceImporter{
|
Importer: &schema.ResourceImporter{
|
||||||
StateContext: utils.ImportSiteAndID,
|
StateContext: base.ImportSiteAndID,
|
||||||
},
|
},
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"github.com/filipowm/terraform-provider-unifi/internal/provider/base"
|
"github.com/filipowm/terraform-provider-unifi/internal/provider/base"
|
||||||
"github.com/filipowm/terraform-provider-unifi/internal/provider/dns"
|
"github.com/filipowm/terraform-provider-unifi/internal/provider/dns"
|
||||||
|
"github.com/filipowm/terraform-provider-unifi/internal/provider/settings"
|
||||||
"github.com/filipowm/terraform-provider-unifi/internal/utils"
|
"github.com/filipowm/terraform-provider-unifi/internal/utils"
|
||||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||||
@@ -167,6 +168,7 @@ func (p *unifiProvider) Configure(ctx context.Context, req provider.ConfigureReq
|
|||||||
func (p *unifiProvider) Resources(_ context.Context) []func() resource.Resource {
|
func (p *unifiProvider) Resources(_ context.Context) []func() resource.Resource {
|
||||||
return []func() resource.Resource{
|
return []func() resource.Resource{
|
||||||
dns.NewDnsRecordResource,
|
dns.NewDnsRecordResource,
|
||||||
|
settings.NewCountryResource,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/filipowm/terraform-provider-unifi/internal/provider/base"
|
|
||||||
"github.com/filipowm/terraform-provider-unifi/internal/utils"
|
|
||||||
|
|
||||||
"github.com/filipowm/go-unifi/unifi"
|
"github.com/filipowm/go-unifi/unifi"
|
||||||
|
"github.com/filipowm/terraform-provider-unifi/internal/provider/base"
|
||||||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
|
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
|
||||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
|
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
|
||||||
@@ -38,7 +36,7 @@ func ResourceAccount() *schema.Resource {
|
|||||||
UpdateContext: resourceAccountUpdate,
|
UpdateContext: resourceAccountUpdate,
|
||||||
DeleteContext: resourceAccountDelete,
|
DeleteContext: resourceAccountDelete,
|
||||||
Importer: &schema.ResourceImporter{
|
Importer: &schema.ResourceImporter{
|
||||||
StateContext: utils.ImportSiteAndID,
|
StateContext: base.ImportSiteAndID,
|
||||||
},
|
},
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func ResourcePortForward() *schema.Resource {
|
|||||||
UpdateContext: resourcePortForwardUpdate,
|
UpdateContext: resourcePortForwardUpdate,
|
||||||
DeleteContext: resourcePortForwardDelete,
|
DeleteContext: resourcePortForwardDelete,
|
||||||
Importer: &schema.ResourceImporter{
|
Importer: &schema.ResourceImporter{
|
||||||
StateContext: utils.ImportSiteAndID,
|
StateContext: base.ImportSiteAndID,
|
||||||
},
|
},
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func ResourceStaticRoute() *schema.Resource {
|
|||||||
UpdateContext: resourceStaticRouteUpdate,
|
UpdateContext: resourceStaticRouteUpdate,
|
||||||
DeleteContext: resourceStaticRouteDelete,
|
DeleteContext: resourceStaticRouteDelete,
|
||||||
Importer: &schema.ResourceImporter{
|
Importer: &schema.ResourceImporter{
|
||||||
StateContext: utils.ImportSiteAndID,
|
StateContext: base.ImportSiteAndID,
|
||||||
},
|
},
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
|
|||||||
176
internal/provider/settings/resource_setting_country.go
Normal file
176
internal/provider/settings/resource_setting_country.go
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"github.com/biter777/countries"
|
||||||
|
"github.com/filipowm/go-unifi/unifi"
|
||||||
|
"github.com/filipowm/terraform-provider-unifi/internal/provider/base"
|
||||||
|
"github.com/filipowm/terraform-provider-unifi/internal/provider/validators"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type countryModel struct {
|
||||||
|
base.Site
|
||||||
|
ID types.String `tfsdk:"id"`
|
||||||
|
Code types.String `tfsdk:"code"`
|
||||||
|
CodeNumeric types.Int32 `tfsdk:"code_numeric"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *countryModel) asUnifiModel() *unifi.SettingCountry {
|
||||||
|
code := countries.ByName(d.Code.ValueString())
|
||||||
|
return &unifi.SettingCountry{
|
||||||
|
ID: d.ID.ValueString(),
|
||||||
|
Code: int(code),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *countryModel) merge(other *unifi.SettingCountry) {
|
||||||
|
d.ID = types.StringValue(other.ID)
|
||||||
|
// UniFi uses numeric codes, so we need to convert the alpha-2 code to the numeric code, but we store both
|
||||||
|
code := countries.ByNumeric(other.Code)
|
||||||
|
d.Code = types.StringValue(code.Alpha2())
|
||||||
|
d.CodeNumeric = types.Int32Value(int32(code))
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ resource.Resource = &countryResource{}
|
||||||
|
_ resource.ResourceWithConfigure = &countryResource{}
|
||||||
|
_ resource.ResourceWithImportState = &countryResource{}
|
||||||
|
_ base.BaseData = &countryResource{}
|
||||||
|
)
|
||||||
|
|
||||||
|
type countryResource struct {
|
||||||
|
client *base.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *countryResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||||
|
id, site := base.ImportIDWithSite(req, resp)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
state := countryModel{
|
||||||
|
ID: types.StringValue(id),
|
||||||
|
Site: base.NewSite(site),
|
||||||
|
}
|
||||||
|
c.read(ctx, site, &state, &resp.Diagnostics)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCountryResource() resource.Resource {
|
||||||
|
return &countryResource{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *countryResource) SetClient(client *base.Client) {
|
||||||
|
c.client = client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *countryResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
||||||
|
base.ConfigureResource(c, req, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *countryResource) Metadata(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||||
|
resp.TypeName = "unifi_setting_country"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *countryResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||||
|
resp.Schema = schema.Schema{
|
||||||
|
MarkdownDescription: "The `unifi_setting_country` resource allows you to configure the country settings for your UniFi network. ",
|
||||||
|
Attributes: map[string]schema.Attribute{
|
||||||
|
"id": base.ID(),
|
||||||
|
"site": base.SiteAttribute(),
|
||||||
|
"code": schema.StringAttribute{
|
||||||
|
Description: "The country code to set for the UniFi site. The country code must be a valid ISO 3166-1 alpha-2 code.",
|
||||||
|
Required: true,
|
||||||
|
Validators: []validator.String{
|
||||||
|
validators.StringLengthExactly(2),
|
||||||
|
validators.CountryCodeAlpha2(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"code_numeric": schema.Int32Attribute{
|
||||||
|
Description: "The numeric representation in ISO 3166-1 of the country code.",
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *countryResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||||
|
var plan countryModel
|
||||||
|
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
body := plan.asUnifiModel()
|
||||||
|
site := c.client.ResolveSite(&plan.Site)
|
||||||
|
|
||||||
|
res, err := c.client.UpdateSettingCountry(ctx, site, body)
|
||||||
|
if err != nil {
|
||||||
|
resp.Diagnostics.AddError("Error creating country settings", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
plan.merge(res)
|
||||||
|
plan.Site.SetSite(site)
|
||||||
|
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *countryResource) read(ctx context.Context, site string, state *countryModel, diag *diag.Diagnostics) {
|
||||||
|
res, err := c.client.GetSettingCountry(ctx, site)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, unifi.ErrNotFound) {
|
||||||
|
diag.AddError("Country settings not found", "The country settings were not found in the UniFi controller")
|
||||||
|
} else {
|
||||||
|
diag.AddError("Error reading country settings", err.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
state.merge(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *countryResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
||||||
|
var state countryModel
|
||||||
|
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
site := c.client.ResolveSite(&state.Site)
|
||||||
|
c.read(ctx, site, &state, &resp.Diagnostics)
|
||||||
|
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
(&state).Site.SetSite(site)
|
||||||
|
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *countryResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
||||||
|
var plan, state countryModel
|
||||||
|
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||||
|
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
body := plan.asUnifiModel()
|
||||||
|
site := c.client.ResolveSite(&plan.Site)
|
||||||
|
|
||||||
|
res, err := c.client.UpdateSettingCountry(ctx, site, body)
|
||||||
|
if err != nil {
|
||||||
|
resp.Diagnostics.AddError("Error updating country settings", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
state.merge(res)
|
||||||
|
state.Site.SetSite(site)
|
||||||
|
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *countryResource) Delete(_ context.Context, _ resource.DeleteRequest, _ *resource.DeleteResponse) {
|
||||||
|
// Not supported
|
||||||
|
}
|
||||||
59
internal/provider/settings/resource_setting_country_test.go
Normal file
59
internal/provider/settings/resource_setting_country_test.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/filipowm/go-unifi/unifi"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSettingCountry_ProperCountryCodeMappingFromModel(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
code string
|
||||||
|
expectedNumericCode int
|
||||||
|
}{
|
||||||
|
{"Poland", "PL", 616},
|
||||||
|
{"United States", "US", 840},
|
||||||
|
{"Unknown", "WP", 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
model := countryModel{
|
||||||
|
Code: types.StringValue(tc.code),
|
||||||
|
}
|
||||||
|
unifiModel := model.asUnifiModel()
|
||||||
|
assert.Equal(t, tc.expectedNumericCode, unifiModel.Code)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSettingCountry_ProperCountryCodeMappingToModel(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
numericCode int
|
||||||
|
expectedCode string
|
||||||
|
}{
|
||||||
|
{"Poland", 616, "PL"},
|
||||||
|
{"United States", 840, "US"},
|
||||||
|
{"Unknown", 0, "Unknown"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
unifiModel := &unifi.SettingCountry{
|
||||||
|
Code: tc.numericCode,
|
||||||
|
}
|
||||||
|
model := countryModel{}
|
||||||
|
model.merge(unifiModel)
|
||||||
|
assert.Equal(t, tc.expectedCode, model.Code.ValueString())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,10 +5,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/filipowm/terraform-provider-unifi/internal/provider/base"
|
|
||||||
"github.com/filipowm/terraform-provider-unifi/internal/utils"
|
|
||||||
|
|
||||||
"github.com/filipowm/go-unifi/unifi"
|
"github.com/filipowm/go-unifi/unifi"
|
||||||
|
"github.com/filipowm/terraform-provider-unifi/internal/provider/base"
|
||||||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
|
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
|
||||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||||
)
|
)
|
||||||
@@ -34,9 +32,10 @@ func ResourceSettingMgmt() *schema.Resource {
|
|||||||
UpdateContext: resourceSettingMgmtUpdate,
|
UpdateContext: resourceSettingMgmtUpdate,
|
||||||
DeleteContext: resourceSettingMgmtDelete,
|
DeleteContext: resourceSettingMgmtDelete,
|
||||||
Importer: &schema.ResourceImporter{
|
Importer: &schema.ResourceImporter{
|
||||||
StateContext: utils.ImportSiteAndID,
|
StateContext: base.ImportSiteAndID,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// TODO add more
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
"id": {
|
"id": {
|
||||||
Description: "The unique identifier of the management settings configuration in the UniFi controller.",
|
Description: "The unique identifier of the management settings configuration in the UniFi controller.",
|
||||||
|
|||||||
@@ -4,10 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/filipowm/terraform-provider-unifi/internal/provider/base"
|
|
||||||
"github.com/filipowm/terraform-provider-unifi/internal/utils"
|
|
||||||
|
|
||||||
"github.com/filipowm/go-unifi/unifi"
|
"github.com/filipowm/go-unifi/unifi"
|
||||||
|
"github.com/filipowm/terraform-provider-unifi/internal/provider/base"
|
||||||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
|
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
|
||||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
|
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
|
||||||
@@ -31,7 +29,7 @@ func ResourceSettingRadius() *schema.Resource {
|
|||||||
UpdateContext: resourceSettingRadiusUpdate,
|
UpdateContext: resourceSettingRadiusUpdate,
|
||||||
DeleteContext: schema.NoopContext,
|
DeleteContext: schema.NoopContext,
|
||||||
Importer: &schema.ResourceImporter{
|
Importer: &schema.ResourceImporter{
|
||||||
StateContext: utils.ImportSiteAndID,
|
StateContext: base.ImportSiteAndID,
|
||||||
},
|
},
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func ResourceSettingUsg() *schema.Resource {
|
|||||||
UpdateContext: resourceSettingUsgLocker(resourceSettingUsgUpsert),
|
UpdateContext: resourceSettingUsgLocker(resourceSettingUsgUpsert),
|
||||||
DeleteContext: schema.NoopContext,
|
DeleteContext: schema.NoopContext,
|
||||||
Importer: &schema.ResourceImporter{
|
Importer: &schema.ResourceImporter{
|
||||||
StateContext: utils.ImportSiteAndID,
|
StateContext: base.ImportSiteAndID,
|
||||||
},
|
},
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
|
|||||||
@@ -20,6 +20,21 @@ func MarkAccTest(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ImportStepWithSite(name string, ignore ...string) resource.TestStep {
|
||||||
|
step := &resource.TestStep{
|
||||||
|
ResourceName: name,
|
||||||
|
ImportState: true,
|
||||||
|
ImportStateVerify: true,
|
||||||
|
ImportStateIdFunc: SiteAndIDImportStateIDFunc(name),
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ignore) > 0 {
|
||||||
|
step.ImportStateVerifyIgnore = ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
return *step
|
||||||
|
}
|
||||||
|
|
||||||
func ImportStep(name string, ignore ...string) resource.TestStep {
|
func ImportStep(name string, ignore ...string) resource.TestStep {
|
||||||
step := resource.TestStep{
|
step := resource.TestStep{
|
||||||
ResourceName: name,
|
ResourceName: name,
|
||||||
@@ -69,8 +84,12 @@ func CheckPlanPreApply(checks ...plancheck.PlanCheck) resource.ConfigPlanChecks
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckResourceAction(resourceAddress string, action plancheck.ResourceActionType) resource.ConfigPlanChecks {
|
func CheckResourceActions(resourceAddress string, actions ...plancheck.ResourceActionType) resource.ConfigPlanChecks {
|
||||||
return CheckPlanPreApply(plancheck.ExpectResourceAction(resourceAddress, action))
|
var checks []plancheck.PlanCheck
|
||||||
|
for _, a := range actions {
|
||||||
|
checks = append(checks, plancheck.ExpectResourceAction(resourceAddress, a))
|
||||||
|
}
|
||||||
|
return CheckPlanPreApply(checks...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ComposeConfig(configs ...string) string {
|
func ComposeConfig(configs ...string) string {
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ func ResourceUser() *schema.Resource {
|
|||||||
UpdateContext: resourceUserUpdate,
|
UpdateContext: resourceUserUpdate,
|
||||||
DeleteContext: resourceUserDelete,
|
DeleteContext: resourceUserDelete,
|
||||||
Importer: &schema.ResourceImporter{
|
Importer: &schema.ResourceImporter{
|
||||||
StateContext: utils.ImportSiteAndID,
|
StateContext: base.ImportSiteAndID,
|
||||||
},
|
},
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
|
|||||||
@@ -4,10 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/filipowm/terraform-provider-unifi/internal/provider/base"
|
|
||||||
"github.com/filipowm/terraform-provider-unifi/internal/utils"
|
|
||||||
|
|
||||||
"github.com/filipowm/go-unifi/unifi"
|
"github.com/filipowm/go-unifi/unifi"
|
||||||
|
"github.com/filipowm/terraform-provider-unifi/internal/provider/base"
|
||||||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
|
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
|
||||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||||
)
|
)
|
||||||
@@ -34,7 +32,7 @@ func ResourceUserGroup() *schema.Resource {
|
|||||||
UpdateContext: resourceUserGroupUpdate,
|
UpdateContext: resourceUserGroupUpdate,
|
||||||
DeleteContext: resourceUserGroupDelete,
|
DeleteContext: resourceUserGroupDelete,
|
||||||
Importer: &schema.ResourceImporter{
|
Importer: &schema.ResourceImporter{
|
||||||
StateContext: utils.ImportSiteAndID,
|
StateContext: base.ImportSiteAndID,
|
||||||
},
|
},
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
|
|||||||
39
internal/provider/validators/country_code.go
Normal file
39
internal/provider/validators/country_code.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package validators
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/biter777/countries"
|
||||||
|
"github.com/filipowm/terraform-provider-unifi/internal/provider/base"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CountryCodeAlpha2() validator.String {
|
||||||
|
return countryCodeAlpha2Validator{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type countryCodeAlpha2Validator struct{}
|
||||||
|
|
||||||
|
func (c countryCodeAlpha2Validator) Description(_ context.Context) string {
|
||||||
|
return "The country code must be a valid ISO 3166-1 alpha-2 code."
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c countryCodeAlpha2Validator) MarkdownDescription(ctx context.Context) string {
|
||||||
|
return c.Description(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c countryCodeAlpha2Validator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) {
|
||||||
|
code := req.ConfigValue
|
||||||
|
if base.IsEmptyString(code) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
codeString := code.ValueString()
|
||||||
|
if len(codeString) != 2 || countries.ByName(codeString) == countries.Unknown {
|
||||||
|
resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic(
|
||||||
|
req.Path,
|
||||||
|
c.Description(ctx),
|
||||||
|
codeString,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
34
internal/provider/validators/country_code_test.go
Normal file
34
internal/provider/validators/country_code_test.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package validators
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCountryCodeValidation(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
code string
|
||||||
|
validationFailed bool
|
||||||
|
}{
|
||||||
|
{"Poland", "PL", false},
|
||||||
|
{"United States", "US", false},
|
||||||
|
{"Empty", "", false},
|
||||||
|
{"Too long", "ABC", true},
|
||||||
|
{"Too short", "A", true},
|
||||||
|
{"Unknown", "WP", true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
v := countryCodeAlpha2Validator{}
|
||||||
|
req, resp := newStringValidatorRequestResponse(tc.code)
|
||||||
|
v.ValidateString(context.Background(), req, resp)
|
||||||
|
assert.Equal(t, tc.validationFailed, resp.Diagnostics.HasError())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
19
internal/provider/validators/helpers_test.go
Normal file
19
internal/provider/validators/helpers_test.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package validators
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newStringValidatorRequestResponse(value string) (validator.StringRequest, *validator.StringResponse) {
|
||||||
|
req := validator.StringRequest{
|
||||||
|
ConfigValue: types.StringValue(value),
|
||||||
|
Path: path.Empty(),
|
||||||
|
}
|
||||||
|
resp := validator.StringResponse{
|
||||||
|
Diagnostics: []diag.Diagnostic{},
|
||||||
|
}
|
||||||
|
return req, &resp
|
||||||
|
}
|
||||||
57
internal/provider/validators/string_length_exactly.go
Normal file
57
internal/provider/validators/string_length_exactly.go
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package validators
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/filipowm/terraform-provider-unifi/internal/provider/base"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||||
|
)
|
||||||
|
|
||||||
|
func StringLengthExactly(len int) validator.String {
|
||||||
|
return stringLengthExactlyValidator{len: len}
|
||||||
|
}
|
||||||
|
|
||||||
|
type stringLengthExactlyValidator struct {
|
||||||
|
len int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v stringLengthExactlyValidator) invalidUsageMessage() string {
|
||||||
|
return "length cannot be less than zero"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v stringLengthExactlyValidator) Description(_ context.Context) string {
|
||||||
|
return fmt.Sprintf("string length must be exactly %d", v.len)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v stringLengthExactlyValidator) MarkdownDescription(ctx context.Context) string {
|
||||||
|
return v.Description(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v stringLengthExactlyValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) {
|
||||||
|
if v.len < 0 {
|
||||||
|
resp.Diagnostics.Append(
|
||||||
|
validatordiag.InvalidValidatorUsageDiagnostic(
|
||||||
|
req.Path,
|
||||||
|
"StringLengthExactly",
|
||||||
|
v.invalidUsageMessage(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
value := req.ConfigValue
|
||||||
|
if !base.IsDefined(value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val := value.ValueString()
|
||||||
|
if len(val) != v.len {
|
||||||
|
resp.Diagnostics.Append(
|
||||||
|
validatordiag.InvalidAttributeValueDiagnostic(
|
||||||
|
req.Path,
|
||||||
|
v.Description(ctx),
|
||||||
|
fmt.Sprintf("%s (length: %d)", val, len(val)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
35
internal/provider/validators/string_length_exactly_test.go
Normal file
35
internal/provider/validators/string_length_exactly_test.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package validators
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStringLengthExactlyValidation(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
value string
|
||||||
|
length int
|
||||||
|
validationFailed bool
|
||||||
|
}{
|
||||||
|
{"", 0, false},
|
||||||
|
{"", 1, true},
|
||||||
|
{"a", 0, true},
|
||||||
|
{"a", 1, false},
|
||||||
|
{"a", 2, true},
|
||||||
|
{"ab", 2, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(fmt.Sprintf("%s-expected-length-%d", tc.value, tc.length), func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
v := StringLengthExactly(tc.length)
|
||||||
|
req, resp := newStringValidatorRequestResponse(tc.value)
|
||||||
|
v.ValidateString(context.Background(), req, resp)
|
||||||
|
assert.Equal(t, tc.validationFailed, resp.Diagnostics.HasError())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ImportSiteAndID(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
|
|
||||||
if id := d.Id(); strings.Contains(id, ":") {
|
|
||||||
importParts := strings.SplitN(id, ":", 2)
|
|
||||||
d.SetId(importParts[1])
|
|
||||||
d.Set("site", importParts[0])
|
|
||||||
}
|
|
||||||
return []*schema.ResourceData{d}, nil
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user