diff --git a/.github/workflows/samples-terraform.yaml b/.github/workflows/samples-terraform.yaml new file mode 100644 index 000000000000..04504f8f7c16 --- /dev/null +++ b/.github/workflows/samples-terraform.yaml @@ -0,0 +1,46 @@ +name: Samples Terraform + +on: + push: + paths: + - 'samples/client/petstore/terraform/**' + - 'samples/client/petstore/terraform-addpet/**' + - 'samples/client/petstore/terraform-server/**' + - 'samples/client/others/terraform/**' + pull_request: + paths: + - 'samples/client/petstore/terraform/**' + - 'samples/client/petstore/terraform-addpet/**' + - 'samples/client/petstore/terraform-server/**' + - 'samples/client/others/terraform/**' + +jobs: + build: + name: Build Terraform Provider + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + sample: + - samples/client/petstore/terraform/ + - samples/client/petstore/terraform-addpet/ + - samples/client/petstore/terraform-server/ + - samples/client/others/terraform/allof-discriminator/ + - samples/client/others/terraform/oneof-anyof-required/ + - samples/client/others/terraform/oneof-discriminator-lookup/ + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-go@v6 + with: + go-version: "stable" + - run: go version + - name: Install Dependencies + working-directory: ${{ matrix.sample }} + run: | + go mod tidy + - name: Build provider + working-directory: ${{ matrix.sample }} + run: go build -v ./... + - name: Run tests + working-directory: ${{ matrix.sample }} + run: go test ./... -v -timeout 120m diff --git a/bin/configs/terraform-provider-allof-discriminator.yaml b/bin/configs/terraform-provider-allof-discriminator.yaml new file mode 100644 index 000000000000..e1b81ffb1def --- /dev/null +++ b/bin/configs/terraform-provider-allof-discriminator.yaml @@ -0,0 +1,11 @@ +generatorName: terraform-provider +outputDir: samples/client/others/terraform/allof-discriminator +inputSpec: modules/openapi-generator/src/test/resources/3_0/go/allof_multiple_ref_and_discriminator.yaml +templateDir: modules/openapi-generator/src/main/resources/terraform-provider +gitHost: github.com +gitUserId: example +gitRepoId: terraform-provider-allof +additionalProperties: + providerName: "allof" + providerAddress: "registry.terraform.io/example/allof" + hideGenerationTimestamp: "true" diff --git a/bin/configs/terraform-provider-oneof-anyof-required.yaml b/bin/configs/terraform-provider-oneof-anyof-required.yaml new file mode 100644 index 000000000000..da6bf834c24b --- /dev/null +++ b/bin/configs/terraform-provider-oneof-anyof-required.yaml @@ -0,0 +1,11 @@ +generatorName: terraform-provider +outputDir: samples/client/others/terraform/oneof-anyof-required +inputSpec: modules/openapi-generator/src/test/resources/3_0/go/spec-with-oneof-anyof-required.yaml +templateDir: modules/openapi-generator/src/main/resources/terraform-provider +gitHost: github.com +gitUserId: example +gitRepoId: terraform-provider-oneof-anyof +additionalProperties: + providerName: "oneof" + providerAddress: "registry.terraform.io/example/oneof-anyof" + hideGenerationTimestamp: "true" diff --git a/bin/configs/terraform-provider-oneof-discriminator-lookup.yaml b/bin/configs/terraform-provider-oneof-discriminator-lookup.yaml new file mode 100644 index 000000000000..0f2a5034dac1 --- /dev/null +++ b/bin/configs/terraform-provider-oneof-discriminator-lookup.yaml @@ -0,0 +1,11 @@ +generatorName: terraform-provider +outputDir: samples/client/others/terraform/oneof-discriminator-lookup +inputSpec: modules/openapi-generator/src/test/resources/3_0/go/spec-with-oneof-discriminator.yaml +templateDir: modules/openapi-generator/src/main/resources/terraform-provider +gitHost: github.com +gitUserId: example +gitRepoId: terraform-provider-oneof-disc +additionalProperties: + providerName: "oneof" + providerAddress: "registry.terraform.io/example/oneof-disc" + hideGenerationTimestamp: "true" diff --git a/bin/configs/terraform-provider-petstore-addpet.yaml b/bin/configs/terraform-provider-petstore-addpet.yaml new file mode 100644 index 000000000000..def0ff729b72 --- /dev/null +++ b/bin/configs/terraform-provider-petstore-addpet.yaml @@ -0,0 +1,11 @@ +generatorName: terraform-provider +outputDir: samples/client/petstore/terraform-addpet +inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore-addpet-only.yaml +templateDir: modules/openapi-generator/src/main/resources/terraform-provider +gitHost: github.com +gitUserId: example +gitRepoId: terraform-provider-petstore-addpet +additionalProperties: + providerName: "petstore" + providerAddress: "registry.terraform.io/example/petstore-addpet" + hideGenerationTimestamp: "true" diff --git a/bin/configs/terraform-provider-petstore-new.yaml b/bin/configs/terraform-provider-petstore-new.yaml new file mode 100644 index 000000000000..d0be301e0d19 --- /dev/null +++ b/bin/configs/terraform-provider-petstore-new.yaml @@ -0,0 +1,11 @@ +generatorName: terraform-provider +outputDir: samples/client/petstore/terraform +inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml +templateDir: modules/openapi-generator/src/main/resources/terraform-provider +gitHost: github.com +gitUserId: example +gitRepoId: terraform-provider-petstore +additionalProperties: + providerName: "petstore" + providerAddress: "registry.terraform.io/example/petstore" + hideGenerationTimestamp: "true" diff --git a/bin/configs/terraform-provider-petstore-server.yaml b/bin/configs/terraform-provider-petstore-server.yaml new file mode 100644 index 000000000000..9d6322fefe35 --- /dev/null +++ b/bin/configs/terraform-provider-petstore-server.yaml @@ -0,0 +1,11 @@ +generatorName: terraform-provider +outputDir: samples/client/petstore/terraform-server +inputSpec: modules/openapi-generator/src/test/resources/3_0/go-server/petstore.yaml +templateDir: modules/openapi-generator/src/main/resources/terraform-provider +gitHost: github.com +gitUserId: example +gitRepoId: terraform-provider-petstore-server +additionalProperties: + providerName: "petstore" + providerAddress: "registry.terraform.io/example/petstore-server" + hideGenerationTimestamp: "true" diff --git a/docs/generators.md b/docs/generators.md index bfa97dd22e54..930bdbd80304 100644 --- a/docs/generators.md +++ b/docs/generators.md @@ -70,6 +70,7 @@ The following generators are available: * [swift-combine](generators/swift-combine.md) * [swift5 (deprecated)](generators/swift5.md) * [swift6](generators/swift6.md) +* [terraform-provider (experimental)](generators/terraform-provider.md) * [typescript (experimental)](generators/typescript.md) * [typescript-angular](generators/typescript-angular.md) * [typescript-aurelia](generators/typescript-aurelia.md) diff --git a/docs/generators/terraform-provider.md b/docs/generators/terraform-provider.md new file mode 100644 index 000000000000..8f14dff0c1c9 --- /dev/null +++ b/docs/generators/terraform-provider.md @@ -0,0 +1,229 @@ +--- +title: Documentation for the terraform-provider Generator +--- + +## METADATA + +| Property | Value | Notes | +| -------- | ----- | ----- | +| generator name | terraform-provider | pass this to the generate command after -g | +| generator stability | EXPERIMENTAL | | +| generator type | CLIENT | | +| generator language | Go | | +| generator default templating engine | mustache | | +| helpTxt | Generates a Terraform provider (Go, using HashiCorp Plugin Framework). | | + +## CONFIG OPTIONS +These options may be applied as additional-properties (cli) or configOptions (plugins). Refer to [configuration docs](https://openapi-generator.tech/docs/configuration) for more details. + +| Option | Description | Values | Default | +| ------ | ----------- | ------ | ------- | +|hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |true| +|packageName|Go package name (convention: lowercase).| |openapi| +|packageVersion|Go package version.| |1.0.0| +|providerAddress|Terraform provider registry address| |registry.terraform.io/example/example| +|providerName|Terraform provider name (e.g. 'petstore')| |example| +|providerVersion|Terraform provider version| |0.1.0| + +## IMPORT MAPPING + +| Type/Alias | Imports | +| ---------- | ------- | + + +## INSTANTIATION TYPES + +| Type/Alias | Instantiated By | +| ---------- | --------------- | + + +## LANGUAGE PRIMITIVES + + + +## RESERVED WORDS + + + +## FEATURE SET + + +### Client Modification Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|BasePath|✗|ToolingExtension +|Authorizations|✗|ToolingExtension +|UserAgent|✗|ToolingExtension +|MockServer|✗|ToolingExtension + +### Data Type Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Custom|✗|OAS2,OAS3 +|Int32|✓|OAS2,OAS3 +|Int64|✓|OAS2,OAS3 +|Float|✓|OAS2,OAS3 +|Double|✓|OAS2,OAS3 +|Decimal|✓|ToolingExtension +|String|✓|OAS2,OAS3 +|Byte|✓|OAS2,OAS3 +|Binary|✓|OAS2,OAS3 +|Boolean|✓|OAS2,OAS3 +|Date|✓|OAS2,OAS3 +|DateTime|✓|OAS2,OAS3 +|Password|✓|OAS2,OAS3 +|File|✓|OAS2 +|Uuid|✗| +|Array|✓|OAS2,OAS3 +|Null|✗|OAS3 +|AnyType|✗|OAS2,OAS3 +|Object|✓|OAS2,OAS3 +|Maps|✓|ToolingExtension +|CollectionFormat|✓|OAS2 +|CollectionFormatMulti|✓|OAS2 +|Enum|✓|OAS2,OAS3 +|ArrayOfEnum|✓|ToolingExtension +|ArrayOfModel|✓|ToolingExtension +|ArrayOfCollectionOfPrimitives|✓|ToolingExtension +|ArrayOfCollectionOfModel|✓|ToolingExtension +|ArrayOfCollectionOfEnum|✓|ToolingExtension +|MapOfEnum|✓|ToolingExtension +|MapOfModel|✓|ToolingExtension +|MapOfCollectionOfPrimitives|✓|ToolingExtension +|MapOfCollectionOfModel|✓|ToolingExtension +|MapOfCollectionOfEnum|✓|ToolingExtension + +### Documentation Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Readme|✓|ToolingExtension +|Model|✓|ToolingExtension +|Api|✓|ToolingExtension + +### Global Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Host|✓|OAS2,OAS3 +|BasePath|✓|OAS2,OAS3 +|Info|✓|OAS2,OAS3 +|Schemes|✗|OAS2,OAS3 +|PartialSchemes|✓|OAS2,OAS3 +|Consumes|✓|OAS2 +|Produces|✓|OAS2 +|ExternalDocumentation|✓|OAS2,OAS3 +|Examples|✓|OAS2,OAS3 +|XMLStructureDefinitions|✗|OAS2,OAS3 +|MultiServer|✗|OAS3 +|ParameterizedServer|✗|OAS3 +|ParameterStyling|✗|OAS3 +|Callbacks|✗|OAS3 +|LinkObjects|✗|OAS3 + +### Parameter Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Path|✓|OAS2,OAS3 +|Query|✓|OAS2,OAS3 +|Header|✓|OAS2,OAS3 +|Body|✓|OAS2 +|FormUnencoded|✓|OAS2 +|FormMultipart|✓|OAS2 +|Cookie|✓|OAS3 + +### Schema Support Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Simple|✓|OAS2,OAS3 +|Composite|✓|OAS2,OAS3 +|Polymorphism|✗|OAS2,OAS3 +|Union|✗|OAS3 +|allOf|✗|OAS2,OAS3 +|anyOf|✗|OAS3 +|oneOf|✗|OAS3 +|not|✗|OAS3 + +### Security Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|BasicAuth|✓|OAS2,OAS3 +|ApiKey|✓|OAS2,OAS3 +|OpenIDConnect|✗|OAS3 +|BearerToken|✓|OAS3 +|OAuth2_Implicit|✗|OAS2,OAS3 +|OAuth2_Password|✗|OAS2,OAS3 +|OAuth2_ClientCredentials|✗|OAS2,OAS3 +|OAuth2_AuthorizationCode|✗|OAS2,OAS3 +|SignatureAuth|✗|OAS3 +|AWSV4Signature|✗|ToolingExtension + +### Wire Format Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|JSON|✓|OAS2,OAS3 +|XML|✗|OAS2,OAS3 +|PROTOBUF|✗|ToolingExtension +|Custom|✗|OAS2,OAS3 diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TerraformProviderCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TerraformProviderCodegen.java new file mode 100644 index 000000000000..7cc6bcfbcf98 --- /dev/null +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TerraformProviderCodegen.java @@ -0,0 +1,556 @@ +/* + * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openapitools.codegen.languages; + +import org.openapitools.codegen.*; +import org.openapitools.codegen.meta.GeneratorMetadata; +import org.openapitools.codegen.meta.Stability; +import org.openapitools.codegen.meta.features.*; +import org.openapitools.codegen.model.ModelMap; +import org.openapitools.codegen.model.ModelsMap; +import org.openapitools.codegen.model.OperationMap; +import org.openapitools.codegen.model.OperationsMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.util.*; + +import static org.openapitools.codegen.utils.StringUtils.camelize; +import static org.openapitools.codegen.utils.StringUtils.underscore; + +public class TerraformProviderCodegen extends AbstractGoCodegen { + + private final Logger LOGGER = LoggerFactory.getLogger(TerraformProviderCodegen.class); + + public static final String PROVIDER_NAME = "providerName"; + public static final String PROVIDER_ADDRESS = "providerAddress"; + public static final String PROVIDER_VERSION = "providerVersion"; + + protected String providerName = "example"; + protected String providerAddress = "registry.terraform.io/example/example"; + protected String providerVersion = "0.1.0"; + + @Override + public CodegenType getTag() { + return CodegenType.CLIENT; + } + + @Override + public String getName() { + return "terraform-provider"; + } + + @Override + public String getHelp() { + return "Generates a Terraform provider (Go, using HashiCorp Plugin Framework)."; + } + + public TerraformProviderCodegen() { + super(); + + generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata) + .stability(Stability.EXPERIMENTAL) + .build(); + + modifyFeatureSet(features -> features + .includeDocumentationFeatures(DocumentationFeature.Readme) + .wireFormatFeatures(EnumSet.of(WireFormatFeature.JSON)) + .securityFeatures(EnumSet.of( + SecurityFeature.BasicAuth, + SecurityFeature.BearerToken, + SecurityFeature.ApiKey + )) + .excludeGlobalFeatures( + GlobalFeature.XMLStructureDefinitions, + GlobalFeature.Callbacks, + GlobalFeature.LinkObjects, + GlobalFeature.ParameterStyling + ) + .excludeSchemaSupportFeatures( + SchemaSupportFeature.Polymorphism + ) + ); + + outputFolder = "generated-code/terraform-provider"; + embeddedTemplateDir = templateDir = "terraform-provider"; + + // API templates: generate per-tag resource, data source, and model files + apiTemplateFiles.put("resource.mustache", "_resource.go"); + apiTemplateFiles.put("data_source.mustache", "_data_source.go"); + apiTemplateFiles.put("resource_model.mustache", "_model.go"); + + // Model templates: generate per-schema Go structs for the client package + modelTemplateFiles.put("model.mustache", ".go"); + + // No doc templates + apiDocTemplateFiles.clear(); + modelDocTemplateFiles.clear(); + + hideGenerationTimestamp = Boolean.TRUE; + + // Override type mappings for Terraform (no time.Time or *os.File) + typeMapping.put("DateTime", "string"); + typeMapping.put("date", "string"); + typeMapping.put("File", "string"); + typeMapping.put("file", "string"); + typeMapping.put("binary", "string"); + + cliOptions.add(new CliOption(PROVIDER_NAME, "Terraform provider name (e.g. 'petstore')") + .defaultValue(providerName)); + cliOptions.add(new CliOption(PROVIDER_ADDRESS, "Terraform provider registry address") + .defaultValue(providerAddress)); + cliOptions.add(new CliOption(PROVIDER_VERSION, "Terraform provider version") + .defaultValue(providerVersion)); + } + + @Override + public void processOpts() { + super.processOpts(); + + if (additionalProperties.containsKey(PROVIDER_NAME)) { + providerName = additionalProperties.get(PROVIDER_NAME).toString(); + } + additionalProperties.put(PROVIDER_NAME, providerName); + + if (additionalProperties.containsKey(PROVIDER_ADDRESS)) { + providerAddress = additionalProperties.get(PROVIDER_ADDRESS).toString(); + } + additionalProperties.put(PROVIDER_ADDRESS, providerAddress); + + if (additionalProperties.containsKey(PROVIDER_VERSION)) { + providerVersion = additionalProperties.get(PROVIDER_VERSION).toString(); + } + additionalProperties.put(PROVIDER_VERSION, providerVersion); + + if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) { + setPackageName((String) additionalProperties.get(CodegenConstants.PACKAGE_NAME)); + } else { + setPackageName("provider"); + } + additionalProperties.put(CodegenConstants.PACKAGE_NAME, packageName); + + apiPackage = "internal" + File.separator + "provider"; + modelPackage = "internal" + File.separator + "client"; + + // Supporting files + supportingFiles.add(new SupportingFile("main.mustache", "", "main.go")); + supportingFiles.add(new SupportingFile("provider.mustache", "internal" + File.separator + "provider", "provider.go")); + supportingFiles.add(new SupportingFile("client.mustache", "internal" + File.separator + "client", "client.go")); + supportingFiles.add(new SupportingFile("go.mod.mustache", "", "go.mod")); + supportingFiles.add(new SupportingFile("GNUmakefile.mustache", "", "GNUmakefile")); + supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); + supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore")); + supportingFiles.add(new SupportingFile("provider_example.mustache", "examples" + File.separator + "provider", "provider.tf")); + } + + @Override + public String apiFileFolder() { + return outputFolder + File.separator + "internal" + File.separator + "provider"; + } + + @Override + public String modelFileFolder() { + return outputFolder + File.separator + "internal" + File.separator + "client"; + } + + @Override + public String toApiFilename(String name) { + return underscore(name); + } + + @Override + public String toModelFilename(String name) { + return "model_" + underscore(name); + } + + @Override + public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List allModels) { + OperationMap objectMap = objs.getOperations(); + List operations = objectMap.getOperation(); + + // Store original uppercase HTTP method and build fmt.Sprintf-ready paths + for (CodegenOperation operation : operations) { + // Store uppercase method for net/http (e.g. "POST", "GET") + operation.vendorExtensions.put("x-terraform-http-method", operation.httpMethod.toUpperCase(Locale.ROOT)); + + // Convert path params to fmt.Sprintf format: /pet/{petId} -> /pet/%v + if (operation.path != null && operation.pathParams != null && !operation.pathParams.isEmpty()) { + String fmtPath = operation.path; + for (CodegenParameter param : operation.pathParams) { + fmtPath = fmtPath.replace("{" + param.baseName + "}", "%v"); + } + operation.vendorExtensions.put("x-terraform-path-fmt", fmtPath); + } + + // http method verb conversion (e.g. PUT => Put) as parent does + operation.httpMethod = camelize(operation.httpMethod.toLowerCase(Locale.ROOT)); + } + + // CRUD detection per tag + CodegenOperation createOp = null; + CodegenOperation readOp = null; + CodegenOperation updateOp = null; + CodegenOperation deleteOp = null; + CodegenOperation listOp = null; + + // First pass: check for explicit vendor extensions + for (CodegenOperation op : operations) { + if (getBooleanVendorExtension(op, "x-terraform-exclude")) { + continue; + } + if (getBooleanVendorExtension(op, "x-terraform-is-create") && createOp == null) { + createOp = op; + } + if (getBooleanVendorExtension(op, "x-terraform-is-read") && readOp == null) { + readOp = op; + } + if (getBooleanVendorExtension(op, "x-terraform-is-update") && updateOp == null) { + updateOp = op; + } + if (getBooleanVendorExtension(op, "x-terraform-is-delete") && deleteOp == null) { + deleteOp = op; + } + if (getBooleanVendorExtension(op, "x-terraform-is-list") && listOp == null) { + listOp = op; + } + } + + // Second pass: auto-detect using REST pattern methods + for (CodegenOperation op : operations) { + if (getBooleanVendorExtension(op, "x-terraform-exclude")) { + continue; + } + if (createOp == null && op.isRestfulCreate()) { + createOp = op; + } else if (readOp == null && op.isRestfulShow()) { + readOp = op; + } else if (updateOp == null && op.isRestfulUpdate()) { + updateOp = op; + } else if (deleteOp == null && op.isRestfulDestroy()) { + deleteOp = op; + } else if (listOp == null && op.isRestfulIndex()) { + listOp = op; + } + } + + // Mark the selected operations with vendor extensions + if (createOp != null) { + createOp.vendorExtensions.put("x-terraform-is-create", true); + } + if (readOp != null) { + readOp.vendorExtensions.put("x-terraform-is-read", true); + } + if (updateOp != null) { + updateOp.vendorExtensions.put("x-terraform-is-update", true); + } + if (deleteOp != null) { + deleteOp.vendorExtensions.put("x-terraform-is-delete", true); + } + if (listOp != null) { + listOp.vendorExtensions.put("x-terraform-is-list", true); + } + + // Tag-level flags and CRUD details for template rendering + objectMap.put("hasCreate", createOp != null); + objectMap.put("hasRead", readOp != null); + objectMap.put("hasUpdate", updateOp != null); + objectMap.put("hasDelete", deleteOp != null); + objectMap.put("hasList", listOp != null); + + // Store CRUD operation details at tag level for simplified template access + if (createOp != null) { + objectMap.put("createMethod", createOp.vendorExtensions.get("x-terraform-http-method")); + objectMap.put("createPath", createOp.path); + } + if (readOp != null) { + objectMap.put("readMethod", readOp.vendorExtensions.get("x-terraform-http-method")); + objectMap.put("readPath", readOp.vendorExtensions.getOrDefault("x-terraform-path-fmt", readOp.path)); + objectMap.put("readHasPathParams", readOp.pathParams != null && !readOp.pathParams.isEmpty()); + } + if (updateOp != null) { + objectMap.put("updateMethod", updateOp.vendorExtensions.get("x-terraform-http-method")); + objectMap.put("updatePath", updateOp.vendorExtensions.getOrDefault("x-terraform-path-fmt", updateOp.path)); + objectMap.put("updateHasPathParams", updateOp.pathParams != null && !updateOp.pathParams.isEmpty()); + } + if (deleteOp != null) { + objectMap.put("deleteMethod", deleteOp.vendorExtensions.get("x-terraform-http-method")); + objectMap.put("deletePath", deleteOp.vendorExtensions.getOrDefault("x-terraform-path-fmt", deleteOp.path)); + objectMap.put("deleteHasPathParams", deleteOp.pathParams != null && !deleteOp.pathParams.isEmpty()); + } + + // Determine resource name from tag + String tag = objectMap.getClassname(); + // Strip common suffixes like "Api", "API" from the tag name + String cleanTag = tag.replaceAll("(?i)api$", ""); + if (cleanTag.isEmpty()) { + cleanTag = tag; + } + String resourceName = underscore(cleanTag).toLowerCase(Locale.ROOT); + // Allow override via x-terraform-resource-name on any operation + for (CodegenOperation op : operations) { + Object nameOverride = op.vendorExtensions.get("x-terraform-resource-name"); + if (nameOverride != null) { + resourceName = nameOverride.toString(); + break; + } + } + objectMap.put("resourceName", resourceName); + objectMap.put("resourceClassName", camelize(resourceName)); + + // Detect ID field from read operation path params + String idField = "id"; + if (readOp != null && readOp.pathParams != null && !readOp.pathParams.isEmpty()) { + idField = readOp.pathParams.get(readOp.pathParams.size() - 1).paramName; + } + objectMap.put("idField", idField); + + // Build model info for the resource schema + // Use the response type from readOp (or createOp) as the resource model + String responseModel = null; + if (readOp != null && readOp.returnType != null) { + responseModel = readOp.returnType; + } else if (createOp != null && createOp.returnType != null) { + responseModel = createOp.returnType; + } + objectMap.put("responseModel", responseModel); + + // Build request model from createOp body params + String requestModel = null; + if (createOp != null && createOp.bodyParam != null && createOp.bodyParam.dataType != null) { + requestModel = createOp.bodyParam.dataType; + } + objectMap.put("requestModel", requestModel); + + // Collect model properties for schema generation and resolve ID field + String idModelGoName = camelize(idField); + String idModelGoType = "string"; + + if (responseModel != null) { + for (ModelMap modelMap : allModels) { + CodegenModel model = modelMap.getModel(); + if (model.classname.equals(responseModel)) { + List> tfAttributes = new ArrayList<>(); + boolean hasListAttributes = false; + for (CodegenProperty prop : model.vars) { + Map attr = new HashMap<>(); + attr.put("name", prop.baseName); + attr.put("terraformName", underscore(prop.baseName).toLowerCase(Locale.ROOT)); + attr.put("goName", camelize(prop.baseName)); + attr.put("goType", prop.dataType); + attr.put("terraformType", goTypeToTerraformType(prop.dataType)); + attr.put("terraformAttrType", goTypeToTerraformAttrType(prop.dataType, prop)); + attr.put("isRequired", prop.required); + attr.put("isComputed", prop.isReadOnly); + attr.put("isOptional", !prop.required && !prop.isReadOnly); + attr.put("description", prop.description != null ? prop.description : ""); + attr.put("isSensitive", prop.isWriteOnly || getBooleanVendorExtension(prop, "x-terraform-sensitive")); + attr.put("isString", "string".equals(prop.dataType)); + attr.put("isInt64", "int64".equals(prop.dataType) || "int32".equals(prop.dataType)); + attr.put("isFloat64", "float64".equals(prop.dataType) || "float32".equals(prop.dataType)); + attr.put("isBool", "bool".equals(prop.dataType)); + attr.put("isList", prop.isArray); + attr.put("isObject", prop.isModel && !prop.isArray); + if (prop.isArray) { + hasListAttributes = true; + if (prop.items != null) { + attr.put("listElementType", goTypeToTerraformElementType(prop.items.dataType)); + } + } + tfAttributes.add(attr); + } + objectMap.put("tfAttributes", tfAttributes); + objectMap.put("hasListAttributes", hasListAttributes); + + // Resolve ID field: match path param name to a model property + boolean idResolved = false; + // Try exact match on baseName (case-insensitive) + for (CodegenProperty prop : model.vars) { + if (prop.baseName.equalsIgnoreCase(idField)) { + idModelGoName = camelize(prop.baseName); + idModelGoType = prop.dataType; + idResolved = true; + break; + } + } + // Try stripping resource name prefix (e.g., petId -> id) + if (!idResolved) { + String strippedId = idField.replaceFirst("(?i)^" + resourceName, ""); + if (!strippedId.isEmpty()) { + for (CodegenProperty prop : model.vars) { + if (prop.baseName.equalsIgnoreCase(strippedId)) { + idModelGoName = camelize(prop.baseName); + idModelGoType = prop.dataType; + idResolved = true; + break; + } + } + } + } + // Fall back to "id" property + if (!idResolved) { + for (CodegenProperty prop : model.vars) { + if ("id".equalsIgnoreCase(prop.baseName)) { + idModelGoName = camelize(prop.baseName); + idModelGoType = prop.dataType; + break; + } + } + } + + break; + } + } + } + + objectMap.put("idFieldExported", idModelGoName); + + // Determine the value accessor for the ID field based on its type + String idValueAccessor; + if ("int64".equals(idModelGoType) || "int32".equals(idModelGoType) || "int".equals(idModelGoType)) { + idValueAccessor = ".ValueInt64()"; + } else if ("float64".equals(idModelGoType) || "float32".equals(idModelGoType)) { + idValueAccessor = ".ValueFloat64()"; + } else { + idValueAccessor = ".ValueString()"; + } + objectMap.put("idFieldValueAccessor", idValueAccessor); + + // Terraform name for the ID field (for path.Root in ImportState) + objectMap.put("idFieldTerraformName", underscore(idModelGoName).toLowerCase(Locale.ROOT)); + + return objs; + } + + @Override + public ModelsMap postProcessModels(ModelsMap objs) { + // Call parent to handle Go-specific post-processing + objs = super.postProcessModels(objs); + + for (ModelMap m : objs.getModels()) { + CodegenModel model = m.getModel(); + for (CodegenProperty prop : model.vars) { + // Ensure x-go-datatag is set for all model vars. + // The parent AbstractGoCodegen only sets x-go-datatag on + // inheritedProperties (oneOf/anyOf entries) when the model has + // composed schemas without allOf, leaving model.vars without + // json tags. We fix this by generating the tag here when missing. + if (!prop.vendorExtensions.containsKey("x-go-datatag")) { + String goDataTag = "json:\"" + prop.baseName; + if (!prop.required) { + goDataTag += ",omitempty"; + } + goDataTag += "\""; + goDataTag = " `" + goDataTag + "`"; + prop.vendorExtensions.put("x-go-datatag", goDataTag); + } + + // Add Terraform-specific vendor extensions + if (prop.isReadOnly) { + prop.vendorExtensions.put("x-terraform-computed", true); + } + if (prop.required) { + prop.vendorExtensions.put("x-terraform-required", true); + } + if (!prop.required && !prop.isReadOnly) { + prop.vendorExtensions.put("x-terraform-optional", true); + } + if (prop.isWriteOnly) { + prop.vendorExtensions.put("x-terraform-sensitive", true); + } + + // Map Go types to Terraform types + prop.vendorExtensions.put("x-terraform-type", goTypeToTerraformType(prop.dataType)); + prop.vendorExtensions.put("x-terraform-attr-type", goTypeToTerraformAttrType(prop.dataType, prop)); + } + } + + return objs; + } + + private String goTypeToTerraformType(String goType) { + if (goType == null) return "types.String"; + switch (goType) { + case "string": + return "types.String"; + case "int32": + case "int64": + case "int": + return "types.Int64"; + case "float32": + case "float64": + return "types.Float64"; + case "bool": + return "types.Bool"; + default: + if (goType.startsWith("[]")) { + return "types.List"; + } + return "types.String"; + } + } + + private String goTypeToTerraformAttrType(String goType, CodegenProperty prop) { + if (goType == null) return "schema.StringAttribute"; + switch (goType) { + case "string": + return "schema.StringAttribute"; + case "int32": + case "int64": + case "int": + return "schema.Int64Attribute"; + case "float32": + case "float64": + return "schema.Float64Attribute"; + case "bool": + return "schema.BoolAttribute"; + default: + if (goType.startsWith("[]")) { + return "schema.ListAttribute"; + } + // Complex objects are serialized as JSON strings + return "schema.StringAttribute"; + } + } + + private String goTypeToTerraformElementType(String goType) { + if (goType == null) return "types.StringType"; + switch (goType) { + case "string": + return "types.StringType"; + case "int32": + case "int64": + case "int": + return "types.Int64Type"; + case "float32": + case "float64": + return "types.Float64Type"; + case "bool": + return "types.BoolType"; + default: + return "types.StringType"; + } + } + + private boolean getBooleanVendorExtension(CodegenOperation op, String key) { + return op.vendorExtensions.containsKey(key) && Boolean.TRUE.equals(op.vendorExtensions.get(key)); + } + + private boolean getBooleanVendorExtension(CodegenProperty prop, String key) { + return prop.vendorExtensions.containsKey(key) && Boolean.TRUE.equals(prop.vendorExtensions.get(key)); + } +} diff --git a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig index 345f9b2543b7..ccfd6366994b 100644 --- a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig +++ b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig @@ -148,6 +148,7 @@ org.openapitools.codegen.languages.StaticHtml2Generator org.openapitools.codegen.languages.Swift5ClientCodegen org.openapitools.codegen.languages.Swift6ClientCodegen org.openapitools.codegen.languages.SwiftCombineClientCodegen +org.openapitools.codegen.languages.TerraformProviderCodegen org.openapitools.codegen.languages.TypeScriptClientCodegen org.openapitools.codegen.languages.TypeScriptAngularClientCodegen org.openapitools.codegen.languages.TypeScriptAureliaClientCodegen diff --git a/modules/openapi-generator/src/main/resources/terraform-provider/GNUmakefile.mustache b/modules/openapi-generator/src/main/resources/terraform-provider/GNUmakefile.mustache new file mode 100644 index 000000000000..341d3d1a573d --- /dev/null +++ b/modules/openapi-generator/src/main/resources/terraform-provider/GNUmakefile.mustache @@ -0,0 +1,27 @@ +default: testacc + +# Run acceptance tests +.PHONY: testacc +testacc: + TF_ACC=1 go test ./... -v $(TESTARGS) -timeout 120m + +# Build provider +.PHONY: build +build: + go build -o terraform-provider-{{providerName}} + +# Install provider locally +.PHONY: install +install: build + mkdir -p ~/.terraform.d/plugins/{{providerAddress}}/{{providerVersion}}/$(shell go env GOOS)_$(shell go env GOARCH) + mv terraform-provider-{{providerName}} ~/.terraform.d/plugins/{{providerAddress}}/{{providerVersion}}/$(shell go env GOOS)_$(shell go env GOARCH)/ + +# Generate documentation +.PHONY: docs +docs: + go generate ./... + +# Run linter +.PHONY: lint +lint: + golangci-lint run ./... diff --git a/modules/openapi-generator/src/main/resources/terraform-provider/README.mustache b/modules/openapi-generator/src/main/resources/terraform-provider/README.mustache new file mode 100644 index 000000000000..d154d5f9ffb4 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/terraform-provider/README.mustache @@ -0,0 +1,61 @@ +# Terraform Provider {{providerName}} + +This Terraform provider was generated using [OpenAPI Generator](https://openapi-generator.tech). + +## Requirements + +- [Terraform](https://www.terraform.io/downloads.html) >= 1.0 +- [Go](https://golang.org/doc/install) >= 1.22 + +## Building The Provider + +1. Clone the repository +2. Enter the repository directory +3. Build the provider using the Go `install` command: + +```shell +go install +``` + +## Using the provider + +```hcl +terraform { + required_providers { + {{providerName}} = { + source = "{{providerAddress}}" + } + } +} + +provider "{{providerName}}" { + endpoint = "{{basePath}}" +} +``` + +## Developing the Provider + +If you wish to work on the provider, you'll first need [Go](http://www.golang.org) installed on your machine (see [Requirements](#requirements) above). + +To compile the provider, run `go install`. This will build the provider and put the provider binary in the `$GOPATH/bin` directory. + +For local development and testing, add a `dev_overrides` block to your `~/.terraformrc` so that Terraform uses the locally built binary instead of fetching from the registry: + +```hcl +provider_installation { + dev_overrides { + "{{providerAddress}}" = "${GOPATH}/bin" + } + direct {} +} +``` + +With `dev_overrides` configured, you do not need to run `terraform init` for the provider. + +To generate or update documentation, run `go generate`. + +In order to run the full suite of Acceptance tests, run `make testacc`. + +```shell +make testacc +``` diff --git a/modules/openapi-generator/src/main/resources/terraform-provider/client.mustache b/modules/openapi-generator/src/main/resources/terraform-provider/client.mustache new file mode 100644 index 000000000000..ca744552d861 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/terraform-provider/client.mustache @@ -0,0 +1,77 @@ +{{>partial_header}} +package client + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" +) + +// Client is the API client used to interact with the API. +type Client struct { + BaseURL string + HTTPClient *http.Client + ApiKey string + Token string + Username string + Password string +} + +// NewClient creates a new API client with the given base URL. +func NewClient(baseURL string) *Client { + return &Client{ + BaseURL: baseURL, + HTTPClient: &http.Client{}, + } +} + +// DoRequest executes an HTTP request and returns the response body. +func (c *Client) DoRequest(ctx context.Context, method, path string, body interface{}) ([]byte, error) { + var reqBody io.Reader + if body != nil { + jsonBody, err := json.Marshal(body) + if err != nil { + return nil, fmt.Errorf("error marshaling request body: %w", err) + } + reqBody = bytes.NewBuffer(jsonBody) + } + + req, err := http.NewRequestWithContext(ctx, method, c.BaseURL+path, reqBody) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + + if body != nil { + req.Header.Set("Content-Type", "application/json") + } + req.Header.Set("Accept", "application/json") + + // Authentication + if c.Token != "" { + req.Header.Set("Authorization", "Bearer "+c.Token) + } else if c.ApiKey != "" { + req.Header.Set("Authorization", c.ApiKey) + } else if c.Username != "" { + req.SetBasicAuth(c.Username, c.Password) + } + + resp, err := c.HTTPClient.Do(req) + if err != nil { + return nil, fmt.Errorf("error making request: %w", err) + } + defer resp.Body.Close() + + respBody, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(respBody)) + } + + return respBody, nil +} diff --git a/modules/openapi-generator/src/main/resources/terraform-provider/data_source.mustache b/modules/openapi-generator/src/main/resources/terraform-provider/data_source.mustache new file mode 100644 index 000000000000..4c6131168153 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/terraform-provider/data_source.mustache @@ -0,0 +1,108 @@ +{{#operations}} +{{>partial_header}} +package provider + +import ( + "context" + "fmt" +{{#hasRead}} + "encoding/json" +{{/hasRead}} + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" +{{#hasRead}} + "github.com/hashicorp/terraform-plugin-log/tflog" +{{/hasRead}} + + "{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/internal/client" +) + +var _ datasource.DataSource = &{{resourceClassName}}DataSource{} + +func New{{resourceClassName}}DataSource() datasource.DataSource { + return &{{resourceClassName}}DataSource{} +} + +type {{resourceClassName}}DataSource struct { + client *client.Client +} + +func (d *{{resourceClassName}}DataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_{{resourceName}}" +} + +func (d *{{resourceClassName}}DataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Fetches a {{resourceName}} data source.", + Attributes: map[string]schema.Attribute{ +{{#tfAttributes}} + "{{terraformName}}": schema.{{#isString}}String{{/isString}}{{#isInt64}}Int64{{/isInt64}}{{#isFloat64}}Float64{{/isFloat64}}{{#isBool}}Bool{{/isBool}}{{^isString}}{{^isInt64}}{{^isFloat64}}{{^isBool}}String{{/isBool}}{{/isFloat64}}{{/isInt64}}{{/isString}}Attribute{ +{{#isRequired}} + Required: true, +{{/isRequired}} +{{^isRequired}} + Computed: true, +{{/isRequired}} + Description: "{{description}}", + }, +{{/tfAttributes}} + }, + } +} + +func (d *{{resourceClassName}}DataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + c, ok := req.ProviderData.(*client.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *client.Client, got: %T.", req.ProviderData), + ) + return + } + + d.client = c +} + +{{#hasRead}} +func (d *{{resourceClassName}}DataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var config {{resourceClassName}}Model + + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + +{{#readHasPathParams}} + respBody, err := d.client.DoRequest(ctx, "{{readMethod}}", fmt.Sprintf("{{readPath}}", config.{{idFieldExported}}{{idFieldValueAccessor}}), nil) +{{/readHasPathParams}} +{{^readHasPathParams}} + respBody, err := d.client.DoRequest(ctx, "{{readMethod}}", "{{readPath}}", nil) +{{/readHasPathParams}} + if err != nil { + resp.Diagnostics.AddError("Error reading {{resourceName}}", err.Error()) + return + } + + var result client.{{responseModel}} + if err := json.Unmarshal(respBody, &result); err != nil { + resp.Diagnostics.AddError("Error parsing response", err.Error()) + return + } + + config.FromClientModel(&result) + + tflog.Trace(ctx, "read {{resourceName}} data source") + resp.Diagnostics.Append(resp.State.Set(ctx, &config)...) +} +{{/hasRead}} +{{^hasRead}} +func (d *{{resourceClassName}}DataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + resp.Diagnostics.AddError("Not Supported", "Read is not supported for {{resourceName}}") +} +{{/hasRead}} +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/terraform-provider/gitignore.mustache b/modules/openapi-generator/src/main/resources/terraform-provider/gitignore.mustache new file mode 100644 index 000000000000..91a35a99db75 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/terraform-provider/gitignore.mustache @@ -0,0 +1,13 @@ +*.dll +*.exe +*.exe~ +*.dylib +*.so +*.test +*.out +*.tfstate +*.tfstate.* +.terraform/ +*.tfvars +*.tfvars.json +terraform-provider-{{providerName}} diff --git a/modules/openapi-generator/src/main/resources/terraform-provider/go.mod.mustache b/modules/openapi-generator/src/main/resources/terraform-provider/go.mod.mustache new file mode 100644 index 000000000000..b7a6caceda95 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/terraform-provider/go.mod.mustache @@ -0,0 +1,11 @@ +module {{gitHost}}/{{gitUserId}}/{{gitRepoId}} + +go 1.24.0 + +toolchain go1.24.4 + +require ( + github.com/hashicorp/terraform-plugin-framework v1.17.0 + github.com/hashicorp/terraform-plugin-log v0.10.0 + github.com/hashicorp/terraform-plugin-testing v1.14.0 +) diff --git a/modules/openapi-generator/src/main/resources/terraform-provider/main.mustache b/modules/openapi-generator/src/main/resources/terraform-provider/main.mustache new file mode 100644 index 000000000000..38b01dc6c8fb --- /dev/null +++ b/modules/openapi-generator/src/main/resources/terraform-provider/main.mustache @@ -0,0 +1,34 @@ +{{>partial_header}} +package main + +import ( + "context" + "flag" + "log" + + "github.com/hashicorp/terraform-plugin-framework/providerserver" + + "{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/internal/provider" +) + +var ( + version string = "{{providerVersion}}" +) + +func main() { + var debug bool + + flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve") + flag.Parse() + + opts := providerserver.ServeOpts{ + Address: "{{providerAddress}}", + Debug: debug, + } + + err := providerserver.Serve(context.Background(), provider.New(version), opts) + + if err != nil { + log.Fatal(err.Error()) + } +} diff --git a/modules/openapi-generator/src/main/resources/terraform-provider/model.mustache b/modules/openapi-generator/src/main/resources/terraform-provider/model.mustache new file mode 100644 index 000000000000..4bb56a40153b --- /dev/null +++ b/modules/openapi-generator/src/main/resources/terraform-provider/model.mustache @@ -0,0 +1,13 @@ +{{>partial_header}} +package client +{{#models}} +{{#model}} + +// {{classname}} - {{{description}}}{{^description}}{{classname}} struct{{/description}} +type {{classname}} struct { +{{#vars}} + {{name}} {{#isArray}}[]{{#items}}{{dataType}}{{/items}}{{/isArray}}{{^isArray}}{{#isMap}}map[string]{{#items}}{{dataType}}{{/items}}{{/isMap}}{{^isMap}}{{dataType}}{{/isMap}}{{/isArray}}{{{vendorExtensions.x-go-datatag}}} +{{/vars}} +} +{{/model}} +{{/models}} diff --git a/modules/openapi-generator/src/main/resources/terraform-provider/partial_header.mustache b/modules/openapi-generator/src/main/resources/terraform-provider/partial_header.mustache new file mode 100644 index 000000000000..df685adbc68e --- /dev/null +++ b/modules/openapi-generator/src/main/resources/terraform-provider/partial_header.mustache @@ -0,0 +1 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. diff --git a/modules/openapi-generator/src/main/resources/terraform-provider/provider.mustache b/modules/openapi-generator/src/main/resources/terraform-provider/provider.mustache new file mode 100644 index 000000000000..a1491908dfa6 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/terraform-provider/provider.mustache @@ -0,0 +1,122 @@ +{{>partial_header}} +package provider + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/provider" + "github.com/hashicorp/terraform-plugin-framework/provider/schema" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/types" + + "{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/internal/client" +) + +var _ provider.Provider = &{{providerName}}Provider{} + +type {{providerName}}Provider struct { + version string +} + +type {{providerName}}ProviderModel struct { + Endpoint types.String `tfsdk:"endpoint"` + ApiKey types.String `tfsdk:"api_key"` + Token types.String `tfsdk:"token"` + Username types.String `tfsdk:"username"` + Password types.String `tfsdk:"password"` +} + +func New(version string) func() provider.Provider { + return func() provider.Provider { + return &{{providerName}}Provider{ + version: version, + } + } +} + +func (p *{{providerName}}Provider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) { + resp.TypeName = "{{providerName}}" + resp.Version = p.version +} + +func (p *{{providerName}}Provider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "endpoint": schema.StringAttribute{ + Optional: true, + Description: "The API endpoint URL.", + }, + "api_key": schema.StringAttribute{ + Optional: true, + Sensitive: true, + Description: "The API key for authentication.", + }, + "token": schema.StringAttribute{ + Optional: true, + Sensitive: true, + Description: "The bearer token for authentication.", + }, + "username": schema.StringAttribute{ + Optional: true, + Description: "The username for basic authentication.", + }, + "password": schema.StringAttribute{ + Optional: true, + Sensitive: true, + Description: "The password for basic authentication.", + }, + }, + } +} + +func (p *{{providerName}}Provider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { + var config {{providerName}}ProviderModel + + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + + if config.Endpoint.IsUnknown() { + resp.Diagnostics.AddWarning( + "Unknown Endpoint Value", + "The provider endpoint value is not yet known. Defaulting to the spec-defined base path.", + ) + } + + endpoint := "{{{basePath}}}" + if !config.Endpoint.IsNull() && !config.Endpoint.IsUnknown() { + endpoint = config.Endpoint.ValueString() + } + + c := client.NewClient(endpoint) + + if !config.ApiKey.IsNull() { + c.ApiKey = config.ApiKey.ValueString() + } + if !config.Token.IsNull() { + c.Token = config.Token.ValueString() + } + if !config.Username.IsNull() { + c.Username = config.Username.ValueString() + } + if !config.Password.IsNull() { + c.Password = config.Password.ValueString() + } + + resp.DataSourceData = c + resp.ResourceData = c +} + +func (p *{{providerName}}Provider) Resources(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ +{{#apiInfo}}{{#apis}}{{#operations}} New{{resourceClassName}}Resource, +{{/operations}}{{/apis}}{{/apiInfo}} } +} + +func (p *{{providerName}}Provider) DataSources(_ context.Context) []func() datasource.DataSource { + return []func() datasource.DataSource{ +{{#apiInfo}}{{#apis}}{{#operations}} New{{resourceClassName}}DataSource, +{{/operations}}{{/apis}}{{/apiInfo}} } +} diff --git a/modules/openapi-generator/src/main/resources/terraform-provider/provider_example.mustache b/modules/openapi-generator/src/main/resources/terraform-provider/provider_example.mustache new file mode 100644 index 000000000000..0c7cfdb143be --- /dev/null +++ b/modules/openapi-generator/src/main/resources/terraform-provider/provider_example.mustache @@ -0,0 +1,13 @@ +terraform { + required_providers { + {{providerName}} = { + source = "{{providerAddress}}" + } + } +} + +provider "{{providerName}}" { + endpoint = "{{{basePath}}}" + # api_key = "your-api-key" + # token = "your-bearer-token" +} diff --git a/modules/openapi-generator/src/main/resources/terraform-provider/resource.mustache b/modules/openapi-generator/src/main/resources/terraform-provider/resource.mustache new file mode 100644 index 000000000000..d5660362a3e4 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/terraform-provider/resource.mustache @@ -0,0 +1,247 @@ +{{#operations}} +{{>partial_header}} +package provider + +import ( + "context" + "fmt" +{{#hasCreate}} + "encoding/json" +{{/hasCreate}} +{{^hasCreate}}{{#hasRead}} + "encoding/json" +{{/hasRead}}{{/hasCreate}} +{{^hasCreate}}{{^hasRead}}{{#hasUpdate}} + "encoding/json" +{{/hasUpdate}}{{/hasRead}}{{/hasCreate}} + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" +{{#hasListAttributes}} + "github.com/hashicorp/terraform-plugin-framework/types" +{{/hasListAttributes}} +{{#hasCreate}} + "github.com/hashicorp/terraform-plugin-log/tflog" +{{/hasCreate}} +{{^hasCreate}}{{#hasUpdate}} + "github.com/hashicorp/terraform-plugin-log/tflog" +{{/hasUpdate}}{{/hasCreate}} +{{^hasCreate}}{{^hasUpdate}}{{#hasDelete}} + "github.com/hashicorp/terraform-plugin-log/tflog" +{{/hasDelete}}{{/hasUpdate}}{{/hasCreate}} + + "{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/internal/client" +) + +var _ resource.Resource = &{{resourceClassName}}Resource{} +var _ resource.ResourceWithImportState = &{{resourceClassName}}Resource{} + +func New{{resourceClassName}}Resource() resource.Resource { + return &{{resourceClassName}}Resource{} +} + +type {{resourceClassName}}Resource struct { + client *client.Client +} + +func (r *{{resourceClassName}}Resource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_{{resourceName}}" +} + +func (r *{{resourceClassName}}Resource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Manages a {{resourceName}} resource.", + Attributes: map[string]schema.Attribute{ +{{#tfAttributes}} + "{{terraformName}}": {{terraformAttrType}}{ +{{#isRequired}} + Required: true, +{{/isRequired}} +{{#isComputed}} + Computed: true, +{{/isComputed}} +{{#isOptional}} + Optional: true, +{{/isOptional}} +{{#isSensitive}} + Sensitive: true, +{{/isSensitive}} +{{#isList}} + ElementType: {{listElementType}}, +{{/isList}} + Description: "{{description}}", + }, +{{/tfAttributes}} + }, + } +} + +func (r *{{resourceClassName}}Resource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + c, ok := req.ProviderData.(*client.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *client.Client, got: %T.", req.ProviderData), + ) + return + } + + r.client = c +} + +{{#hasCreate}} +func (r *{{resourceClassName}}Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan {{resourceClassName}}Model + + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + reqBody := plan.ToClientModel() + + respBody, err := r.client.DoRequest(ctx, "{{createMethod}}", "{{createPath}}", reqBody) + if err != nil { + resp.Diagnostics.AddError("Error creating {{resourceName}}", err.Error()) + return + } + + var result client.{{responseModel}} + if err := json.Unmarshal(respBody, &result); err != nil { + resp.Diagnostics.AddError("Error parsing response", err.Error()) + return + } + + plan.FromClientModel(&result) + + tflog.Trace(ctx, "created {{resourceName}} resource") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} +{{/hasCreate}} +{{^hasCreate}} +func (r *{{resourceClassName}}Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + resp.Diagnostics.AddError("Not Supported", "Create is not supported for {{resourceName}}") +} +{{/hasCreate}} + +{{#hasRead}} +func (r *{{resourceClassName}}Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state {{resourceClassName}}Model + + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + +{{#readHasPathParams}} + respBody, err := r.client.DoRequest(ctx, "{{readMethod}}", fmt.Sprintf("{{readPath}}", state.{{idFieldExported}}{{idFieldValueAccessor}}), nil) +{{/readHasPathParams}} +{{^readHasPathParams}} + respBody, err := r.client.DoRequest(ctx, "{{readMethod}}", "{{readPath}}", nil) +{{/readHasPathParams}} + if err != nil { + resp.Diagnostics.AddError("Error reading {{resourceName}}", err.Error()) + return + } + + var result client.{{responseModel}} + if err := json.Unmarshal(respBody, &result); err != nil { + resp.Diagnostics.AddError("Error parsing response", err.Error()) + return + } + + state.FromClientModel(&result) + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} +{{/hasRead}} +{{^hasRead}} +func (r *{{resourceClassName}}Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + // No read endpoint available; keep existing state as-is. +} +{{/hasRead}} + +{{#hasUpdate}} +func (r *{{resourceClassName}}Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan {{resourceClassName}}Model + + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + reqBody := plan.ToClientModel() + +{{#updateHasPathParams}} + respBody, err := r.client.DoRequest(ctx, "{{updateMethod}}", fmt.Sprintf("{{updatePath}}", plan.{{idFieldExported}}{{idFieldValueAccessor}}), reqBody) +{{/updateHasPathParams}} +{{^updateHasPathParams}} + respBody, err := r.client.DoRequest(ctx, "{{updateMethod}}", "{{updatePath}}", reqBody) +{{/updateHasPathParams}} + if err != nil { + resp.Diagnostics.AddError("Error updating {{resourceName}}", err.Error()) + return + } + + var result client.{{responseModel}} + if err := json.Unmarshal(respBody, &result); err != nil { + resp.Diagnostics.AddError("Error parsing response", err.Error()) + return + } + + plan.FromClientModel(&result) + + tflog.Trace(ctx, "updated {{resourceName}} resource") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} +{{/hasUpdate}} +{{^hasUpdate}} +func (r *{{resourceClassName}}Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + // No update endpoint available; persist the planned values into state. + var plan {{resourceClassName}}Model + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} +{{/hasUpdate}} + +{{#hasDelete}} +func (r *{{resourceClassName}}Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state {{resourceClassName}}Model + + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + +{{#deleteHasPathParams}} + _, err := r.client.DoRequest(ctx, "{{deleteMethod}}", fmt.Sprintf("{{deletePath}}", state.{{idFieldExported}}{{idFieldValueAccessor}}), nil) +{{/deleteHasPathParams}} +{{^deleteHasPathParams}} + _, err := r.client.DoRequest(ctx, "{{deleteMethod}}", "{{deletePath}}", nil) +{{/deleteHasPathParams}} + if err != nil { + resp.Diagnostics.AddError("Error deleting {{resourceName}}", err.Error()) + return + } + + tflog.Trace(ctx, "deleted {{resourceName}} resource") +} +{{/hasDelete}} +{{^hasDelete}} +func (r *{{resourceClassName}}Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // No delete endpoint available; remove the resource from state. +} +{{/hasDelete}} + +func (r *{{resourceClassName}}Resource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("{{idFieldTerraformName}}"), req, resp) +} +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/terraform-provider/resource_model.mustache b/modules/openapi-generator/src/main/resources/terraform-provider/resource_model.mustache new file mode 100644 index 000000000000..4ad9ec21de5a --- /dev/null +++ b/modules/openapi-generator/src/main/resources/terraform-provider/resource_model.mustache @@ -0,0 +1,67 @@ +{{#operations}} +{{>partial_header}} +package provider + +{{#responseModel}} +import ( + "github.com/hashicorp/terraform-plugin-framework/types" + + "{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/internal/client" +) +{{/responseModel}} + +// {{resourceClassName}}Model is the Terraform model for {{resourceName}}. +type {{resourceClassName}}Model struct { +{{#tfAttributes}} + {{goName}} {{terraformType}} `tfsdk:"{{terraformName}}"` +{{/tfAttributes}} +} + +{{#responseModel}} +// ToClientModel converts a Terraform model to a client model. +func (m *{{resourceClassName}}Model) ToClientModel() *client.{{responseModel}} { + out := &client.{{responseModel}}{} +{{#tfAttributes}} +{{#isString}} + if !m.{{goName}}.IsNull() && !m.{{goName}}.IsUnknown() { + out.{{goName}} = m.{{goName}}.ValueString() + } +{{/isString}} +{{#isInt64}} + if !m.{{goName}}.IsNull() && !m.{{goName}}.IsUnknown() { + out.{{goName}} = {{goType}}(m.{{goName}}.ValueInt64()) + } +{{/isInt64}} +{{#isFloat64}} + if !m.{{goName}}.IsNull() && !m.{{goName}}.IsUnknown() { + out.{{goName}} = {{goType}}(m.{{goName}}.ValueFloat64()) + } +{{/isFloat64}} +{{#isBool}} + if !m.{{goName}}.IsNull() && !m.{{goName}}.IsUnknown() { + out.{{goName}} = m.{{goName}}.ValueBool() + } +{{/isBool}} +{{/tfAttributes}} + return out +} + +// FromClientModel updates the Terraform model from a client model. +func (m *{{resourceClassName}}Model) FromClientModel(c *client.{{responseModel}}) { +{{#tfAttributes}} +{{#isString}} + m.{{goName}} = types.StringValue(c.{{goName}}) +{{/isString}} +{{#isInt64}} + m.{{goName}} = types.Int64Value(int64(c.{{goName}})) +{{/isInt64}} +{{#isFloat64}} + m.{{goName}} = types.Float64Value(float64(c.{{goName}})) +{{/isFloat64}} +{{#isBool}} + m.{{goName}} = types.BoolValue(c.{{goName}}) +{{/isBool}} +{{/tfAttributes}} +} +{{/responseModel}} +{{/operations}} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/terraform/provider/TerraformProviderCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/terraform/provider/TerraformProviderCodegenTest.java new file mode 100644 index 000000000000..ecb86411ac22 --- /dev/null +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/terraform/provider/TerraformProviderCodegenTest.java @@ -0,0 +1,180 @@ +/* + * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openapitools.codegen.terraform.provider; + +import org.openapitools.codegen.*; +import org.openapitools.codegen.config.CodegenConfigurator; +import org.openapitools.codegen.languages.TerraformProviderCodegen; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class TerraformProviderCodegenTest { + + @Test + public void testInitialConfigValues() throws Exception { + final TerraformProviderCodegen codegen = new TerraformProviderCodegen(); + codegen.processOpts(); + + Assert.assertEquals(codegen.getName(), "terraform-provider"); + Assert.assertEquals(codegen.getTag(), CodegenType.CLIENT); + Assert.assertNotNull(codegen.getHelp()); + Assert.assertTrue(codegen.getHelp().contains("Terraform")); + } + + @Test + public void testProviderNameOption() throws Exception { + final TerraformProviderCodegen codegen = new TerraformProviderCodegen(); + codegen.additionalProperties().put(TerraformProviderCodegen.PROVIDER_NAME, "myapi"); + codegen.processOpts(); + + Assert.assertEquals(codegen.additionalProperties().get(TerraformProviderCodegen.PROVIDER_NAME), "myapi"); + } + + @Test + public void testGeneratePetstore() throws Exception { + File output = Files.createTempDirectory("terraform-provider-test").toFile(); + output.deleteOnExit(); + + Map properties = new HashMap<>(); + properties.put(TerraformProviderCodegen.PROVIDER_NAME, "petstore"); + properties.put(TerraformProviderCodegen.PROVIDER_ADDRESS, "registry.terraform.io/example/petstore"); + properties.put("gitHost", "github.com"); + properties.put("gitUserId", "example"); + properties.put("gitRepoId", "terraform-provider-petstore"); + + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("terraform-provider") + .setAdditionalProperties(properties) + .setInputSpec("src/test/resources/3_0/petstore.yaml") + .setOutputDir(output.getAbsolutePath().replace("\\", "/")); + + DefaultGenerator generator = new DefaultGenerator(); + List files = generator.opts(configurator.toClientOptInput()).generate(); + files.forEach(File::deleteOnExit); + + // Verify key files exist + TestUtils.assertFileExists(Paths.get(output + "/main.go")); + TestUtils.assertFileExists(Paths.get(output + "/go.mod")); + TestUtils.assertFileExists(Paths.get(output + "/GNUmakefile")); + TestUtils.assertFileExists(Paths.get(output + "/README.md")); + TestUtils.assertFileExists(Paths.get(output + "/.gitignore")); + TestUtils.assertFileExists(Paths.get(output + "/internal/provider/provider.go")); + TestUtils.assertFileExists(Paths.get(output + "/internal/client/client.go")); + TestUtils.assertFileExists(Paths.get(output + "/examples/provider/provider.tf")); + } + + @Test + public void testMainGoContent() throws Exception { + File output = Files.createTempDirectory("terraform-provider-test").toFile(); + output.deleteOnExit(); + + Map properties = new HashMap<>(); + properties.put(TerraformProviderCodegen.PROVIDER_NAME, "petstore"); + properties.put(TerraformProviderCodegen.PROVIDER_ADDRESS, "registry.terraform.io/example/petstore"); + + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("terraform-provider") + .setAdditionalProperties(properties) + .setGitHost("github.com") + .setGitUserId("example") + .setGitRepoId("terraform-provider-petstore") + .setInputSpec("src/test/resources/3_0/petstore.yaml") + .setOutputDir(output.getAbsolutePath().replace("\\", "/")); + + DefaultGenerator generator = new DefaultGenerator(); + List files = generator.opts(configurator.toClientOptInput()).generate(); + files.forEach(File::deleteOnExit); + + // Verify main.go content + TestUtils.assertFileContains(Paths.get(output + "/main.go"), "providerserver.Serve"); + TestUtils.assertFileContains(Paths.get(output + "/main.go"), "registry.terraform.io/example/petstore"); + + // Verify provider.go content + TestUtils.assertFileContains(Paths.get(output + "/internal/provider/provider.go"), "petstoreProvider"); + TestUtils.assertFileContains(Paths.get(output + "/internal/provider/provider.go"), "provider.Provider"); + + // Verify client.go content + TestUtils.assertFileContains(Paths.get(output + "/internal/client/client.go"), "type Client struct"); + TestUtils.assertFileContains(Paths.get(output + "/internal/client/client.go"), "func NewClient"); + + // Verify go.mod content + TestUtils.assertFileContains(Paths.get(output + "/go.mod"), "github.com/example/terraform-provider-petstore"); + TestUtils.assertFileContains(Paths.get(output + "/go.mod"), "terraform-plugin-framework"); + } + + @Test + public void testTypeMapping() throws Exception { + final TerraformProviderCodegen codegen = new TerraformProviderCodegen(); + codegen.processOpts(); + + // Terraform provider should map DateTime to string (not time.Time) + Assert.assertEquals(codegen.typeMapping().get("DateTime"), "string"); + Assert.assertEquals(codegen.typeMapping().get("File"), "string"); + Assert.assertEquals(codegen.typeMapping().get("binary"), "string"); + + // Standard Go type mappings should still work + Assert.assertEquals(codegen.typeMapping().get("integer"), "int32"); + Assert.assertEquals(codegen.typeMapping().get("long"), "int64"); + Assert.assertEquals(codegen.typeMapping().get("boolean"), "bool"); + Assert.assertEquals(codegen.typeMapping().get("string"), "string"); + } + + @Test + public void testFilenames() throws Exception { + final TerraformProviderCodegen codegen = new TerraformProviderCodegen(); + + Assert.assertEquals(codegen.toApiFilename("Pet"), "pet"); + Assert.assertEquals(codegen.toApiFilename("UserProfile"), "user_profile"); + Assert.assertEquals(codegen.toModelFilename("Pet"), "model_pet"); + Assert.assertEquals(codegen.toModelFilename("ApiResponse"), "model_api_response"); + } + + @Test + public void testProviderExampleContent() throws Exception { + File output = Files.createTempDirectory("terraform-provider-test").toFile(); + output.deleteOnExit(); + + Map properties = new HashMap<>(); + properties.put(TerraformProviderCodegen.PROVIDER_NAME, "petstore"); + properties.put(TerraformProviderCodegen.PROVIDER_ADDRESS, "registry.terraform.io/example/petstore"); + properties.put("gitHost", "github.com"); + properties.put("gitUserId", "example"); + properties.put("gitRepoId", "terraform-provider-petstore"); + + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("terraform-provider") + .setAdditionalProperties(properties) + .setInputSpec("src/test/resources/3_0/petstore.yaml") + .setOutputDir(output.getAbsolutePath().replace("\\", "/")); + + DefaultGenerator generator = new DefaultGenerator(); + List files = generator.opts(configurator.toClientOptInput()).generate(); + files.forEach(File::deleteOnExit); + + // Verify provider example + TestUtils.assertFileContains(Paths.get(output + "/examples/provider/provider.tf"), "petstore"); + TestUtils.assertFileContains(Paths.get(output + "/examples/provider/provider.tf"), "registry.terraform.io/example/petstore"); + } +} diff --git a/samples/client/others/terraform/allof-discriminator/.gitignore b/samples/client/others/terraform/allof-discriminator/.gitignore new file mode 100644 index 000000000000..d948a4c42de8 --- /dev/null +++ b/samples/client/others/terraform/allof-discriminator/.gitignore @@ -0,0 +1,13 @@ +*.dll +*.exe +*.exe~ +*.dylib +*.so +*.test +*.out +*.tfstate +*.tfstate.* +.terraform/ +*.tfvars +*.tfvars.json +terraform-provider-allof diff --git a/samples/client/others/terraform/allof-discriminator/.openapi-generator-ignore b/samples/client/others/terraform/allof-discriminator/.openapi-generator-ignore new file mode 100644 index 000000000000..7484ee590a38 --- /dev/null +++ b/samples/client/others/terraform/allof-discriminator/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/samples/client/others/terraform/allof-discriminator/.openapi-generator/FILES b/samples/client/others/terraform/allof-discriminator/.openapi-generator/FILES new file mode 100644 index 000000000000..637a144f29ec --- /dev/null +++ b/samples/client/others/terraform/allof-discriminator/.openapi-generator/FILES @@ -0,0 +1,11 @@ +.gitignore +GNUmakefile +README.md +examples/provider/provider.tf +go.mod +internal/client/client.go +internal/client/model_additional_data.go +internal/client/model_base/item.go +internal/client/model_final_item.go +internal/provider/provider.go +main.go diff --git a/samples/client/others/terraform/allof-discriminator/.openapi-generator/VERSION b/samples/client/others/terraform/allof-discriminator/.openapi-generator/VERSION new file mode 100644 index 000000000000..193a12d6e891 --- /dev/null +++ b/samples/client/others/terraform/allof-discriminator/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.20.0-SNAPSHOT diff --git a/samples/client/others/terraform/allof-discriminator/GNUmakefile b/samples/client/others/terraform/allof-discriminator/GNUmakefile new file mode 100644 index 000000000000..2ab48f3d4332 --- /dev/null +++ b/samples/client/others/terraform/allof-discriminator/GNUmakefile @@ -0,0 +1,27 @@ +default: testacc + +# Run acceptance tests +.PHONY: testacc +testacc: + TF_ACC=1 go test ./... -v $(TESTARGS) -timeout 120m + +# Build provider +.PHONY: build +build: + go build -o terraform-provider-allof + +# Install provider locally +.PHONY: install +install: build + mkdir -p ~/.terraform.d/plugins/registry.terraform.io/example/allof/0.1.0/$(shell go env GOOS)_$(shell go env GOARCH) + mv terraform-provider-allof ~/.terraform.d/plugins/registry.terraform.io/example/allof/0.1.0/$(shell go env GOOS)_$(shell go env GOARCH)/ + +# Generate documentation +.PHONY: docs +docs: + go generate ./... + +# Run linter +.PHONY: lint +lint: + golangci-lint run ./... diff --git a/samples/client/others/terraform/allof-discriminator/README.md b/samples/client/others/terraform/allof-discriminator/README.md new file mode 100644 index 000000000000..cd7dee82af68 --- /dev/null +++ b/samples/client/others/terraform/allof-discriminator/README.md @@ -0,0 +1,61 @@ +# Terraform Provider allof + +This Terraform provider was generated using [OpenAPI Generator](https://openapi-generator.tech). + +## Requirements + +- [Terraform](https://www.terraform.io/downloads.html) >= 1.0 +- [Go](https://golang.org/doc/install) >= 1.22 + +## Building The Provider + +1. Clone the repository +2. Enter the repository directory +3. Build the provider using the Go `install` command: + +```shell +go install +``` + +## Using the provider + +```hcl +terraform { + required_providers { + allof = { + source = "registry.terraform.io/example/allof" + } + } +} + +provider "allof" { + endpoint = "http://localhost" +} +``` + +## Developing the Provider + +If you wish to work on the provider, you'll first need [Go](http://www.golang.org) installed on your machine (see [Requirements](#requirements) above). + +To compile the provider, run `go install`. This will build the provider and put the provider binary in the `$GOPATH/bin` directory. + +For local development and testing, add a `dev_overrides` block to your `~/.terraformrc` so that Terraform uses the locally built binary instead of fetching from the registry: + +```hcl +provider_installation { + dev_overrides { + "registry.terraform.io/example/allof" = "${GOPATH}/bin" + } + direct {} +} +``` + +With `dev_overrides` configured, you do not need to run `terraform init` for the provider. + +To generate or update documentation, run `go generate`. + +In order to run the full suite of Acceptance tests, run `make testacc`. + +```shell +make testacc +``` diff --git a/samples/client/others/terraform/allof-discriminator/examples/provider/provider.tf b/samples/client/others/terraform/allof-discriminator/examples/provider/provider.tf new file mode 100644 index 000000000000..a6123fa8379a --- /dev/null +++ b/samples/client/others/terraform/allof-discriminator/examples/provider/provider.tf @@ -0,0 +1,13 @@ +terraform { + required_providers { + allof = { + source = "registry.terraform.io/example/allof" + } + } +} + +provider "allof" { + endpoint = "http://localhost" + # api_key = "your-api-key" + # token = "your-bearer-token" +} diff --git a/samples/client/others/terraform/allof-discriminator/go.mod b/samples/client/others/terraform/allof-discriminator/go.mod new file mode 100644 index 000000000000..f6770d9df332 --- /dev/null +++ b/samples/client/others/terraform/allof-discriminator/go.mod @@ -0,0 +1,11 @@ +module github.com/example/terraform-provider-allof + +go 1.24.0 + +toolchain go1.24.4 + +require ( + github.com/hashicorp/terraform-plugin-framework v1.17.0 + github.com/hashicorp/terraform-plugin-log v0.10.0 + github.com/hashicorp/terraform-plugin-testing v1.14.0 +) diff --git a/samples/client/others/terraform/allof-discriminator/go.sum b/samples/client/others/terraform/allof-discriminator/go.sum new file mode 100644 index 000000000000..8888a9727e10 --- /dev/null +++ b/samples/client/others/terraform/allof-discriminator/go.sum @@ -0,0 +1,99 @@ +github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= +github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA= +github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/terraform-plugin-framework v1.17.0 h1:JdX50CFrYcYFY31gkmitAEAzLKoBgsK+iaJjDC8OexY= +github.com/hashicorp/terraform-plugin-framework v1.17.0/go.mod h1:4OUXKdHNosX+ys6rLgVlgklfxN3WHR5VHSOABeS/BM0= +github.com/hashicorp/terraform-plugin-go v0.29.0 h1:1nXKl/nSpaYIUBU1IG/EsDOX0vv+9JxAltQyDMpq5mU= +github.com/hashicorp/terraform-plugin-go v0.29.0/go.mod h1:vYZbIyvxyy0FWSmDHChCqKvI40cFTDGSb3D8D70i9GM= +github.com/hashicorp/terraform-plugin-log v0.10.0 h1:eu2kW6/QBVdN4P3Ju2WiB2W3ObjkAsyfBsL3Wh1fj3g= +github.com/hashicorp/terraform-plugin-log v0.10.0/go.mod h1:/9RR5Cv2aAbrqcTSdNmY1NRHP4E3ekrXRGjqORpXyB0= +github.com/hashicorp/terraform-registry-address v0.4.0 h1:S1yCGomj30Sao4l5BMPjTGZmCNzuv7/GDTDX99E9gTk= +github.com/hashicorp/terraform-registry-address v0.4.0/go.mod h1:LRS1Ay0+mAiRkUyltGT+UHWkIqTFvigGn/LbMshfflE= +github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= +github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc= +github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= +github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= +github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94= +github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= +go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= +go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= +go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= +go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= +go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= +go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI= +google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= +google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/samples/client/others/terraform/allof-discriminator/internal/client/client.go b/samples/client/others/terraform/allof-discriminator/internal/client/client.go new file mode 100644 index 000000000000..247e45a1c730 --- /dev/null +++ b/samples/client/others/terraform/allof-discriminator/internal/client/client.go @@ -0,0 +1,78 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" +) + +// Client is the API client used to interact with the API. +type Client struct { + BaseURL string + HTTPClient *http.Client + ApiKey string + Token string + Username string + Password string +} + +// NewClient creates a new API client with the given base URL. +func NewClient(baseURL string) *Client { + return &Client{ + BaseURL: baseURL, + HTTPClient: &http.Client{}, + } +} + +// DoRequest executes an HTTP request and returns the response body. +func (c *Client) DoRequest(ctx context.Context, method, path string, body interface{}) ([]byte, error) { + var reqBody io.Reader + if body != nil { + jsonBody, err := json.Marshal(body) + if err != nil { + return nil, fmt.Errorf("error marshaling request body: %w", err) + } + reqBody = bytes.NewBuffer(jsonBody) + } + + req, err := http.NewRequestWithContext(ctx, method, c.BaseURL+path, reqBody) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + + if body != nil { + req.Header.Set("Content-Type", "application/json") + } + req.Header.Set("Accept", "application/json") + + // Authentication + if c.Token != "" { + req.Header.Set("Authorization", "Bearer "+c.Token) + } else if c.ApiKey != "" { + req.Header.Set("Authorization", c.ApiKey) + } else if c.Username != "" { + req.SetBasicAuth(c.Username, c.Password) + } + + resp, err := c.HTTPClient.Do(req) + if err != nil { + return nil, fmt.Errorf("error making request: %w", err) + } + defer resp.Body.Close() + + respBody, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(respBody)) + } + + return respBody, nil +} diff --git a/samples/client/others/terraform/allof-discriminator/internal/client/model_additional_data.go b/samples/client/others/terraform/allof-discriminator/internal/client/model_additional_data.go new file mode 100644 index 000000000000..a45565ac18e6 --- /dev/null +++ b/samples/client/others/terraform/allof-discriminator/internal/client/model_additional_data.go @@ -0,0 +1,11 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// AdditionalData - AdditionalData struct +type AdditionalData struct { + Prop1 string `json:"prop1"` + Quantity int32 `json:"quantity"` + UnitPrice float64 `json:"unitPrice"` + TotalPrice float64 `json:"totalPrice"` +} diff --git a/samples/client/others/terraform/allof-discriminator/internal/client/model_base/item.go b/samples/client/others/terraform/allof-discriminator/internal/client/model_base/item.go new file mode 100644 index 000000000000..4d1460216564 --- /dev/null +++ b/samples/client/others/terraform/allof-discriminator/internal/client/model_base/item.go @@ -0,0 +1,9 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// BaseItem - BaseItem struct +type BaseItem struct { + Title string `json:"title"` + Type string `json:"type"` +} diff --git a/samples/client/others/terraform/allof-discriminator/internal/client/model_final_item.go b/samples/client/others/terraform/allof-discriminator/internal/client/model_final_item.go new file mode 100644 index 000000000000..9c6acab04a40 --- /dev/null +++ b/samples/client/others/terraform/allof-discriminator/internal/client/model_final_item.go @@ -0,0 +1,11 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// FinalItem - FinalItem struct +type FinalItem struct { + Prop1 string `json:"prop1"` + Quantity int32 `json:"quantity"` + UnitPrice float64 `json:"unitPrice"` + TotalPrice float64 `json:"totalPrice"` +} diff --git a/samples/client/others/terraform/allof-discriminator/internal/provider/provider.go b/samples/client/others/terraform/allof-discriminator/internal/provider/provider.go new file mode 100644 index 000000000000..8c499ac6568b --- /dev/null +++ b/samples/client/others/terraform/allof-discriminator/internal/provider/provider.go @@ -0,0 +1,121 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package provider + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/provider" + "github.com/hashicorp/terraform-plugin-framework/provider/schema" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/types" + + "github.com/example/terraform-provider-allof/internal/client" +) + +var _ provider.Provider = &allofProvider{} + +type allofProvider struct { + version string +} + +type allofProviderModel struct { + Endpoint types.String `tfsdk:"endpoint"` + ApiKey types.String `tfsdk:"api_key"` + Token types.String `tfsdk:"token"` + Username types.String `tfsdk:"username"` + Password types.String `tfsdk:"password"` +} + +func New(version string) func() provider.Provider { + return func() provider.Provider { + return &allofProvider{ + version: version, + } + } +} + +func (p *allofProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) { + resp.TypeName = "allof" + resp.Version = p.version +} + +func (p *allofProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "endpoint": schema.StringAttribute{ + Optional: true, + Description: "The API endpoint URL.", + }, + "api_key": schema.StringAttribute{ + Optional: true, + Sensitive: true, + Description: "The API key for authentication.", + }, + "token": schema.StringAttribute{ + Optional: true, + Sensitive: true, + Description: "The bearer token for authentication.", + }, + "username": schema.StringAttribute{ + Optional: true, + Description: "The username for basic authentication.", + }, + "password": schema.StringAttribute{ + Optional: true, + Sensitive: true, + Description: "The password for basic authentication.", + }, + }, + } +} + +func (p *allofProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { + var config allofProviderModel + + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + + if config.Endpoint.IsUnknown() { + resp.Diagnostics.AddWarning( + "Unknown Endpoint Value", + "The provider endpoint value is not yet known. Defaulting to the spec-defined base path.", + ) + } + + endpoint := "http://localhost" + if !config.Endpoint.IsNull() && !config.Endpoint.IsUnknown() { + endpoint = config.Endpoint.ValueString() + } + + c := client.NewClient(endpoint) + + if !config.ApiKey.IsNull() { + c.ApiKey = config.ApiKey.ValueString() + } + if !config.Token.IsNull() { + c.Token = config.Token.ValueString() + } + if !config.Username.IsNull() { + c.Username = config.Username.ValueString() + } + if !config.Password.IsNull() { + c.Password = config.Password.ValueString() + } + + resp.DataSourceData = c + resp.ResourceData = c +} + +func (p *allofProvider) Resources(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + } +} + +func (p *allofProvider) DataSources(_ context.Context) []func() datasource.DataSource { + return []func() datasource.DataSource{ + } +} diff --git a/samples/client/others/terraform/allof-discriminator/main.go b/samples/client/others/terraform/allof-discriminator/main.go new file mode 100644 index 000000000000..467e120d0615 --- /dev/null +++ b/samples/client/others/terraform/allof-discriminator/main.go @@ -0,0 +1,35 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package main + +import ( + "context" + "flag" + "log" + + "github.com/hashicorp/terraform-plugin-framework/providerserver" + + "github.com/example/terraform-provider-allof/internal/provider" +) + +var ( + version string = "0.1.0" +) + +func main() { + var debug bool + + flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve") + flag.Parse() + + opts := providerserver.ServeOpts{ + Address: "registry.terraform.io/example/allof", + Debug: debug, + } + + err := providerserver.Serve(context.Background(), provider.New(version), opts) + + if err != nil { + log.Fatal(err.Error()) + } +} diff --git a/samples/client/others/terraform/oneof-anyof-required/.gitignore b/samples/client/others/terraform/oneof-anyof-required/.gitignore new file mode 100644 index 000000000000..4c10d2907894 --- /dev/null +++ b/samples/client/others/terraform/oneof-anyof-required/.gitignore @@ -0,0 +1,13 @@ +*.dll +*.exe +*.exe~ +*.dylib +*.so +*.test +*.out +*.tfstate +*.tfstate.* +.terraform/ +*.tfvars +*.tfvars.json +terraform-provider-oneof diff --git a/samples/client/others/terraform/oneof-anyof-required/.openapi-generator-ignore b/samples/client/others/terraform/oneof-anyof-required/.openapi-generator-ignore new file mode 100644 index 000000000000..7484ee590a38 --- /dev/null +++ b/samples/client/others/terraform/oneof-anyof-required/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/samples/client/others/terraform/oneof-anyof-required/.openapi-generator/FILES b/samples/client/others/terraform/oneof-anyof-required/.openapi-generator/FILES new file mode 100644 index 000000000000..2f87d316690a --- /dev/null +++ b/samples/client/others/terraform/oneof-anyof-required/.openapi-generator/FILES @@ -0,0 +1,12 @@ +.gitignore +GNUmakefile +README.md +examples/provider/provider.tf +go.mod +internal/client/client.go +internal/client/model_nested_object1.go +internal/client/model_nested_object2.go +internal/client/model_object.go +internal/client/model_object2.go +internal/provider/provider.go +main.go diff --git a/samples/client/others/terraform/oneof-anyof-required/.openapi-generator/VERSION b/samples/client/others/terraform/oneof-anyof-required/.openapi-generator/VERSION new file mode 100644 index 000000000000..193a12d6e891 --- /dev/null +++ b/samples/client/others/terraform/oneof-anyof-required/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.20.0-SNAPSHOT diff --git a/samples/client/others/terraform/oneof-anyof-required/GNUmakefile b/samples/client/others/terraform/oneof-anyof-required/GNUmakefile new file mode 100644 index 000000000000..6278c63e1414 --- /dev/null +++ b/samples/client/others/terraform/oneof-anyof-required/GNUmakefile @@ -0,0 +1,27 @@ +default: testacc + +# Run acceptance tests +.PHONY: testacc +testacc: + TF_ACC=1 go test ./... -v $(TESTARGS) -timeout 120m + +# Build provider +.PHONY: build +build: + go build -o terraform-provider-oneof + +# Install provider locally +.PHONY: install +install: build + mkdir -p ~/.terraform.d/plugins/registry.terraform.io/example/oneof-anyof/0.1.0/$(shell go env GOOS)_$(shell go env GOARCH) + mv terraform-provider-oneof ~/.terraform.d/plugins/registry.terraform.io/example/oneof-anyof/0.1.0/$(shell go env GOOS)_$(shell go env GOARCH)/ + +# Generate documentation +.PHONY: docs +docs: + go generate ./... + +# Run linter +.PHONY: lint +lint: + golangci-lint run ./... diff --git a/samples/client/others/terraform/oneof-anyof-required/README.md b/samples/client/others/terraform/oneof-anyof-required/README.md new file mode 100644 index 000000000000..fffc99f3ef06 --- /dev/null +++ b/samples/client/others/terraform/oneof-anyof-required/README.md @@ -0,0 +1,61 @@ +# Terraform Provider oneof + +This Terraform provider was generated using [OpenAPI Generator](https://openapi-generator.tech). + +## Requirements + +- [Terraform](https://www.terraform.io/downloads.html) >= 1.0 +- [Go](https://golang.org/doc/install) >= 1.22 + +## Building The Provider + +1. Clone the repository +2. Enter the repository directory +3. Build the provider using the Go `install` command: + +```shell +go install +``` + +## Using the provider + +```hcl +terraform { + required_providers { + oneof = { + source = "registry.terraform.io/example/oneof-anyof" + } + } +} + +provider "oneof" { + endpoint = "http://localhost" +} +``` + +## Developing the Provider + +If you wish to work on the provider, you'll first need [Go](http://www.golang.org) installed on your machine (see [Requirements](#requirements) above). + +To compile the provider, run `go install`. This will build the provider and put the provider binary in the `$GOPATH/bin` directory. + +For local development and testing, add a `dev_overrides` block to your `~/.terraformrc` so that Terraform uses the locally built binary instead of fetching from the registry: + +```hcl +provider_installation { + dev_overrides { + "registry.terraform.io/example/oneof-anyof" = "${GOPATH}/bin" + } + direct {} +} +``` + +With `dev_overrides` configured, you do not need to run `terraform init` for the provider. + +To generate or update documentation, run `go generate`. + +In order to run the full suite of Acceptance tests, run `make testacc`. + +```shell +make testacc +``` diff --git a/samples/client/others/terraform/oneof-anyof-required/examples/provider/provider.tf b/samples/client/others/terraform/oneof-anyof-required/examples/provider/provider.tf new file mode 100644 index 000000000000..65c15e68d717 --- /dev/null +++ b/samples/client/others/terraform/oneof-anyof-required/examples/provider/provider.tf @@ -0,0 +1,13 @@ +terraform { + required_providers { + oneof = { + source = "registry.terraform.io/example/oneof-anyof" + } + } +} + +provider "oneof" { + endpoint = "http://localhost" + # api_key = "your-api-key" + # token = "your-bearer-token" +} diff --git a/samples/client/others/terraform/oneof-anyof-required/go.mod b/samples/client/others/terraform/oneof-anyof-required/go.mod new file mode 100644 index 000000000000..4767c9e41bdf --- /dev/null +++ b/samples/client/others/terraform/oneof-anyof-required/go.mod @@ -0,0 +1,11 @@ +module github.com/example/terraform-provider-oneof-anyof + +go 1.24.0 + +toolchain go1.24.4 + +require ( + github.com/hashicorp/terraform-plugin-framework v1.17.0 + github.com/hashicorp/terraform-plugin-log v0.10.0 + github.com/hashicorp/terraform-plugin-testing v1.14.0 +) diff --git a/samples/client/others/terraform/oneof-anyof-required/go.sum b/samples/client/others/terraform/oneof-anyof-required/go.sum new file mode 100644 index 000000000000..8888a9727e10 --- /dev/null +++ b/samples/client/others/terraform/oneof-anyof-required/go.sum @@ -0,0 +1,99 @@ +github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= +github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA= +github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/terraform-plugin-framework v1.17.0 h1:JdX50CFrYcYFY31gkmitAEAzLKoBgsK+iaJjDC8OexY= +github.com/hashicorp/terraform-plugin-framework v1.17.0/go.mod h1:4OUXKdHNosX+ys6rLgVlgklfxN3WHR5VHSOABeS/BM0= +github.com/hashicorp/terraform-plugin-go v0.29.0 h1:1nXKl/nSpaYIUBU1IG/EsDOX0vv+9JxAltQyDMpq5mU= +github.com/hashicorp/terraform-plugin-go v0.29.0/go.mod h1:vYZbIyvxyy0FWSmDHChCqKvI40cFTDGSb3D8D70i9GM= +github.com/hashicorp/terraform-plugin-log v0.10.0 h1:eu2kW6/QBVdN4P3Ju2WiB2W3ObjkAsyfBsL3Wh1fj3g= +github.com/hashicorp/terraform-plugin-log v0.10.0/go.mod h1:/9RR5Cv2aAbrqcTSdNmY1NRHP4E3ekrXRGjqORpXyB0= +github.com/hashicorp/terraform-registry-address v0.4.0 h1:S1yCGomj30Sao4l5BMPjTGZmCNzuv7/GDTDX99E9gTk= +github.com/hashicorp/terraform-registry-address v0.4.0/go.mod h1:LRS1Ay0+mAiRkUyltGT+UHWkIqTFvigGn/LbMshfflE= +github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= +github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc= +github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= +github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= +github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94= +github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= +go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= +go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= +go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= +go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= +go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= +go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI= +google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= +google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/samples/client/others/terraform/oneof-anyof-required/internal/client/client.go b/samples/client/others/terraform/oneof-anyof-required/internal/client/client.go new file mode 100644 index 000000000000..247e45a1c730 --- /dev/null +++ b/samples/client/others/terraform/oneof-anyof-required/internal/client/client.go @@ -0,0 +1,78 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" +) + +// Client is the API client used to interact with the API. +type Client struct { + BaseURL string + HTTPClient *http.Client + ApiKey string + Token string + Username string + Password string +} + +// NewClient creates a new API client with the given base URL. +func NewClient(baseURL string) *Client { + return &Client{ + BaseURL: baseURL, + HTTPClient: &http.Client{}, + } +} + +// DoRequest executes an HTTP request and returns the response body. +func (c *Client) DoRequest(ctx context.Context, method, path string, body interface{}) ([]byte, error) { + var reqBody io.Reader + if body != nil { + jsonBody, err := json.Marshal(body) + if err != nil { + return nil, fmt.Errorf("error marshaling request body: %w", err) + } + reqBody = bytes.NewBuffer(jsonBody) + } + + req, err := http.NewRequestWithContext(ctx, method, c.BaseURL+path, reqBody) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + + if body != nil { + req.Header.Set("Content-Type", "application/json") + } + req.Header.Set("Accept", "application/json") + + // Authentication + if c.Token != "" { + req.Header.Set("Authorization", "Bearer "+c.Token) + } else if c.ApiKey != "" { + req.Header.Set("Authorization", c.ApiKey) + } else if c.Username != "" { + req.SetBasicAuth(c.Username, c.Password) + } + + resp, err := c.HTTPClient.Do(req) + if err != nil { + return nil, fmt.Errorf("error making request: %w", err) + } + defer resp.Body.Close() + + respBody, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(respBody)) + } + + return respBody, nil +} diff --git a/samples/client/others/terraform/oneof-anyof-required/internal/client/model_nested_object1.go b/samples/client/others/terraform/oneof-anyof-required/internal/client/model_nested_object1.go new file mode 100644 index 000000000000..99a802896c24 --- /dev/null +++ b/samples/client/others/terraform/oneof-anyof-required/internal/client/model_nested_object1.go @@ -0,0 +1,8 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// NestedObject1 - NestedObject1 struct +type NestedObject1 struct { + Field1 string `json:"field1"` +} diff --git a/samples/client/others/terraform/oneof-anyof-required/internal/client/model_nested_object2.go b/samples/client/others/terraform/oneof-anyof-required/internal/client/model_nested_object2.go new file mode 100644 index 000000000000..058a68c6d903 --- /dev/null +++ b/samples/client/others/terraform/oneof-anyof-required/internal/client/model_nested_object2.go @@ -0,0 +1,8 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// NestedObject2 - NestedObject2 struct +type NestedObject2 struct { + Field2 string `json:"field2"` +} diff --git a/samples/client/others/terraform/oneof-anyof-required/internal/client/model_object.go b/samples/client/others/terraform/oneof-anyof-required/internal/client/model_object.go new file mode 100644 index 000000000000..0aebcc2d28c0 --- /dev/null +++ b/samples/client/others/terraform/oneof-anyof-required/internal/client/model_object.go @@ -0,0 +1,9 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// Object - Object struct +type Object struct { + Field1 string `json:"field1"` + Field2 string `json:"field2"` +} diff --git a/samples/client/others/terraform/oneof-anyof-required/internal/client/model_object2.go b/samples/client/others/terraform/oneof-anyof-required/internal/client/model_object2.go new file mode 100644 index 000000000000..85f4c85784a9 --- /dev/null +++ b/samples/client/others/terraform/oneof-anyof-required/internal/client/model_object2.go @@ -0,0 +1,9 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// Object2 - Object2 struct +type Object2 struct { + Field1 string `json:"field1"` + Field2 string `json:"field2"` +} diff --git a/samples/client/others/terraform/oneof-anyof-required/internal/provider/provider.go b/samples/client/others/terraform/oneof-anyof-required/internal/provider/provider.go new file mode 100644 index 000000000000..437358f37983 --- /dev/null +++ b/samples/client/others/terraform/oneof-anyof-required/internal/provider/provider.go @@ -0,0 +1,121 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package provider + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/provider" + "github.com/hashicorp/terraform-plugin-framework/provider/schema" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/types" + + "github.com/example/terraform-provider-oneof-anyof/internal/client" +) + +var _ provider.Provider = &oneofProvider{} + +type oneofProvider struct { + version string +} + +type oneofProviderModel struct { + Endpoint types.String `tfsdk:"endpoint"` + ApiKey types.String `tfsdk:"api_key"` + Token types.String `tfsdk:"token"` + Username types.String `tfsdk:"username"` + Password types.String `tfsdk:"password"` +} + +func New(version string) func() provider.Provider { + return func() provider.Provider { + return &oneofProvider{ + version: version, + } + } +} + +func (p *oneofProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) { + resp.TypeName = "oneof" + resp.Version = p.version +} + +func (p *oneofProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "endpoint": schema.StringAttribute{ + Optional: true, + Description: "The API endpoint URL.", + }, + "api_key": schema.StringAttribute{ + Optional: true, + Sensitive: true, + Description: "The API key for authentication.", + }, + "token": schema.StringAttribute{ + Optional: true, + Sensitive: true, + Description: "The bearer token for authentication.", + }, + "username": schema.StringAttribute{ + Optional: true, + Description: "The username for basic authentication.", + }, + "password": schema.StringAttribute{ + Optional: true, + Sensitive: true, + Description: "The password for basic authentication.", + }, + }, + } +} + +func (p *oneofProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { + var config oneofProviderModel + + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + + if config.Endpoint.IsUnknown() { + resp.Diagnostics.AddWarning( + "Unknown Endpoint Value", + "The provider endpoint value is not yet known. Defaulting to the spec-defined base path.", + ) + } + + endpoint := "http://localhost" + if !config.Endpoint.IsNull() && !config.Endpoint.IsUnknown() { + endpoint = config.Endpoint.ValueString() + } + + c := client.NewClient(endpoint) + + if !config.ApiKey.IsNull() { + c.ApiKey = config.ApiKey.ValueString() + } + if !config.Token.IsNull() { + c.Token = config.Token.ValueString() + } + if !config.Username.IsNull() { + c.Username = config.Username.ValueString() + } + if !config.Password.IsNull() { + c.Password = config.Password.ValueString() + } + + resp.DataSourceData = c + resp.ResourceData = c +} + +func (p *oneofProvider) Resources(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + } +} + +func (p *oneofProvider) DataSources(_ context.Context) []func() datasource.DataSource { + return []func() datasource.DataSource{ + } +} diff --git a/samples/client/others/terraform/oneof-anyof-required/main.go b/samples/client/others/terraform/oneof-anyof-required/main.go new file mode 100644 index 000000000000..f920bff94b3f --- /dev/null +++ b/samples/client/others/terraform/oneof-anyof-required/main.go @@ -0,0 +1,35 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package main + +import ( + "context" + "flag" + "log" + + "github.com/hashicorp/terraform-plugin-framework/providerserver" + + "github.com/example/terraform-provider-oneof-anyof/internal/provider" +) + +var ( + version string = "0.1.0" +) + +func main() { + var debug bool + + flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve") + flag.Parse() + + opts := providerserver.ServeOpts{ + Address: "registry.terraform.io/example/oneof-anyof", + Debug: debug, + } + + err := providerserver.Serve(context.Background(), provider.New(version), opts) + + if err != nil { + log.Fatal(err.Error()) + } +} diff --git a/samples/client/others/terraform/oneof-discriminator-lookup/.gitignore b/samples/client/others/terraform/oneof-discriminator-lookup/.gitignore new file mode 100644 index 000000000000..4c10d2907894 --- /dev/null +++ b/samples/client/others/terraform/oneof-discriminator-lookup/.gitignore @@ -0,0 +1,13 @@ +*.dll +*.exe +*.exe~ +*.dylib +*.so +*.test +*.out +*.tfstate +*.tfstate.* +.terraform/ +*.tfvars +*.tfvars.json +terraform-provider-oneof diff --git a/samples/client/others/terraform/oneof-discriminator-lookup/.openapi-generator-ignore b/samples/client/others/terraform/oneof-discriminator-lookup/.openapi-generator-ignore new file mode 100644 index 000000000000..7484ee590a38 --- /dev/null +++ b/samples/client/others/terraform/oneof-discriminator-lookup/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/samples/client/others/terraform/oneof-discriminator-lookup/.openapi-generator/FILES b/samples/client/others/terraform/oneof-discriminator-lookup/.openapi-generator/FILES new file mode 100644 index 000000000000..df1c0ded3413 --- /dev/null +++ b/samples/client/others/terraform/oneof-discriminator-lookup/.openapi-generator/FILES @@ -0,0 +1,11 @@ +.gitignore +GNUmakefile +README.md +examples/provider/provider.tf +go.mod +internal/client/client.go +internal/client/model_nested_object1.go +internal/client/model_nested_object2.go +internal/client/model_object.go +internal/provider/provider.go +main.go diff --git a/samples/client/others/terraform/oneof-discriminator-lookup/.openapi-generator/VERSION b/samples/client/others/terraform/oneof-discriminator-lookup/.openapi-generator/VERSION new file mode 100644 index 000000000000..193a12d6e891 --- /dev/null +++ b/samples/client/others/terraform/oneof-discriminator-lookup/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.20.0-SNAPSHOT diff --git a/samples/client/others/terraform/oneof-discriminator-lookup/GNUmakefile b/samples/client/others/terraform/oneof-discriminator-lookup/GNUmakefile new file mode 100644 index 000000000000..2b4a6b95a2a2 --- /dev/null +++ b/samples/client/others/terraform/oneof-discriminator-lookup/GNUmakefile @@ -0,0 +1,27 @@ +default: testacc + +# Run acceptance tests +.PHONY: testacc +testacc: + TF_ACC=1 go test ./... -v $(TESTARGS) -timeout 120m + +# Build provider +.PHONY: build +build: + go build -o terraform-provider-oneof + +# Install provider locally +.PHONY: install +install: build + mkdir -p ~/.terraform.d/plugins/registry.terraform.io/example/oneof-disc/0.1.0/$(shell go env GOOS)_$(shell go env GOARCH) + mv terraform-provider-oneof ~/.terraform.d/plugins/registry.terraform.io/example/oneof-disc/0.1.0/$(shell go env GOOS)_$(shell go env GOARCH)/ + +# Generate documentation +.PHONY: docs +docs: + go generate ./... + +# Run linter +.PHONY: lint +lint: + golangci-lint run ./... diff --git a/samples/client/others/terraform/oneof-discriminator-lookup/README.md b/samples/client/others/terraform/oneof-discriminator-lookup/README.md new file mode 100644 index 000000000000..e1fd479793b6 --- /dev/null +++ b/samples/client/others/terraform/oneof-discriminator-lookup/README.md @@ -0,0 +1,61 @@ +# Terraform Provider oneof + +This Terraform provider was generated using [OpenAPI Generator](https://openapi-generator.tech). + +## Requirements + +- [Terraform](https://www.terraform.io/downloads.html) >= 1.0 +- [Go](https://golang.org/doc/install) >= 1.22 + +## Building The Provider + +1. Clone the repository +2. Enter the repository directory +3. Build the provider using the Go `install` command: + +```shell +go install +``` + +## Using the provider + +```hcl +terraform { + required_providers { + oneof = { + source = "registry.terraform.io/example/oneof-disc" + } + } +} + +provider "oneof" { + endpoint = "http://localhost" +} +``` + +## Developing the Provider + +If you wish to work on the provider, you'll first need [Go](http://www.golang.org) installed on your machine (see [Requirements](#requirements) above). + +To compile the provider, run `go install`. This will build the provider and put the provider binary in the `$GOPATH/bin` directory. + +For local development and testing, add a `dev_overrides` block to your `~/.terraformrc` so that Terraform uses the locally built binary instead of fetching from the registry: + +```hcl +provider_installation { + dev_overrides { + "registry.terraform.io/example/oneof-disc" = "${GOPATH}/bin" + } + direct {} +} +``` + +With `dev_overrides` configured, you do not need to run `terraform init` for the provider. + +To generate or update documentation, run `go generate`. + +In order to run the full suite of Acceptance tests, run `make testacc`. + +```shell +make testacc +``` diff --git a/samples/client/others/terraform/oneof-discriminator-lookup/examples/provider/provider.tf b/samples/client/others/terraform/oneof-discriminator-lookup/examples/provider/provider.tf new file mode 100644 index 000000000000..2960b9988580 --- /dev/null +++ b/samples/client/others/terraform/oneof-discriminator-lookup/examples/provider/provider.tf @@ -0,0 +1,13 @@ +terraform { + required_providers { + oneof = { + source = "registry.terraform.io/example/oneof-disc" + } + } +} + +provider "oneof" { + endpoint = "http://localhost" + # api_key = "your-api-key" + # token = "your-bearer-token" +} diff --git a/samples/client/others/terraform/oneof-discriminator-lookup/go.mod b/samples/client/others/terraform/oneof-discriminator-lookup/go.mod new file mode 100644 index 000000000000..fe32278b0259 --- /dev/null +++ b/samples/client/others/terraform/oneof-discriminator-lookup/go.mod @@ -0,0 +1,11 @@ +module github.com/example/terraform-provider-oneof-disc + +go 1.24.0 + +toolchain go1.24.4 + +require ( + github.com/hashicorp/terraform-plugin-framework v1.17.0 + github.com/hashicorp/terraform-plugin-log v0.10.0 + github.com/hashicorp/terraform-plugin-testing v1.14.0 +) diff --git a/samples/client/others/terraform/oneof-discriminator-lookup/go.sum b/samples/client/others/terraform/oneof-discriminator-lookup/go.sum new file mode 100644 index 000000000000..8888a9727e10 --- /dev/null +++ b/samples/client/others/terraform/oneof-discriminator-lookup/go.sum @@ -0,0 +1,99 @@ +github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= +github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA= +github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/terraform-plugin-framework v1.17.0 h1:JdX50CFrYcYFY31gkmitAEAzLKoBgsK+iaJjDC8OexY= +github.com/hashicorp/terraform-plugin-framework v1.17.0/go.mod h1:4OUXKdHNosX+ys6rLgVlgklfxN3WHR5VHSOABeS/BM0= +github.com/hashicorp/terraform-plugin-go v0.29.0 h1:1nXKl/nSpaYIUBU1IG/EsDOX0vv+9JxAltQyDMpq5mU= +github.com/hashicorp/terraform-plugin-go v0.29.0/go.mod h1:vYZbIyvxyy0FWSmDHChCqKvI40cFTDGSb3D8D70i9GM= +github.com/hashicorp/terraform-plugin-log v0.10.0 h1:eu2kW6/QBVdN4P3Ju2WiB2W3ObjkAsyfBsL3Wh1fj3g= +github.com/hashicorp/terraform-plugin-log v0.10.0/go.mod h1:/9RR5Cv2aAbrqcTSdNmY1NRHP4E3ekrXRGjqORpXyB0= +github.com/hashicorp/terraform-registry-address v0.4.0 h1:S1yCGomj30Sao4l5BMPjTGZmCNzuv7/GDTDX99E9gTk= +github.com/hashicorp/terraform-registry-address v0.4.0/go.mod h1:LRS1Ay0+mAiRkUyltGT+UHWkIqTFvigGn/LbMshfflE= +github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= +github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc= +github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= +github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= +github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94= +github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= +go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= +go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= +go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= +go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= +go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= +go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI= +google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= +google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/samples/client/others/terraform/oneof-discriminator-lookup/internal/client/client.go b/samples/client/others/terraform/oneof-discriminator-lookup/internal/client/client.go new file mode 100644 index 000000000000..247e45a1c730 --- /dev/null +++ b/samples/client/others/terraform/oneof-discriminator-lookup/internal/client/client.go @@ -0,0 +1,78 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" +) + +// Client is the API client used to interact with the API. +type Client struct { + BaseURL string + HTTPClient *http.Client + ApiKey string + Token string + Username string + Password string +} + +// NewClient creates a new API client with the given base URL. +func NewClient(baseURL string) *Client { + return &Client{ + BaseURL: baseURL, + HTTPClient: &http.Client{}, + } +} + +// DoRequest executes an HTTP request and returns the response body. +func (c *Client) DoRequest(ctx context.Context, method, path string, body interface{}) ([]byte, error) { + var reqBody io.Reader + if body != nil { + jsonBody, err := json.Marshal(body) + if err != nil { + return nil, fmt.Errorf("error marshaling request body: %w", err) + } + reqBody = bytes.NewBuffer(jsonBody) + } + + req, err := http.NewRequestWithContext(ctx, method, c.BaseURL+path, reqBody) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + + if body != nil { + req.Header.Set("Content-Type", "application/json") + } + req.Header.Set("Accept", "application/json") + + // Authentication + if c.Token != "" { + req.Header.Set("Authorization", "Bearer "+c.Token) + } else if c.ApiKey != "" { + req.Header.Set("Authorization", c.ApiKey) + } else if c.Username != "" { + req.SetBasicAuth(c.Username, c.Password) + } + + resp, err := c.HTTPClient.Do(req) + if err != nil { + return nil, fmt.Errorf("error making request: %w", err) + } + defer resp.Body.Close() + + respBody, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(respBody)) + } + + return respBody, nil +} diff --git a/samples/client/others/terraform/oneof-discriminator-lookup/internal/client/model_nested_object1.go b/samples/client/others/terraform/oneof-discriminator-lookup/internal/client/model_nested_object1.go new file mode 100644 index 000000000000..dbfccaa34fd7 --- /dev/null +++ b/samples/client/others/terraform/oneof-discriminator-lookup/internal/client/model_nested_object1.go @@ -0,0 +1,9 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// NestedObject1 - NestedObject1 struct +type NestedObject1 struct { + Field1 string `json:"field1"` + Type string `json:"type,omitempty"` +} diff --git a/samples/client/others/terraform/oneof-discriminator-lookup/internal/client/model_nested_object2.go b/samples/client/others/terraform/oneof-discriminator-lookup/internal/client/model_nested_object2.go new file mode 100644 index 000000000000..dcb9e9fab3a9 --- /dev/null +++ b/samples/client/others/terraform/oneof-discriminator-lookup/internal/client/model_nested_object2.go @@ -0,0 +1,9 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// NestedObject2 - NestedObject2 struct +type NestedObject2 struct { + Field1 string `json:"field1,omitempty"` + Type string `json:"type,omitempty"` +} diff --git a/samples/client/others/terraform/oneof-discriminator-lookup/internal/client/model_object.go b/samples/client/others/terraform/oneof-discriminator-lookup/internal/client/model_object.go new file mode 100644 index 000000000000..7a5614cb5c49 --- /dev/null +++ b/samples/client/others/terraform/oneof-discriminator-lookup/internal/client/model_object.go @@ -0,0 +1,9 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// Object - Object struct +type Object struct { + Field1 string `json:"field1"` + Type string `json:"type,omitempty"` +} diff --git a/samples/client/others/terraform/oneof-discriminator-lookup/internal/provider/provider.go b/samples/client/others/terraform/oneof-discriminator-lookup/internal/provider/provider.go new file mode 100644 index 000000000000..8d625d154eb9 --- /dev/null +++ b/samples/client/others/terraform/oneof-discriminator-lookup/internal/provider/provider.go @@ -0,0 +1,121 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package provider + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/provider" + "github.com/hashicorp/terraform-plugin-framework/provider/schema" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/types" + + "github.com/example/terraform-provider-oneof-disc/internal/client" +) + +var _ provider.Provider = &oneofProvider{} + +type oneofProvider struct { + version string +} + +type oneofProviderModel struct { + Endpoint types.String `tfsdk:"endpoint"` + ApiKey types.String `tfsdk:"api_key"` + Token types.String `tfsdk:"token"` + Username types.String `tfsdk:"username"` + Password types.String `tfsdk:"password"` +} + +func New(version string) func() provider.Provider { + return func() provider.Provider { + return &oneofProvider{ + version: version, + } + } +} + +func (p *oneofProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) { + resp.TypeName = "oneof" + resp.Version = p.version +} + +func (p *oneofProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "endpoint": schema.StringAttribute{ + Optional: true, + Description: "The API endpoint URL.", + }, + "api_key": schema.StringAttribute{ + Optional: true, + Sensitive: true, + Description: "The API key for authentication.", + }, + "token": schema.StringAttribute{ + Optional: true, + Sensitive: true, + Description: "The bearer token for authentication.", + }, + "username": schema.StringAttribute{ + Optional: true, + Description: "The username for basic authentication.", + }, + "password": schema.StringAttribute{ + Optional: true, + Sensitive: true, + Description: "The password for basic authentication.", + }, + }, + } +} + +func (p *oneofProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { + var config oneofProviderModel + + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + + if config.Endpoint.IsUnknown() { + resp.Diagnostics.AddWarning( + "Unknown Endpoint Value", + "The provider endpoint value is not yet known. Defaulting to the spec-defined base path.", + ) + } + + endpoint := "http://localhost" + if !config.Endpoint.IsNull() && !config.Endpoint.IsUnknown() { + endpoint = config.Endpoint.ValueString() + } + + c := client.NewClient(endpoint) + + if !config.ApiKey.IsNull() { + c.ApiKey = config.ApiKey.ValueString() + } + if !config.Token.IsNull() { + c.Token = config.Token.ValueString() + } + if !config.Username.IsNull() { + c.Username = config.Username.ValueString() + } + if !config.Password.IsNull() { + c.Password = config.Password.ValueString() + } + + resp.DataSourceData = c + resp.ResourceData = c +} + +func (p *oneofProvider) Resources(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + } +} + +func (p *oneofProvider) DataSources(_ context.Context) []func() datasource.DataSource { + return []func() datasource.DataSource{ + } +} diff --git a/samples/client/others/terraform/oneof-discriminator-lookup/main.go b/samples/client/others/terraform/oneof-discriminator-lookup/main.go new file mode 100644 index 000000000000..21fb266dc5b6 --- /dev/null +++ b/samples/client/others/terraform/oneof-discriminator-lookup/main.go @@ -0,0 +1,35 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package main + +import ( + "context" + "flag" + "log" + + "github.com/hashicorp/terraform-plugin-framework/providerserver" + + "github.com/example/terraform-provider-oneof-disc/internal/provider" +) + +var ( + version string = "0.1.0" +) + +func main() { + var debug bool + + flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve") + flag.Parse() + + opts := providerserver.ServeOpts{ + Address: "registry.terraform.io/example/oneof-disc", + Debug: debug, + } + + err := providerserver.Serve(context.Background(), provider.New(version), opts) + + if err != nil { + log.Fatal(err.Error()) + } +} diff --git a/samples/client/petstore/terraform-addpet/.gitignore b/samples/client/petstore/terraform-addpet/.gitignore new file mode 100644 index 000000000000..5cc9ba00d4f9 --- /dev/null +++ b/samples/client/petstore/terraform-addpet/.gitignore @@ -0,0 +1,13 @@ +*.dll +*.exe +*.exe~ +*.dylib +*.so +*.test +*.out +*.tfstate +*.tfstate.* +.terraform/ +*.tfvars +*.tfvars.json +terraform-provider-petstore diff --git a/samples/client/petstore/terraform-addpet/.openapi-generator-ignore b/samples/client/petstore/terraform-addpet/.openapi-generator-ignore new file mode 100644 index 000000000000..7484ee590a38 --- /dev/null +++ b/samples/client/petstore/terraform-addpet/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/samples/client/petstore/terraform-addpet/.openapi-generator/FILES b/samples/client/petstore/terraform-addpet/.openapi-generator/FILES new file mode 100644 index 000000000000..8d4b253aac2a --- /dev/null +++ b/samples/client/petstore/terraform-addpet/.openapi-generator/FILES @@ -0,0 +1,14 @@ +.gitignore +GNUmakefile +README.md +examples/provider/provider.tf +go.mod +internal/client/client.go +internal/client/model_category.go +internal/client/model_pet.go +internal/client/model_tag.go +internal/provider/pet_data_source.go +internal/provider/pet_model.go +internal/provider/pet_resource.go +internal/provider/provider.go +main.go diff --git a/samples/client/petstore/terraform-addpet/.openapi-generator/VERSION b/samples/client/petstore/terraform-addpet/.openapi-generator/VERSION new file mode 100644 index 000000000000..193a12d6e891 --- /dev/null +++ b/samples/client/petstore/terraform-addpet/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.20.0-SNAPSHOT diff --git a/samples/client/petstore/terraform-addpet/GNUmakefile b/samples/client/petstore/terraform-addpet/GNUmakefile new file mode 100644 index 000000000000..399b00b0b2fe --- /dev/null +++ b/samples/client/petstore/terraform-addpet/GNUmakefile @@ -0,0 +1,27 @@ +default: testacc + +# Run acceptance tests +.PHONY: testacc +testacc: + TF_ACC=1 go test ./... -v $(TESTARGS) -timeout 120m + +# Build provider +.PHONY: build +build: + go build -o terraform-provider-petstore + +# Install provider locally +.PHONY: install +install: build + mkdir -p ~/.terraform.d/plugins/registry.terraform.io/example/petstore-addpet/0.1.0/$(shell go env GOOS)_$(shell go env GOARCH) + mv terraform-provider-petstore ~/.terraform.d/plugins/registry.terraform.io/example/petstore-addpet/0.1.0/$(shell go env GOOS)_$(shell go env GOARCH)/ + +# Generate documentation +.PHONY: docs +docs: + go generate ./... + +# Run linter +.PHONY: lint +lint: + golangci-lint run ./... diff --git a/samples/client/petstore/terraform-addpet/README.md b/samples/client/petstore/terraform-addpet/README.md new file mode 100644 index 000000000000..cd476b444d3d --- /dev/null +++ b/samples/client/petstore/terraform-addpet/README.md @@ -0,0 +1,61 @@ +# Terraform Provider petstore + +This Terraform provider was generated using [OpenAPI Generator](https://openapi-generator.tech). + +## Requirements + +- [Terraform](https://www.terraform.io/downloads.html) >= 1.0 +- [Go](https://golang.org/doc/install) >= 1.22 + +## Building The Provider + +1. Clone the repository +2. Enter the repository directory +3. Build the provider using the Go `install` command: + +```shell +go install +``` + +## Using the provider + +```hcl +terraform { + required_providers { + petstore = { + source = "registry.terraform.io/example/petstore-addpet" + } + } +} + +provider "petstore" { + endpoint = "http://petstore.swagger.io/v2" +} +``` + +## Developing the Provider + +If you wish to work on the provider, you'll first need [Go](http://www.golang.org) installed on your machine (see [Requirements](#requirements) above). + +To compile the provider, run `go install`. This will build the provider and put the provider binary in the `$GOPATH/bin` directory. + +For local development and testing, add a `dev_overrides` block to your `~/.terraformrc` so that Terraform uses the locally built binary instead of fetching from the registry: + +```hcl +provider_installation { + dev_overrides { + "registry.terraform.io/example/petstore-addpet" = "${GOPATH}/bin" + } + direct {} +} +``` + +With `dev_overrides` configured, you do not need to run `terraform init` for the provider. + +To generate or update documentation, run `go generate`. + +In order to run the full suite of Acceptance tests, run `make testacc`. + +```shell +make testacc +``` diff --git a/samples/client/petstore/terraform-addpet/examples/provider/provider.tf b/samples/client/petstore/terraform-addpet/examples/provider/provider.tf new file mode 100644 index 000000000000..bc1cbb6028ad --- /dev/null +++ b/samples/client/petstore/terraform-addpet/examples/provider/provider.tf @@ -0,0 +1,13 @@ +terraform { + required_providers { + petstore = { + source = "registry.terraform.io/example/petstore-addpet" + } + } +} + +provider "petstore" { + endpoint = "http://petstore.swagger.io/v2" + # api_key = "your-api-key" + # token = "your-bearer-token" +} diff --git a/samples/client/petstore/terraform-addpet/go.mod b/samples/client/petstore/terraform-addpet/go.mod new file mode 100644 index 000000000000..a409fb8c6db8 --- /dev/null +++ b/samples/client/petstore/terraform-addpet/go.mod @@ -0,0 +1,11 @@ +module github.com/example/terraform-provider-petstore-addpet + +go 1.24.0 + +toolchain go1.24.4 + +require ( + github.com/hashicorp/terraform-plugin-framework v1.17.0 + github.com/hashicorp/terraform-plugin-log v0.10.0 + github.com/hashicorp/terraform-plugin-testing v1.14.0 +) diff --git a/samples/client/petstore/terraform-addpet/go.sum b/samples/client/petstore/terraform-addpet/go.sum new file mode 100644 index 000000000000..8888a9727e10 --- /dev/null +++ b/samples/client/petstore/terraform-addpet/go.sum @@ -0,0 +1,99 @@ +github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= +github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA= +github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/terraform-plugin-framework v1.17.0 h1:JdX50CFrYcYFY31gkmitAEAzLKoBgsK+iaJjDC8OexY= +github.com/hashicorp/terraform-plugin-framework v1.17.0/go.mod h1:4OUXKdHNosX+ys6rLgVlgklfxN3WHR5VHSOABeS/BM0= +github.com/hashicorp/terraform-plugin-go v0.29.0 h1:1nXKl/nSpaYIUBU1IG/EsDOX0vv+9JxAltQyDMpq5mU= +github.com/hashicorp/terraform-plugin-go v0.29.0/go.mod h1:vYZbIyvxyy0FWSmDHChCqKvI40cFTDGSb3D8D70i9GM= +github.com/hashicorp/terraform-plugin-log v0.10.0 h1:eu2kW6/QBVdN4P3Ju2WiB2W3ObjkAsyfBsL3Wh1fj3g= +github.com/hashicorp/terraform-plugin-log v0.10.0/go.mod h1:/9RR5Cv2aAbrqcTSdNmY1NRHP4E3ekrXRGjqORpXyB0= +github.com/hashicorp/terraform-registry-address v0.4.0 h1:S1yCGomj30Sao4l5BMPjTGZmCNzuv7/GDTDX99E9gTk= +github.com/hashicorp/terraform-registry-address v0.4.0/go.mod h1:LRS1Ay0+mAiRkUyltGT+UHWkIqTFvigGn/LbMshfflE= +github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= +github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc= +github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= +github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= +github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94= +github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= +go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= +go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= +go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= +go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= +go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= +go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI= +google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= +google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/samples/client/petstore/terraform-addpet/internal/client/client.go b/samples/client/petstore/terraform-addpet/internal/client/client.go new file mode 100644 index 000000000000..247e45a1c730 --- /dev/null +++ b/samples/client/petstore/terraform-addpet/internal/client/client.go @@ -0,0 +1,78 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" +) + +// Client is the API client used to interact with the API. +type Client struct { + BaseURL string + HTTPClient *http.Client + ApiKey string + Token string + Username string + Password string +} + +// NewClient creates a new API client with the given base URL. +func NewClient(baseURL string) *Client { + return &Client{ + BaseURL: baseURL, + HTTPClient: &http.Client{}, + } +} + +// DoRequest executes an HTTP request and returns the response body. +func (c *Client) DoRequest(ctx context.Context, method, path string, body interface{}) ([]byte, error) { + var reqBody io.Reader + if body != nil { + jsonBody, err := json.Marshal(body) + if err != nil { + return nil, fmt.Errorf("error marshaling request body: %w", err) + } + reqBody = bytes.NewBuffer(jsonBody) + } + + req, err := http.NewRequestWithContext(ctx, method, c.BaseURL+path, reqBody) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + + if body != nil { + req.Header.Set("Content-Type", "application/json") + } + req.Header.Set("Accept", "application/json") + + // Authentication + if c.Token != "" { + req.Header.Set("Authorization", "Bearer "+c.Token) + } else if c.ApiKey != "" { + req.Header.Set("Authorization", c.ApiKey) + } else if c.Username != "" { + req.SetBasicAuth(c.Username, c.Password) + } + + resp, err := c.HTTPClient.Do(req) + if err != nil { + return nil, fmt.Errorf("error making request: %w", err) + } + defer resp.Body.Close() + + respBody, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(respBody)) + } + + return respBody, nil +} diff --git a/samples/client/petstore/terraform-addpet/internal/client/model_category.go b/samples/client/petstore/terraform-addpet/internal/client/model_category.go new file mode 100644 index 000000000000..1813ce260524 --- /dev/null +++ b/samples/client/petstore/terraform-addpet/internal/client/model_category.go @@ -0,0 +1,9 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// Category - A category for a pet +type Category struct { + Id int64 `json:"id,omitempty"` + Name string `json:"name,omitempty" validate:"regexp=^[a-zA-Z0-9]+[a-zA-Z0-9\\\\.\\\\-_]*[a-zA-Z0-9]+$"` +} diff --git a/samples/client/petstore/terraform-addpet/internal/client/model_pet.go b/samples/client/petstore/terraform-addpet/internal/client/model_pet.go new file mode 100644 index 000000000000..96e95cec74c7 --- /dev/null +++ b/samples/client/petstore/terraform-addpet/internal/client/model_pet.go @@ -0,0 +1,13 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// Pet - A pet for sale in the pet store +type Pet struct { + Id int64 `json:"id,omitempty"` + Category Category `json:"category,omitempty"` + Name string `json:"name"` + PhotoUrls []string `json:"photoUrls"` + Tags []Tag `json:"tags,omitempty"` + Status string `json:"status,omitempty"` +} diff --git a/samples/client/petstore/terraform-addpet/internal/client/model_tag.go b/samples/client/petstore/terraform-addpet/internal/client/model_tag.go new file mode 100644 index 000000000000..6e81eb958ae7 --- /dev/null +++ b/samples/client/petstore/terraform-addpet/internal/client/model_tag.go @@ -0,0 +1,9 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// Tag - A tag for a pet +type Tag struct { + Id int64 `json:"id,omitempty"` + Name string `json:"name,omitempty"` +} diff --git a/samples/client/petstore/terraform-addpet/internal/provider/pet_data_source.go b/samples/client/petstore/terraform-addpet/internal/provider/pet_data_source.go new file mode 100644 index 000000000000..91db64b61406 --- /dev/null +++ b/samples/client/petstore/terraform-addpet/internal/provider/pet_data_source.go @@ -0,0 +1,79 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. +package provider + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + + "github.com/example/terraform-provider-petstore-addpet/internal/client" +) + +var _ datasource.DataSource = &PetDataSource{} + +func NewPetDataSource() datasource.DataSource { + return &PetDataSource{} +} + +type PetDataSource struct { + client *client.Client +} + +func (d *PetDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_pet" +} + +func (d *PetDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Fetches a pet data source.", + Attributes: map[string]schema.Attribute{ + "id": schema.Int64Attribute{ + Computed: true, + Description: "", + }, + "category": schema.StringAttribute{ + Computed: true, + Description: "", + }, + "name": schema.StringAttribute{ + Required: true, + Description: "", + }, + "photo_urls": schema.StringAttribute{ + Required: true, + Description: "", + }, + "tags": schema.StringAttribute{ + Computed: true, + Description: "", + }, + "status": schema.StringAttribute{ + Computed: true, + Description: "pet status in the store", + }, + }, + } +} + +func (d *PetDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + c, ok := req.ProviderData.(*client.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *client.Client, got: %T.", req.ProviderData), + ) + return + } + + d.client = c +} + +func (d *PetDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + resp.Diagnostics.AddError("Not Supported", "Read is not supported for pet") +} diff --git a/samples/client/petstore/terraform-addpet/internal/provider/pet_model.go b/samples/client/petstore/terraform-addpet/internal/provider/pet_model.go new file mode 100644 index 000000000000..7c63ef759e59 --- /dev/null +++ b/samples/client/petstore/terraform-addpet/internal/provider/pet_model.go @@ -0,0 +1,40 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. +package provider + +import ( + "github.com/hashicorp/terraform-plugin-framework/types" + + "github.com/example/terraform-provider-petstore-addpet/internal/client" +) + +// PetModel is the Terraform model for pet. +type PetModel struct { + Id types.Int64 `tfsdk:"id"` + Category types.String `tfsdk:"category"` + Name types.String `tfsdk:"name"` + PhotoUrls types.List `tfsdk:"photo_urls"` + Tags types.List `tfsdk:"tags"` + Status types.String `tfsdk:"status"` +} + +// ToClientModel converts a Terraform model to a client model. +func (m *PetModel) ToClientModel() *client.Pet { + out := &client.Pet{} + if !m.Id.IsNull() && !m.Id.IsUnknown() { + out.Id = int64(m.Id.ValueInt64()) + } + if !m.Name.IsNull() && !m.Name.IsUnknown() { + out.Name = m.Name.ValueString() + } + if !m.Status.IsNull() && !m.Status.IsUnknown() { + out.Status = m.Status.ValueString() + } + return out +} + +// FromClientModel updates the Terraform model from a client model. +func (m *PetModel) FromClientModel(c *client.Pet) { + m.Id = types.Int64Value(int64(c.Id)) + m.Name = types.StringValue(c.Name) + m.Status = types.StringValue(c.Status) +} diff --git a/samples/client/petstore/terraform-addpet/internal/provider/pet_resource.go b/samples/client/petstore/terraform-addpet/internal/provider/pet_resource.go new file mode 100644 index 000000000000..337d9309f47a --- /dev/null +++ b/samples/client/petstore/terraform-addpet/internal/provider/pet_resource.go @@ -0,0 +1,136 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. +package provider + +import ( + "context" + "fmt" + "encoding/json" + + + + "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/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + + + + "github.com/example/terraform-provider-petstore-addpet/internal/client" +) + +var _ resource.Resource = &PetResource{} +var _ resource.ResourceWithImportState = &PetResource{} + +func NewPetResource() resource.Resource { + return &PetResource{} +} + +type PetResource struct { + client *client.Client +} + +func (r *PetResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_pet" +} + +func (r *PetResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Manages a pet resource.", + Attributes: map[string]schema.Attribute{ + "id": schema.Int64Attribute{ + Optional: true, + Description: "", + }, + "category": schema.StringAttribute{ + Optional: true, + Description: "", + }, + "name": schema.StringAttribute{ + Required: true, + Description: "", + }, + "photo_urls": schema.ListAttribute{ + Required: true, + ElementType: types.StringType, + Description: "", + }, + "tags": schema.ListAttribute{ + Optional: true, + ElementType: types.StringType, + Description: "", + }, + "status": schema.StringAttribute{ + Optional: true, + Description: "pet status in the store", + }, + }, + } +} + +func (r *PetResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + c, ok := req.ProviderData.(*client.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *client.Client, got: %T.", req.ProviderData), + ) + return + } + + r.client = c +} + +func (r *PetResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan PetModel + + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + reqBody := plan.ToClientModel() + + respBody, err := r.client.DoRequest(ctx, "POST", "/pet", reqBody) + if err != nil { + resp.Diagnostics.AddError("Error creating pet", err.Error()) + return + } + + var result client.Pet + if err := json.Unmarshal(respBody, &result); err != nil { + resp.Diagnostics.AddError("Error parsing response", err.Error()) + return + } + + plan.FromClientModel(&result) + + tflog.Trace(ctx, "created pet resource") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *PetResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + // No read endpoint available; keep existing state as-is. +} + +func (r *PetResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + // No update endpoint available; persist the planned values into state. + var plan PetModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *PetResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // No delete endpoint available; remove the resource from state. +} + +func (r *PetResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} diff --git a/samples/client/petstore/terraform-addpet/internal/provider/provider.go b/samples/client/petstore/terraform-addpet/internal/provider/provider.go new file mode 100644 index 000000000000..770a515f30ff --- /dev/null +++ b/samples/client/petstore/terraform-addpet/internal/provider/provider.go @@ -0,0 +1,123 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package provider + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/provider" + "github.com/hashicorp/terraform-plugin-framework/provider/schema" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/types" + + "github.com/example/terraform-provider-petstore-addpet/internal/client" +) + +var _ provider.Provider = &petstoreProvider{} + +type petstoreProvider struct { + version string +} + +type petstoreProviderModel struct { + Endpoint types.String `tfsdk:"endpoint"` + ApiKey types.String `tfsdk:"api_key"` + Token types.String `tfsdk:"token"` + Username types.String `tfsdk:"username"` + Password types.String `tfsdk:"password"` +} + +func New(version string) func() provider.Provider { + return func() provider.Provider { + return &petstoreProvider{ + version: version, + } + } +} + +func (p *petstoreProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) { + resp.TypeName = "petstore" + resp.Version = p.version +} + +func (p *petstoreProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "endpoint": schema.StringAttribute{ + Optional: true, + Description: "The API endpoint URL.", + }, + "api_key": schema.StringAttribute{ + Optional: true, + Sensitive: true, + Description: "The API key for authentication.", + }, + "token": schema.StringAttribute{ + Optional: true, + Sensitive: true, + Description: "The bearer token for authentication.", + }, + "username": schema.StringAttribute{ + Optional: true, + Description: "The username for basic authentication.", + }, + "password": schema.StringAttribute{ + Optional: true, + Sensitive: true, + Description: "The password for basic authentication.", + }, + }, + } +} + +func (p *petstoreProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { + var config petstoreProviderModel + + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + + if config.Endpoint.IsUnknown() { + resp.Diagnostics.AddWarning( + "Unknown Endpoint Value", + "The provider endpoint value is not yet known. Defaulting to the spec-defined base path.", + ) + } + + endpoint := "http://petstore.swagger.io/v2" + if !config.Endpoint.IsNull() && !config.Endpoint.IsUnknown() { + endpoint = config.Endpoint.ValueString() + } + + c := client.NewClient(endpoint) + + if !config.ApiKey.IsNull() { + c.ApiKey = config.ApiKey.ValueString() + } + if !config.Token.IsNull() { + c.Token = config.Token.ValueString() + } + if !config.Username.IsNull() { + c.Username = config.Username.ValueString() + } + if !config.Password.IsNull() { + c.Password = config.Password.ValueString() + } + + resp.DataSourceData = c + resp.ResourceData = c +} + +func (p *petstoreProvider) Resources(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + NewPetResource, + } +} + +func (p *petstoreProvider) DataSources(_ context.Context) []func() datasource.DataSource { + return []func() datasource.DataSource{ + NewPetDataSource, + } +} diff --git a/samples/client/petstore/terraform-addpet/main.go b/samples/client/petstore/terraform-addpet/main.go new file mode 100644 index 000000000000..fe5e836d4041 --- /dev/null +++ b/samples/client/petstore/terraform-addpet/main.go @@ -0,0 +1,35 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package main + +import ( + "context" + "flag" + "log" + + "github.com/hashicorp/terraform-plugin-framework/providerserver" + + "github.com/example/terraform-provider-petstore-addpet/internal/provider" +) + +var ( + version string = "0.1.0" +) + +func main() { + var debug bool + + flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve") + flag.Parse() + + opts := providerserver.ServeOpts{ + Address: "registry.terraform.io/example/petstore-addpet", + Debug: debug, + } + + err := providerserver.Serve(context.Background(), provider.New(version), opts) + + if err != nil { + log.Fatal(err.Error()) + } +} diff --git a/samples/client/petstore/terraform-server/.gitignore b/samples/client/petstore/terraform-server/.gitignore new file mode 100644 index 000000000000..5cc9ba00d4f9 --- /dev/null +++ b/samples/client/petstore/terraform-server/.gitignore @@ -0,0 +1,13 @@ +*.dll +*.exe +*.exe~ +*.dylib +*.so +*.test +*.out +*.tfstate +*.tfstate.* +.terraform/ +*.tfvars +*.tfvars.json +terraform-provider-petstore diff --git a/samples/client/petstore/terraform-server/.openapi-generator-ignore b/samples/client/petstore/terraform-server/.openapi-generator-ignore new file mode 100644 index 000000000000..7484ee590a38 --- /dev/null +++ b/samples/client/petstore/terraform-server/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/samples/client/petstore/terraform-server/.openapi-generator/FILES b/samples/client/petstore/terraform-server/.openapi-generator/FILES new file mode 100644 index 000000000000..c087d63e2ade --- /dev/null +++ b/samples/client/petstore/terraform-server/.openapi-generator/FILES @@ -0,0 +1,33 @@ +.gitignore +GNUmakefile +README.md +examples/provider/provider.tf +go.mod +internal/client/client.go +internal/client/model_an_object.go +internal/client/model_api_response.go +internal/client/model_category.go +internal/client/model_colour.go +internal/client/model_gender.go +internal/client/model_order.go +internal/client/model_order_info.go +internal/client/model_pet.go +internal/client/model_special_info.go +internal/client/model_species.go +internal/client/model_tag.go +internal/client/model_user.go +internal/client/model_user_nullable.go +internal/provider/fake_data_source.go +internal/provider/fake_model.go +internal/provider/fake_resource.go +internal/provider/pet_data_source.go +internal/provider/pet_model.go +internal/provider/pet_resource.go +internal/provider/provider.go +internal/provider/store_data_source.go +internal/provider/store_model.go +internal/provider/store_resource.go +internal/provider/user_data_source.go +internal/provider/user_model.go +internal/provider/user_resource.go +main.go diff --git a/samples/client/petstore/terraform-server/.openapi-generator/VERSION b/samples/client/petstore/terraform-server/.openapi-generator/VERSION new file mode 100644 index 000000000000..193a12d6e891 --- /dev/null +++ b/samples/client/petstore/terraform-server/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.20.0-SNAPSHOT diff --git a/samples/client/petstore/terraform-server/GNUmakefile b/samples/client/petstore/terraform-server/GNUmakefile new file mode 100644 index 000000000000..322ade6623e2 --- /dev/null +++ b/samples/client/petstore/terraform-server/GNUmakefile @@ -0,0 +1,27 @@ +default: testacc + +# Run acceptance tests +.PHONY: testacc +testacc: + TF_ACC=1 go test ./... -v $(TESTARGS) -timeout 120m + +# Build provider +.PHONY: build +build: + go build -o terraform-provider-petstore + +# Install provider locally +.PHONY: install +install: build + mkdir -p ~/.terraform.d/plugins/registry.terraform.io/example/petstore-server/0.1.0/$(shell go env GOOS)_$(shell go env GOARCH) + mv terraform-provider-petstore ~/.terraform.d/plugins/registry.terraform.io/example/petstore-server/0.1.0/$(shell go env GOOS)_$(shell go env GOARCH)/ + +# Generate documentation +.PHONY: docs +docs: + go generate ./... + +# Run linter +.PHONY: lint +lint: + golangci-lint run ./... diff --git a/samples/client/petstore/terraform-server/README.md b/samples/client/petstore/terraform-server/README.md new file mode 100644 index 000000000000..eab0fd629c1e --- /dev/null +++ b/samples/client/petstore/terraform-server/README.md @@ -0,0 +1,61 @@ +# Terraform Provider petstore + +This Terraform provider was generated using [OpenAPI Generator](https://openapi-generator.tech). + +## Requirements + +- [Terraform](https://www.terraform.io/downloads.html) >= 1.0 +- [Go](https://golang.org/doc/install) >= 1.22 + +## Building The Provider + +1. Clone the repository +2. Enter the repository directory +3. Build the provider using the Go `install` command: + +```shell +go install +``` + +## Using the provider + +```hcl +terraform { + required_providers { + petstore = { + source = "registry.terraform.io/example/petstore-server" + } + } +} + +provider "petstore" { + endpoint = "http://petstore.swagger.io/v2" +} +``` + +## Developing the Provider + +If you wish to work on the provider, you'll first need [Go](http://www.golang.org) installed on your machine (see [Requirements](#requirements) above). + +To compile the provider, run `go install`. This will build the provider and put the provider binary in the `$GOPATH/bin` directory. + +For local development and testing, add a `dev_overrides` block to your `~/.terraformrc` so that Terraform uses the locally built binary instead of fetching from the registry: + +```hcl +provider_installation { + dev_overrides { + "registry.terraform.io/example/petstore-server" = "${GOPATH}/bin" + } + direct {} +} +``` + +With `dev_overrides` configured, you do not need to run `terraform init` for the provider. + +To generate or update documentation, run `go generate`. + +In order to run the full suite of Acceptance tests, run `make testacc`. + +```shell +make testacc +``` diff --git a/samples/client/petstore/terraform-server/examples/provider/provider.tf b/samples/client/petstore/terraform-server/examples/provider/provider.tf new file mode 100644 index 000000000000..4392153e6617 --- /dev/null +++ b/samples/client/petstore/terraform-server/examples/provider/provider.tf @@ -0,0 +1,13 @@ +terraform { + required_providers { + petstore = { + source = "registry.terraform.io/example/petstore-server" + } + } +} + +provider "petstore" { + endpoint = "http://petstore.swagger.io/v2" + # api_key = "your-api-key" + # token = "your-bearer-token" +} diff --git a/samples/client/petstore/terraform-server/go.mod b/samples/client/petstore/terraform-server/go.mod new file mode 100644 index 000000000000..c585d873bf38 --- /dev/null +++ b/samples/client/petstore/terraform-server/go.mod @@ -0,0 +1,11 @@ +module github.com/example/terraform-provider-petstore-server + +go 1.24.0 + +toolchain go1.24.4 + +require ( + github.com/hashicorp/terraform-plugin-framework v1.17.0 + github.com/hashicorp/terraform-plugin-log v0.10.0 + github.com/hashicorp/terraform-plugin-testing v1.14.0 +) diff --git a/samples/client/petstore/terraform-server/go.sum b/samples/client/petstore/terraform-server/go.sum new file mode 100644 index 000000000000..8888a9727e10 --- /dev/null +++ b/samples/client/petstore/terraform-server/go.sum @@ -0,0 +1,99 @@ +github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= +github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA= +github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/terraform-plugin-framework v1.17.0 h1:JdX50CFrYcYFY31gkmitAEAzLKoBgsK+iaJjDC8OexY= +github.com/hashicorp/terraform-plugin-framework v1.17.0/go.mod h1:4OUXKdHNosX+ys6rLgVlgklfxN3WHR5VHSOABeS/BM0= +github.com/hashicorp/terraform-plugin-go v0.29.0 h1:1nXKl/nSpaYIUBU1IG/EsDOX0vv+9JxAltQyDMpq5mU= +github.com/hashicorp/terraform-plugin-go v0.29.0/go.mod h1:vYZbIyvxyy0FWSmDHChCqKvI40cFTDGSb3D8D70i9GM= +github.com/hashicorp/terraform-plugin-log v0.10.0 h1:eu2kW6/QBVdN4P3Ju2WiB2W3ObjkAsyfBsL3Wh1fj3g= +github.com/hashicorp/terraform-plugin-log v0.10.0/go.mod h1:/9RR5Cv2aAbrqcTSdNmY1NRHP4E3ekrXRGjqORpXyB0= +github.com/hashicorp/terraform-registry-address v0.4.0 h1:S1yCGomj30Sao4l5BMPjTGZmCNzuv7/GDTDX99E9gTk= +github.com/hashicorp/terraform-registry-address v0.4.0/go.mod h1:LRS1Ay0+mAiRkUyltGT+UHWkIqTFvigGn/LbMshfflE= +github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= +github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc= +github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= +github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= +github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94= +github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= +go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= +go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= +go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= +go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= +go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= +go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI= +google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= +google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/samples/client/petstore/terraform-server/internal/client/client.go b/samples/client/petstore/terraform-server/internal/client/client.go new file mode 100644 index 000000000000..247e45a1c730 --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/client/client.go @@ -0,0 +1,78 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" +) + +// Client is the API client used to interact with the API. +type Client struct { + BaseURL string + HTTPClient *http.Client + ApiKey string + Token string + Username string + Password string +} + +// NewClient creates a new API client with the given base URL. +func NewClient(baseURL string) *Client { + return &Client{ + BaseURL: baseURL, + HTTPClient: &http.Client{}, + } +} + +// DoRequest executes an HTTP request and returns the response body. +func (c *Client) DoRequest(ctx context.Context, method, path string, body interface{}) ([]byte, error) { + var reqBody io.Reader + if body != nil { + jsonBody, err := json.Marshal(body) + if err != nil { + return nil, fmt.Errorf("error marshaling request body: %w", err) + } + reqBody = bytes.NewBuffer(jsonBody) + } + + req, err := http.NewRequestWithContext(ctx, method, c.BaseURL+path, reqBody) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + + if body != nil { + req.Header.Set("Content-Type", "application/json") + } + req.Header.Set("Accept", "application/json") + + // Authentication + if c.Token != "" { + req.Header.Set("Authorization", "Bearer "+c.Token) + } else if c.ApiKey != "" { + req.Header.Set("Authorization", c.ApiKey) + } else if c.Username != "" { + req.SetBasicAuth(c.Username, c.Password) + } + + resp, err := c.HTTPClient.Do(req) + if err != nil { + return nil, fmt.Errorf("error making request: %w", err) + } + defer resp.Body.Close() + + respBody, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(respBody)) + } + + return respBody, nil +} diff --git a/samples/client/petstore/terraform-server/internal/client/model_an_object.go b/samples/client/petstore/terraform-server/internal/client/model_an_object.go new file mode 100644 index 000000000000..96b9497536f4 --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/client/model_an_object.go @@ -0,0 +1,9 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// AnObject - An array 3-deep. +type AnObject struct { + Tag Tag `json:"tag,omitempty"` + Pet []Pet `json:"Pet,omitempty"` +} diff --git a/samples/client/petstore/terraform-server/internal/client/model_api_response.go b/samples/client/petstore/terraform-server/internal/client/model_api_response.go new file mode 100644 index 000000000000..8512ff6a6f5c --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/client/model_api_response.go @@ -0,0 +1,10 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// ApiResponse - Describes the result of uploading an image resource +type ApiResponse struct { + Code int32 `json:"code,omitempty"` + Type string `json:"type,omitempty"` + Message string `json:"message,omitempty"` +} diff --git a/samples/client/petstore/terraform-server/internal/client/model_category.go b/samples/client/petstore/terraform-server/internal/client/model_category.go new file mode 100644 index 000000000000..1813ce260524 --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/client/model_category.go @@ -0,0 +1,9 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// Category - A category for a pet +type Category struct { + Id int64 `json:"id,omitempty"` + Name string `json:"name,omitempty" validate:"regexp=^[a-zA-Z0-9]+[a-zA-Z0-9\\\\.\\\\-_]*[a-zA-Z0-9]+$"` +} diff --git a/samples/client/petstore/terraform-server/internal/client/model_colour.go b/samples/client/petstore/terraform-server/internal/client/model_colour.go new file mode 100644 index 000000000000..7a6d050fb7f3 --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/client/model_colour.go @@ -0,0 +1,7 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// Colour - Colour struct +type Colour struct { +} diff --git a/samples/client/petstore/terraform-server/internal/client/model_gender.go b/samples/client/petstore/terraform-server/internal/client/model_gender.go new file mode 100644 index 000000000000..03f368de9425 --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/client/model_gender.go @@ -0,0 +1,7 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// Gender - Gender struct +type Gender struct { +} diff --git a/samples/client/petstore/terraform-server/internal/client/model_order.go b/samples/client/petstore/terraform-server/internal/client/model_order.go new file mode 100644 index 000000000000..c22eaa085bbd --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/client/model_order.go @@ -0,0 +1,14 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// Order - Order struct +type Order struct { + PetId int64 `json:"petId,omitempty"` + Quantity int32 `json:"quantity,omitempty"` + ShipDate string `json:"shipDate,omitempty"` + Id int64 `json:"id,omitempty"` + Status string `json:"status,omitempty"` + Complete bool `json:"complete,omitempty"` + Comment string `json:"comment"` +} diff --git a/samples/client/petstore/terraform-server/internal/client/model_order_info.go b/samples/client/petstore/terraform-server/internal/client/model_order_info.go new file mode 100644 index 000000000000..143a569a623e --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/client/model_order_info.go @@ -0,0 +1,10 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// OrderInfo - An order info for a pets from the pet store +type OrderInfo struct { + PetId int64 `json:"petId,omitempty"` + Quantity int32 `json:"quantity,omitempty"` + ShipDate string `json:"shipDate,omitempty"` +} diff --git a/samples/client/petstore/terraform-server/internal/client/model_pet.go b/samples/client/petstore/terraform-server/internal/client/model_pet.go new file mode 100644 index 000000000000..96e95cec74c7 --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/client/model_pet.go @@ -0,0 +1,13 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// Pet - A pet for sale in the pet store +type Pet struct { + Id int64 `json:"id,omitempty"` + Category Category `json:"category,omitempty"` + Name string `json:"name"` + PhotoUrls []string `json:"photoUrls"` + Tags []Tag `json:"tags,omitempty"` + Status string `json:"status,omitempty"` +} diff --git a/samples/client/petstore/terraform-server/internal/client/model_special_info.go b/samples/client/petstore/terraform-server/internal/client/model_special_info.go new file mode 100644 index 000000000000..a1f3cb62bef3 --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/client/model_special_info.go @@ -0,0 +1,9 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// SpecialInfo - An order info for a pets from the pet store +type SpecialInfo struct { + Promotion bool `json:"promotion,omitempty"` + Type string `json:"type,omitempty"` +} diff --git a/samples/client/petstore/terraform-server/internal/client/model_species.go b/samples/client/petstore/terraform-server/internal/client/model_species.go new file mode 100644 index 000000000000..eab306cbe57d --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/client/model_species.go @@ -0,0 +1,7 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// Species - Species struct +type Species struct { +} diff --git a/samples/client/petstore/terraform-server/internal/client/model_tag.go b/samples/client/petstore/terraform-server/internal/client/model_tag.go new file mode 100644 index 000000000000..6e81eb958ae7 --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/client/model_tag.go @@ -0,0 +1,9 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// Tag - A tag for a pet +type Tag struct { + Id int64 `json:"id,omitempty"` + Name string `json:"name,omitempty"` +} diff --git a/samples/client/petstore/terraform-server/internal/client/model_user.go b/samples/client/petstore/terraform-server/internal/client/model_user.go new file mode 100644 index 000000000000..431bd78fb915 --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/client/model_user.go @@ -0,0 +1,17 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// User - A User who is purchasing from the pet store +type User struct { + Id int64 `json:"id,omitempty"` + Username string `json:"username,omitempty"` + FirstName string `json:"firstName,omitempty"` + LastName string `json:"lastName,omitempty"` + Email string `json:"email,omitempty"` + Password string `json:"password,omitempty"` + Phone string `json:"phone,omitempty"` + UserStatus int32 `json:"userStatus,omitempty"` + DeepSliceModel [][][]Tag `json:"deepSliceModel"` + DeepSliceMap [][]AnObject `json:"deepSliceMap,omitempty"` +} diff --git a/samples/client/petstore/terraform-server/internal/client/model_user_nullable.go b/samples/client/petstore/terraform-server/internal/client/model_user_nullable.go new file mode 100644 index 000000000000..2297a6b3016c --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/client/model_user_nullable.go @@ -0,0 +1,17 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// UserNullable - A User who is purchasing from the pet store +type UserNullable struct { + Id int64 `json:"id,omitempty"` + Username string `json:"username,omitempty"` + FirstName string `json:"firstName,omitempty"` + LastName string `json:"lastName,omitempty"` + Email string `json:"email,omitempty"` + Password string `json:"password,omitempty"` + Phone string `json:"phone,omitempty"` + UserStatus int32 `json:"userStatus,omitempty"` + DeepSliceModel [][][]Tag `json:"deepSliceModel"` + DeepSliceMap [][]AnObject `json:"deepSliceMap,omitempty"` +} diff --git a/samples/client/petstore/terraform-server/internal/provider/fake_data_source.go b/samples/client/petstore/terraform-server/internal/provider/fake_data_source.go new file mode 100644 index 000000000000..c08c8317f8d8 --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/provider/fake_data_source.go @@ -0,0 +1,55 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. +package provider + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + + "github.com/example/terraform-provider-petstore-server/internal/client" +) + +var _ datasource.DataSource = &FakeDataSource{} + +func NewFakeDataSource() datasource.DataSource { + return &FakeDataSource{} +} + +type FakeDataSource struct { + client *client.Client +} + +func (d *FakeDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_fake" +} + +func (d *FakeDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Fetches a fake data source.", + Attributes: map[string]schema.Attribute{ + }, + } +} + +func (d *FakeDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + c, ok := req.ProviderData.(*client.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *client.Client, got: %T.", req.ProviderData), + ) + return + } + + d.client = c +} + +func (d *FakeDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + resp.Diagnostics.AddError("Not Supported", "Read is not supported for fake") +} diff --git a/samples/client/petstore/terraform-server/internal/provider/fake_model.go b/samples/client/petstore/terraform-server/internal/provider/fake_model.go new file mode 100644 index 000000000000..1f70b769304a --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/provider/fake_model.go @@ -0,0 +1,8 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. +package provider + + +// FakeModel is the Terraform model for fake. +type FakeModel struct { +} + diff --git a/samples/client/petstore/terraform-server/internal/provider/fake_resource.go b/samples/client/petstore/terraform-server/internal/provider/fake_resource.go new file mode 100644 index 000000000000..a9791e3c5d06 --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/provider/fake_resource.go @@ -0,0 +1,83 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. +package provider + +import ( + "context" + "fmt" + + + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + + + + "github.com/example/terraform-provider-petstore-server/internal/client" +) + +var _ resource.Resource = &FakeResource{} +var _ resource.ResourceWithImportState = &FakeResource{} + +func NewFakeResource() resource.Resource { + return &FakeResource{} +} + +type FakeResource struct { + client *client.Client +} + +func (r *FakeResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_fake" +} + +func (r *FakeResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Manages a fake resource.", + Attributes: map[string]schema.Attribute{ + }, + } +} + +func (r *FakeResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + c, ok := req.ProviderData.(*client.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *client.Client, got: %T.", req.ProviderData), + ) + return + } + + r.client = c +} + +func (r *FakeResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + resp.Diagnostics.AddError("Not Supported", "Create is not supported for fake") +} + +func (r *FakeResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + // No read endpoint available; keep existing state as-is. +} + +func (r *FakeResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + // No update endpoint available; persist the planned values into state. + var plan FakeModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *FakeResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // No delete endpoint available; remove the resource from state. +} + +func (r *FakeResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} diff --git a/samples/client/petstore/terraform-server/internal/provider/pet_data_source.go b/samples/client/petstore/terraform-server/internal/provider/pet_data_source.go new file mode 100644 index 000000000000..17be3904991b --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/provider/pet_data_source.go @@ -0,0 +1,103 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. +package provider + +import ( + "context" + "fmt" + "encoding/json" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-log/tflog" + + "github.com/example/terraform-provider-petstore-server/internal/client" +) + +var _ datasource.DataSource = &PetDataSource{} + +func NewPetDataSource() datasource.DataSource { + return &PetDataSource{} +} + +type PetDataSource struct { + client *client.Client +} + +func (d *PetDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_pet" +} + +func (d *PetDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Fetches a pet data source.", + Attributes: map[string]schema.Attribute{ + "id": schema.Int64Attribute{ + Computed: true, + Description: "", + }, + "category": schema.StringAttribute{ + Computed: true, + Description: "", + }, + "name": schema.StringAttribute{ + Required: true, + Description: "", + }, + "photo_urls": schema.StringAttribute{ + Required: true, + Description: "", + }, + "tags": schema.StringAttribute{ + Computed: true, + Description: "", + }, + "status": schema.StringAttribute{ + Computed: true, + Description: "pet status in the store", + }, + }, + } +} + +func (d *PetDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + c, ok := req.ProviderData.(*client.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *client.Client, got: %T.", req.ProviderData), + ) + return + } + + d.client = c +} + +func (d *PetDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var config PetModel + + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + + respBody, err := d.client.DoRequest(ctx, "GET", fmt.Sprintf("/pet/%v", config.Id.ValueInt64()), nil) + if err != nil { + resp.Diagnostics.AddError("Error reading pet", err.Error()) + return + } + + var result client.Pet + if err := json.Unmarshal(respBody, &result); err != nil { + resp.Diagnostics.AddError("Error parsing response", err.Error()) + return + } + + config.FromClientModel(&result) + + tflog.Trace(ctx, "read pet data source") + resp.Diagnostics.Append(resp.State.Set(ctx, &config)...) +} diff --git a/samples/client/petstore/terraform-server/internal/provider/pet_model.go b/samples/client/petstore/terraform-server/internal/provider/pet_model.go new file mode 100644 index 000000000000..e9b082143fbb --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/provider/pet_model.go @@ -0,0 +1,40 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. +package provider + +import ( + "github.com/hashicorp/terraform-plugin-framework/types" + + "github.com/example/terraform-provider-petstore-server/internal/client" +) + +// PetModel is the Terraform model for pet. +type PetModel struct { + Id types.Int64 `tfsdk:"id"` + Category types.String `tfsdk:"category"` + Name types.String `tfsdk:"name"` + PhotoUrls types.List `tfsdk:"photo_urls"` + Tags types.List `tfsdk:"tags"` + Status types.String `tfsdk:"status"` +} + +// ToClientModel converts a Terraform model to a client model. +func (m *PetModel) ToClientModel() *client.Pet { + out := &client.Pet{} + if !m.Id.IsNull() && !m.Id.IsUnknown() { + out.Id = int64(m.Id.ValueInt64()) + } + if !m.Name.IsNull() && !m.Name.IsUnknown() { + out.Name = m.Name.ValueString() + } + if !m.Status.IsNull() && !m.Status.IsUnknown() { + out.Status = m.Status.ValueString() + } + return out +} + +// FromClientModel updates the Terraform model from a client model. +func (m *PetModel) FromClientModel(c *client.Pet) { + m.Id = types.Int64Value(int64(c.Id)) + m.Name = types.StringValue(c.Name) + m.Status = types.StringValue(c.Status) +} diff --git a/samples/client/petstore/terraform-server/internal/provider/pet_resource.go b/samples/client/petstore/terraform-server/internal/provider/pet_resource.go new file mode 100644 index 000000000000..d4915dd1c155 --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/provider/pet_resource.go @@ -0,0 +1,170 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. +package provider + +import ( + "context" + "fmt" + "encoding/json" + + + + "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/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + + + + "github.com/example/terraform-provider-petstore-server/internal/client" +) + +var _ resource.Resource = &PetResource{} +var _ resource.ResourceWithImportState = &PetResource{} + +func NewPetResource() resource.Resource { + return &PetResource{} +} + +type PetResource struct { + client *client.Client +} + +func (r *PetResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_pet" +} + +func (r *PetResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Manages a pet resource.", + Attributes: map[string]schema.Attribute{ + "id": schema.Int64Attribute{ + Optional: true, + Description: "", + }, + "category": schema.StringAttribute{ + Optional: true, + Description: "", + }, + "name": schema.StringAttribute{ + Required: true, + Description: "", + }, + "photo_urls": schema.ListAttribute{ + Required: true, + ElementType: types.StringType, + Description: "", + }, + "tags": schema.ListAttribute{ + Optional: true, + ElementType: types.StringType, + Description: "", + }, + "status": schema.StringAttribute{ + Optional: true, + Description: "pet status in the store", + }, + }, + } +} + +func (r *PetResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + c, ok := req.ProviderData.(*client.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *client.Client, got: %T.", req.ProviderData), + ) + return + } + + r.client = c +} + +func (r *PetResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan PetModel + + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + reqBody := plan.ToClientModel() + + respBody, err := r.client.DoRequest(ctx, "POST", "/pet", reqBody) + if err != nil { + resp.Diagnostics.AddError("Error creating pet", err.Error()) + return + } + + var result client.Pet + if err := json.Unmarshal(respBody, &result); err != nil { + resp.Diagnostics.AddError("Error parsing response", err.Error()) + return + } + + plan.FromClientModel(&result) + + tflog.Trace(ctx, "created pet resource") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *PetResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state PetModel + + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + respBody, err := r.client.DoRequest(ctx, "GET", fmt.Sprintf("/pet/%v", state.Id.ValueInt64()), nil) + if err != nil { + resp.Diagnostics.AddError("Error reading pet", err.Error()) + return + } + + var result client.Pet + if err := json.Unmarshal(respBody, &result); err != nil { + resp.Diagnostics.AddError("Error parsing response", err.Error()) + return + } + + state.FromClientModel(&result) + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *PetResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + // No update endpoint available; persist the planned values into state. + var plan PetModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *PetResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state PetModel + + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + _, err := r.client.DoRequest(ctx, "DELETE", fmt.Sprintf("/pet/%v", state.Id.ValueInt64()), nil) + if err != nil { + resp.Diagnostics.AddError("Error deleting pet", err.Error()) + return + } + + tflog.Trace(ctx, "deleted pet resource") +} + +func (r *PetResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} diff --git a/samples/client/petstore/terraform-server/internal/provider/provider.go b/samples/client/petstore/terraform-server/internal/provider/provider.go new file mode 100644 index 000000000000..c84fed6023d3 --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/provider/provider.go @@ -0,0 +1,129 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package provider + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/provider" + "github.com/hashicorp/terraform-plugin-framework/provider/schema" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/types" + + "github.com/example/terraform-provider-petstore-server/internal/client" +) + +var _ provider.Provider = &petstoreProvider{} + +type petstoreProvider struct { + version string +} + +type petstoreProviderModel struct { + Endpoint types.String `tfsdk:"endpoint"` + ApiKey types.String `tfsdk:"api_key"` + Token types.String `tfsdk:"token"` + Username types.String `tfsdk:"username"` + Password types.String `tfsdk:"password"` +} + +func New(version string) func() provider.Provider { + return func() provider.Provider { + return &petstoreProvider{ + version: version, + } + } +} + +func (p *petstoreProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) { + resp.TypeName = "petstore" + resp.Version = p.version +} + +func (p *petstoreProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "endpoint": schema.StringAttribute{ + Optional: true, + Description: "The API endpoint URL.", + }, + "api_key": schema.StringAttribute{ + Optional: true, + Sensitive: true, + Description: "The API key for authentication.", + }, + "token": schema.StringAttribute{ + Optional: true, + Sensitive: true, + Description: "The bearer token for authentication.", + }, + "username": schema.StringAttribute{ + Optional: true, + Description: "The username for basic authentication.", + }, + "password": schema.StringAttribute{ + Optional: true, + Sensitive: true, + Description: "The password for basic authentication.", + }, + }, + } +} + +func (p *petstoreProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { + var config petstoreProviderModel + + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + + if config.Endpoint.IsUnknown() { + resp.Diagnostics.AddWarning( + "Unknown Endpoint Value", + "The provider endpoint value is not yet known. Defaulting to the spec-defined base path.", + ) + } + + endpoint := "http://petstore.swagger.io/v2" + if !config.Endpoint.IsNull() && !config.Endpoint.IsUnknown() { + endpoint = config.Endpoint.ValueString() + } + + c := client.NewClient(endpoint) + + if !config.ApiKey.IsNull() { + c.ApiKey = config.ApiKey.ValueString() + } + if !config.Token.IsNull() { + c.Token = config.Token.ValueString() + } + if !config.Username.IsNull() { + c.Username = config.Username.ValueString() + } + if !config.Password.IsNull() { + c.Password = config.Password.ValueString() + } + + resp.DataSourceData = c + resp.ResourceData = c +} + +func (p *petstoreProvider) Resources(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + NewFakeResource, + NewPetResource, + NewStoreResource, + NewUserResource, + } +} + +func (p *petstoreProvider) DataSources(_ context.Context) []func() datasource.DataSource { + return []func() datasource.DataSource{ + NewFakeDataSource, + NewPetDataSource, + NewStoreDataSource, + NewUserDataSource, + } +} diff --git a/samples/client/petstore/terraform-server/internal/provider/store_data_source.go b/samples/client/petstore/terraform-server/internal/provider/store_data_source.go new file mode 100644 index 000000000000..f22960d1a923 --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/provider/store_data_source.go @@ -0,0 +1,55 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. +package provider + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + + "github.com/example/terraform-provider-petstore-server/internal/client" +) + +var _ datasource.DataSource = &StoreDataSource{} + +func NewStoreDataSource() datasource.DataSource { + return &StoreDataSource{} +} + +type StoreDataSource struct { + client *client.Client +} + +func (d *StoreDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_store" +} + +func (d *StoreDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Fetches a store data source.", + Attributes: map[string]schema.Attribute{ + }, + } +} + +func (d *StoreDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + c, ok := req.ProviderData.(*client.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *client.Client, got: %T.", req.ProviderData), + ) + return + } + + d.client = c +} + +func (d *StoreDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + resp.Diagnostics.AddError("Not Supported", "Read is not supported for store") +} diff --git a/samples/client/petstore/terraform-server/internal/provider/store_model.go b/samples/client/petstore/terraform-server/internal/provider/store_model.go new file mode 100644 index 000000000000..40435f32d9e9 --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/provider/store_model.go @@ -0,0 +1,8 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. +package provider + + +// StoreModel is the Terraform model for store. +type StoreModel struct { +} + diff --git a/samples/client/petstore/terraform-server/internal/provider/store_resource.go b/samples/client/petstore/terraform-server/internal/provider/store_resource.go new file mode 100644 index 000000000000..7feca31b171e --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/provider/store_resource.go @@ -0,0 +1,83 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. +package provider + +import ( + "context" + "fmt" + + + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + + + + "github.com/example/terraform-provider-petstore-server/internal/client" +) + +var _ resource.Resource = &StoreResource{} +var _ resource.ResourceWithImportState = &StoreResource{} + +func NewStoreResource() resource.Resource { + return &StoreResource{} +} + +type StoreResource struct { + client *client.Client +} + +func (r *StoreResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_store" +} + +func (r *StoreResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Manages a store resource.", + Attributes: map[string]schema.Attribute{ + }, + } +} + +func (r *StoreResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + c, ok := req.ProviderData.(*client.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *client.Client, got: %T.", req.ProviderData), + ) + return + } + + r.client = c +} + +func (r *StoreResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + resp.Diagnostics.AddError("Not Supported", "Create is not supported for store") +} + +func (r *StoreResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + // No read endpoint available; keep existing state as-is. +} + +func (r *StoreResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + // No update endpoint available; persist the planned values into state. + var plan StoreModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *StoreResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // No delete endpoint available; remove the resource from state. +} + +func (r *StoreResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} diff --git a/samples/client/petstore/terraform-server/internal/provider/user_data_source.go b/samples/client/petstore/terraform-server/internal/provider/user_data_source.go new file mode 100644 index 000000000000..af5018d55da9 --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/provider/user_data_source.go @@ -0,0 +1,119 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. +package provider + +import ( + "context" + "fmt" + "encoding/json" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-log/tflog" + + "github.com/example/terraform-provider-petstore-server/internal/client" +) + +var _ datasource.DataSource = &UserDataSource{} + +func NewUserDataSource() datasource.DataSource { + return &UserDataSource{} +} + +type UserDataSource struct { + client *client.Client +} + +func (d *UserDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_user" +} + +func (d *UserDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Fetches a user data source.", + Attributes: map[string]schema.Attribute{ + "id": schema.Int64Attribute{ + Computed: true, + Description: "", + }, + "username": schema.StringAttribute{ + Computed: true, + Description: "", + }, + "first_name": schema.StringAttribute{ + Computed: true, + Description: "", + }, + "last_name": schema.StringAttribute{ + Computed: true, + Description: "", + }, + "email": schema.StringAttribute{ + Computed: true, + Description: "", + }, + "password": schema.StringAttribute{ + Computed: true, + Description: "", + }, + "phone": schema.StringAttribute{ + Computed: true, + Description: "", + }, + "user_status": schema.Int64Attribute{ + Computed: true, + Description: "User Status", + }, + "deep_slice_model": schema.StringAttribute{ + Required: true, + Description: "An array 1-deep.", + }, + "deep_slice_map": schema.StringAttribute{ + Computed: true, + Description: "An array 1-deep.", + }, + }, + } +} + +func (d *UserDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + c, ok := req.ProviderData.(*client.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *client.Client, got: %T.", req.ProviderData), + ) + return + } + + d.client = c +} + +func (d *UserDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var config UserModel + + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + + respBody, err := d.client.DoRequest(ctx, "GET", fmt.Sprintf("/user/%v", config.Username.ValueString()), nil) + if err != nil { + resp.Diagnostics.AddError("Error reading user", err.Error()) + return + } + + var result client.User + if err := json.Unmarshal(respBody, &result); err != nil { + resp.Diagnostics.AddError("Error parsing response", err.Error()) + return + } + + config.FromClientModel(&result) + + tflog.Trace(ctx, "read user data source") + resp.Diagnostics.Append(resp.State.Set(ctx, &config)...) +} diff --git a/samples/client/petstore/terraform-server/internal/provider/user_model.go b/samples/client/petstore/terraform-server/internal/provider/user_model.go new file mode 100644 index 000000000000..3ec8303758f8 --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/provider/user_model.go @@ -0,0 +1,64 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. +package provider + +import ( + "github.com/hashicorp/terraform-plugin-framework/types" + + "github.com/example/terraform-provider-petstore-server/internal/client" +) + +// UserModel is the Terraform model for user. +type UserModel struct { + Id types.Int64 `tfsdk:"id"` + Username types.String `tfsdk:"username"` + FirstName types.String `tfsdk:"first_name"` + LastName types.String `tfsdk:"last_name"` + Email types.String `tfsdk:"email"` + Password types.String `tfsdk:"password"` + Phone types.String `tfsdk:"phone"` + UserStatus types.Int64 `tfsdk:"user_status"` + DeepSliceModel types.List `tfsdk:"deep_slice_model"` + DeepSliceMap types.List `tfsdk:"deep_slice_map"` +} + +// ToClientModel converts a Terraform model to a client model. +func (m *UserModel) ToClientModel() *client.User { + out := &client.User{} + if !m.Id.IsNull() && !m.Id.IsUnknown() { + out.Id = int64(m.Id.ValueInt64()) + } + if !m.Username.IsNull() && !m.Username.IsUnknown() { + out.Username = m.Username.ValueString() + } + if !m.FirstName.IsNull() && !m.FirstName.IsUnknown() { + out.FirstName = m.FirstName.ValueString() + } + if !m.LastName.IsNull() && !m.LastName.IsUnknown() { + out.LastName = m.LastName.ValueString() + } + if !m.Email.IsNull() && !m.Email.IsUnknown() { + out.Email = m.Email.ValueString() + } + if !m.Password.IsNull() && !m.Password.IsUnknown() { + out.Password = m.Password.ValueString() + } + if !m.Phone.IsNull() && !m.Phone.IsUnknown() { + out.Phone = m.Phone.ValueString() + } + if !m.UserStatus.IsNull() && !m.UserStatus.IsUnknown() { + out.UserStatus = int32(m.UserStatus.ValueInt64()) + } + return out +} + +// FromClientModel updates the Terraform model from a client model. +func (m *UserModel) FromClientModel(c *client.User) { + m.Id = types.Int64Value(int64(c.Id)) + m.Username = types.StringValue(c.Username) + m.FirstName = types.StringValue(c.FirstName) + m.LastName = types.StringValue(c.LastName) + m.Email = types.StringValue(c.Email) + m.Password = types.StringValue(c.Password) + m.Phone = types.StringValue(c.Phone) + m.UserStatus = types.Int64Value(int64(c.UserStatus)) +} diff --git a/samples/client/petstore/terraform-server/internal/provider/user_resource.go b/samples/client/petstore/terraform-server/internal/provider/user_resource.go new file mode 100644 index 000000000000..0b2a8340dbab --- /dev/null +++ b/samples/client/petstore/terraform-server/internal/provider/user_resource.go @@ -0,0 +1,204 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. +package provider + +import ( + "context" + "fmt" + "encoding/json" + + + + "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/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + + + + "github.com/example/terraform-provider-petstore-server/internal/client" +) + +var _ resource.Resource = &UserResource{} +var _ resource.ResourceWithImportState = &UserResource{} + +func NewUserResource() resource.Resource { + return &UserResource{} +} + +type UserResource struct { + client *client.Client +} + +func (r *UserResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_user" +} + +func (r *UserResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Manages a user resource.", + Attributes: map[string]schema.Attribute{ + "id": schema.Int64Attribute{ + Optional: true, + Description: "", + }, + "username": schema.StringAttribute{ + Optional: true, + Description: "", + }, + "first_name": schema.StringAttribute{ + Optional: true, + Description: "", + }, + "last_name": schema.StringAttribute{ + Optional: true, + Description: "", + }, + "email": schema.StringAttribute{ + Optional: true, + Description: "", + }, + "password": schema.StringAttribute{ + Optional: true, + Description: "", + }, + "phone": schema.StringAttribute{ + Optional: true, + Description: "", + }, + "user_status": schema.Int64Attribute{ + Optional: true, + Description: "User Status", + }, + "deep_slice_model": schema.ListAttribute{ + Required: true, + ElementType: types.StringType, + Description: "An array 1-deep.", + }, + "deep_slice_map": schema.ListAttribute{ + Optional: true, + ElementType: types.StringType, + Description: "An array 1-deep.", + }, + }, + } +} + +func (r *UserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + c, ok := req.ProviderData.(*client.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *client.Client, got: %T.", req.ProviderData), + ) + return + } + + r.client = c +} + +func (r *UserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan UserModel + + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + reqBody := plan.ToClientModel() + + respBody, err := r.client.DoRequest(ctx, "POST", "/user", reqBody) + if err != nil { + resp.Diagnostics.AddError("Error creating user", err.Error()) + return + } + + var result client.User + if err := json.Unmarshal(respBody, &result); err != nil { + resp.Diagnostics.AddError("Error parsing response", err.Error()) + return + } + + plan.FromClientModel(&result) + + tflog.Trace(ctx, "created user resource") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *UserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state UserModel + + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + respBody, err := r.client.DoRequest(ctx, "GET", fmt.Sprintf("/user/%v", state.Username.ValueString()), nil) + if err != nil { + resp.Diagnostics.AddError("Error reading user", err.Error()) + return + } + + var result client.User + if err := json.Unmarshal(respBody, &result); err != nil { + resp.Diagnostics.AddError("Error parsing response", err.Error()) + return + } + + state.FromClientModel(&result) + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *UserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan UserModel + + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + reqBody := plan.ToClientModel() + + respBody, err := r.client.DoRequest(ctx, "PUT", fmt.Sprintf("/user/%v", plan.Username.ValueString()), reqBody) + if err != nil { + resp.Diagnostics.AddError("Error updating user", err.Error()) + return + } + + var result client.User + if err := json.Unmarshal(respBody, &result); err != nil { + resp.Diagnostics.AddError("Error parsing response", err.Error()) + return + } + + plan.FromClientModel(&result) + + tflog.Trace(ctx, "updated user resource") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *UserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state UserModel + + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + _, err := r.client.DoRequest(ctx, "DELETE", fmt.Sprintf("/user/%v", state.Username.ValueString()), nil) + if err != nil { + resp.Diagnostics.AddError("Error deleting user", err.Error()) + return + } + + tflog.Trace(ctx, "deleted user resource") +} + +func (r *UserResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("username"), req, resp) +} diff --git a/samples/client/petstore/terraform-server/main.go b/samples/client/petstore/terraform-server/main.go new file mode 100644 index 000000000000..11ae54929bef --- /dev/null +++ b/samples/client/petstore/terraform-server/main.go @@ -0,0 +1,35 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package main + +import ( + "context" + "flag" + "log" + + "github.com/hashicorp/terraform-plugin-framework/providerserver" + + "github.com/example/terraform-provider-petstore-server/internal/provider" +) + +var ( + version string = "0.1.0" +) + +func main() { + var debug bool + + flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve") + flag.Parse() + + opts := providerserver.ServeOpts{ + Address: "registry.terraform.io/example/petstore-server", + Debug: debug, + } + + err := providerserver.Serve(context.Background(), provider.New(version), opts) + + if err != nil { + log.Fatal(err.Error()) + } +} diff --git a/samples/client/petstore/terraform/.gitignore b/samples/client/petstore/terraform/.gitignore new file mode 100644 index 000000000000..5cc9ba00d4f9 --- /dev/null +++ b/samples/client/petstore/terraform/.gitignore @@ -0,0 +1,13 @@ +*.dll +*.exe +*.exe~ +*.dylib +*.so +*.test +*.out +*.tfstate +*.tfstate.* +.terraform/ +*.tfvars +*.tfvars.json +terraform-provider-petstore diff --git a/samples/client/petstore/terraform/.openapi-generator-ignore b/samples/client/petstore/terraform/.openapi-generator-ignore new file mode 100644 index 000000000000..7484ee590a38 --- /dev/null +++ b/samples/client/petstore/terraform/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/samples/client/petstore/terraform/.openapi-generator/FILES b/samples/client/petstore/terraform/.openapi-generator/FILES new file mode 100644 index 000000000000..219ca57a71ef --- /dev/null +++ b/samples/client/petstore/terraform/.openapi-generator/FILES @@ -0,0 +1,23 @@ +.gitignore +GNUmakefile +README.md +examples/provider/provider.tf +go.mod +internal/client/client.go +internal/client/model_api_response.go +internal/client/model_category.go +internal/client/model_order.go +internal/client/model_pet.go +internal/client/model_tag.go +internal/client/model_user.go +internal/provider/pet_data_source.go +internal/provider/pet_model.go +internal/provider/pet_resource.go +internal/provider/provider.go +internal/provider/store_data_source.go +internal/provider/store_model.go +internal/provider/store_resource.go +internal/provider/user_data_source.go +internal/provider/user_model.go +internal/provider/user_resource.go +main.go diff --git a/samples/client/petstore/terraform/.openapi-generator/VERSION b/samples/client/petstore/terraform/.openapi-generator/VERSION new file mode 100644 index 000000000000..193a12d6e891 --- /dev/null +++ b/samples/client/petstore/terraform/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.20.0-SNAPSHOT diff --git a/samples/client/petstore/terraform/GNUmakefile b/samples/client/petstore/terraform/GNUmakefile new file mode 100644 index 000000000000..685e3c9b2449 --- /dev/null +++ b/samples/client/petstore/terraform/GNUmakefile @@ -0,0 +1,27 @@ +default: testacc + +# Run acceptance tests +.PHONY: testacc +testacc: + TF_ACC=1 go test ./... -v $(TESTARGS) -timeout 120m + +# Build provider +.PHONY: build +build: + go build -o terraform-provider-petstore + +# Install provider locally +.PHONY: install +install: build + mkdir -p ~/.terraform.d/plugins/registry.terraform.io/example/petstore/0.1.0/$(shell go env GOOS)_$(shell go env GOARCH) + mv terraform-provider-petstore ~/.terraform.d/plugins/registry.terraform.io/example/petstore/0.1.0/$(shell go env GOOS)_$(shell go env GOARCH)/ + +# Generate documentation +.PHONY: docs +docs: + go generate ./... + +# Run linter +.PHONY: lint +lint: + golangci-lint run ./... diff --git a/samples/client/petstore/terraform/README.md b/samples/client/petstore/terraform/README.md new file mode 100644 index 000000000000..27ed9be64720 --- /dev/null +++ b/samples/client/petstore/terraform/README.md @@ -0,0 +1,61 @@ +# Terraform Provider petstore + +This Terraform provider was generated using [OpenAPI Generator](https://openapi-generator.tech). + +## Requirements + +- [Terraform](https://www.terraform.io/downloads.html) >= 1.0 +- [Go](https://golang.org/doc/install) >= 1.22 + +## Building The Provider + +1. Clone the repository +2. Enter the repository directory +3. Build the provider using the Go `install` command: + +```shell +go install +``` + +## Using the provider + +```hcl +terraform { + required_providers { + petstore = { + source = "registry.terraform.io/example/petstore" + } + } +} + +provider "petstore" { + endpoint = "http://petstore.swagger.io/v2" +} +``` + +## Developing the Provider + +If you wish to work on the provider, you'll first need [Go](http://www.golang.org) installed on your machine (see [Requirements](#requirements) above). + +To compile the provider, run `go install`. This will build the provider and put the provider binary in the `$GOPATH/bin` directory. + +For local development and testing, add a `dev_overrides` block to your `~/.terraformrc` so that Terraform uses the locally built binary instead of fetching from the registry: + +```hcl +provider_installation { + dev_overrides { + "registry.terraform.io/example/petstore" = "${GOPATH}/bin" + } + direct {} +} +``` + +With `dev_overrides` configured, you do not need to run `terraform init` for the provider. + +To generate or update documentation, run `go generate`. + +In order to run the full suite of Acceptance tests, run `make testacc`. + +```shell +make testacc +``` diff --git a/samples/client/petstore/terraform/examples/data-sources/pet/data-source.tf b/samples/client/petstore/terraform/examples/data-sources/pet/data-source.tf new file mode 100644 index 000000000000..dfcbdb90bcb0 --- /dev/null +++ b/samples/client/petstore/terraform/examples/data-sources/pet/data-source.tf @@ -0,0 +1,8 @@ +data "petstore_pet" "example" { + name = "fido" + photo_urls = "https://example.com/fido.jpg" +} + +output "pet_status" { + value = data.petstore_pet.example.status +} diff --git a/samples/client/petstore/terraform/examples/data-sources/user/data-source.tf b/samples/client/petstore/terraform/examples/data-sources/user/data-source.tf new file mode 100644 index 000000000000..0ded06531e87 --- /dev/null +++ b/samples/client/petstore/terraform/examples/data-sources/user/data-source.tf @@ -0,0 +1,7 @@ +data "petstore_user" "example" { + username = "johndoe" +} + +output "user_email" { + value = data.petstore_user.example.email +} diff --git a/samples/client/petstore/terraform/examples/provider/provider.tf b/samples/client/petstore/terraform/examples/provider/provider.tf new file mode 100644 index 000000000000..79cc30d2e4e7 --- /dev/null +++ b/samples/client/petstore/terraform/examples/provider/provider.tf @@ -0,0 +1,13 @@ +terraform { + required_providers { + petstore = { + source = "registry.terraform.io/example/petstore" + } + } +} + +provider "petstore" { + endpoint = "http://petstore.swagger.io/v2" + # api_key = "your-api-key" + # token = "your-bearer-token" +} diff --git a/samples/client/petstore/terraform/examples/resources/pet/resource.tf b/samples/client/petstore/terraform/examples/resources/pet/resource.tf new file mode 100644 index 000000000000..75e0b92fdcc4 --- /dev/null +++ b/samples/client/petstore/terraform/examples/resources/pet/resource.tf @@ -0,0 +1,5 @@ +resource "petstore_pet" "example" { + name = "fido" + status = "available" + photo_urls = ["https://example.com/fido.jpg"] +} diff --git a/samples/client/petstore/terraform/examples/resources/user/resource.tf b/samples/client/petstore/terraform/examples/resources/user/resource.tf new file mode 100644 index 000000000000..a74edec24914 --- /dev/null +++ b/samples/client/petstore/terraform/examples/resources/user/resource.tf @@ -0,0 +1,8 @@ +resource "petstore_user" "example" { + username = "johndoe" + first_name = "John" + last_name = "Doe" + email = "john@example.com" + password = "secret123" + phone = "555-1234" +} diff --git a/samples/client/petstore/terraform/go.mod b/samples/client/petstore/terraform/go.mod new file mode 100644 index 000000000000..c88995b9897c --- /dev/null +++ b/samples/client/petstore/terraform/go.mod @@ -0,0 +1,11 @@ +module github.com/example/terraform-provider-petstore + +go 1.24.0 + +toolchain go1.24.4 + +require ( + github.com/hashicorp/terraform-plugin-framework v1.17.0 + github.com/hashicorp/terraform-plugin-log v0.10.0 + github.com/hashicorp/terraform-plugin-testing v1.14.0 +) diff --git a/samples/client/petstore/terraform/go.sum b/samples/client/petstore/terraform/go.sum new file mode 100644 index 000000000000..9067c4eb87bd --- /dev/null +++ b/samples/client/petstore/terraform/go.sum @@ -0,0 +1,241 @@ +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= +github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE= +github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= +github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= +github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= +github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= +github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= +github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= +github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= +github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= +github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= +github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= +github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= +github.com/go-git/go-git/v5 v5.14.0 h1:/MD3lCrGjCen5WfEAzKg00MJJffKhC8gzS80ycmCi60= +github.com/go-git/go-git/v5 v5.14.0/go.mod h1:Z5Xhoia5PcWA3NF8vRLURn9E5FRhSl7dGj9ItW3Wk5k= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= +github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU= +github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-cty v1.5.0 h1:EkQ/v+dDNUqnuVpmS5fPqyY71NXVgT5gf32+57xY8g0= +github.com/hashicorp/go-cty v1.5.0/go.mod h1:lFUCG5kd8exDobgSfyj4ONE/dc822kiYMguVKdHGMLM= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA= +github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8= +github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= +github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/hc-install v0.9.2 h1:v80EtNX4fCVHqzL9Lg/2xkp62bbvQMnvPQ0G+OmtO24= +github.com/hashicorp/hc-install v0.9.2/go.mod h1:XUqBQNnuT4RsxoxiM9ZaUk0NX8hi2h+Lb6/c0OZnC/I= +github.com/hashicorp/hcl/v2 v2.24.0 h1:2QJdZ454DSsYGoaE6QheQZjtKZSUs9Nh2izTWiwQxvE= +github.com/hashicorp/hcl/v2 v2.24.0/go.mod h1:oGoO1FIQYfn/AgyOhlg9qLC6/nOJPX3qGbkZpYAcqfM= +github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/terraform-exec v0.24.0 h1:mL0xlk9H5g2bn0pPF6JQZk5YlByqSqrO5VoaNtAf8OE= +github.com/hashicorp/terraform-exec v0.24.0/go.mod h1:lluc/rDYfAhYdslLJQg3J0oDqo88oGQAdHR+wDqFvo4= +github.com/hashicorp/terraform-json v0.27.2 h1:BwGuzM6iUPqf9JYM/Z4AF1OJ5VVJEEzoKST/tRDBJKU= +github.com/hashicorp/terraform-json v0.27.2/go.mod h1:GzPLJ1PLdUG5xL6xn1OXWIjteQRT2CNT9o/6A9mi9hE= +github.com/hashicorp/terraform-plugin-framework v1.17.0 h1:JdX50CFrYcYFY31gkmitAEAzLKoBgsK+iaJjDC8OexY= +github.com/hashicorp/terraform-plugin-framework v1.17.0/go.mod h1:4OUXKdHNosX+ys6rLgVlgklfxN3WHR5VHSOABeS/BM0= +github.com/hashicorp/terraform-plugin-go v0.29.0 h1:1nXKl/nSpaYIUBU1IG/EsDOX0vv+9JxAltQyDMpq5mU= +github.com/hashicorp/terraform-plugin-go v0.29.0/go.mod h1:vYZbIyvxyy0FWSmDHChCqKvI40cFTDGSb3D8D70i9GM= +github.com/hashicorp/terraform-plugin-log v0.10.0 h1:eu2kW6/QBVdN4P3Ju2WiB2W3ObjkAsyfBsL3Wh1fj3g= +github.com/hashicorp/terraform-plugin-log v0.10.0/go.mod h1:/9RR5Cv2aAbrqcTSdNmY1NRHP4E3ekrXRGjqORpXyB0= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.38.1 h1:mlAq/OrMlg04IuJT7NpefI1wwtdpWudnEmjuQs04t/4= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.38.1/go.mod h1:GQhpKVvvuwzD79e8/NZ+xzj+ZpWovdPAe8nfV/skwNU= +github.com/hashicorp/terraform-plugin-testing v1.14.0 h1:5t4VKrjOJ0rg0sVuSJ86dz5K7PHsMO6OKrHFzDBerWA= +github.com/hashicorp/terraform-plugin-testing v1.14.0/go.mod h1:1qfWkecyYe1Do2EEOK/5/WnTyvC8wQucUkkhiGLg5nk= +github.com/hashicorp/terraform-registry-address v0.4.0 h1:S1yCGomj30Sao4l5BMPjTGZmCNzuv7/GDTDX99E9gTk= +github.com/hashicorp/terraform-registry-address v0.4.0/go.mod h1:LRS1Ay0+mAiRkUyltGT+UHWkIqTFvigGn/LbMshfflE= +github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= +github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc= +github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= +github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94= +github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= +github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= +github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= +github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= +github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= +github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zclconf/go-cty v1.17.0 h1:seZvECve6XX4tmnvRzWtJNHdscMtYEx5R7bnnVyd/d0= +github.com/zclconf/go-cty v1.17.0/go.mod h1:wqFzcImaLTI6A5HfsRwB0nj5n0MRZFwmey8YoFPPs3U= +github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= +github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= +go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= +go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= +go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= +go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= +go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= +go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= +golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI= +google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= +google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/samples/client/petstore/terraform/internal/client/client.go b/samples/client/petstore/terraform/internal/client/client.go new file mode 100644 index 000000000000..247e45a1c730 --- /dev/null +++ b/samples/client/petstore/terraform/internal/client/client.go @@ -0,0 +1,78 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" +) + +// Client is the API client used to interact with the API. +type Client struct { + BaseURL string + HTTPClient *http.Client + ApiKey string + Token string + Username string + Password string +} + +// NewClient creates a new API client with the given base URL. +func NewClient(baseURL string) *Client { + return &Client{ + BaseURL: baseURL, + HTTPClient: &http.Client{}, + } +} + +// DoRequest executes an HTTP request and returns the response body. +func (c *Client) DoRequest(ctx context.Context, method, path string, body interface{}) ([]byte, error) { + var reqBody io.Reader + if body != nil { + jsonBody, err := json.Marshal(body) + if err != nil { + return nil, fmt.Errorf("error marshaling request body: %w", err) + } + reqBody = bytes.NewBuffer(jsonBody) + } + + req, err := http.NewRequestWithContext(ctx, method, c.BaseURL+path, reqBody) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + + if body != nil { + req.Header.Set("Content-Type", "application/json") + } + req.Header.Set("Accept", "application/json") + + // Authentication + if c.Token != "" { + req.Header.Set("Authorization", "Bearer "+c.Token) + } else if c.ApiKey != "" { + req.Header.Set("Authorization", c.ApiKey) + } else if c.Username != "" { + req.SetBasicAuth(c.Username, c.Password) + } + + resp, err := c.HTTPClient.Do(req) + if err != nil { + return nil, fmt.Errorf("error making request: %w", err) + } + defer resp.Body.Close() + + respBody, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(respBody)) + } + + return respBody, nil +} diff --git a/samples/client/petstore/terraform/internal/client/model_api_response.go b/samples/client/petstore/terraform/internal/client/model_api_response.go new file mode 100644 index 000000000000..8512ff6a6f5c --- /dev/null +++ b/samples/client/petstore/terraform/internal/client/model_api_response.go @@ -0,0 +1,10 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// ApiResponse - Describes the result of uploading an image resource +type ApiResponse struct { + Code int32 `json:"code,omitempty"` + Type string `json:"type,omitempty"` + Message string `json:"message,omitempty"` +} diff --git a/samples/client/petstore/terraform/internal/client/model_category.go b/samples/client/petstore/terraform/internal/client/model_category.go new file mode 100644 index 000000000000..1813ce260524 --- /dev/null +++ b/samples/client/petstore/terraform/internal/client/model_category.go @@ -0,0 +1,9 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// Category - A category for a pet +type Category struct { + Id int64 `json:"id,omitempty"` + Name string `json:"name,omitempty" validate:"regexp=^[a-zA-Z0-9]+[a-zA-Z0-9\\\\.\\\\-_]*[a-zA-Z0-9]+$"` +} diff --git a/samples/client/petstore/terraform/internal/client/model_order.go b/samples/client/petstore/terraform/internal/client/model_order.go new file mode 100644 index 000000000000..9b259e1de869 --- /dev/null +++ b/samples/client/petstore/terraform/internal/client/model_order.go @@ -0,0 +1,13 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// Order - An order for a pets from the pet store +type Order struct { + Id int64 `json:"id,omitempty"` + PetId int64 `json:"petId,omitempty"` + Quantity int32 `json:"quantity,omitempty"` + ShipDate string `json:"shipDate,omitempty"` + Status string `json:"status,omitempty"` + Complete bool `json:"complete,omitempty"` +} diff --git a/samples/client/petstore/terraform/internal/client/model_pet.go b/samples/client/petstore/terraform/internal/client/model_pet.go new file mode 100644 index 000000000000..96e95cec74c7 --- /dev/null +++ b/samples/client/petstore/terraform/internal/client/model_pet.go @@ -0,0 +1,13 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// Pet - A pet for sale in the pet store +type Pet struct { + Id int64 `json:"id,omitempty"` + Category Category `json:"category,omitempty"` + Name string `json:"name"` + PhotoUrls []string `json:"photoUrls"` + Tags []Tag `json:"tags,omitempty"` + Status string `json:"status,omitempty"` +} diff --git a/samples/client/petstore/terraform/internal/client/model_tag.go b/samples/client/petstore/terraform/internal/client/model_tag.go new file mode 100644 index 000000000000..6e81eb958ae7 --- /dev/null +++ b/samples/client/petstore/terraform/internal/client/model_tag.go @@ -0,0 +1,9 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// Tag - A tag for a pet +type Tag struct { + Id int64 `json:"id,omitempty"` + Name string `json:"name,omitempty"` +} diff --git a/samples/client/petstore/terraform/internal/client/model_user.go b/samples/client/petstore/terraform/internal/client/model_user.go new file mode 100644 index 000000000000..db3f822931ec --- /dev/null +++ b/samples/client/petstore/terraform/internal/client/model_user.go @@ -0,0 +1,15 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +// User - A User who is purchasing from the pet store +type User struct { + Id int64 `json:"id,omitempty"` + Username string `json:"username,omitempty"` + FirstName string `json:"firstName,omitempty"` + LastName string `json:"lastName,omitempty"` + Email string `json:"email,omitempty"` + Password string `json:"password,omitempty"` + Phone string `json:"phone,omitempty"` + UserStatus int32 `json:"userStatus,omitempty"` +} diff --git a/samples/client/petstore/terraform/internal/provider/pet_data_source.go b/samples/client/petstore/terraform/internal/provider/pet_data_source.go new file mode 100644 index 000000000000..d5ea1ecc2a67 --- /dev/null +++ b/samples/client/petstore/terraform/internal/provider/pet_data_source.go @@ -0,0 +1,103 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. +package provider + +import ( + "context" + "fmt" + "encoding/json" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-log/tflog" + + "github.com/example/terraform-provider-petstore/internal/client" +) + +var _ datasource.DataSource = &PetDataSource{} + +func NewPetDataSource() datasource.DataSource { + return &PetDataSource{} +} + +type PetDataSource struct { + client *client.Client +} + +func (d *PetDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_pet" +} + +func (d *PetDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Fetches a pet data source.", + Attributes: map[string]schema.Attribute{ + "id": schema.Int64Attribute{ + Computed: true, + Description: "", + }, + "category": schema.StringAttribute{ + Computed: true, + Description: "", + }, + "name": schema.StringAttribute{ + Required: true, + Description: "", + }, + "photo_urls": schema.StringAttribute{ + Required: true, + Description: "", + }, + "tags": schema.StringAttribute{ + Computed: true, + Description: "", + }, + "status": schema.StringAttribute{ + Computed: true, + Description: "pet status in the store", + }, + }, + } +} + +func (d *PetDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + c, ok := req.ProviderData.(*client.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *client.Client, got: %T.", req.ProviderData), + ) + return + } + + d.client = c +} + +func (d *PetDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var config PetModel + + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + + respBody, err := d.client.DoRequest(ctx, "GET", fmt.Sprintf("/pet/%v", config.Id.ValueInt64()), nil) + if err != nil { + resp.Diagnostics.AddError("Error reading pet", err.Error()) + return + } + + var result client.Pet + if err := json.Unmarshal(respBody, &result); err != nil { + resp.Diagnostics.AddError("Error parsing response", err.Error()) + return + } + + config.FromClientModel(&result) + + tflog.Trace(ctx, "read pet data source") + resp.Diagnostics.Append(resp.State.Set(ctx, &config)...) +} diff --git a/samples/client/petstore/terraform/internal/provider/pet_model.go b/samples/client/petstore/terraform/internal/provider/pet_model.go new file mode 100644 index 000000000000..7b6836431f94 --- /dev/null +++ b/samples/client/petstore/terraform/internal/provider/pet_model.go @@ -0,0 +1,40 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. +package provider + +import ( + "github.com/hashicorp/terraform-plugin-framework/types" + + "github.com/example/terraform-provider-petstore/internal/client" +) + +// PetModel is the Terraform model for pet. +type PetModel struct { + Id types.Int64 `tfsdk:"id"` + Category types.String `tfsdk:"category"` + Name types.String `tfsdk:"name"` + PhotoUrls types.List `tfsdk:"photo_urls"` + Tags types.List `tfsdk:"tags"` + Status types.String `tfsdk:"status"` +} + +// ToClientModel converts a Terraform model to a client model. +func (m *PetModel) ToClientModel() *client.Pet { + out := &client.Pet{} + if !m.Id.IsNull() && !m.Id.IsUnknown() { + out.Id = int64(m.Id.ValueInt64()) + } + if !m.Name.IsNull() && !m.Name.IsUnknown() { + out.Name = m.Name.ValueString() + } + if !m.Status.IsNull() && !m.Status.IsUnknown() { + out.Status = m.Status.ValueString() + } + return out +} + +// FromClientModel updates the Terraform model from a client model. +func (m *PetModel) FromClientModel(c *client.Pet) { + m.Id = types.Int64Value(int64(c.Id)) + m.Name = types.StringValue(c.Name) + m.Status = types.StringValue(c.Status) +} diff --git a/samples/client/petstore/terraform/internal/provider/pet_resource.go b/samples/client/petstore/terraform/internal/provider/pet_resource.go new file mode 100644 index 000000000000..066e02e939cb --- /dev/null +++ b/samples/client/petstore/terraform/internal/provider/pet_resource.go @@ -0,0 +1,170 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. +package provider + +import ( + "context" + "fmt" + "encoding/json" + + + + "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/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + + + + "github.com/example/terraform-provider-petstore/internal/client" +) + +var _ resource.Resource = &PetResource{} +var _ resource.ResourceWithImportState = &PetResource{} + +func NewPetResource() resource.Resource { + return &PetResource{} +} + +type PetResource struct { + client *client.Client +} + +func (r *PetResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_pet" +} + +func (r *PetResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Manages a pet resource.", + Attributes: map[string]schema.Attribute{ + "id": schema.Int64Attribute{ + Optional: true, + Description: "", + }, + "category": schema.StringAttribute{ + Optional: true, + Description: "", + }, + "name": schema.StringAttribute{ + Required: true, + Description: "", + }, + "photo_urls": schema.ListAttribute{ + Required: true, + ElementType: types.StringType, + Description: "", + }, + "tags": schema.ListAttribute{ + Optional: true, + ElementType: types.StringType, + Description: "", + }, + "status": schema.StringAttribute{ + Optional: true, + Description: "pet status in the store", + }, + }, + } +} + +func (r *PetResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + c, ok := req.ProviderData.(*client.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *client.Client, got: %T.", req.ProviderData), + ) + return + } + + r.client = c +} + +func (r *PetResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan PetModel + + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + reqBody := plan.ToClientModel() + + respBody, err := r.client.DoRequest(ctx, "POST", "/pet", reqBody) + if err != nil { + resp.Diagnostics.AddError("Error creating pet", err.Error()) + return + } + + var result client.Pet + if err := json.Unmarshal(respBody, &result); err != nil { + resp.Diagnostics.AddError("Error parsing response", err.Error()) + return + } + + plan.FromClientModel(&result) + + tflog.Trace(ctx, "created pet resource") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *PetResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state PetModel + + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + respBody, err := r.client.DoRequest(ctx, "GET", fmt.Sprintf("/pet/%v", state.Id.ValueInt64()), nil) + if err != nil { + resp.Diagnostics.AddError("Error reading pet", err.Error()) + return + } + + var result client.Pet + if err := json.Unmarshal(respBody, &result); err != nil { + resp.Diagnostics.AddError("Error parsing response", err.Error()) + return + } + + state.FromClientModel(&result) + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *PetResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + // No update endpoint available; persist the planned values into state. + var plan PetModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *PetResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state PetModel + + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + _, err := r.client.DoRequest(ctx, "DELETE", fmt.Sprintf("/pet/%v", state.Id.ValueInt64()), nil) + if err != nil { + resp.Diagnostics.AddError("Error deleting pet", err.Error()) + return + } + + tflog.Trace(ctx, "deleted pet resource") +} + +func (r *PetResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} diff --git a/samples/client/petstore/terraform/internal/provider/pet_resource_test.go b/samples/client/petstore/terraform/internal/provider/pet_resource_test.go new file mode 100644 index 000000000000..de73f8c7d883 --- /dev/null +++ b/samples/client/petstore/terraform/internal/provider/pet_resource_test.go @@ -0,0 +1,59 @@ +package provider + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccPetResource_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create and Read testing + { + Config: testAccPetResourceConfig("fido", "available"), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("petstore_pet.test", "name", "fido"), + resource.TestCheckResourceAttr("petstore_pet.test", "status", "available"), + resource.TestCheckResourceAttrSet("petstore_pet.test", "id"), + ), + }, + // ImportState testing + { + ResourceName: "petstore_pet.test", + ImportState: true, + ImportStateVerify: true, + }, + // Delete testing is automatically handled by TestCase + }, + }) +} + +func TestAccPetResource_statusPending(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccPetResourceConfig("buddy", "pending"), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("petstore_pet.test", "name", "buddy"), + resource.TestCheckResourceAttr("petstore_pet.test", "status", "pending"), + ), + }, + }, + }) +} + +func testAccPetResourceConfig(name string, status string) string { + return fmt.Sprintf(` +%s + +resource "petstore_pet" "test" { + name = %[2]q + status = %[3]q + photo_urls = ["https://example.com/photo.jpg"] +} +`, testAccProviderConfig, name, status) +} diff --git a/samples/client/petstore/terraform/internal/provider/provider.go b/samples/client/petstore/terraform/internal/provider/provider.go new file mode 100644 index 000000000000..ea03fe4b3cbb --- /dev/null +++ b/samples/client/petstore/terraform/internal/provider/provider.go @@ -0,0 +1,127 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package provider + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/provider" + "github.com/hashicorp/terraform-plugin-framework/provider/schema" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/types" + + "github.com/example/terraform-provider-petstore/internal/client" +) + +var _ provider.Provider = &petstoreProvider{} + +type petstoreProvider struct { + version string +} + +type petstoreProviderModel struct { + Endpoint types.String `tfsdk:"endpoint"` + ApiKey types.String `tfsdk:"api_key"` + Token types.String `tfsdk:"token"` + Username types.String `tfsdk:"username"` + Password types.String `tfsdk:"password"` +} + +func New(version string) func() provider.Provider { + return func() provider.Provider { + return &petstoreProvider{ + version: version, + } + } +} + +func (p *petstoreProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) { + resp.TypeName = "petstore" + resp.Version = p.version +} + +func (p *petstoreProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "endpoint": schema.StringAttribute{ + Optional: true, + Description: "The API endpoint URL.", + }, + "api_key": schema.StringAttribute{ + Optional: true, + Sensitive: true, + Description: "The API key for authentication.", + }, + "token": schema.StringAttribute{ + Optional: true, + Sensitive: true, + Description: "The bearer token for authentication.", + }, + "username": schema.StringAttribute{ + Optional: true, + Description: "The username for basic authentication.", + }, + "password": schema.StringAttribute{ + Optional: true, + Sensitive: true, + Description: "The password for basic authentication.", + }, + }, + } +} + +func (p *petstoreProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { + var config petstoreProviderModel + + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + + if config.Endpoint.IsUnknown() { + resp.Diagnostics.AddWarning( + "Unknown Endpoint Value", + "The provider endpoint value is not yet known. Defaulting to the spec-defined base path.", + ) + } + + endpoint := "http://petstore.swagger.io/v2" + if !config.Endpoint.IsNull() && !config.Endpoint.IsUnknown() { + endpoint = config.Endpoint.ValueString() + } + + c := client.NewClient(endpoint) + + if !config.ApiKey.IsNull() { + c.ApiKey = config.ApiKey.ValueString() + } + if !config.Token.IsNull() { + c.Token = config.Token.ValueString() + } + if !config.Username.IsNull() { + c.Username = config.Username.ValueString() + } + if !config.Password.IsNull() { + c.Password = config.Password.ValueString() + } + + resp.DataSourceData = c + resp.ResourceData = c +} + +func (p *petstoreProvider) Resources(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + NewPetResource, + NewStoreResource, + NewUserResource, + } +} + +func (p *petstoreProvider) DataSources(_ context.Context) []func() datasource.DataSource { + return []func() datasource.DataSource{ + NewPetDataSource, + NewStoreDataSource, + NewUserDataSource, + } +} diff --git a/samples/client/petstore/terraform/internal/provider/provider_test.go b/samples/client/petstore/terraform/internal/provider/provider_test.go new file mode 100644 index 000000000000..27663b792b7a --- /dev/null +++ b/samples/client/petstore/terraform/internal/provider/provider_test.go @@ -0,0 +1,19 @@ +package provider + +import ( + "github.com/hashicorp/terraform-plugin-framework/providerserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" +) + +// testAccProtoV6ProviderFactories are used to instantiate the provider during +// acceptance testing. The factory function is called for every Terraform CLI +// command executed to create a provider server to which the CLI can reattach. +var testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){ + "petstore": providerserver.NewProtocol6WithError(New("test")()), +} + +const testAccProviderConfig = ` +provider "petstore" { + endpoint = "http://petstore.swagger.io/v2" +} +` diff --git a/samples/client/petstore/terraform/internal/provider/store_data_source.go b/samples/client/petstore/terraform/internal/provider/store_data_source.go new file mode 100644 index 000000000000..10452affcc5f --- /dev/null +++ b/samples/client/petstore/terraform/internal/provider/store_data_source.go @@ -0,0 +1,55 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. +package provider + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + + "github.com/example/terraform-provider-petstore/internal/client" +) + +var _ datasource.DataSource = &StoreDataSource{} + +func NewStoreDataSource() datasource.DataSource { + return &StoreDataSource{} +} + +type StoreDataSource struct { + client *client.Client +} + +func (d *StoreDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_store" +} + +func (d *StoreDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Fetches a store data source.", + Attributes: map[string]schema.Attribute{ + }, + } +} + +func (d *StoreDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + c, ok := req.ProviderData.(*client.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *client.Client, got: %T.", req.ProviderData), + ) + return + } + + d.client = c +} + +func (d *StoreDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + resp.Diagnostics.AddError("Not Supported", "Read is not supported for store") +} diff --git a/samples/client/petstore/terraform/internal/provider/store_model.go b/samples/client/petstore/terraform/internal/provider/store_model.go new file mode 100644 index 000000000000..40435f32d9e9 --- /dev/null +++ b/samples/client/petstore/terraform/internal/provider/store_model.go @@ -0,0 +1,8 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. +package provider + + +// StoreModel is the Terraform model for store. +type StoreModel struct { +} + diff --git a/samples/client/petstore/terraform/internal/provider/store_resource.go b/samples/client/petstore/terraform/internal/provider/store_resource.go new file mode 100644 index 000000000000..dc7fa689dbcc --- /dev/null +++ b/samples/client/petstore/terraform/internal/provider/store_resource.go @@ -0,0 +1,83 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. +package provider + +import ( + "context" + "fmt" + + + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + + + + "github.com/example/terraform-provider-petstore/internal/client" +) + +var _ resource.Resource = &StoreResource{} +var _ resource.ResourceWithImportState = &StoreResource{} + +func NewStoreResource() resource.Resource { + return &StoreResource{} +} + +type StoreResource struct { + client *client.Client +} + +func (r *StoreResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_store" +} + +func (r *StoreResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Manages a store resource.", + Attributes: map[string]schema.Attribute{ + }, + } +} + +func (r *StoreResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + c, ok := req.ProviderData.(*client.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *client.Client, got: %T.", req.ProviderData), + ) + return + } + + r.client = c +} + +func (r *StoreResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + resp.Diagnostics.AddError("Not Supported", "Create is not supported for store") +} + +func (r *StoreResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + // No read endpoint available; keep existing state as-is. +} + +func (r *StoreResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + // No update endpoint available; persist the planned values into state. + var plan StoreModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *StoreResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // No delete endpoint available; remove the resource from state. +} + +func (r *StoreResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} diff --git a/samples/client/petstore/terraform/internal/provider/user_data_source.go b/samples/client/petstore/terraform/internal/provider/user_data_source.go new file mode 100644 index 000000000000..5946345c17eb --- /dev/null +++ b/samples/client/petstore/terraform/internal/provider/user_data_source.go @@ -0,0 +1,111 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. +package provider + +import ( + "context" + "fmt" + "encoding/json" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-log/tflog" + + "github.com/example/terraform-provider-petstore/internal/client" +) + +var _ datasource.DataSource = &UserDataSource{} + +func NewUserDataSource() datasource.DataSource { + return &UserDataSource{} +} + +type UserDataSource struct { + client *client.Client +} + +func (d *UserDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_user" +} + +func (d *UserDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Fetches a user data source.", + Attributes: map[string]schema.Attribute{ + "id": schema.Int64Attribute{ + Computed: true, + Description: "", + }, + "username": schema.StringAttribute{ + Computed: true, + Description: "", + }, + "first_name": schema.StringAttribute{ + Computed: true, + Description: "", + }, + "last_name": schema.StringAttribute{ + Computed: true, + Description: "", + }, + "email": schema.StringAttribute{ + Computed: true, + Description: "", + }, + "password": schema.StringAttribute{ + Computed: true, + Description: "", + }, + "phone": schema.StringAttribute{ + Computed: true, + Description: "", + }, + "user_status": schema.Int64Attribute{ + Computed: true, + Description: "User Status", + }, + }, + } +} + +func (d *UserDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + c, ok := req.ProviderData.(*client.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *client.Client, got: %T.", req.ProviderData), + ) + return + } + + d.client = c +} + +func (d *UserDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var config UserModel + + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + + respBody, err := d.client.DoRequest(ctx, "GET", fmt.Sprintf("/user/%v", config.Username.ValueString()), nil) + if err != nil { + resp.Diagnostics.AddError("Error reading user", err.Error()) + return + } + + var result client.User + if err := json.Unmarshal(respBody, &result); err != nil { + resp.Diagnostics.AddError("Error parsing response", err.Error()) + return + } + + config.FromClientModel(&result) + + tflog.Trace(ctx, "read user data source") + resp.Diagnostics.Append(resp.State.Set(ctx, &config)...) +} diff --git a/samples/client/petstore/terraform/internal/provider/user_model.go b/samples/client/petstore/terraform/internal/provider/user_model.go new file mode 100644 index 000000000000..62587f6b8ddf --- /dev/null +++ b/samples/client/petstore/terraform/internal/provider/user_model.go @@ -0,0 +1,62 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. +package provider + +import ( + "github.com/hashicorp/terraform-plugin-framework/types" + + "github.com/example/terraform-provider-petstore/internal/client" +) + +// UserModel is the Terraform model for user. +type UserModel struct { + Id types.Int64 `tfsdk:"id"` + Username types.String `tfsdk:"username"` + FirstName types.String `tfsdk:"first_name"` + LastName types.String `tfsdk:"last_name"` + Email types.String `tfsdk:"email"` + Password types.String `tfsdk:"password"` + Phone types.String `tfsdk:"phone"` + UserStatus types.Int64 `tfsdk:"user_status"` +} + +// ToClientModel converts a Terraform model to a client model. +func (m *UserModel) ToClientModel() *client.User { + out := &client.User{} + if !m.Id.IsNull() && !m.Id.IsUnknown() { + out.Id = int64(m.Id.ValueInt64()) + } + if !m.Username.IsNull() && !m.Username.IsUnknown() { + out.Username = m.Username.ValueString() + } + if !m.FirstName.IsNull() && !m.FirstName.IsUnknown() { + out.FirstName = m.FirstName.ValueString() + } + if !m.LastName.IsNull() && !m.LastName.IsUnknown() { + out.LastName = m.LastName.ValueString() + } + if !m.Email.IsNull() && !m.Email.IsUnknown() { + out.Email = m.Email.ValueString() + } + if !m.Password.IsNull() && !m.Password.IsUnknown() { + out.Password = m.Password.ValueString() + } + if !m.Phone.IsNull() && !m.Phone.IsUnknown() { + out.Phone = m.Phone.ValueString() + } + if !m.UserStatus.IsNull() && !m.UserStatus.IsUnknown() { + out.UserStatus = int32(m.UserStatus.ValueInt64()) + } + return out +} + +// FromClientModel updates the Terraform model from a client model. +func (m *UserModel) FromClientModel(c *client.User) { + m.Id = types.Int64Value(int64(c.Id)) + m.Username = types.StringValue(c.Username) + m.FirstName = types.StringValue(c.FirstName) + m.LastName = types.StringValue(c.LastName) + m.Email = types.StringValue(c.Email) + m.Password = types.StringValue(c.Password) + m.Phone = types.StringValue(c.Phone) + m.UserStatus = types.Int64Value(int64(c.UserStatus)) +} diff --git a/samples/client/petstore/terraform/internal/provider/user_resource.go b/samples/client/petstore/terraform/internal/provider/user_resource.go new file mode 100644 index 000000000000..dc8102202c44 --- /dev/null +++ b/samples/client/petstore/terraform/internal/provider/user_resource.go @@ -0,0 +1,193 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. +package provider + +import ( + "context" + "fmt" + "encoding/json" + + + + "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-log/tflog" + + + + "github.com/example/terraform-provider-petstore/internal/client" +) + +var _ resource.Resource = &UserResource{} +var _ resource.ResourceWithImportState = &UserResource{} + +func NewUserResource() resource.Resource { + return &UserResource{} +} + +type UserResource struct { + client *client.Client +} + +func (r *UserResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_user" +} + +func (r *UserResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Manages a user resource.", + Attributes: map[string]schema.Attribute{ + "id": schema.Int64Attribute{ + Optional: true, + Description: "", + }, + "username": schema.StringAttribute{ + Optional: true, + Description: "", + }, + "first_name": schema.StringAttribute{ + Optional: true, + Description: "", + }, + "last_name": schema.StringAttribute{ + Optional: true, + Description: "", + }, + "email": schema.StringAttribute{ + Optional: true, + Description: "", + }, + "password": schema.StringAttribute{ + Optional: true, + Description: "", + }, + "phone": schema.StringAttribute{ + Optional: true, + Description: "", + }, + "user_status": schema.Int64Attribute{ + Optional: true, + Description: "User Status", + }, + }, + } +} + +func (r *UserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + c, ok := req.ProviderData.(*client.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *client.Client, got: %T.", req.ProviderData), + ) + return + } + + r.client = c +} + +func (r *UserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan UserModel + + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + reqBody := plan.ToClientModel() + + respBody, err := r.client.DoRequest(ctx, "POST", "/user", reqBody) + if err != nil { + resp.Diagnostics.AddError("Error creating user", err.Error()) + return + } + + var result client.User + if err := json.Unmarshal(respBody, &result); err != nil { + resp.Diagnostics.AddError("Error parsing response", err.Error()) + return + } + + plan.FromClientModel(&result) + + tflog.Trace(ctx, "created user resource") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *UserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state UserModel + + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + respBody, err := r.client.DoRequest(ctx, "GET", fmt.Sprintf("/user/%v", state.Username.ValueString()), nil) + if err != nil { + resp.Diagnostics.AddError("Error reading user", err.Error()) + return + } + + var result client.User + if err := json.Unmarshal(respBody, &result); err != nil { + resp.Diagnostics.AddError("Error parsing response", err.Error()) + return + } + + state.FromClientModel(&result) + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *UserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan UserModel + + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + reqBody := plan.ToClientModel() + + respBody, err := r.client.DoRequest(ctx, "PUT", fmt.Sprintf("/user/%v", plan.Username.ValueString()), reqBody) + if err != nil { + resp.Diagnostics.AddError("Error updating user", err.Error()) + return + } + + var result client.User + if err := json.Unmarshal(respBody, &result); err != nil { + resp.Diagnostics.AddError("Error parsing response", err.Error()) + return + } + + plan.FromClientModel(&result) + + tflog.Trace(ctx, "updated user resource") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *UserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state UserModel + + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + _, err := r.client.DoRequest(ctx, "DELETE", fmt.Sprintf("/user/%v", state.Username.ValueString()), nil) + if err != nil { + resp.Diagnostics.AddError("Error deleting user", err.Error()) + return + } + + tflog.Trace(ctx, "deleted user resource") +} + +func (r *UserResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("username"), req, resp) +} diff --git a/samples/client/petstore/terraform/internal/provider/user_resource_test.go b/samples/client/petstore/terraform/internal/provider/user_resource_test.go new file mode 100644 index 000000000000..644853dcdf36 --- /dev/null +++ b/samples/client/petstore/terraform/internal/provider/user_resource_test.go @@ -0,0 +1,93 @@ +package provider + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccUserResource_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create and Read testing + { + Config: testAccUserResourceConfig("johndoe", "John", "Doe", "john@example.com"), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("petstore_user.test", "username", "johndoe"), + resource.TestCheckResourceAttr("petstore_user.test", "first_name", "John"), + resource.TestCheckResourceAttr("petstore_user.test", "last_name", "Doe"), + resource.TestCheckResourceAttr("petstore_user.test", "email", "john@example.com"), + ), + }, + // ImportState testing + { + ResourceName: "petstore_user.test", + ImportState: true, + ImportStateVerify: true, + // password is sensitive and may not be returned on read + ImportStateVerifyIgnore: []string{"password"}, + }, + // Update testing + { + Config: testAccUserResourceConfig("johndoe", "Jane", "Doe", "jane@example.com"), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("petstore_user.test", "username", "johndoe"), + resource.TestCheckResourceAttr("petstore_user.test", "first_name", "Jane"), + resource.TestCheckResourceAttr("petstore_user.test", "email", "jane@example.com"), + ), + }, + // Delete testing is automatically handled by TestCase + }, + }) +} + +func TestAccUserResource_allFields(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccUserResourceAllFieldsConfig(), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("petstore_user.test", "username", "testuser"), + resource.TestCheckResourceAttr("petstore_user.test", "first_name", "Test"), + resource.TestCheckResourceAttr("petstore_user.test", "last_name", "User"), + resource.TestCheckResourceAttr("petstore_user.test", "email", "test@example.com"), + resource.TestCheckResourceAttr("petstore_user.test", "phone", "555-1234"), + resource.TestCheckResourceAttr("petstore_user.test", "user_status", "1"), + ), + }, + }, + }) +} + +func testAccUserResourceConfig(username, firstName, lastName, email string) string { + return fmt.Sprintf(` +%s + +resource "petstore_user" "test" { + username = %[2]q + first_name = %[3]q + last_name = %[4]q + email = %[5]q + password = "secret123" +} +`, testAccProviderConfig, username, firstName, lastName, email) +} + +func testAccUserResourceAllFieldsConfig() string { + return fmt.Sprintf(` +%s + +resource "petstore_user" "test" { + username = "testuser" + first_name = "Test" + last_name = "User" + email = "test@example.com" + password = "secret123" + phone = "555-1234" + user_status = 1 +} +`, testAccProviderConfig) +} diff --git a/samples/client/petstore/terraform/main.go b/samples/client/petstore/terraform/main.go new file mode 100644 index 000000000000..f783fef81479 --- /dev/null +++ b/samples/client/petstore/terraform/main.go @@ -0,0 +1,35 @@ +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package main + +import ( + "context" + "flag" + "log" + + "github.com/hashicorp/terraform-plugin-framework/providerserver" + + "github.com/example/terraform-provider-petstore/internal/provider" +) + +var ( + version string = "0.1.0" +) + +func main() { + var debug bool + + flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve") + flag.Parse() + + opts := providerserver.ServeOpts{ + Address: "registry.terraform.io/example/petstore", + Debug: debug, + } + + err := providerserver.Serve(context.Background(), provider.New(version), opts) + + if err != nil { + log.Fatal(err.Error()) + } +}