diff --git a/.speakeasy/gen.lock b/.speakeasy/gen.lock index f810fa48e..e90e466d2 100755 --- a/.speakeasy/gen.lock +++ b/.speakeasy/gen.lock @@ -1,12 +1,12 @@ lockVersion: 2.0.0 id: 9d20c53e-7836-4ea8-88b8-a75fb7dce3a2 management: - docChecksum: 41a63cf4311cedcc5cb26f85e0f7c2bd + docChecksum: aa3a97868a10f80607f479b90527c9d1 docVersion: 2.0.0 speakeasyVersion: 1.541.2 generationVersion: 2.595.4 - releaseVersion: 2.7.1 - configChecksum: a2e5ba59a72f94f643bf86672ec2320b + releaseVersion: 2.7.2 + configChecksum: db9e9fe5085728f581238a0a6a03792e features: terraform: additionalDependencies: 0.1.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c96383c7..882e5f682 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 2.7.2 +> Released on 2025/05/06 + +### Bug fixes + +* Fixed output of `terraform plan` for `konnect_api_product_document` and `konnect_api_product_specification` resources. + ## 2.7.1 > Released on 2025/05/01 diff --git a/docs/index.md b/docs/index.md index 4934d8214..6c58d39c8 100644 --- a/docs/index.md +++ b/docs/index.md @@ -17,7 +17,7 @@ terraform { required_providers { konnect = { source = "kong/konnect" - version = "2.7.1" + version = "2.7.2" } } } diff --git a/gen.yaml b/gen.yaml index ef5e3344e..18616db6e 100644 --- a/gen.yaml +++ b/gen.yaml @@ -17,7 +17,7 @@ generation: oAuth2PasswordEnabled: false baseServerURL: "" terraform: - version: 2.7.1 + version: 2.7.2 additionalDataSources: [] additionalDependencies: {} additionalEphemeralResources: [] diff --git a/go.mod b/go.mod index e0d876916..01dfa8aa3 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,9 @@ module github.com/kong/terraform-provider-konnect/v2 -go 1.23.0 +go 1.23.4 require ( + github.com/Kong/shared-speakeasy/customtypes v0.1.0 github.com/ericlagergren/decimal v0.0.0-20221120152707-495c53812d05 github.com/hashicorp/go-uuid v1.0.3 github.com/hashicorp/terraform-plugin-docs v0.20.1 diff --git a/go.sum b/go.sum index 1b0f56409..9764928af 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/Kong/shared-speakeasy/customtypes v0.1.0 h1:xCEU9Dc7TJO5vuSxcH7Zi5/PUW7C6TMExGRJv9DjqJU= +github.com/Kong/shared-speakeasy/customtypes v0.1.0/go.mod h1:R3le6Th+bdGiOaroady+wkiKULzpiMYFYcDJv/rKkSE= github.com/Kunde21/markdownfmt/v3 v3.1.0 h1:KiZu9LKs+wFFBQKhrZJrFZwtLnCCWJahL+S+E/3VnM0= github.com/Kunde21/markdownfmt/v3 v3.1.0/go.mod h1:tPXN1RTyOzJwhfHoon9wUr4HGYmWgVxSQN6VBJDkrVc= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= diff --git a/internal/provider/apiproductdocument_resource.go b/internal/provider/apiproductdocument_resource.go index 0ed5faf23..45c8aca39 100644 --- a/internal/provider/apiproductdocument_resource.go +++ b/internal/provider/apiproductdocument_resource.go @@ -7,6 +7,7 @@ import ( "context" "encoding/json" "fmt" + "github.com/Kong/shared-speakeasy/customtypes/encodedstring" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" @@ -35,16 +36,16 @@ type APIProductDocumentResource struct { // APIProductDocumentResourceModel describes the resource data model. type APIProductDocumentResourceModel struct { - APIProductID types.String `tfsdk:"api_product_id"` - Content types.String `tfsdk:"content"` - CreatedAt types.String `tfsdk:"created_at"` - ID types.String `tfsdk:"id"` - Metadata *tfTypes.Metadata `tfsdk:"metadata"` - ParentDocumentID types.String `tfsdk:"parent_document_id"` - Slug types.String `tfsdk:"slug"` - Status types.String `tfsdk:"status"` - Title types.String `tfsdk:"title"` - UpdatedAt types.String `tfsdk:"updated_at"` + APIProductID types.String `tfsdk:"api_product_id"` + Content encodedstring.Base64OrPlainInput `tfsdk:"content"` + CreatedAt types.String `tfsdk:"created_at"` + ID types.String `tfsdk:"id"` + Metadata *tfTypes.Metadata `tfsdk:"metadata"` + ParentDocumentID types.String `tfsdk:"parent_document_id"` + Slug types.String `tfsdk:"slug"` + Status types.String `tfsdk:"status"` + Title types.String `tfsdk:"title"` + UpdatedAt types.String `tfsdk:"updated_at"` } func (r *APIProductDocumentResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { @@ -60,6 +61,7 @@ func (r *APIProductDocumentResource) Schema(ctx context.Context, req resource.Sc Description: `The API product identifier`, }, "content": schema.StringAttribute{ + CustomType: encodedstring.Base64OrPlainInputType{}, Computed: true, Optional: true, Description: `Can be markdown string content or base64 encoded string`, diff --git a/internal/provider/apiproductdocument_resource_sdk.go b/internal/provider/apiproductdocument_resource_sdk.go index d93e4a41f..424d39607 100644 --- a/internal/provider/apiproductdocument_resource_sdk.go +++ b/internal/provider/apiproductdocument_resource_sdk.go @@ -4,6 +4,7 @@ package provider import ( "context" + "github.com/Kong/shared-speakeasy/customtypes/encodedstring" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/kong/terraform-provider-konnect/v2/internal/provider/typeconvert" @@ -183,7 +184,9 @@ func (r *APIProductDocumentResourceModel) RefreshFromSharedAPIProductDocument(ct var diags diag.Diagnostics if resp != nil { - r.Content = types.StringValue(resp.Content) + contentValuable, contentDiags := encodedstring.Base64OrPlainInputType{}.ValueFromString(ctx, types.StringValue(resp.Content)) + diags.Append(contentDiags...) + r.Content, _ = contentValuable.(encodedstring.Base64OrPlainInput) r.CreatedAt = types.StringValue(typeconvert.TimeToString(resp.CreatedAt)) r.ID = types.StringValue(resp.ID) if r.Metadata == nil { diff --git a/internal/provider/apiproductspecification_resource.go b/internal/provider/apiproductspecification_resource.go index a6fd73577..5064227a9 100644 --- a/internal/provider/apiproductspecification_resource.go +++ b/internal/provider/apiproductspecification_resource.go @@ -7,6 +7,7 @@ import ( "context" "encoding/json" "fmt" + "github.com/Kong/shared-speakeasy/customtypes/encodedstring" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" @@ -34,13 +35,13 @@ type APIProductSpecificationResource struct { // APIProductSpecificationResourceModel describes the resource data model. type APIProductSpecificationResourceModel struct { - APIProductID types.String `tfsdk:"api_product_id"` - APIProductVersionID types.String `tfsdk:"api_product_version_id"` - Content types.String `tfsdk:"content"` - CreatedAt types.String `tfsdk:"created_at"` - ID types.String `tfsdk:"id"` - Name types.String `tfsdk:"name"` - UpdatedAt types.String `tfsdk:"updated_at"` + APIProductID types.String `tfsdk:"api_product_id"` + APIProductVersionID types.String `tfsdk:"api_product_version_id"` + Content encodedstring.Base64Input `tfsdk:"content"` + CreatedAt types.String `tfsdk:"created_at"` + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + UpdatedAt types.String `tfsdk:"updated_at"` } func (r *APIProductSpecificationResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { @@ -60,6 +61,7 @@ func (r *APIProductSpecificationResource) Schema(ctx context.Context, req resour Description: `The API product version identifier`, }, "content": schema.StringAttribute{ + CustomType: encodedstring.Base64InputType{}, Required: true, Description: `The base64 encoded contents of the API product version specification`, Validators: []validator.String{ diff --git a/internal/provider/apiproductspecification_resource_sdk.go b/internal/provider/apiproductspecification_resource_sdk.go index 393ec21c0..5dcc387ce 100644 --- a/internal/provider/apiproductspecification_resource_sdk.go +++ b/internal/provider/apiproductspecification_resource_sdk.go @@ -4,6 +4,7 @@ package provider import ( "context" + "github.com/Kong/shared-speakeasy/customtypes/encodedstring" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/kong/terraform-provider-konnect/v2/internal/provider/typeconvert" @@ -151,7 +152,9 @@ func (r *APIProductSpecificationResourceModel) RefreshFromSharedAPIProductVersio var diags diag.Diagnostics if resp != nil { - r.Content = types.StringValue(resp.Content) + contentValuable, contentDiags := encodedstring.Base64InputType{}.ValueFromString(ctx, types.StringValue(resp.Content)) + diags.Append(contentDiags...) + r.Content, _ = contentValuable.(encodedstring.Base64Input) r.CreatedAt = types.StringValue(typeconvert.TimeToString(resp.CreatedAt)) r.ID = types.StringValue(resp.ID) r.Name = types.StringValue(resp.Name) diff --git a/internal/sdk/konnect.go b/internal/sdk/konnect.go index ee880135e..2f3d97b07 100644 --- a/internal/sdk/konnect.go +++ b/internal/sdk/konnect.go @@ -296,9 +296,9 @@ func New(opts ...SDKOption) *Konnect { sdkConfiguration: sdkConfiguration{ Language: "go", OpenAPIDocVersion: "2.0.0", - SDKVersion: "2.7.1", + SDKVersion: "2.7.2", GenVersion: "2.595.4", - UserAgent: "speakeasy-sdk/terraform 2.7.1 2.595.4 2.0.0 github.com/kong/terraform-provider-konnect/v2/internal/sdk", + UserAgent: "speakeasy-sdk/terraform 2.7.2 2.595.4 2.0.0 github.com/kong/terraform-provider-konnect/v2/internal/sdk", Hooks: hooks.New(), }, } diff --git a/openapi.yaml b/openapi.yaml index 5414f2005..3c9001410 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -14708,6 +14708,11 @@ components: format: byte minLength: 1 type: string + x-speakeasy-terraform-custom-type: + imports: + - github.com/Kong/shared-speakeasy/customtypes/encodedstring + schemaType: 'encodedstring.Base64InputType{}' + valueType: encodedstring.Base64Input additionalProperties: false required: - name @@ -14740,6 +14745,11 @@ components: description: Can be markdown string content or base64 encoded string example: '## My Markdown' type: string + x-speakeasy-terraform-custom-type: + imports: + - github.com/Kong/shared-speakeasy/customtypes/encodedstring + schemaType: 'encodedstring.Base64OrPlainInputType{}' + valueType: encodedstring.Base64OrPlainInput metadata: description: metadata of the document type: object @@ -14780,6 +14790,11 @@ components: description: Can be markdown string content or base64 encoded string example: YmFzZTY0LWVuY29kZWQgdGV4dCBzdHJpbmc= type: string + x-speakeasy-terraform-custom-type: + imports: + - github.com/Kong/shared-speakeasy/customtypes/encodedstring + schemaType: 'encodedstring.Base64OrPlainInputType{}' + valueType: encodedstring.Base64OrPlainInput metadata: type: object nullable: false @@ -14845,6 +14860,11 @@ components: format: byte minLength: 1 type: string + x-speakeasy-terraform-custom-type: + imports: + - github.com/Kong/shared-speakeasy/customtypes/encodedstring + schemaType: 'encodedstring.Base64InputType{}' + valueType: encodedstring.Base64Input additionalProperties: false title: Update API Product Version Specification Request LegacyGatewayServicePayload: diff --git a/tests/resources/api_product_document_test.go b/tests/resources/api_product_document_test.go new file mode 100644 index 000000000..d3a69a484 --- /dev/null +++ b/tests/resources/api_product_document_test.go @@ -0,0 +1,35 @@ +package tests + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" +) + +func TestAPIProductDocument(t *testing.T) { + t.Run("plan-diff", func(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: providerFactory, + Steps: []resource.TestStep{ + { + Config: providerConfigUs, + ConfigDirectory: config.TestNameDirectory(), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("konnect_api_product_document.my_apiproductdocument", "title", "My Konnect API Product Document"), + ), + }, + { + Config: providerConfigUs, + ConfigDirectory: config.TestNameDirectory(), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectEmptyPlan(), + }, + }, + }, + }, + }) + }) +} diff --git a/tests/resources/api_product_specification_test.go b/tests/resources/api_product_specification_test.go new file mode 100644 index 000000000..265d24c2d --- /dev/null +++ b/tests/resources/api_product_specification_test.go @@ -0,0 +1,35 @@ +package tests + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" +) + +func TestAPIProductSpecification(t *testing.T) { + t.Run("plan-diff", func(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: providerFactory, + Steps: []resource.TestStep{ + { + Config: providerConfigUs, + ConfigDirectory: config.TestNameDirectory(), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("konnect_api_product_specification.my_apiproductspecification", "name", "oas.yaml"), + ), + }, + { + Config: providerConfigUs, + ConfigDirectory: config.TestNameDirectory(), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectEmptyPlan(), + }, + }, + }, + }, + }) + }) +} diff --git a/tests/resources/portal_appearance_test.go b/tests/resources/portal_appearance_test.go new file mode 100644 index 000000000..84277867f --- /dev/null +++ b/tests/resources/portal_appearance_test.go @@ -0,0 +1,35 @@ +package tests + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" +) + +func TestPortal(t *testing.T) { + t.Run("appearance", func(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: providerFactory, + Steps: []resource.TestStep{ + { + Config: providerConfigUs, + ConfigDirectory: config.TestNameDirectory(), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet("konnect_portal_appearance.test", "portal_id"), + ), + }, + { + Config: providerConfigUs, + ConfigDirectory: config.TestNameDirectory(), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectEmptyPlan(), + }, + }, + }, + }, + }) + }) +} diff --git a/tests/resources/testdata/TestAPIProductDocument/plan-diff/main.tf b/tests/resources/testdata/TestAPIProductDocument/plan-diff/main.tf new file mode 100644 index 000000000..2c4e726ad --- /dev/null +++ b/tests/resources/testdata/TestAPIProductDocument/plan-diff/main.tf @@ -0,0 +1,30 @@ +resource "konnect_portal" "my_portal" { + name = "My New Portal with API product document" + auto_approve_applications = false + auto_approve_developers = false + custom_domain = "api-product-doc-test.example.com" + is_public = false + rbac_enabled = false +} + +resource "konnect_api_product" "my_apiproduct" { + description = "Text describing the API product" + labels = { + key = "value" + } + name = "API Product" + portal_ids = [ + konnect_portal.my_portal.id + ] +} + +resource "konnect_api_product_document" "my_apiproductdocument" { + api_product_id = konnect_api_product.my_apiproduct.id + content = "YmFzZTY0LWVuY29kZWQgdGV4dCBzdHJpbmc=" + slug = "path-for-seo" + status = "unpublished" + title = "My Konnect API Product Document" + metadata = { + author = "John Doe" + } +} \ No newline at end of file diff --git a/tests/resources/testdata/TestAPIProductSpecification/plan-diff/main.tf b/tests/resources/testdata/TestAPIProductSpecification/plan-diff/main.tf new file mode 100644 index 000000000..0986b34bd --- /dev/null +++ b/tests/resources/testdata/TestAPIProductSpecification/plan-diff/main.tf @@ -0,0 +1,31 @@ +resource "konnect_portal" "my_portal" { + name = "My New Test Portal" + auto_approve_applications = false + auto_approve_developers = false + custom_domain = "api-product-spec-test.example.com" + is_public = false + rbac_enabled = false +} + +resource "konnect_api_product" "my_apiproduct" { + description = "Text describing the API product" + labels = { + key = "value" + } + name = "API Product" + portal_ids = [ + konnect_portal.my_portal.id + ] +} + +resource "konnect_api_product_version" "my_apiproductversion" { + api_product_id = konnect_api_product.my_apiproduct.id + name = "v1" +} + +resource "konnect_api_product_specification" "my_apiproductspecification" { + api_product_id = konnect_api_product.my_apiproduct.id + api_product_version_id = konnect_api_product_version.my_apiproductversion.id + content = "TXkgWUFNTCBvciBKU09OIGZvcm1hdHRlZCBPQVMgY29udGVudA==" + name = "oas.yaml" +} diff --git a/tests/resources/testdata/TestPortal/appearance/main.tf b/tests/resources/testdata/TestPortal/appearance/main.tf new file mode 100644 index 000000000..de9ceec69 --- /dev/null +++ b/tests/resources/testdata/TestPortal/appearance/main.tf @@ -0,0 +1,28 @@ +resource "konnect_portal" "my_portal" { + name = "My New Portal for appearance" + auto_approve_applications = false + auto_approve_developers = false + custom_domain = "portal.example.com" + is_public = false + rbac_enabled = false +} + +resource "konnect_portal_appearance" "test" { + portal_id = konnect_portal.my_portal.id + images = { + logo = { + data = "data:image/png;base64,AAAAHGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZgAAAOptZXRhAAAAAAAAACFoZGxyAAAAAAAAAABwaWN0AAAAAAAAAAAAAAAAAAAAAA5waXRtAAAAAAABAAAAImlsb2MAAAAAREAAAQABAAAAAAEOAAEAAAAAAAABFwAAACNpaW5mAAAAAAABAAAAFWluZmUCAAAAAAEAAGF2MDEAAAAAamlwcnAAAABLaXBjbwAAABNjb2xybmNseAABAA0ABoAAAAAMYXYxQ4EADAAAAAAUaXNwZQAAAAAAAAFoAAABZQAAABBwaXhpAAAAAAMICAgAAAAXaXBtYQAAAAAAAAABAAEEAYIDBAAAAR9tZGF0EgAKChgiLPZGCAhoNCAyhgIRQAEEEEFAtF684hmwEq9iyzdtLrTlKmftfA5/uTHWKKbr0z+BdfqHh9CQCmHfZj7xS9+algZ9VfLEhMN7ZVwRwe86yAUxs1YWrxw1MwXsTrg0zdS/QMG9qT/TPTe2JE4KHJ0ZgT/yzo2gMnZBJVlGsRRF3B7GfVABBu9DjuGf8bZ6yCrPOfT17wwm2RFpbu7u1uxcBQuxAh+bK4Izw6IdHkeI+DL+e0AE3HwZlRyb8g5Ok6pE40FRtVtepURmt+BqkHsOihpUul05pPsdoLktPasYDkDf5MaSrP0256mvI4OGn3Goq34YakB1TpmZzDxmZOGJW6OxblU1m152Ox56uWUPIDYK" + filename = "red-square-logo.png" + } + + catalog_cover = { + "data" = "data:image/png;base64,AAAAHGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZgAAAOptZXRhAAAAAAAAACFoZGxyAAAAAAAAAABwaWN0AAAAAAAAAAAAAAAAAAAAAA5waXRtAAAAAAABAAAAImlsb2MAAAAAREAAAQABAAAAAAEOAAEAAAAAAAABFwAAACNpaW5mAAAAAAABAAAAFWluZmUCAAAAAAEAAGF2MDEAAAAAamlwcnAAAABLaXBjbwAAABNjb2xybmNseAABAA0ABoAAAAAMYXYxQ4EADAAAAAAUaXNwZQAAAAAAAAFoAAABZQAAABBwaXhpAAAAAAMICAgAAAAXaXBtYQAAAAAAAAABAAEEAYIDBAAAAR9tZGF0EgAKChgiLPZGCAhoNCAyhgIRQAEEEEFAtF684hmwEq9iyzdtLrTlKmftfA5/uTHWKKbr0z+BdfqHh9CQCmHfZj7xS9+algZ9VfLEhMN7ZVwRwe86yAUxs1YWrxw1MwXsTrg0zdS/QMG9qT/TPTe2JE4KHJ0ZgT/yzo2gMnZBJVlGsRRF3B7GfVABBu9DjuGf8bZ6yCrPOfT17wwm2RFpbu7u1uxcBQuxAh+bK4Izw6IdHkeI+DL+e0AE3HwZlRyb8g5Ok6pE40FRtVtepURmt+BqkHsOihpUul05pPsdoLktPasYDkDf5MaSrP0256mvI4OGn3Goq34YakB1TpmZzDxmZOGJW6OxblU1m152Ox56uWUPIDYK" + filename = "red-square-banner.png" + } + + favicon = { + data = "data:image/png;base64,AAAAHGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZgAAAOptZXRhAAAAAAAAACFoZGxyAAAAAAAAAABwaWN0AAAAAAAAAAAAAAAAAAAAAA5waXRtAAAAAAABAAAAImlsb2MAAAAAREAAAQABAAAAAAEOAAEAAAAAAAABFwAAACNpaW5mAAAAAAABAAAAFWluZmUCAAAAAAEAAGF2MDEAAAAAamlwcnAAAABLaXBjbwAAABNjb2xybmNseAABAA0ABoAAAAAMYXYxQ4EADAAAAAAUaXNwZQAAAAAAAAFoAAABZQAAABBwaXhpAAAAAAMICAgAAAAXaXBtYQAAAAAAAAABAAEEAYIDBAAAAR9tZGF0EgAKChgiLPZGCAhoNCAyhgIRQAEEEEFAtF684hmwEq9iyzdtLrTlKmftfA5/uTHWKKbr0z+BdfqHh9CQCmHfZj7xS9+algZ9VfLEhMN7ZVwRwe86yAUxs1YWrxw1MwXsTrg0zdS/QMG9qT/TPTe2JE4KHJ0ZgT/yzo2gMnZBJVlGsRRF3B7GfVABBu9DjuGf8bZ6yCrPOfT17wwm2RFpbu7u1uxcBQuxAh+bK4Izw6IdHkeI+DL+e0AE3HwZlRyb8g5Ok6pE40FRtVtepURmt+BqkHsOihpUul05pPsdoLktPasYDkDf5MaSrP0256mvI4OGn3Goq34YakB1TpmZzDxmZOGJW6OxblU1m152Ox56uWUPIDYK" + filename = "red-square.png" + } + } +}