diff --git a/.github/workflows/generate.yml b/.github/workflows/generate.yml new file mode 100644 index 0000000..bf28e32 --- /dev/null +++ b/.github/workflows/generate.yml @@ -0,0 +1,30 @@ +name: Generate + +on: + workflow_dispatch: # Allows manual triggering of the workflow to generate SDK + inputs: + force: + description: "Force generation of SDKs on main branch" + type: boolean + default: false + schedule: + - cron: 0 0 * * * # Runs every day at midnight + push: + branches: + - '*' +jobs: + generate: + uses: speakeasy-api/sdk-generation-action/.github/workflows/sdk-generation.yaml@v14 + with: + speakeasy_version: latest + openapi_doc_auth_header: Authorization + openapi_docs: | + - https://raw.githubusercontent.com/abbeylabs/edge/main/api/openapi.yaml + languages: | + - terraform + create_release: true + force: ${{ github.event.inputs.force }} + secrets: + github_access_token: ${{ secrets.GITHUB_TOKEN }} + speakeasy_api_key: ${{ secrets.SPEAKEASY_API_KEY }} + openapi_doc_auth_token: ${{ secrets.EDGE_SPEC_TOKEN }} diff --git a/.gitignore b/.gitignore index 899c13d..4e383e5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,12 @@ +.terraform +.terraform* +*.tfstate* # Created by https://www.toptal.com/developers/gitignore/api/go,goland+all,vim,macos,visualstudiocode,git # Edit at https://www.toptal.com/developers/gitignore?templates=go,goland+all,vim,macos,visualstudiocode,git - ### Git ### # Created by git for backups. To disable backups in Git: # $ git config --global mergetool.keepBackup false *.orig - # Created by git when using merge tools for conflicts *.BACKUP.* *.BASE.* @@ -15,7 +16,6 @@ *_BASE_*.txt *_LOCAL_*.txt *_REMOTE_*.txt - ### Go ### # If you prefer the allow list template instead of the deny list, see community template: # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore @@ -26,43 +26,32 @@ *.dll *.so *.dylib - # Test binary, built with `go test -c` *.test - # Output of the go coverage tool, specifically when used with LiteIDE *.out - # Dependency directories (remove the comment below to include it) # vendor/ - # Go workspace file go.work - # Go Air live reloading tmp/ - # dotenv and direnv .envrc .env - ### GoLand+all ### # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - # User-specific stuff .idea/**/workspace.xml .idea/**/tasks.xml .idea/**/usage.statistics.xml .idea/**/dictionaries .idea/**/shelf - # AWS User-specific .idea/**/aws.xml - # Generated files .idea/**/contentModel.xml - # Sensitive or high-churn files .idea/**/dataSources/ .idea/**/dataSources.ids @@ -71,11 +60,9 @@ tmp/ .idea/**/dynamic.xml .idea/**/uiDesigner.xml .idea/**/dbnavigator.xml - # Gradle .idea/**/gradle.xml .idea/**/libraries - # Gradle and Maven with auto-import # When using Gradle or Maven with auto-import, you should exclude module files, # since they will be recreated, and may cause churn. Uncomment if using @@ -88,65 +75,46 @@ tmp/ # .idea/modules # *.iml # *.ipr - # CMake cmake-build-*/ - # Mongo Explorer plugin .idea/**/mongoSettings.xml - # File-based project format *.iws - # IntelliJ out/ - # mpeltonen/sbt-idea plugin .idea_modules/ - # JIRA plugin atlassian-ide-plugin.xml - # Cursive Clojure plugin .idea/replstate.xml - # SonarLint plugin .idea/sonarlint/ - # Crashlytics plugin (for Android Studio and IntelliJ) com_crashlytics_export_strings.xml crashlytics.properties crashlytics-build.properties fabric.properties - # Editor-based Rest Client .idea/httpRequests - # Android studio 3.1+ serialized cache file .idea/caches/build_file_checksums.ser - ### GoLand+all Patch ### # Ignore everything but code style settings and run configurations # that are supposed to be shared within teams. - .idea/* - !.idea/codeStyles !.idea/runConfigurations - ### macOS ### # General .DS_Store .AppleDouble .LSOverride - # Icon must end with two \r Icon - - # Thumbnails ._* - # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd @@ -155,18 +123,15 @@ Icon .Trashes .VolumeIcon.icns .com.apple.timemachine.donotpresent - # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk - ### macOS Patch ### # iCloud generated files *.icloud - ### Vim ### # Swap [._]*.s[a-v][a-z] @@ -175,11 +140,9 @@ Temporary Items [._]s[a-rt-v][a-z] [._]ss[a-gi-z] [._]sw[a-p] - # Session Session.vim Sessionx.vim - # Temporary .netrwhist *~ @@ -187,7 +150,6 @@ Sessionx.vim tags # Persistent undo [._]*.un~ - ### VisualStudioCode ### .vscode/* !.vscode/settings.json @@ -195,16 +157,12 @@ tags !.vscode/launch.json !.vscode/extensions.json !.vscode/*.code-snippets - # Local History for Visual Studio Code .history/ - # Built Visual Studio Code Extensions *.vsix - ### VisualStudioCode Patch ### # Ignore all local history of files .history .ionide - # End of https://www.toptal.com/developers/gitignore/api/go,goland+all,vim,macos,visualstudiocode,git diff --git a/RELEASES.md b/RELEASES.md new file mode 100644 index 0000000..39d65a5 --- /dev/null +++ b/RELEASES.md @@ -0,0 +1,25 @@ + + +## 2023-06-23 20:28:15 +### Changes +Based on: +- OpenAPI Doc 0.1.0 +- Speakeasy CLI 1.50.1 (2.43.2) https://github.com/speakeasy-api/speakeasy + +## 2023-06-23 23:30:03 +### Changes +Based on: +- OpenAPI Doc 0.1.0 +- Speakeasy CLI 1.50.1 (2.43.2) https://github.com/speakeasy-api/speakeasy + +## 2023-07-10 19:36:58 +### Changes +Based on: +- OpenAPI Doc 0.1.0 +- Speakeasy CLI 1.55.0 (2.59.0) https://github.com/speakeasy-api/speakeasy + +## 2023-07-10 21:58:11 +### Changes +Based on: +- OpenAPI Doc 0.1.0 +- Speakeasy CLI 1.55.0 (2.59.0) https://github.com/speakeasy-api/speakeasy \ No newline at end of file diff --git a/USAGE.md b/USAGE.md new file mode 100755 index 0000000..d45e8dc --- /dev/null +++ b/USAGE.md @@ -0,0 +1,16 @@ + +## Testing the provider locally + +Should you want to validate a change locally, the `--debug` flag allows you to execute the provider against a terraform instance locally. + +This also allows for debuggers (e.g. delve) to be attached to the provider. + +```sh +go run main.go --debug +# Copy the TF_REATTACH_PROVIDERS env var +# In a new terminal +cd examples/your-example +TF_REATTACH_PROVIDERS=... terraform init +TF_REATTACH_PROVIDERS=... terraform apply +``` + \ No newline at end of file diff --git a/examples/README.md b/examples/README.md new file mode 100755 index 0000000..f87f5c1 --- /dev/null +++ b/examples/README.md @@ -0,0 +1 @@ +# TODO \ No newline at end of file diff --git a/files.gen b/files.gen new file mode 100755 index 0000000..075eb5e --- /dev/null +++ b/files.gen @@ -0,0 +1,140 @@ +internal/sdk/apikeys.go +internal/sdk/connectionspecs.go +internal/sdk/connections.go +internal/sdk/grantkits.go +internal/sdk/grants.go +internal/sdk/identities.go +internal/sdk/requests.go +internal/sdk/reviews.go +internal/sdk/sdk.go +internal/provider/type_output.go +internal/provider/type_policy.go +internal/provider/type_reviewers.go +internal/provider/type_step.go +internal/provider/type_grant_workflow.go +examples/README.md +go.mod +go.sum +internal/planmodifiers/boolplanmodifier/suppress_diff.go +internal/planmodifiers/float64planmodifier/suppress_diff.go +internal/planmodifiers/int64planmodifier/suppress_diff.go +internal/planmodifiers/listplanmodifier/suppress_diff.go +internal/planmodifiers/mapplanmodifier/suppress_diff.go +internal/planmodifiers/numberplanmodifier/suppress_diff.go +internal/planmodifiers/objectplanmodifier/suppress_diff.go +internal/planmodifiers/setplanmodifier/suppress_diff.go +internal/planmodifiers/stringplanmodifier/suppress_diff.go +internal/planmodifiers/utils/state_check.go +internal/provider/reflect/diags.go +internal/provider/reflect/doc.go +internal/provider/reflect/generic_attr_value.go +internal/provider/reflect/helpers.go +internal/provider/reflect/interfaces.go +internal/provider/reflect/into.go +internal/provider/reflect/map.go +internal/provider/reflect/number.go +internal/provider/reflect/options.go +internal/provider/reflect/outof.go +internal/provider/reflect/pointer.go +internal/provider/reflect/primitive.go +internal/provider/reflect/slice.go +internal/provider/reflect/struct.go +internal/provider/utils.go +internal/sdk/pkg/types/bigint.go +internal/sdk/pkg/types/date.go +internal/sdk/pkg/types/datetime.go +internal/sdk/pkg/utils/contenttype.go +internal/sdk/pkg/utils/form.go +internal/sdk/pkg/utils/headers.go +internal/sdk/pkg/utils/pathparams.go +internal/sdk/pkg/utils/queryparams.go +internal/sdk/pkg/utils/requestbody.go +internal/sdk/pkg/utils/retries.go +internal/sdk/pkg/utils/security.go +internal/sdk/pkg/utils/utils.go +internal/validators/DateValidator.go +internal/validators/ExactlyOneChild.go +internal/validators/JSONParseValidator.go +internal/validators/RFC3339Validator.go +main.go +terraform-registry-manifest.json +tools/tools.go +internal/sdk/pkg/models/operations/createapikey.go +internal/sdk/pkg/models/operations/getapikeys.go +internal/sdk/pkg/models/operations/deleteapikey.go +internal/sdk/pkg/models/operations/listconnectionspecs.go +internal/sdk/pkg/models/operations/createconnection.go +internal/sdk/pkg/models/operations/getconnection.go +internal/sdk/pkg/models/operations/listconnections.go +internal/sdk/pkg/models/operations/updateconnection.go +internal/sdk/pkg/models/operations/creategrantkit.go +internal/sdk/pkg/models/operations/getgrantkits.go +internal/sdk/pkg/models/operations/deletegrantkit.go +internal/sdk/pkg/models/operations/getgrantkitbyid.go +internal/sdk/pkg/models/operations/listgrantkitversionsbyid.go +internal/sdk/pkg/models/operations/updategrantkit.go +internal/sdk/pkg/models/operations/listgrants.go +internal/sdk/pkg/models/operations/getgrantbyid.go +internal/sdk/pkg/models/operations/revokegrant.go +internal/sdk/pkg/models/operations/createidentity.go +internal/sdk/pkg/models/operations/deleteidentity.go +internal/sdk/pkg/models/operations/getidentity.go +internal/sdk/pkg/models/operations/cancelrequestbyid.go +internal/sdk/pkg/models/operations/createrequest.go +internal/sdk/pkg/models/operations/getrequestbyid.go +internal/sdk/pkg/models/operations/listrequests.go +internal/sdk/pkg/models/operations/listreviews.go +internal/sdk/pkg/models/operations/approvereview.go +internal/sdk/pkg/models/operations/denyreview.go +internal/sdk/pkg/models/operations/getreviewbyid.go +internal/sdk/pkg/models/shared/error.go +internal/sdk/pkg/models/shared/apikey.go +internal/sdk/pkg/models/shared/apikeyscreateparams.go +internal/sdk/pkg/models/shared/apikeys.go +internal/sdk/pkg/models/shared/connectionspeclisting.go +internal/sdk/pkg/models/shared/connectionspec.go +internal/sdk/pkg/models/shared/connectionrequestspec.go +internal/sdk/pkg/models/shared/connectionauth.go +internal/sdk/pkg/models/shared/oauth2flow.go +internal/sdk/pkg/models/shared/keyvaluepair.go +internal/sdk/pkg/models/shared/oauth2flowpkce.go +internal/sdk/pkg/models/shared/pkcemethod.go +internal/sdk/pkg/models/shared/oauth2flowexchange.go +internal/sdk/pkg/models/shared/requestspec.go +internal/sdk/pkg/models/shared/httpmethod.go +internal/sdk/pkg/models/shared/requestcontenttype.go +internal/sdk/pkg/models/shared/connectionauthtypeenum.go +internal/sdk/pkg/models/shared/connection.go +internal/sdk/pkg/models/shared/connectiontype.go +internal/sdk/pkg/models/shared/connectionparams.go +internal/sdk/pkg/models/shared/connectionparamsgithubvariant.go +internal/sdk/pkg/models/shared/connectionparamsgithub.go +internal/sdk/pkg/models/shared/pagerdutyconnectionvalue.go +internal/sdk/pkg/models/shared/pagerdutyconnection.go +internal/sdk/pkg/models/shared/connectionlisting.go +internal/sdk/pkg/models/shared/connectionupdateparams.go +internal/sdk/pkg/models/shared/grantkit.go +internal/sdk/pkg/models/shared/grantworkflow.go +internal/sdk/pkg/models/shared/step.go +internal/sdk/pkg/models/shared/policy.go +internal/sdk/pkg/models/shared/reviewers.go +internal/sdk/pkg/models/shared/output.go +internal/sdk/pkg/models/shared/grantkitcreateparams.go +internal/sdk/pkg/models/shared/grantkitversion.go +internal/sdk/pkg/models/shared/grantkitupdateparams.go +internal/sdk/pkg/models/shared/grant.go +internal/sdk/pkg/models/shared/identity.go +internal/sdk/pkg/models/shared/identityparams.go +internal/sdk/pkg/models/shared/request.go +internal/sdk/pkg/models/shared/requeststatus.go +internal/sdk/pkg/models/shared/review.go +internal/sdk/pkg/models/shared/reviewstatus.go +internal/sdk/pkg/models/shared/requestcancelparams.go +internal/sdk/pkg/models/shared/requestparams.go +internal/sdk/pkg/models/shared/reviewupdateparams.go +USAGE.md +internal/provider/provider.go +internal/provider/grantkit_resource.go +internal/provider/grantkit_resource_sdk.go +internal/provider/identity_resource.go +internal/provider/identity_resource_sdk.go \ No newline at end of file diff --git a/gen.yaml b/gen.yaml new file mode 100755 index 0000000..b89ae91 --- /dev/null +++ b/gen.yaml @@ -0,0 +1,13 @@ +configVersion: 1.0.0 +management: + docChecksum: 37b9bcae2e29f26f5dfd6598846123db + docVersion: 0.1.0 + speakeasyVersion: 1.55.0 + generationVersion: 2.59.0 +generation: + sdkClassName: SDK + singleTagPerOp: false +terraform: + version: 1.1.1 + author: abbeylabs + packageName: abbey diff --git a/go.mod b/go.mod old mode 100644 new mode 100755 index 34f0c7f..8511719 --- a/go.mod +++ b/go.mod @@ -1,75 +1,63 @@ -module abbey.io/terraform-provider-abbey +module abbey -go 1.20 +go 1.18 require ( - github.com/hashicorp/terraform-plugin-docs v0.14.1 - github.com/hashicorp/terraform-plugin-framework v1.1.1 - github.com/hashicorp/terraform-plugin-framework-validators v0.10.0 - github.com/hashicorp/terraform-plugin-go v0.14.3 - github.com/hashicorp/terraform-plugin-testing v1.1.0 - github.com/moznion/go-optional v0.10.0 - github.com/onsi/gomega v1.27.4 + github.com/cenkalti/backoff/v4 v4.2.0 + github.com/hashicorp/terraform-plugin-framework v1.3.0 + github.com/hashicorp/terraform-plugin-go v0.15.0 ) require ( - github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.1.1 // indirect - github.com/Masterminds/sprig/v3 v3.2.2 // indirect - github.com/agext/levenshtein v1.2.3 // indirect - github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect - github.com/armon/go-radix v1.0.0 // indirect - github.com/bgentry/speakeasy v0.1.0 // indirect - github.com/fatih/color v1.14.1 // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/google/go-cmp v0.5.9 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-checkpoint v0.5.0 // indirect - github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect - github.com/hashicorp/go-hclog v1.4.0 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-plugin v1.4.9 // indirect - github.com/hashicorp/go-uuid v1.0.3 // indirect - github.com/hashicorp/go-version v1.6.0 // indirect - github.com/hashicorp/hc-install v0.5.0 // indirect - github.com/hashicorp/hcl/v2 v2.16.1 // indirect - github.com/hashicorp/logutils v1.0.0 // indirect - github.com/hashicorp/terraform-exec v0.18.1 // indirect - github.com/hashicorp/terraform-json v0.15.0 // indirect - github.com/hashicorp/terraform-plugin-log v0.8.0 // indirect - github.com/hashicorp/terraform-plugin-sdk/v2 v2.25.0 // indirect - github.com/hashicorp/terraform-registry-address v0.1.0 // indirect - github.com/hashicorp/terraform-svchost v0.1.0 // indirect - github.com/hashicorp/yamux v0.1.1 // indirect - github.com/huandu/xstrings v1.3.2 // indirect - github.com/imdario/mergo v0.3.13 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect - github.com/mitchellh/cli v1.1.5 // indirect - github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/go-testing-interface v1.14.1 // indirect - github.com/mitchellh/go-wordwrap v1.0.1 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/oklog/run v1.1.0 // indirect - github.com/posener/complete v1.2.3 // indirect - github.com/russross/blackfriday v1.6.0 // indirect - github.com/shopspring/decimal v1.3.1 // indirect - github.com/spf13/cast v1.5.0 // indirect - github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect - github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect - github.com/vmihailenco/tagparser v0.1.2 // indirect - github.com/zclconf/go-cty v1.13.0 // indirect - golang.org/x/crypto v0.6.0 // indirect - golang.org/x/mod v0.9.0 // indirect - golang.org/x/net v0.8.0 // indirect - golang.org/x/sys v0.6.0 // indirect - golang.org/x/text v0.8.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488 // indirect - google.golang.org/grpc v1.53.0 // indirect - google.golang.org/protobuf v1.28.1 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect + github.com/Masterminds/goutils v1.1.1 // indirect + github.com/Masterminds/semver/v3 v3.1.1 // indirect + github.com/Masterminds/sprig/v3 v3.2.2 // indirect + github.com/armon/go-radix v1.0.0 // indirect + github.com/bgentry/speakeasy v0.1.0 // indirect + github.com/fatih/color v1.13.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-checkpoint v0.5.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-hclog v1.5.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-plugin v1.4.9 // indirect + github.com/hashicorp/go-uuid v1.0.3 // indirect + github.com/hashicorp/go-version v1.6.0 // indirect + github.com/hashicorp/hc-install v0.4.0 // indirect + github.com/hashicorp/terraform-exec v0.17.2 // indirect + github.com/hashicorp/terraform-json v0.14.0 // indirect + github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect + github.com/hashicorp/terraform-registry-address v0.2.0 // indirect + github.com/hashicorp/terraform-svchost v0.0.1 // indirect + github.com/huandu/xstrings v1.3.2 // indirect + github.com/imdario/mergo v0.3.13 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mitchellh/cli v1.1.4 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/go-testing-interface v1.14.1 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/oklog/run v1.0.0 // indirect + github.com/posener/complete v1.2.3 // indirect + github.com/russross/blackfriday v1.6.0 // indirect + github.com/shopspring/decimal v1.3.1 // indirect + github.com/spf13/cast v1.5.0 // indirect + github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + github.com/zclconf/go-cty v1.12.1 // indirect + golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect + golang.org/x/net v0.8.0 // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/text v0.8.0 // indirect + google.golang.org/grpc v1.54.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect +) + +require ( + github.com/hashicorp/terraform-plugin-docs v0.13.0 + github.com/hashicorp/terraform-plugin-framework-validators v0.10.0 + github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect + google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect ) diff --git a/go.sum b/go.sum old mode 100644 new mode 100755 index 0ce794c..d555d87 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/sprig/v3 v3.2.1/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= +github.com/Masterminds/sprig/v3 v3.2.0/go.mod h1:tWhwTbUTndesPNeF0C900vKoq283u6zp4APT9vaF3SI= github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= @@ -13,12 +13,8 @@ github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C6 github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= -github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= -github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= -github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= -github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= @@ -26,6 +22,8 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4= +github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -33,9 +31,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= -github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= @@ -47,22 +44,17 @@ github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4= github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= -github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 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.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= @@ -73,13 +65,10 @@ github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv 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.1/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.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUKaCaGKZ/dR2roBXv0vKbSCnssIldfQdI= -github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs= -github.com/hashicorp/go-hclog v1.4.0 h1:ctuWFGrhFha8BnnzxqeRGidlEcQkDyL5u8J8t5eA11I= -github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= +github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 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= @@ -88,40 +77,31 @@ github.com/hashicorp/go-plugin v1.4.9/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHG 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.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/hc-install v0.5.0 h1:D9bl4KayIYKEeJ4vUDe9L5huqxZXczKaykSRcmQ0xY0= -github.com/hashicorp/hc-install v0.5.0/go.mod h1:JyzMfbzfSBSjoDCRPna1vi/24BEDxFaCPfdHtM5SCdo= -github.com/hashicorp/hcl/v2 v2.16.1 h1:BwuxEMD/tsYgbhIW7UuI3crjovf3MzuFWiVgiv57iHg= -github.com/hashicorp/hcl/v2 v2.16.1/go.mod h1:JRmR89jycNkrrqnMmvPDMd56n1rQJ2Q6KocSLCMCXng= -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.18.1 h1:LAbfDvNQU1l0NOQlTuudjczVhHj061fNX5H8XZxHlH4= -github.com/hashicorp/terraform-exec v0.18.1/go.mod h1:58wg4IeuAJ6LVsLUeD2DWZZoc/bYi6dzhLHzxM41980= -github.com/hashicorp/terraform-json v0.15.0 h1:/gIyNtR6SFw6h5yzlbDbACyGvIhKtQi8mTsbkNd79lE= -github.com/hashicorp/terraform-json v0.15.0/go.mod h1:+L1RNzjDU5leLFZkHTFTbJXaoqUC6TqXlFgDoOXrtvk= -github.com/hashicorp/terraform-plugin-docs v0.14.1 h1:MikFi59KxrP/ewrZoaowrB9he5Vu4FtvhamZFustiA4= -github.com/hashicorp/terraform-plugin-docs v0.14.1/go.mod h1:k2NW8+t113jAus6bb5tQYQgEAX/KueE/u8X2Z45V1GM= -github.com/hashicorp/terraform-plugin-framework v1.1.1 h1:PbnEKHsIU8KTTzoztHQGgjZUWx7Kk8uGtpGMMc1p+oI= -github.com/hashicorp/terraform-plugin-framework v1.1.1/go.mod h1:DyZPxQA+4OKK5ELxFIIcqggcszqdWWUpTLPHAhS/tkY= +github.com/hashicorp/hc-install v0.4.0 h1:cZkRFr1WVa0Ty6x5fTvL1TuO1flul231rWkGH92oYYk= +github.com/hashicorp/hc-install v0.4.0/go.mod h1:5d155H8EC5ewegao9A4PUTMNPZaq+TbOzkJJZ4vrXeI= +github.com/hashicorp/terraform-exec v0.17.2 h1:EU7i3Fh7vDUI9nNRdMATCEfnm9axzTnad8zszYZ73Go= +github.com/hashicorp/terraform-exec v0.17.2/go.mod h1:tuIbsL2l4MlwwIZx9HPM+LOV9vVyEfBYu2GsO1uH3/8= +github.com/hashicorp/terraform-json v0.14.0 h1:sh9iZ1Y8IFJLx+xQiKHGud6/TSUCM0N8e17dKDpqV7s= +github.com/hashicorp/terraform-json v0.14.0/go.mod h1:5A9HIWPkk4e5aeeXIBbkcOvaZbIYnAIkEyqP2pNSckM= +github.com/hashicorp/terraform-plugin-docs v0.13.0 h1:6e+VIWsVGb6jYJewfzq2ok2smPzZrt1Wlm9koLeKazY= +github.com/hashicorp/terraform-plugin-docs v0.13.0/go.mod h1:W0oCmHAjIlTHBbvtppWHe8fLfZ2BznQbuv8+UD8OucQ= +github.com/hashicorp/terraform-plugin-framework v1.3.0 h1:WtP1CIaWAfbzME17xoUXvJcyh5Ewu9attdhbfWNnYLs= +github.com/hashicorp/terraform-plugin-framework v1.3.0/go.mod h1:A1WD3Ry7FhrThViUTbkx4ZDsMq9oaAv4U9oTI8bBzCU= github.com/hashicorp/terraform-plugin-framework-validators v0.10.0 h1:4L0tmy/8esP6OcvocVymw52lY0HyQ5OxB7VNl7k4bS0= github.com/hashicorp/terraform-plugin-framework-validators v0.10.0/go.mod h1:qdQJCdimB9JeX2YwOpItEu+IrfoJjWQ5PhLpAOMDQAE= -github.com/hashicorp/terraform-plugin-go v0.14.3 h1:nlnJ1GXKdMwsC8g1Nh05tK2wsC3+3BL/DBBxFEki+j0= -github.com/hashicorp/terraform-plugin-go v0.14.3/go.mod h1:7ees7DMZ263q8wQ6E4RdIdR6nHHJtrdt4ogX5lPkX1A= -github.com/hashicorp/terraform-plugin-log v0.8.0 h1:pX2VQ/TGKu+UU1rCay0OlzosNKe4Nz1pepLXj95oyy0= -github.com/hashicorp/terraform-plugin-log v0.8.0/go.mod h1:1myFrhVsBLeylQzYYEV17VVjtG8oYPRFdaZs7xdW2xs= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.25.0 h1:iNRjaJCatQS1rIbHs/vDvJ0GECsaGgxx780chA2Irpk= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.25.0/go.mod h1:XnVNLIS6bdMJbjSDujhX4Rlk24QpbGKbnrVFM4tZ7OU= -github.com/hashicorp/terraform-plugin-testing v1.1.0 h1:l5UuTAt7yQcThGe0dFGSCOHR4M1k0VVTqW60K2+q6AE= -github.com/hashicorp/terraform-plugin-testing v1.1.0/go.mod h1:D52zIrX/2hgLsUYMj3tfiLAOFJzhGf8GDv/8nCCtPKA= -github.com/hashicorp/terraform-registry-address v0.1.0 h1:W6JkV9wbum+m516rCl5/NjKxCyTVaaUBbzYcMzBDO3U= -github.com/hashicorp/terraform-registry-address v0.1.0/go.mod h1:EnyO2jYO6j29DTHbJcm00E5nQTFeTtyZH3H5ycydQ5A= -github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg= -github.com/hashicorp/terraform-svchost v0.1.0 h1:0+RcgZdZYNd81Vw7tu62g9JiLLvbOigp7QtyNh6CjXk= -github.com/hashicorp/terraform-svchost v0.1.0/go.mod h1:ut8JaH0vumgdCfJaihdcZULqkAwHdQNwNH7taIDdsZM= -github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= -github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/hashicorp/terraform-plugin-go v0.15.0 h1:1BJNSUFs09DS8h/XNyJNJaeusQuWc/T9V99ylU9Zwp0= +github.com/hashicorp/terraform-plugin-go v0.15.0/go.mod h1:tk9E3/Zx4RlF/9FdGAhwxHExqIHHldqiQGt20G6g+nQ= +github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= +github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= +github.com/hashicorp/terraform-registry-address v0.2.0 h1:92LUg03NhfgZv44zpNTLBGIbiyTokQCDcdH5BhVHT3s= +github.com/hashicorp/terraform-registry-address v0.2.0/go.mod h1:478wuzJPzdmqT6OGbB/iH82EDcI8VFM4yujknh/1nIs= +github.com/hashicorp/terraform-svchost v0.0.1 h1:Zj6fR5wnpOHnJUmLyWozjMeDaVuE+cstMPj41/eKmSQ= +github.com/hashicorp/terraform-svchost v0.0.1/go.mod h1:ut8JaH0vumgdCfJaihdcZULqkAwHdQNwNH7taIDdsZM= +github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= +github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= @@ -144,21 +124,17 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= 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.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= 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.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mitchellh/cli v1.1.5 h1:OxRIeJXpAMztws/XHlN2vu6imG5Dpq+j61AzAX5fLng= -github.com/mitchellh/cli v1.1.5/go.mod h1:v8+iFts2sPIKUV1ltktPXMCC8fumSKFItNcD2cLtRR4= +github.com/mitchellh/cli v1.1.4 h1:qj8czE26AU4PbiaPXK5uVmMSM+V5BYsFBiM9HhGRLUA= +github.com/mitchellh/cli v1.1.4/go.mod h1:vTLESy5mRhKOs9KDp0/RATawxP1UqBmdrpVRMnpcvKQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= 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= @@ -166,21 +142,12 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 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.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/moznion/go-optional v0.10.0 h1:YE42pzLDp6vc9zi/2hyaHYJesjahZEgFXEN1u5DMwMA= -github.com/moznion/go-optional v0.10.0/go.mod h1:l3mLmsyp2bWTvWKjEm5MT7lo3g5MRlNIflxFB0XTASA= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -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/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk= -github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= -github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ= +github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -209,24 +176,21 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 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/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvCazn8G65U= github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= +github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= +github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= -github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc= -github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= +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.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= -github.com/zclconf/go-cty v1.13.0 h1:It5dfKTTZHe9aeppbNOda3mN7Ag7sg6QkBNm6TkyFa0= -github.com/zclconf/go-cty v1.13.0/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0= +github.com/zclconf/go-cty v1.12.1 h1:PcupnljUm9EIvbgSHQnHhUr3fO6oFmkOrvs2BAFNXXY= +github.com/zclconf/go-cty v1.12.1/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA= github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -234,34 +198,18 @@ golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= -golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/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/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -278,48 +226,31 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc 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.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= -golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 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.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -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= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488 h1:QQF+HdiI4iocoxUjjpLgvTYDHKm99C/VtTBFnfiCJos= -google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= -google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= +google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= 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.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 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-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/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= @@ -327,7 +258,6 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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/internal/abbey/entity/policy_set.go b/internal/abbey/entity/policy_set.go deleted file mode 100644 index 44883b5..0000000 --- a/internal/abbey/entity/policy_set.go +++ /dev/null @@ -1,63 +0,0 @@ -package entity - -import ( - "context" - - "github.com/hashicorp/terraform-plugin-framework/types" - . "github.com/moznion/go-optional" -) - -type ( - PolicySet struct { - GrantIf Option[[]Policy] `json:"grant_if"` - RevokeIf Option[[]Policy] `json:"revoke_if"` - } - - Policy struct { - Bundle Option[string] `json:"bundle"` - Query Option[string] `json:"query"` - } -) - -func PolicyFromObject(ctx context.Context, object types.Object) (invalid Policy, err error) { - var ( - attrs = object.Attributes() - bundle Option[string] - query Option[string] - ) - - bundleValue, err := attrs["bundle"].ToTerraformValue(ctx) - if err != nil { - return invalid, err - } - - queryValue, err := attrs["query"].ToTerraformValue(ctx) - if err != nil { - return invalid, err - } - - if !bundleValue.IsNull() { - var s string - - if err := bundleValue.As(&s); err != nil { - return invalid, err - } - - bundle = Some(s) - } - - if !queryValue.IsNull() { - var s string - - if err := queryValue.As(&s); err != nil { - return invalid, err - } - - query = Some(s) - } - - return Policy{ - Bundle: bundle, - Query: query, - }, nil -} diff --git a/internal/abbey/provider.go b/internal/abbey/provider.go deleted file mode 100644 index bdff475..0000000 --- a/internal/abbey/provider.go +++ /dev/null @@ -1,115 +0,0 @@ -package abbey - -import ( - "context" - "net/http" - "os" - - "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" - - abbeyprovider "abbey.io/terraform-provider-abbey/internal/abbey/provider" - grantkitresource "abbey.io/terraform-provider-abbey/internal/abbey/resources/grantkit" - identityresource "abbey.io/terraform-provider-abbey/internal/abbey/resources/identity" -) - -const ( - envKeyToken = "ABBEY_TOKEN" -) - -type provider_ struct { - version string - - // The following fields are populated by [provider.Provider.Configure]. - - host string -} - -var _ provider.Provider = (*provider_)(nil) - -func New(version string, defaultHost string) func() provider.Provider { - return func() provider.Provider { - return &provider_{ - version: version, - host: defaultHost, - } - } -} - -func (p *provider_) Metadata( - _ context.Context, - _ provider.MetadataRequest, - response *provider.MetadataResponse, -) { - response.TypeName = "abbey" - response.Version = p.version -} - -func (p *provider_) Schema( - _ context.Context, - _ provider.SchemaRequest, - response *provider.SchemaResponse, -) { - response.Schema = schema.Schema{ - Attributes: map[string]schema.Attribute{ - "host": schema.StringAttribute{ - CustomType: nil, - Required: false, - Optional: true, - Sensitive: false, - Description: "", - MarkdownDescription: "The Abbey API host. Defaults to `https://api.abbey.io`.", - DeprecationMessage: "", - Validators: nil, - }, - }, - Blocks: nil, - Description: "", - MarkdownDescription: "", - DeprecationMessage: "", - } -} - -type Config struct { - Host types.String `tfsdk:"host"` -} - -func (p *provider_) Configure( - ctx context.Context, - request provider.ConfigureRequest, - response *provider.ConfigureResponse, -) { - var config Config - - response.Diagnostics.Append(request.Config.Get(ctx, &config)...) - if response.Diagnostics.HasError() { - return - } - - host := p.host - token := os.Getenv(envKeyToken) - - if !config.Host.IsNull() { - host = config.Host.ValueString() - } - - response.ResourceData = &abbeyprovider.ResourceData{ - Client: http.DefaultClient, - Host: host, - Token: token, - } -} - -func (p *provider_) DataSources(context.Context) []func() datasource.DataSource { - return []func() datasource.DataSource{} -} - -func (p *provider_) Resources(context.Context) []func() resource.Resource { - return []func() resource.Resource{ - grantkitresource.New, - identityresource.New, - } -} diff --git a/internal/abbey/provider/provider.go b/internal/abbey/provider/provider.go deleted file mode 100644 index e144f35..0000000 --- a/internal/abbey/provider/provider.go +++ /dev/null @@ -1,21 +0,0 @@ -package provider - -import ( - "fmt" - "net/http" -) - -const ( - DefaultHost = "https://api.abbey.io" - TypePrefix = "abbey" -) - -func NewTypeName(name string) string { - return fmt.Sprintf("%s_%s", TypePrefix, name) -} - -type ResourceData struct { - Client *http.Client - Host string - Token string -} diff --git a/internal/abbey/resources/grantkit/grant_kit.go b/internal/abbey/resources/grantkit/grant_kit.go deleted file mode 100644 index c9595aa..0000000 --- a/internal/abbey/resources/grantkit/grant_kit.go +++ /dev/null @@ -1,145 +0,0 @@ -package grantkit - -import ( - "context" - - "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/types" - . "github.com/moznion/go-optional" - - "abbey.io/terraform-provider-abbey/internal/abbey/entity" - "abbey.io/terraform-provider-abbey/internal/abbey/resources/requestable" -) - -type Model struct { - Id types.String `tfsdk:"id"` - Name types.String `tfsdk:"name"` - Description types.String `tfsdk:"description"` - Workflow types.Object `tfsdk:"workflow"` - Output types.Object `tfsdk:"output"` - Policies types.Object `tfsdk:"policies"` -} - -func (self Model) ToRequestableView(ctx context.Context) (*requestable.View, diag.Diagnostics) { - workflow, diags := WorkflowFromObject(ctx, self.Workflow) - if diags.HasError() { - return nil, diags - } - - grant, diags_ := RequestableGrantFromOutputObject(ctx, self.Output) - diags.Append(diags_...) - if diags.HasError() { - return nil, diags - } - - var policies Option[entity.PolicySet] - - if !self.Policies.IsNull() { - policySet, diags_ := PolicySetFromObject(ctx, self.Policies) - diags.Append(diags_...) - if diags.HasError() { - return nil, diags - } - - policies_, diags_ := policySet.ToView(ctx) - diags.Append(diags_...) - if diags.HasError() { - return nil, diags - } - - policies = Some(policies_) - } - - return &requestable.View{ - Id: self.Id.ValueString(), - Name: self.Name.ValueString(), - Description: self.Description.ValueString(), - Workflow: workflow, - Grant: grant, - Policies: policies, - }, nil -} - -func ModelFromRequestableView(view requestable.View) (*Model, diag.Diagnostics) { - var ( - diags diag.Diagnostics - diags_ diag.Diagnostics - workflowObject = types.ObjectNull(WorkflowAttrTypes()) - outputObject = types.ObjectNull(OutputAttrTypes()) - policiesObject = types.ObjectNull(PolicySetAttrTypes()) - ) - - if view.Workflow != nil { - view.Workflow.Value.VisitWorkflow(requestable.WorkflowVisitor{ - GrantKit: func(reviewWorkflow requestable.ReviewWorkflow) { - workflowObject, diags_ = reviewWorkflow.ToObject() - diags.Append(diags_...) - if diags.HasError() { - return - } - }, - Builtin: func(builtinWorkflow requestable.BuiltinWorkflow) { - workflow, diags_ := WorkflowFromRequestableBuiltinWorkflow(builtinWorkflow) - diags.Append(diags_...) - if diags.HasError() { - return - } - - workflowObject, diags_ = workflow.ToObject() - diags.Append(diags_...) - if diags.HasError() { - return - } - }, - }) - } - if diags.HasError() { - return nil, diags - } - - if view.Grant != nil { - view.Grant.Value.VisitGrant(requestable.GrantVisitor{ - Generate: func(generate requestable.GenerateGrant) { - generate.Value.VisitGenerateGrant(requestable.GenerateGrantVisitor{ - Github: func(github requestable.GithubGenerateDestination) { - output := OutputFromRequestableGithubDestination(github) - outputObject, diags_ = output.ToObject() - diags.Append(diags_...) - if diags.HasError() { - return - } - }, - }) - }, - }) - } - if diags.HasError() { - return nil, diags - } - - view.Policies.IfSome(func(view entity.PolicySet) { - policySet, diags_ := PolicySetFromView(view) - diags.Append(diags_...) - if diags.HasError() { - return - } - - policiesObject, diags_ = policySet.ToObject() - diags.Append(diags_...) - if diags.HasError() { - return - } - }) - if diags.HasError() { - return nil, diags - } - - return &Model{ - Id: types.StringValue(view.Id), - Name: types.StringValue(view.Name), - Description: types.StringValue(view.Description), - Workflow: workflowObject, - Output: outputObject, - Policies: policiesObject, - }, nil -} diff --git a/internal/abbey/resources/grantkit/output.go b/internal/abbey/resources/grantkit/output.go deleted file mode 100644 index 79b009c..0000000 --- a/internal/abbey/resources/grantkit/output.go +++ /dev/null @@ -1,91 +0,0 @@ -package grantkit - -import ( - "context" - "fmt" - "strings" - - "github.com/hashicorp/terraform-plugin-framework/attr" - "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - - "abbey.io/terraform-provider-abbey/internal/abbey/resources/requestable" -) - -type Output struct { - Location types.String `tfsdk:"location"` - Append types.String `tfsdk:"append"` - Overwrite types.String `tfsdk:"overwrite"` -} - -func (self Output) ToObject() (types.Object, diag.Diagnostics) { - return types.ObjectValue( - OutputAttrTypes(), - map[string]attr.Value{ - "location": self.Location, - "append": self.Append, - "overwrite": self.Overwrite, - }, - ) -} - -func OutputFromRequestableGithubDestination(github requestable.GithubGenerateDestination) Output { - return Output{ - Location: types.StringValue(fmt.Sprintf("github://%s/%s", github.Repo, github.Path)), - Append: types.StringValue(github.Append), - Overwrite: types.StringNull(), - } -} - -func OutputAttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "location": types.StringType, - "append": types.StringType, - "overwrite": types.StringType, - } -} - -func RequestableGrantFromOutputObject( - ctx context.Context, - object types.Object, -) (*requestable.Grant, diag.Diagnostics) { - var ( - diags diag.Diagnostics - output Output - ) - - diags.Append(object.As(ctx, &output, basetypes.ObjectAsOptions{ - UnhandledNullAsEmpty: false, - UnhandledUnknownAsEmpty: false, - })...) - if diags.HasError() { - return nil, diags - } - - s, ok := strings.CutPrefix(output.Location.ValueString(), "github://") - if !ok { - diags.AddError("Invalid Input", "Only `github://` scheme is supported.") - return nil, diags - } - - segments := strings.SplitN(s, "/", 3) - if len(segments) != 3 { - diags.AddError("Invalid Input", "Failed to parse GitHub location.") - return nil, diags - } - - owner := segments[0] - repo := segments[1] - path := segments[2] - - return &requestable.Grant{ - Value: requestable.GenerateGrant{ - Value: requestable.GithubGenerateDestination{ - Repo: strings.Join([]string{owner, repo}, "/"), - Path: path, - Append: output.Append.ValueString(), - }, - }, - }, diags -} diff --git a/internal/abbey/resources/grantkit/policy.go b/internal/abbey/resources/grantkit/policy.go deleted file mode 100644 index c067fe5..0000000 --- a/internal/abbey/resources/grantkit/policy.go +++ /dev/null @@ -1,189 +0,0 @@ -package grantkit - -import ( - "context" - - "github.com/hashicorp/terraform-plugin-framework/attr" - "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - . "github.com/moznion/go-optional" - - "abbey.io/terraform-provider-abbey/internal/abbey/entity" -) - -type ( - Policy struct { - Bundle types.String `tfsdk:"bundle"` - Query types.String `tfsdk:"query"` - } - - PolicySet struct { - GrantIf types.List `tfsdk:"grant_if"` - RevokeIf types.List `tfsdk:"revoke_if"` - } -) - -func PolicyAttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "bundle": types.StringType, - "query": types.StringType, - } -} - -func PolicyType() attr.Type { - return types.ObjectType{AttrTypes: PolicyAttrTypes()} -} - -func (self Policy) ToObject() (types.Object, diag.Diagnostics) { - return types.ObjectValue(PolicyAttrTypes(), map[string]attr.Value{ - "bundle": self.Bundle, - "query": self.Query, - }) -} - -func PolicyFromView(policy entity.Policy) Policy { - bundle := types.StringNull() - query := types.StringNull() - - policy.Bundle.IfSome(func(v string) { - bundle = types.StringValue(v) - }) - policy.Query.IfSome(func(v string) { - query = types.StringValue(v) - }) - - return Policy{ - Bundle: bundle, - Query: query, - } -} - -func PolicySetAttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "grant_if": types.ListType{ElemType: PolicyType()}, - "revoke_if": types.ListType{ElemType: PolicyType()}, - } -} - -func PolicySetFromView(policySet entity.PolicySet) (invalid PolicySet, diags diag.Diagnostics) { - var ( - diags_ diag.Diagnostics - grantIf = types.ListNull(PolicyType()) - revokeIf = types.ListNull(PolicyType()) - ) - - policySet.GrantIf.IfSome(func(policies []entity.Policy) { - list := make([]attr.Value, 0, len(policies)) - for _, policy := range policies { - p, diags_ := PolicyFromView(policy).ToObject() - diags.Append(diags_...) - if diags.HasError() { - return - } - - list = append(list, p) - } - - grantIf, diags = types.ListValue(PolicyType(), list) - diags.Append(diags_...) - if diags.HasError() { - return - } - }) - if diags.HasError() { - return invalid, diags - } - - policySet.RevokeIf.IfSome(func(policies []entity.Policy) { - list := make([]attr.Value, 0, len(policies)) - for _, policy := range policies { - p, diags_ := PolicyFromView(policy).ToObject() - diags.Append(diags_...) - if diags.HasError() { - return - } - - list = append(list, p) - } - - revokeIf, diags = types.ListValue(PolicyType(), list) - diags.Append(diags_...) - if diags.HasError() { - return - } - }) - if diags.HasError() { - return invalid, diags - } - - return PolicySet{ - GrantIf: grantIf, - RevokeIf: revokeIf, - }, diags -} - -func PolicySetFromObject(ctx context.Context, object types.Object) (invalid PolicySet, diags diag.Diagnostics) { - var policySet PolicySet - - diags.Append(object.As(ctx, &policySet, basetypes.ObjectAsOptions{ - UnhandledNullAsEmpty: false, - UnhandledUnknownAsEmpty: false, - })...) - if diags.HasError() { - return invalid, diags - } - - return policySet, diags -} - -func (self PolicySet) ToObject() (types.Object, diag.Diagnostics) { - return types.ObjectValue(PolicySetAttrTypes(), map[string]attr.Value{ - "grant_if": self.GrantIf, - "revoke_if": self.RevokeIf, - }) -} - -func (self PolicySet) ToView(ctx context.Context) (invalid entity.PolicySet, diags diag.Diagnostics) { - var ( - grantIf Option[[]entity.Policy] - revokeIf Option[[]entity.Policy] - ) - - if !self.GrantIf.IsNull() { - policies := make([]entity.Policy, 0, len(self.GrantIf.Elements())) - - for _, element := range self.GrantIf.Elements() { - policy, err := entity.PolicyFromObject(ctx, element.(types.Object)) - if err != nil { - diags.AddError("Unexpected", err.Error()) - return invalid, diags - } - - policies = append(policies, policy) - } - - grantIf = Some(policies) - } - - if !self.RevokeIf.IsNull() { - policies := make([]entity.Policy, 0, len(self.RevokeIf.Elements())) - - for _, element := range self.RevokeIf.Elements() { - policy, err := entity.PolicyFromObject(ctx, element.(types.Object)) - if err != nil { - diags.AddError("Unexpected", err.Error()) - return invalid, diags - } - - policies = append(policies, policy) - } - - revokeIf = Some(policies) - } - - return entity.PolicySet{ - GrantIf: grantIf, - RevokeIf: revokeIf, - }, diags -} diff --git a/internal/abbey/resources/grantkit/resource.go b/internal/abbey/resources/grantkit/resource.go deleted file mode 100644 index f1fadbf..0000000 --- a/internal/abbey/resources/grantkit/resource.go +++ /dev/null @@ -1,406 +0,0 @@ -package grantkit - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "net/http" - - "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" - "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" - "github.com/hashicorp/terraform-plugin-framework/path" - . "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/schema/validator" - "github.com/hashicorp/terraform-plugin-framework/types" - - "abbey.io/terraform-provider-abbey/internal/abbey/provider" - "abbey.io/terraform-provider-abbey/internal/abbey/resources/requestable" - abbeyvalidator "abbey.io/terraform-provider-abbey/validator" -) - -var ( - _ Resource = (*resource)(nil) - _ ResourceWithConfigure = (*resource)(nil) -) - -func New() Resource { - return &resource{data: nil} -} - -type resource struct { - data *provider.ResourceData -} - -func (r *resource) Configure(_ context.Context, request ConfigureRequest, response *ConfigureResponse) { - if request.ProviderData == nil { - return - } - - providerData, ok := request.ProviderData.(*provider.ResourceData) - if !ok { - response.Diagnostics.AddError( - "Unexpected Resource Configure type_", - fmt.Sprintf("Got: %T. Please report this issue to the provider developers.", request.ProviderData), - ) - return - } - - r.data = providerData -} - -func (r resource) Metadata(_ context.Context, _ MetadataRequest, response *MetadataResponse) { - response.TypeName = provider.NewTypeName("grant_kit") -} - -func (r resource) Schema(_ context.Context, _ SchemaRequest, response *SchemaResponse) { - response.Schema = schema.Schema{ - Description: "The resource `grant_kit` allows you to automate access control to sensitive data." + - "\n" + - "This resource can be used to create access request workflows to help you with security and compliance. " + - "You can also add OPA-based access policies for your target systems to control access by roles, attributes, " + - "or time.", - Attributes: map[string]schema.Attribute{ - "id": schema.StringAttribute{ - Computed: true, - }, - "name": schema.StringAttribute{ - Required: true, - Description: "The human-readable name of this resource.", - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - }, - }, - "description": schema.StringAttribute{ - Required: true, - Description: "The text describing what this Grant Kit is used for and what it can do.", - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - }, - }, - "workflow": schema.SingleNestedAttribute{ - Optional: true, - Description: "The workflow for _how_ someone gets access to a resource. " + - "A workflow contains a list `steps` to be run sequentially.", - Attributes: map[string]schema.Attribute{ - "steps": schema.ListNestedAttribute{ - Required: true, - Description: "The chain of decisions that needs to determine if access to a resource is approved or denied. " + - "Each step contains a list of `reviewers` that are responsible " + - "for submitting an approve or deny decision. " + - "A step may require approvals from `one_of` or `all_of` the reviewers.", - Validators: []validator.List{listvalidator.SizeAtLeast(1)}, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "reviewers": schema.SingleNestedAttribute{ - Required: true, - Description: "The list of Primary Identities, Secondary Identities, or Groups which are " + - "responsible for submitting an approve or deny decision.", - Attributes: map[string]schema.Attribute{ - "one_of": schema.ListAttribute{ - Optional: true, - ElementType: types.StringType, - Description: "Require only one reviewer in the `reviewers` list to approve " + - "in order to advance this step.", - Validators: []validator.List{ - listvalidator.ExactlyOneOf(path.Expressions{ - path.MatchRelative().AtParent().AtName("all_of"), - }...), - }, - }, - "all_of": schema.ListAttribute{ - Optional: true, - ElementType: types.StringType, - Description: "Require all reviewers in the `reviewers` list to approve " + - "in order to advance this step.", - }, - }, - }, - "skip_if": schema.ListNestedAttribute{ - Optional: true, - Description: "The condition that determines whether this step should be run.", - Validators: []validator.List{ - listvalidator.SizeAtLeast(1), - }, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "bundle": schema.StringAttribute{ - Optional: true, - Description: "An RFC 3986 URI. Supports `github://` only. Schemes " + - "such as `https://`, `file://`, and `s3://` to come in future releases. " + - "You should use either `bundle` to contain your OPA Policies or supply them " + - "directly in the `query` field.", - Validators: []validator.String{ - stringvalidator.ExactlyOneOf(path.Expressions{ - path.MatchRelative().AtParent().AtName("query"), - }...), - abbeyvalidator.IsRFC3986(), - }, - }, - "query": schema.StringAttribute{ - Optional: true, - Description: "The UTF-8 text string containing Rego rules using the " + - "Abbey OPA Framework." + - "Rules should be written using `deny[msg] { ... }` for mandatory enforcement " + - "and `warn[msg] { ... }` for advisory enforcement.", - }, - }, - }, - }, - }, - }, - }, - }, - PlanModifiers: []planmodifier.Object{ - objectplanmodifier.RequiresReplace(), - }, - }, - "policies": schema.SingleNestedAttribute{ - Optional: true, - Description: "The access policies that determine if the resource requester get access to this resource.", - Attributes: map[string]schema.Attribute{ - "grant_if": schema.ListNestedAttribute{ - Optional: true, - Description: "Determines the conditions for which this resource should be granted access to the requester.", - Validators: []validator.List{ - listvalidator.AtLeastOneOf(path.Expressions{ - path.MatchRelative().AtParent().AtName("revoke_if"), - }...), - listvalidator.SizeAtLeast(1), - }, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "bundle": schema.StringAttribute{ - Optional: true, - Description: "An RFC 3986 URI. Supports `github://` only. Schemes " + - "such as `https://`, `file://`, and `s3://` to come in future releases. " + - "You should use either `bundle` to contain your OPA Policies or supply them " + - "directly in the `query` field.", - Validators: []validator.String{ - stringvalidator.ExactlyOneOf(path.Expressions{ - path.MatchRelative().AtParent().AtName("query"), - }...), - abbeyvalidator.IsRFC3986(), - }, - }, - "query": schema.StringAttribute{ - Optional: true, - Description: "The UTF-8 text string containing Rego rules using the " + - "Abbey OPA Framework." + - "Rules should be written using `deny[msg] { ... }` for mandatory enforcement " + - "and `warn[msg] { ... }` for advisory enforcement.", - }, - }, - }, - }, - "revoke_if": schema.ListNestedAttribute{ - Optional: true, - Description: "Determines the conditions for which access to this resource should be revoked from the requester.", - Validators: []validator.List{ - listvalidator.SizeAtLeast(1), - }, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "bundle": schema.StringAttribute{ - Optional: true, - Description: "An RFC 3986 URI. Supports `github://` only. Schemes " + - "such as `https://`, `file://`, and `s3://` to come in future releases. " + - "You should use either `bundle` to contain your OPA Policies or supply them " + - "directly in the `query` field.", - Validators: []validator.String{ - stringvalidator.ExactlyOneOf(path.Expressions{ - path.MatchRelative().AtParent().AtName("query"), - }...), - abbeyvalidator.IsRFC3986(), - }, - }, - "query": schema.StringAttribute{ - Optional: true, - Description: "The UTF-8 text string containing Rego rules using the " + - "Abbey OPA Framework." + - "Rules should be written using `deny[msg] { ... }` for mandatory enforcement " + - "and `warn[msg] { ... }` for advisory enforcement.", - }, - }, - }, - }, - }, - PlanModifiers: []planmodifier.Object{ - objectplanmodifier.RequiresReplace(), - }, - }, - "output": schema.SingleNestedAttribute{ - Required: true, - Description: "The output represents how and where access changes should be made. " + - "This generates HCL code in a Terraform file at the `location` with either `append` or `overwrite` behavior.", - Attributes: map[string]schema.Attribute{ - "location": schema.StringAttribute{ - Required: true, - Description: "An RFC 3986 URI. Supports `github://` only. Schemes " + - "such as `https://`, `file://`, and `s3://` to come in future releases.", - Validators: []validator.String{ - abbeyvalidator.IsRFC3986(), - }, - }, - "append": schema.StringAttribute{ - Optional: true, - Description: "Appends this UTF-8 text string to the file at `location`.", - Validators: []validator.String{ - stringvalidator.ExactlyOneOf(path.Expressions{ - path.MatchRelative().AtParent().AtName("overwrite"), - }...), - }, - }, - "overwrite": schema.StringAttribute{ - Optional: true, - Description: "Overwrites the file at `location` with this UTF-8 text string.", - }, - }, - PlanModifiers: []planmodifier.Object{ - objectplanmodifier.RequiresReplace(), - }, - }, - }, - } -} - -func (r resource) Create(ctx context.Context, request CreateRequest, response *CreateResponse) { - var plan Model - - response.Diagnostics.Append(request.Plan.Get(ctx, &plan)...) - if response.Diagnostics.HasError() { - return - } - - view, diags := plan.ToRequestableView(ctx) - response.Diagnostics.Append(diags...) - if response.Diagnostics.HasError() { - return - } - - body, err := json.Marshal(view) - if err != nil { - response.Diagnostics. - AddError("Unknown", fmt.Sprintf("Failed to serialize generate config: %v.", err)) - return - } - - req, err := http.NewRequestWithContext( - ctx, http.MethodPost, - fmt.Sprintf("%s/v1/requestables", r.data.Host), - bytes.NewReader(body), - ) - if err != nil { - response.Diagnostics. - AddError("Unknown", fmt.Sprintf("Failed to build request: %v.", err)) - return - } - - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", r.data.Token)) - - resp, err := r.data.Client.Do(req) - if err != nil { - response.Diagnostics. - AddError("Unknown", fmt.Sprintf("Failed to execute request: %v", err)) - return - } - - defer func() { - if err := resp.Body.Close(); err != nil { - response.Diagnostics.AddWarning("Unknown", fmt.Sprintf("Failed to close response body: %v", err)) - } - }() - - if resp.StatusCode != http.StatusCreated { - response.Diagnostics. - AddError("Unknown", fmt.Sprintf("Expected status %d, got %s.", http.StatusCreated, resp.Status)) - return - } - - var newView requestable.View - - err = json.NewDecoder(resp.Body).Decode(&newView) - if err != nil { - response.Diagnostics. - AddError("Unknown", fmt.Sprintf("Failed to deserialize response body: %v", err)) - return - } - - state, diags_ := ModelFromRequestableView(newView) - response.Diagnostics.Append(diags_...) - if response.Diagnostics.HasError() { - return - } - - response.Diagnostics.Append(response.State.Set(ctx, state)...) -} - -func (r resource) Read(ctx context.Context, request ReadRequest, response *ReadResponse) { - var model Model - - response.Diagnostics.Append(request.State.Get(ctx, &model)...) - if response.Diagnostics.HasError() { - return - } - - response.Diagnostics.Append(response.State.Set(ctx, model)...) -} - -func (r resource) Update(ctx context.Context, request UpdateRequest, response *UpdateResponse) { - response.Diagnostics.AddWarning( - "Update Operation Not Implemented Yet", - "You can workaround this by destroying your target and re-applying your configuration.\nTo destroy this resource, run:\n\n\tterraform destroy -target ") -} - -func (r resource) Delete(ctx context.Context, request DeleteRequest, response *DeleteResponse) { - var state Model - - response.Diagnostics.Append(request.State.Get(ctx, &state)...) - if response.Diagnostics.HasError() { - return - } - - view, diags := state.ToRequestableView(ctx) - response.Diagnostics.Append(diags...) - if response.Diagnostics.HasError() { - return - } - - req, err := http.NewRequestWithContext( - ctx, http.MethodDelete, - fmt.Sprintf("%s/v1/requestables/%s", r.data.Host, view.Id), - nil, - ) - if err != nil { - response.Diagnostics. - AddError("Unknown", fmt.Sprintf("Failed to build request: %v.", err)) - return - } - - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", r.data.Token)) - - resp, err := r.data.Client.Do(req) - if err != nil { - response.Diagnostics. - AddError("Unknown", fmt.Sprintf("Failed to execute request: %v", err)) - return - } - - defer func() { - if err := resp.Body.Close(); err != nil { - response.Diagnostics.AddWarning("Unknown", fmt.Sprintf("Failed to close response body: %v", err)) - } - }() - - if resp.StatusCode != http.StatusNoContent { - response.Diagnostics. - AddError("Unknown", fmt.Sprintf("Expected status %d, got %s.", http.StatusNoContent, resp.Status)) - return - } -} diff --git a/internal/abbey/resources/grantkit/resource_test.go b/internal/abbey/resources/grantkit/resource_test.go deleted file mode 100644 index 90c47c5..0000000 --- a/internal/abbey/resources/grantkit/resource_test.go +++ /dev/null @@ -1,229 +0,0 @@ -package grantkit_test - -import ( - "fmt" - "net/http" - "os" - "testing" - - "abbey.io/terraform-provider-abbey/internal/abbey" - abbeyprovider "abbey.io/terraform-provider-abbey/internal/abbey/provider" - "github.com/hashicorp/terraform-plugin-framework/providerserver" - "github.com/hashicorp/terraform-plugin-go/tfprotov6" - "github.com/hashicorp/terraform-plugin-testing/helper/acctest" - "github.com/hashicorp/terraform-plugin-testing/helper/resource" - - "github.com/hashicorp/terraform-plugin-testing/terraform" - . "github.com/onsi/gomega" -) - -// var testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){ -// "abbey": providerserver.NewProtocol6WithError(abbey.New("test", abbeyprovider.DefaultHost)()), -// } - -var ( - providerFactories map[string]func() (tfprotov6.ProviderServer, error) -) - -func init() { - providerFactories = map[string]func() (tfprotov6.ProviderServer, error){ - "abbey": providerserver.NewProtocol6WithError( - abbey.New("test", abbeyprovider.DefaultHost)(), - ), - } -} - -func TestAccGrantKit(t *testing.T) { - g := NewGomegaWithT(t) - randomPostfix := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) - name := fmt.Sprintf("acc-test-%s", randomPostfix) - - t.Run("Ok", func(t *testing.T) { - resource.Test(t, resource.TestCase{ - ProtoV6ProviderFactories: providerFactories, - CheckDestroy: func(state *terraform.State) error { - var res = state.RootModule().Resources["abbey_grant_kit.test"] - - request, err := http.NewRequest( - http.MethodGet, - fmt.Sprintf("%s/v1/requestables/%s", abbeyprovider.DefaultHost, res.Primary.ID), - nil, - ) - g.Expect(err).NotTo(HaveOccurred()) - - request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", os.Getenv("ABBEY_TOKEN"))) - - response, err := http.DefaultClient.Do(request) - g.Expect(err).NotTo(HaveOccurred()) - g.Expect(response.StatusCode).To(Equal(http.StatusNotFound)) - - return nil - }, - Steps: []resource.TestStep{ - { - Config: fmt.Sprintf( - ` - resource "abbey_grant_kit" "test" { - name = "%s" - description = "test description" - - workflow = { - steps = [ - { - reviewers = { - one_of = ["primary-id-1"] - } - } - ] - } - - output = { - location = "github://path/to/access.tf" - append = "test" - } - } - `, - name, - ), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttrSet("abbey_grant_kit.test", "id"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "name", name), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "description", "test description"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "workflow.steps.#", "1"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "workflow.steps.0.reviewers.one_of.#", "1"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "workflow.steps.0.reviewers.one_of.0", "primary-id-1"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "output.location", "github://path/to/access.tf"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "output.append", "test"), - ), - }, - // Mutate `name`. - { - Config: fmt.Sprintf( - ` - resource "abbey_grant_kit" "test" { - name = "%s-updated" - description = "test description" - - workflow = { - steps = [ - { - reviewers = { - one_of = ["primary-id-1"] - } - } - ] - } - - output = { - location = "github://path/to/access.tf" - append = "test" - } - } - `, - name, - ), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttrSet("abbey_grant_kit.test", "id"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "name", fmt.Sprintf("%s-updated", name)), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "description", "test description"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "workflow.steps.#", "1"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "workflow.steps.0.reviewers.one_of.#", "1"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "workflow.steps.0.reviewers.one_of.0", "primary-id-1"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "output.location", "github://path/to/access.tf"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "output.append", "test"), - ), - }, - // Mutate a nested attribute. - { - Config: fmt.Sprintf( - ` - resource "abbey_grant_kit" "test" { - name = "%s-updated" - description = "test description" - - workflow = { - steps = [ - { - reviewers = { - one_of = ["different-primary-id"] - } - } - ] - } - - output = { - location = "github://path/to/access.tf" - append = "test" - } - } - `, - name, - ), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttrSet("abbey_grant_kit.test", "id"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "name", fmt.Sprintf("%s-updated", name)), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "description", "test description"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "workflow.steps.#", "1"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "workflow.steps.0.reviewers.one_of.#", "1"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "workflow.steps.0.reviewers.one_of.0", "different-primary-id"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "output.location", "github://path/to/access.tf"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "output.append", "test"), - ), - }, - }, - }) - resource.Test(t, resource.TestCase{ - ProtoV6ProviderFactories: providerFactories, - Steps: []resource.TestStep{ - { - Config: fmt.Sprintf( - ` - resource "abbey_grant_kit" "test" { - name = "%s" - description = "test description" - - workflow = { - steps = [ - { - reviewers = { - one_of = ["primary-id-1"] - } - } - ] - } - - policies = { - grant_if = [ - { bundle = "github://organization/repository/path/to/bundle.tar.gz" }, - ] - revoke_if = [ - { query = "input.Requester == true" }, - ] - } - - output = { - location = "github://path/to/access.tf" - append = "test" - } - } - `, - name, - ), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttrSet("abbey_grant_kit.test", "id"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "name", name), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "description", "test description"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "workflow.steps.#", "1"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "workflow.steps.0.reviewers.one_of.#", "1"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "workflow.steps.0.reviewers.one_of.0", "primary-id-1"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "output.location", "github://path/to/access.tf"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "output.append", "test"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "policies.grant_if.#", "1"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "policies.grant_if.0.bundle", "github://organization/repository/path/to/bundle.tar.gz"), - resource.TestCheckResourceAttr("abbey_grant_kit.test", "policies.revoke_if.0.query", "input.Requester == true"), - ), - }, - }, - }) - }) -} diff --git a/internal/abbey/resources/grantkit/step.go b/internal/abbey/resources/grantkit/step.go deleted file mode 100644 index 06b367e..0000000 --- a/internal/abbey/resources/grantkit/step.go +++ /dev/null @@ -1,205 +0,0 @@ -package grantkit - -import ( - "context" - - "github.com/hashicorp/terraform-plugin-framework/attr" - "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/types" - - "abbey.io/terraform-provider-abbey/internal/abbey/entity" - "abbey.io/terraform-provider-abbey/internal/abbey/resources/requestable" -) - -type Step struct { - Reviewers types.Object `tfsdk:"reviewers"` - SkipIf types.List `tfsdk:"skip_if"` -} - -func StepAttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "reviewers": ReviewerSpecType(), - "skip_if": types.ListType{ElemType: PolicyType()}, - } -} - -func StepType() attr.Type { - return types.ObjectType{AttrTypes: StepAttrTypes()} -} - -func StepFromRequestableBuiltinWorkflow( - builtinWorkflow requestable.BuiltinWorkflow, -) (step *Step, diags diag.Diagnostics) { - var reviewers ReviewerSpec - - builtinWorkflow.Value.VisitBuiltinWorkflow(requestable.BuiltinWorkflowVisitor{ - AllOf: func(allOf requestable.BuiltinWorkflowAllOf) { - steps := make([]attr.Value, 0, len(allOf.Reviewers)) - - for _, reviewer := range allOf.Reviewers { - reviewer.Value.VisitUserQuery(requestable.UserQueryVisitor{ - AuthId: func(authId requestable.AuthId) { - steps = append(steps, types.StringValue(authId.Value)) - }, - }) - } - - allOfValue, diags_ := types.ListValue(types.StringType, steps) - diags.Append(diags_...) - if diags.HasError() { - return - } - - reviewers = ReviewerSpec{ - AllOf: allOfValue, - OneOf: types.ListNull(types.StringType), - } - }, - OneOf: func(oneOf requestable.BuiltinWorkflowOneOf) { - steps := make([]attr.Value, 0, len(oneOf.Reviewers)) - - for _, reviewer := range oneOf.Reviewers { - reviewer.Value.VisitUserQuery(requestable.UserQueryVisitor{ - AuthId: func(authId requestable.AuthId) { - steps = append(steps, types.StringValue(authId.Value)) - }, - }) - } - - oneOfValue, diags_ := types.ListValue(types.StringType, steps) - diags.Append(diags_...) - if diags.HasError() { - return - } - - reviewers = ReviewerSpec{ - AllOf: types.ListNull(types.StringType), - OneOf: oneOfValue, - } - }, - }) - if diags.HasError() { - return nil, diags - } - - reviewersValue, diags_ := reviewers.ToObject() - diags.Append(diags_...) - if diags.HasError() { - return nil, diags - } - - return &Step{ - Reviewers: reviewersValue, - SkipIf: types.ListNull(PolicyType()), - }, nil -} - -func (self Step) ToReviewStep(ctx context.Context) (*requestable.ReviewStep, diag.Diagnostics) { - var ( - diags diag.Diagnostics - skipIf []entity.Policy - reviewerQuantifier requestable.ReviewerQuantifier - ) - - attrs := self.Reviewers.Attributes() - var value []string - - if x, ok := attrs["one_of"]; ok { - diags.Append(x.(types.List).ElementsAs(ctx, &value, false)...) - reviewerQuantifier = requestable.ReviewerQuantifierOneOf(value) - } else if x, ok := attrs["all_of"]; ok { - diags.Append(x.(types.List).ElementsAs(ctx, &value, false)...) - reviewerQuantifier = requestable.ReviewerQuantifierAllOf(value) - } else { - diags.AddError("Unknown reviewer quantifier", attrs["type"].(types.String).ValueString()) - } - if diags.HasError() { - return nil, diags - } - - diags.Append(self.SkipIf.ElementsAs(ctx, &skipIf, false)...) - if diags.HasError() { - return nil, diags - } - - return &requestable.ReviewStep{ - Reviewers: requestable.ReviewerQuantifierEnvelope{ - Value: reviewerQuantifier, - }, - SkipIf: skipIf, - }, diags -} - -func (self Step) ToObject() (types.Object, diag.Diagnostics) { - return types.ObjectValue(StepAttrTypes(), map[string]attr.Value{ - "reviewers": self.Reviewers, - "skip_if": self.SkipIf, - }) -} - -type ReviewerSpec struct { - AllOf types.List `tfsdk:"all_of"` - OneOf types.List `tfsdk:"one_of"` -} - -func ReviewerSpecAttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "all_of": types.ListType{ElemType: types.StringType}, - "one_of": types.ListType{ElemType: types.StringType}, - } -} - -func ReviewerSpecType() attr.Type { - return types.ObjectType{AttrTypes: ReviewerSpecAttrTypes()} -} - -func (self ReviewerSpec) ToObject() (types.Object, diag.Diagnostics) { - return types.ObjectValue(ReviewerSpecAttrTypes(), map[string]attr.Value{ - "all_of": self.AllOf, - "one_of": self.OneOf, - }) -} - -func (self ReviewerSpec) ToBuiltinWorkflowView() (*requestable.BuiltinWorkflow, diag.Diagnostics) { - var ( - diags diag.Diagnostics - enum requestable.BuiltinWorkflowEnum - ) - - if !self.AllOf.IsNull() && !self.OneOf.IsNull() { - diags.AddError("Invalid Input", "Only one of `all_of` and `one_of` expected.") - return nil, diags - } - - if !self.AllOf.IsNull() { - elements := self.AllOf.Elements() - userQueries := make([]requestable.UserQuery, 0, len(elements)) - - for _, element := range elements { - userQueries = append(userQueries, requestable.UserQuery{ - Value: requestable.AuthId{ - Value: element.(types.String).ValueString(), - }, - }) - } - - enum = requestable.BuiltinWorkflowAllOf{Reviewers: userQueries} - } - - if !self.OneOf.IsNull() { - elements := self.OneOf.Elements() - userQueries := make([]requestable.UserQuery, 0, len(elements)) - - for _, element := range elements { - userQueries = append(userQueries, requestable.UserQuery{ - Value: requestable.AuthId{ - Value: element.(types.String).ValueString(), - }, - }) - } - - enum = requestable.BuiltinWorkflowOneOf{Reviewers: userQueries} - } - - return &requestable.BuiltinWorkflow{Value: enum}, nil -} diff --git a/internal/abbey/resources/grantkit/workflow.go b/internal/abbey/resources/grantkit/workflow.go deleted file mode 100644 index 707c63b..0000000 --- a/internal/abbey/resources/grantkit/workflow.go +++ /dev/null @@ -1,100 +0,0 @@ -package grantkit - -import ( - "context" - "fmt" - - "github.com/hashicorp/terraform-plugin-framework/attr" - "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - - "abbey.io/terraform-provider-abbey/internal/abbey/resources/requestable" -) - -type Workflow struct { - Steps types.List `tfsdk:"steps"` -} - -func (self Workflow) ToObject() (types.Object, diag.Diagnostics) { - return types.ObjectValue(WorkflowAttrTypes(), map[string]attr.Value{ - "steps": self.Steps, - }) -} - -func WorkflowAttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "steps": types.ListType{ - ElemType: StepType(), - }, - } -} - -func WorkflowFromRequestableBuiltinWorkflow(builtinWorkflow requestable.BuiltinWorkflow) (*Workflow, diag.Diagnostics) { - step, diags := StepFromRequestableBuiltinWorkflow(builtinWorkflow) - if diags.HasError() { - return nil, diags - } - - stepObject, diags_ := step.ToObject() - diags.Append(diags_...) - if diags.HasError() { - return nil, diags - } - - steps, diags := types.ListValue(StepType(), []attr.Value{stepObject}) - - return &Workflow{Steps: steps}, nil -} - -func WorkflowFromObject(ctx context.Context, object types.Object) (*requestable.Workflow, diag.Diagnostics) { - var ( - workflow Workflow - diags diag.Diagnostics - ) - - diags.Append(object.As(ctx, &workflow, basetypes.ObjectAsOptions{ - UnhandledNullAsEmpty: false, - UnhandledUnknownAsEmpty: false, - })...) - if diags.HasError() { - return nil, diags - } - - if workflow.Steps.IsNull() { - return nil, nil - } - - elements := workflow.Steps.Elements() - - if len(elements) < 1 { - diags.AddError( - "Invalid Input", - fmt.Sprintf("Expected at least 1 review step, got %d.", len(elements)), - ) - return nil, diags - } - - var steps []Step - - diags.Append(workflow.Steps.ElementsAs(ctx, &steps, false)...) - if diags.HasError() { - return nil, diags - } - - reviewSteps := make([]requestable.ReviewStep, 0, len(steps)) - - for _, step := range steps { - reviewStep, diags_ := step.ToReviewStep(ctx) - diags.Append(diags_...) - if diags.HasError() { - return nil, diags - } - - reviewSteps = append(reviewSteps, *reviewStep) - } - - return &requestable.Workflow{Value: requestable.ReviewWorkflow{ - Steps: reviewSteps, - }}, nil -} diff --git a/internal/abbey/resources/identity/resource.go b/internal/abbey/resources/identity/resource.go deleted file mode 100644 index d599d28..0000000 --- a/internal/abbey/resources/identity/resource.go +++ /dev/null @@ -1,309 +0,0 @@ -package identity - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "net/http" - "time" - - . "github.com/hashicorp/terraform-plugin-framework/resource" - . "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - - "abbey.io/terraform-provider-abbey/internal/abbey/provider" -) - -func New() Resource { - return &resource{data: nil} -} - -type resource struct { - data *provider.ResourceData -} - -var ( - _ Resource = (*resource)(nil) - _ ResourceWithConfigure = (*resource)(nil) -) - -func (r *resource) Metadata( - _ context.Context, - _ MetadataRequest, - response *MetadataResponse, -) { - response.TypeName = provider.NewTypeName("identity") -} - -func (r *resource) Schema( - _ context.Context, - _ SchemaRequest, - response *SchemaResponse, -) { - response.Schema = Schema{ - Attributes: map[string]Attribute{ - "id": StringAttribute{ - Computed: true, - }, - "name": StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - }, - }, - "created_at": StringAttribute{Computed: true}, - "linked": StringAttribute{ - Optional: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - }, - }, - }, - } -} - -type model struct { - Id types.String `tfsdk:"id"` - CreatedAt types.String `tfsdk:"created_at"` - Name types.String `tfsdk:"name"` - Linked types.String `tfsdk:"linked"` -} - -func modelFromView(v view) model { - return model{ - Id: basetypes.NewStringValue(v.Id), - CreatedAt: basetypes.NewStringValue(v.CreatedAt.Format(time.RFC3339)), - Name: basetypes.NewStringValue(v.Name), - Linked: basetypes.NewStringValue(string(v.Linked)), - } -} - -type view struct { - Id string `json:"id"` - CreatedAt time.Time `json:"created_at"` - Name string `json:"name"` - Linked json.RawMessage `json:"linked"` -} - -func (r *resource) Create( - ctx context.Context, - request CreateRequest, - response *CreateResponse, -) { - var m model - - response.Diagnostics.Append(request.Plan.Get(ctx, &m)...) - if response.Diagnostics.HasError() { - return - } - - body := new(bytes.Buffer) - requestBody := struct { - Name string `json:"name"` - Linked json.RawMessage `json:"linked"` - }{ - Name: m.Name.ValueString(), - Linked: []byte(m.Linked.ValueString()), - } - - err := json.NewEncoder(body).Encode(requestBody) - if err != nil { - response.Diagnostics.AddError( - "Unknown", - fmt.Sprintf("Failed to serialize generate config: %v", err), - ) - return - } - - req, err := http.NewRequestWithContext( - ctx, - http.MethodPost, - fmt.Sprintf("%s/v1/identities", r.data.Host), - body, - ) - if err != nil { - response.Diagnostics.AddError( - "Unknown", - fmt.Sprintf("Failed to build request: %v", err), - ) - return - } - - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", r.data.Token)) - - resp, err := r.data.Client.Do(req) - if err != nil { - response.Diagnostics.AddError( - "Unknown", - fmt.Sprintf("Failed to execute request: %v", err), - ) - return - } - - if resp.StatusCode != http.StatusCreated { - response.Diagnostics.AddError( - "Unknown", - fmt.Sprintf("Expected status %d, got %s.", http.StatusCreated, resp.Status), - ) - return - } - - var v view - - err = json.NewDecoder(resp.Body).Decode(&v) - if err != nil { - response.Diagnostics.AddError( - "Unknown", - fmt.Sprintf("Failed to deserialize response body: %v", err), - ) - return - } - - response.Diagnostics.Append(response.State.Set(ctx, modelFromView(v))...) -} - -func (r *resource) Read( - ctx context.Context, - request ReadRequest, - response *ReadResponse, -) { - var m model - response.Diagnostics.Append(request.State.Get(ctx, &m)...) - if response.Diagnostics.HasError() { - return - } - - req, err := http.NewRequestWithContext( - ctx, - http.MethodGet, - fmt.Sprintf("%s/v1/identities/%s", r.data.Host, m.Id.ValueString()), - nil, - ) - if err != nil { - response.Diagnostics.AddError( - "Unknown", - fmt.Sprintf("Failed to build request: %v", err), - ) - return - } - - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", r.data.Token)) - - resp, err := r.data.Client.Do(req) - if err != nil { - response.Diagnostics.AddError( - "Unknown", - fmt.Sprintf("Failed to execute request: %v", err), - ) - return - } - - if resp.StatusCode == http.StatusNotFound { - response.State.RemoveResource(ctx) - return - } - - if resp.StatusCode != http.StatusOK { - response.Diagnostics.AddError( - "Unknown", - fmt.Sprintf("Expected status %d, got %s.", http.StatusOK, resp.Status), - ) - return - } - - var v view - err = json.NewDecoder(resp.Body).Decode(&v) - if err != nil { - response.Diagnostics.AddError( - "Unknown", - fmt.Sprintf("Failed to deserialize response body: %v", err), - ) - return - } - - response.Diagnostics.Append(response.State.Set(ctx, modelFromView(v))...) -} - -func (r *resource) Update( - _ context.Context, - _ UpdateRequest, - response *UpdateResponse, -) { - response.Diagnostics.AddWarning( - "Update Operation Not Implemented Yet", - "You can workaround this by destroying your target and re-applying your configuration.\nTo destroy this resource, run:\n\n\t```\nterraform destroy -target ```") -} - -func (r *resource) Delete( - ctx context.Context, - request DeleteRequest, - response *DeleteResponse, -) { - var state model - - response.Diagnostics.Append(request.State.Get(ctx, &state)...) - if response.Diagnostics.HasError() { - return - } - - req, err := http.NewRequestWithContext( - ctx, - http.MethodDelete, - fmt.Sprintf("%s/v1/identities/%s", r.data.Host, state.Id.ValueString()), - nil, - ) - if err != nil { - response.Diagnostics.AddError( - "Unknown", - fmt.Sprintf("Failed to build request: %v", err), - ) - return - } - - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", r.data.Token)) - - resp, err := r.data.Client.Do(req) - if err != nil { - response.Diagnostics.AddError( - "Unknown", - fmt.Sprintf("Failed to execute request: %v", err), - ) - return - } - - if resp.StatusCode != http.StatusNoContent { - response.Diagnostics.AddError( - "Unknown", - fmt.Sprintf("Expected status %d, got %s.", http.StatusNoContent, resp.Status), - ) - return - } - - response.State.RemoveResource(ctx) -} - -func (r *resource) Configure( - _ context.Context, - request ConfigureRequest, - response *ConfigureResponse, -) { - if request.ProviderData == nil { - return - } - - providerData, ok := request.ProviderData.(*provider.ResourceData) - if !ok { - response.Diagnostics.AddError( - "Unexpected Resource Configure type_", - fmt.Sprintf("Got: %T. Please report this issue to the provider developers.", request.ProviderData), - ) - return - } - - r.data = providerData -} diff --git a/internal/abbey/resources/identity/resource_test.go b/internal/abbey/resources/identity/resource_test.go deleted file mode 100644 index 1b4a045..0000000 --- a/internal/abbey/resources/identity/resource_test.go +++ /dev/null @@ -1,113 +0,0 @@ -package identity_test - -import ( - "fmt" - "testing" - - "github.com/hashicorp/terraform-plugin-framework/providerserver" - "github.com/hashicorp/terraform-plugin-go/tfprotov6" - "github.com/hashicorp/terraform-plugin-testing/helper/acctest" - "github.com/hashicorp/terraform-plugin-testing/helper/resource" - - "abbey.io/terraform-provider-abbey/internal/abbey" - "abbey.io/terraform-provider-abbey/internal/abbey/provider" -) - -var testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){ - "abbey": providerserver.NewProtocol6WithError(abbey.New("test", provider.DefaultHost)()), -} - -func TestAccIdentity(t *testing.T) { - randomPostfix := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) - name := fmt.Sprintf("acc-test-%s", randomPostfix) - newName := fmt.Sprintf("%s-updated", name) - - t.Run("Ok", func(t *testing.T) { - resource.Test(t, resource.TestCase{ - ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, - Steps: []resource.TestStep{ - { - Config: fmt.Sprintf( - ` - resource "abbey_identity" "test" { - name = "%s" - linked = jsonencode({ - abbey = [ - { - type = "AuthId" - value = "email@example.com" - }, - ] - a = [1] - b = [ - { prop = true }, - { prop = false }, - ] - }) - } - `, - name, - ), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttrSet("abbey_identity.test", "id"), - resource.TestCheckResourceAttr("abbey_identity.test", "name", name), - resource.TestCheckResourceAttrSet("abbey_identity.test", "linked"), - ), - }, - { - Config: fmt.Sprintf( - ` - resource "abbey_identity" "test" { - name = "%s" - linked = jsonencode({ - abbey = [ - { - type = "AuthId" - value = "email@example.com" - }, - ] - a = [1] - b = [ - { prop = true }, - { prop = false }, - ] - }) - } - `, - newName, - ), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttrSet("abbey_identity.test", "id"), - resource.TestCheckResourceAttr("abbey_identity.test", "name", newName), - resource.TestCheckResourceAttrSet("abbey_identity.test", "linked"), - ), - }, - // Mutating `linked` should work. - { - Config: fmt.Sprintf( - ` - resource "abbey_identity" "test" { - name = "%s" - linked = jsonencode({ - abbey = [ - { - type = "AuthId" - value = "email@example.com" - }, - ] - a = [1] - }) - } - `, - newName, - ), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttrSet("abbey_identity.test", "id"), - resource.TestCheckResourceAttr("abbey_identity.test", "name", newName), - resource.TestCheckNoResourceAttr("abbey_identity.test", "linked.b"), - ), - }, - }, - }) - }) -} diff --git a/internal/abbey/resources/requestable/enum.go b/internal/abbey/resources/requestable/enum.go deleted file mode 100644 index 59765fd..0000000 --- a/internal/abbey/resources/requestable/enum.go +++ /dev/null @@ -1,8 +0,0 @@ -package requestable - -import "encoding/json" - -type enum struct { - Type string `json:"type"` - Value json.RawMessage `json:"value"` -} diff --git a/internal/abbey/resources/requestable/grant.go b/internal/abbey/resources/requestable/grant.go deleted file mode 100644 index 90e95a2..0000000 --- a/internal/abbey/resources/requestable/grant.go +++ /dev/null @@ -1,158 +0,0 @@ -package requestable - -import ( - "encoding/json" - "fmt" -) - -const ( - grantTypeGenerate = "Generate" - - generateGrantTypeGithub = "Github" -) - -type ( - Grant struct { - Value GrantEnum - } - - GrantEnum interface { - VisitGrant(GrantVisitor) - } - - GrantVisitor struct { - Generate func(GenerateGrant) - } -) - -var _ GrantEnum = (*GenerateGrant)(nil) - -func (g GenerateGrant) VisitGrant(visitor GrantVisitor) { - visitor.Generate(g) -} - -func (g Grant) MarshalJSON() ([]byte, error) { - var ( - err error - type_ string - value json.RawMessage - ) - - g.Value.VisitGrant(GrantVisitor{ - Generate: func(grant GenerateGrant) { - type_ = grantTypeGenerate - value, err = json.Marshal(grant) - }, - }) - if err != nil { - return nil, err - } - - return json.Marshal(enum{ - Type: type_, - Value: value, - }) -} - -func (g *Grant) UnmarshalJSON(b []byte) error { - var ( - e enum - value GrantEnum - ) - - if err := json.Unmarshal(b, &e); err != nil { - return err - } - - switch e.Type { - case grantTypeGenerate: - var x GenerateGrant - if err := json.Unmarshal(e.Value, &x); err != nil { - return err - } - - value = &x - default: - return fmt.Errorf("unknown grant type: %s", e.Type) - } - - *g = Grant{Value: value} - - return nil -} - -type ( - GenerateGrant struct { - Value GenerateGrantEnum - } - - GenerateGrantEnum interface { - VisitGenerateGrant(GenerateGrantVisitor) - } - - GenerateGrantVisitor struct { - Github func(GithubGenerateDestination) - } - - GithubGenerateDestination struct { - Repo string `json:"repo" tfsdk:"repo"` - Path string `json:"path" tfsdk:"path"` - Append string `json:"append" tfsdk:"append"` - } -) - -var _ GenerateGrantEnum = (*GithubGenerateDestination)(nil) - -func (g GithubGenerateDestination) VisitGenerateGrant(visitor GenerateGrantVisitor) { - visitor.Github(g) -} - -func (g GenerateGrant) MarshalJSON() ([]byte, error) { - var ( - err error - type_ string - value json.RawMessage - ) - - g.Value.VisitGenerateGrant(GenerateGrantVisitor{ - Github: func(destination GithubGenerateDestination) { - type_ = generateGrantTypeGithub - value, err = json.Marshal(destination) - }, - }) - if err != nil { - return nil, err - } - - return json.Marshal(enum{ - Type: type_, - Value: value, - }) -} - -func (g *GenerateGrant) UnmarshalJSON(b []byte) error { - var ( - e enum - value GenerateGrantEnum - ) - - if err := json.Unmarshal(b, &e); err != nil { - return err - } - - switch e.Type { - case generateGrantTypeGithub: - var x GithubGenerateDestination - if err := json.Unmarshal(e.Value, &x); err != nil { - return err - } - - value = &x - default: - return fmt.Errorf("unknown generate type: %s", e.Type) - } - - g.Value = value - - return nil -} diff --git a/internal/abbey/resources/requestable/grant_mapper.go b/internal/abbey/resources/requestable/grant_mapper.go deleted file mode 100644 index 1d55fac..0000000 --- a/internal/abbey/resources/requestable/grant_mapper.go +++ /dev/null @@ -1,85 +0,0 @@ -package requestable - -import ( - "context" - "fmt" - - "github.com/hashicorp/terraform-plugin-go/tftypes" -) - -const ( - grantTypeGenerateTf = "generate" - - generateGrantTypeGithubTf = "github" -) - -func GenerateGrantFromTfTypesValue(ctx context.Context, value tftypes.Value) (ret *GenerateGrant, err error) { - var m map[string]tftypes.Value - if err := value.As(&m); err != nil { - return nil, err - } - - if len(m) == 0 { - return nil, nil - } - - var inner GenerateGrantEnum - - for key, val := range m { - switch key { - case generateGrantTypeGithubTf: - inner_, err := GithubGenerateDestinationFromTfTypesValue(ctx, val) - if err != nil { - return nil, err - } - if inner_ == nil { - continue - } - - inner = inner_ - default: - return nil, fmt.Errorf("unknown key: %s", key) - } - if err != nil { - return nil, err - } - } - - return &GenerateGrant{Value: inner}, nil -} - -func GithubGenerateDestinationFromTfTypesValue(_ context.Context, value tftypes.Value) (ret *GithubGenerateDestination, err error) { - var m *map[string]tftypes.Value - if err := value.As(&m); err != nil { - return nil, err - } - - if m == nil { - return nil, nil - } - - var ( - repo string - path string - append_ string - ) - - if err := (*m)["repo"].As(&repo); err != nil { - return nil, err - } - if err := (*m)["path"].As(&path); err != nil { - return nil, err - } - - if (*m)["append"].IsKnown() { - if err := (*m)["append"].As(&append_); err != nil { - return nil, err - } - } - - return &GithubGenerateDestination{ - Repo: repo, - Path: path, - Append: append_, - }, nil -} diff --git a/internal/abbey/resources/requestable/grant_tf.go b/internal/abbey/resources/requestable/grant_tf.go deleted file mode 100644 index 59f0ca6..0000000 --- a/internal/abbey/resources/requestable/grant_tf.go +++ /dev/null @@ -1,389 +0,0 @@ -package requestable - -import ( - "context" - "fmt" - - "github.com/hashicorp/terraform-plugin-framework/attr" - . "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" - - "abbey.io/terraform-provider-abbey/internal/abbey/value" -) - -type GrantTf struct { - Grant - - state value.State -} - -func NewGrant(g Grant) GrantTf { - return GrantTf{ - Grant: g, - state: value.NewValidState(), - } -} - -func NewNullGrant() GrantTf { - var g Grant - - return GrantTf{ - Grant: g, - state: value.NewNullState(), - } -} - -func NewUnknownGrant() GrantTf { - var g Grant - - return GrantTf{ - Grant: g, - state: value.NewUnknownState(), - } -} - -func (g GrantTf) ToObjectValue(ctx context.Context) (object basetypes.ObjectValue, diags Diagnostics) { - g.state.Visit(value.StateVisitor{ - Null: func() { - object = types.ObjectNull(g.AttrTypes(ctx)) - }, - Unknown: func() { - object = types.ObjectUnknown(g.AttrTypes(ctx)) - }, - Valid: func() { - var ( - generate GenerateGrant - generateValue attr.Value = types.ObjectNull(generate.AttrTypes(ctx)) - diags_ Diagnostics - ) - - g.Value.VisitGrant(GrantVisitor{ - Generate: func(grant GenerateGrant) { - generateValue, diags = grant.ToObjectValue(ctx) - }, - }) - if diags.HasError() { - return - } - - object, diags_ = types.ObjectValue( - map[string]attr.Type{ - grantTypeGenerateTf: generate.Type(ctx), - }, - map[string]attr.Value{ - grantTypeGenerateTf: generateValue, - }, - ) - diags.Append(diags_...) - }, - }) - - return object, diags -} - -func (g GrantTf) AttrTypes(ctx context.Context) map[string]attr.Type { - var generate GenerateGrant - - return map[string]attr.Type{ - grantTypeGenerateTf: generate.Type(ctx), - } -} - -func GrantTfTfTypesType() tftypes.Type { - return tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - grantTypeGenerateTf: GenerateGrantTfTypesType(), - }, - OptionalAttributes: nil, - } -} - -func (g GrantTf) Type(context.Context) attr.Type { - return GrantType{} -} - -func (g GrantTf) ToTerraformValue(ctx context.Context) (tftypes.Value, error) { - var ( - val tftypes.Value - err error - ) - - g.state.Visit(value.StateVisitor{ - Null: func() { - val = tftypes.NewValue(GrantTfTfTypesType(), nil) - }, - Unknown: func() { - val = tftypes.NewValue(GrantTfTfTypesType(), tftypes.UnknownValue) - }, - Valid: func() { - var generateValue tftypes.Value - - g.Value.VisitGrant(GrantVisitor{ - Generate: func(grant GenerateGrant) { - generateValue, err = grant.ToTerraformValue(ctx) - }, - }) - if err != nil { - return - } - - val = tftypes.NewValue( - GrantTfTfTypesType(), - map[string]tftypes.Value{ - grantTypeGenerateTf: generateValue, - }, - ) - }, - }) - if err != nil { - return val, err - } - - return val, nil -} - -func (g GrantTf) Equal(value attr.Value) bool { - rhs, err := value.ToTerraformValue(context.Background()) - if err != nil { - return false - } - - lhs, err := g.ToTerraformValue(context.Background()) - if err != nil { - return false - } - - return lhs.Equal(rhs) -} - -func (g GrantTf) IsNull() (isNull bool) { - g.state.Visit(value.StateVisitor{ - Null: func() { - isNull = true - }, - Unknown: func() {}, - Valid: func() {}, - }) - - return isNull -} - -func (g GrantTf) IsUnknown() (unknown bool) { - g.state.Visit(value.StateVisitor{ - Null: func() {}, - Unknown: func() { - unknown = true - }, - Valid: func() {}, - }) - - return unknown -} - -func (g GrantTf) String() string { - var inner string - - g.Value.VisitGrant(GrantVisitor{ - Generate: func(grant GenerateGrant) { - inner = grant.String() - }, - }) - - return fmt.Sprintf("Grant{%s}", inner) -} - -func GenerateGrantTfTypesType() tftypes.Type { - return tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - generateGrantTypeGithubTf: GithubGenerateDestinationTfTypesType(), - }, - OptionalAttributes: nil, - } -} - -func (g GenerateGrant) AttrTypes(ctx context.Context) map[string]attr.Type { - var github GithubGenerateDestination - - return map[string]attr.Type{ - generateGrantTypeGithubTf: github.Type(ctx), - } -} - -func (g GenerateGrant) Type(ctx context.Context) attr.Type { - return types.ObjectType{AttrTypes: g.AttrTypes(ctx)} -} - -func (g GenerateGrant) ToObjectValue(ctx context.Context) (object basetypes.ObjectValue, diags Diagnostics) { - var ( - github GithubGenerateDestination - githubValue attr.Value = types.ObjectNull(github.AttrTypes()) - ) - - g.Value.VisitGenerateGrant(GenerateGrantVisitor{ - Github: func(dest GithubGenerateDestination) { - githubValue, diags = dest.ToObjectValue(ctx) - }, - }) - if diags.HasError() { - return object, diags - } - - return types.ObjectValue( - map[string]attr.Type{ - generateGrantTypeGithubTf: github.Type(ctx), - }, - map[string]attr.Value{ - generateGrantTypeGithubTf: githubValue, - }, - ) -} - -func (g GenerateGrant) ToTerraformValue(ctx context.Context) (value tftypes.Value, err error) { - var githubValue tftypes.Value - - g.Value.VisitGenerateGrant(GenerateGrantVisitor{ - Github: func(github GithubGenerateDestination) { - githubValue, err = github.ToTerraformValue(ctx) - }, - }) - if err != nil { - return value, err - } - - return tftypes.NewValue( - GenerateGrantTfTypesType(), - map[string]tftypes.Value{ - generateGrantTypeGithubTf: githubValue, - }, - ), nil -} - -func (g GenerateGrant) Equal(value attr.Value) bool { - rhs, err := value.ToTerraformValue(context.Background()) - if err != nil { - return false - } - - lhs, err := g.ToTerraformValue(context.Background()) - if err != nil { - return false - } - - return lhs.Equal(rhs) -} - -func (g GenerateGrant) IsNull() bool { - defined := false - - g.Value.VisitGenerateGrant(GenerateGrantVisitor{ - Github: func(GithubGenerateDestination) { - defined = true - }, - }) - - return !defined -} - -func (g GenerateGrant) IsUnknown() bool { - defined := false - - g.Value.VisitGenerateGrant(GenerateGrantVisitor{ - Github: func(GithubGenerateDestination) { - defined = true - }, - }) - - return !defined -} - -func (g GenerateGrant) String() string { - var inner string - - g.Value.VisitGenerateGrant(GenerateGrantVisitor{ - Github: func(github GithubGenerateDestination) { - inner = github.String() - }, - }) - - return fmt.Sprintf("GenerateGrant{%s}", inner) -} - -func GithubGenerateDestinationTfTypesType() tftypes.Type { - return tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "repo": tftypes.String, - "path": tftypes.String, - "append": tftypes.String, - }, - OptionalAttributes: nil, - } -} - -func (g GithubGenerateDestination) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "repo": types.StringType, - "path": types.StringType, - "append": types.StringType, - } -} - -func (g GithubGenerateDestination) Type(context.Context) attr.Type { - return types.ObjectType{AttrTypes: g.AttrTypes()} -} - -func (g GithubGenerateDestination) ToObjectValue(context.Context) (object basetypes.ObjectValue, diags Diagnostics) { - return types.ObjectValue( - g.AttrTypes(), - map[string]attr.Value{ - "repo": basetypes.NewStringValue(g.Repo), - "path": basetypes.NewStringValue(g.Path), - "append": basetypes.NewStringValue(g.Append), - }, - ) -} - -func (g GithubGenerateDestination) ToTerraformValue(context.Context) (value tftypes.Value, err error) { - return tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "repo": tftypes.String, - "path": tftypes.String, - "append": tftypes.String, - }, - OptionalAttributes: nil, - }, - map[string]tftypes.Value{ - "repo": tftypes.NewValue(tftypes.String, g.Repo), - "path": tftypes.NewValue(tftypes.String, g.Path), - "append": tftypes.NewValue(tftypes.String, g.Append), - }, - ), nil -} - -func (g GithubGenerateDestination) Equal(value attr.Value) bool { - rhs, err := value.ToTerraformValue(context.Background()) - if err != nil { - return false - } - - lhs, err := g.ToTerraformValue(context.Background()) - if err != nil { - return false - } - - return lhs.Equal(rhs) -} - -func (g GithubGenerateDestination) IsNull() bool { - return false -} - -func (g GithubGenerateDestination) IsUnknown() bool { - return false -} - -func (g GithubGenerateDestination) String() string { - return fmt.Sprintf("GenerateGrant{Repo: %q, Path: %q, Append: %q}", g.Repo, g.Path, g.Append) -} diff --git a/internal/abbey/resources/requestable/grant_type.go b/internal/abbey/resources/requestable/grant_type.go deleted file mode 100644 index 01c5c4e..0000000 --- a/internal/abbey/resources/requestable/grant_type.go +++ /dev/null @@ -1,103 +0,0 @@ -package requestable - -import ( - "context" - "fmt" - - "github.com/hashicorp/terraform-plugin-framework/attr" - . "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" -) - -type GrantType struct{} - -func (t GrantType) TerraformType(context.Context) tftypes.Type { - return tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - grantTypeGenerateTf: GenerateGrantTfTypesType(), - }, - OptionalAttributes: nil, - } -} - -func (t GrantType) ValueFromTerraform(ctx context.Context, value tftypes.Value) (value_ attr.Value, err error) { - if !value.IsKnown() { - return NewUnknownGrant(), nil - } - - var m *map[string]tftypes.Value - if err := value.As(&m); err != nil { - return nil, err - } - - if m == nil { - return NewNullGrant(), nil - } - - var inner GrantEnum - - for key, val := range *m { - switch key { - case grantTypeGenerateTf: - inner_, err := GenerateGrantFromTfTypesValue(ctx, val) - if err != nil { - return value_, err - } - if inner_ == nil { - continue - } - - inner = inner_ - default: - return value_, fmt.Errorf("unknown key: %s", key) - } - } - - return NewGrant(Grant{Value: inner}), nil -} - -func (t GrantType) ValueType(context.Context) attr.Value { - var g GrantTf - return g -} - -func (t GrantType) Equal(ty attr.Type) bool { - _, ok := ty.(GrantType) - return ok -} - -func (GrantType) String() string { - return "Grant" -} - -func (w GrantType) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (any, error) { - attrName, ok := step.(tftypes.AttributeName) - if !ok { - return nil, fmt.Errorf("cannot apply step %T to GrantType", step) - } - - switch string(attrName) { - case grantTypeGenerateTf: - return BuiltinWorkflowTfTypesType(), nil - default: - return nil, fmt.Errorf("undefined attribute name %s in GrantType", attrName) - } -} - -func (t GrantType) ValueFromObject( - ctx context.Context, - value basetypes.ObjectValue, -) (basetypes.ObjectValuable, Diagnostics) { - var g GrantTf - - diags := value.As(ctx, &g, basetypes.ObjectAsOptions{ - UnhandledNullAsEmpty: false, - UnhandledUnknownAsEmpty: false, - }) - if diags.HasError() { - return nil, diags - } - - return &g, diags -} diff --git a/internal/abbey/resources/requestable/requestable.go b/internal/abbey/resources/requestable/requestable.go deleted file mode 100644 index 1a34a6a..0000000 --- a/internal/abbey/resources/requestable/requestable.go +++ /dev/null @@ -1,71 +0,0 @@ -package requestable - -import ( - _ "embed" - "github.com/hashicorp/terraform-plugin-framework/types" - . "github.com/moznion/go-optional" - - . "abbey.io/terraform-provider-abbey/internal/abbey/entity" -) - -type Model struct { - Id types.String `tfsdk:"id"` - Name types.String `tfsdk:"name"` - Workflow WorkflowTf `tfsdk:"workflow"` - Grant GrantTf `tfsdk:"grant"` -} - -func (m Model) ToView() *View { - var ( - workflow *Workflow - grant *Grant - ) - - if !m.Workflow.IsNull() && !m.Grant.IsUnknown() { - workflow = &m.Workflow.Workflow - } - - if !m.Grant.IsNull() && !m.Grant.IsUnknown() { - grant = &m.Grant.Grant - } - - return &View{ - Id: m.Id.ValueString(), - Name: m.Name.ValueString(), - Description: "", - Workflow: workflow, - Grant: grant, - Policies: nil, - } -} - -type View struct { - Id string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - Description string `json:"description"` - Workflow *Workflow `json:"workflow,omitempty"` - Grant *Grant `json:"grant,omitempty"` - Policies Option[PolicySet] `json:"policies,omitempty"` -} - -func (v View) ToModel() *Model { - var ( - workflow WorkflowTf - grant GrantTf - ) - - if v.Workflow != nil { - workflow = NewWorkflow(*v.Workflow) - } - - if v.Grant != nil { - grant = NewGrant(*v.Grant) - } - - return &Model{ - Id: types.StringValue(v.Id), - Name: types.StringValue(v.Name), - Workflow: workflow, - Grant: grant, - } -} diff --git a/internal/abbey/resources/requestable/schema_append_description.md b/internal/abbey/resources/requestable/schema_append_description.md deleted file mode 100644 index 8706832..0000000 --- a/internal/abbey/resources/requestable/schema_append_description.md +++ /dev/null @@ -1,48 +0,0 @@ -String to append to the file. -In addition to normal [Terraform string interpolation][1], -you can also use [Go-style template][1] syntax. -Go-style template will be rendered when a user submits a resource -request with the following context: - -```go -package abbey - -type Context struct { - // Input is the user provided arbitrary input at request time. - // The context also contains conversion functions for the input, - // for example, `to_map` converts the input to a map to access - // nested data. - Input any - - // Requester contains identity and organizational metadata about - // the requester. - Requester struct{ - CanonicalIdentity struct{ - Username string - } - - // PrimitiveIdentities of the requester. For example, if the user - // has a GitHub account, you can access it with - // `.PrimitiveIdentities.Github`. - PrimitiveIdentities map[string]struct{ - Username string - } - } -} -``` - -A sample template looks like this: - -``` -append = <<-EOT - {{"{{"}} $input := .Input | to_map -{{"}}"}} - resource "github_repository_collaborator" "${trimprefix(each.value.full_name, "abbeylabs/")}_{{"{{"}} $.Requester.CanonicalIdentity.Username {{"}}"}} { - repository = "${ trimprefix(each.value.full_name, "abbeylabs/") }" - username = "{{"{{"}} $.Requester.PrimitiveIdentities.Github.Username {{"}}"}} - permission = "{{"{{"}} $input.permission {{"}}"}} - } -EOT -``` - -[1]: https://developer.hashicorp.com/terraform/language/expressions/strings#string-templates -[2]: https://pkg.go.dev/text/template diff --git a/internal/abbey/resources/requestable/user_query.go b/internal/abbey/resources/requestable/user_query.go deleted file mode 100644 index ab37ad1..0000000 --- a/internal/abbey/resources/requestable/user_query.go +++ /dev/null @@ -1,101 +0,0 @@ -package requestable - -import ( - "encoding/json" - "fmt" -) - -const userQueryTypeAuthId = "AuthId" - -type ( - UserQuery struct { - Value UserQueryEnum - } - - UserQueryEnum interface { - VisitUserQuery(UserQueryVisitor) - } - - UserQueryVisitor struct { - AuthId func(AuthId) - } - - AuthId struct { - Value string - } -) - -var _ UserQueryEnum = (*AuthId)(nil) - -func (u UserQuery) VisitUserQuery(visitor UserQueryVisitor) { - u.Value.VisitUserQuery(visitor) -} - -func (u UserQuery) MarshalJSON() ([]byte, error) { - var ( - err error - type_ string - value json.RawMessage - ) - - u.Value.VisitUserQuery(UserQueryVisitor{ - AuthId: func(authId AuthId) { - type_ = userQueryTypeAuthId - value, err = json.Marshal(authId) - }, - }) - if err != nil { - return nil, err - } - - return json.Marshal(enum{ - Type: type_, - Value: value, - }) -} - -func (u *UserQuery) UnmarshalJSON(b []byte) error { - var ( - e enum - value UserQueryEnum - ) - - if err := json.Unmarshal(b, &e); err != nil { - return err - } - - switch e.Type { - case userQueryTypeAuthId: - var x AuthId - if err := json.Unmarshal(e.Value, &x); err != nil { - return err - } - - value = &x - default: - return fmt.Errorf("unknown user query type: %s", e.Type) - } - - *u = UserQuery{Value: value} - - return nil -} - -func (a AuthId) VisitUserQuery(visitor UserQueryVisitor) { - visitor.AuthId(a) -} - -func (a AuthId) MarshalJSON() ([]byte, error) { - return json.Marshal(a.Value) -} - -func (a *AuthId) UnmarshalJSON(data []byte) error { - var s string - if err := json.Unmarshal(data, &s); err != nil { - return err - } - - *a = AuthId{Value: s} - - return nil -} diff --git a/internal/abbey/resources/requestable/user_query_tf.go b/internal/abbey/resources/requestable/user_query_tf.go deleted file mode 100644 index 0937d04..0000000 --- a/internal/abbey/resources/requestable/user_query_tf.go +++ /dev/null @@ -1,152 +0,0 @@ -package requestable - -import ( - "context" - "fmt" - - "github.com/hashicorp/terraform-plugin-framework/attr" - . "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" -) - -const userQueryTypeAuthIdTf = "auth_id" - -func UserQueryType() attr.Type { - return types.ObjectType{AttrTypes: map[string]attr.Type{ - userQueryTypeAuthIdTf: AuthIdType(), - }} -} - -func UserQueryTfTypesType() tftypes.Type { - return tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - userQueryTypeAuthIdTf: AuthIdTfTypesType(), - }, - OptionalAttributes: nil, - } -} - -func (q UserQuery) ToObjectValue(context.Context) (object basetypes.ObjectValue, diags Diagnostics) { - var authIdValue attr.Value = types.StringNull() - - q.Value.VisitUserQuery(UserQueryVisitor{ - AuthId: func(a AuthId) { - authIdValue = types.StringValue(a.Value) - }, - }) - - return types.ObjectValue( - map[string]attr.Type{ - userQueryTypeAuthIdTf: types.StringType, - }, - map[string]attr.Value{ - userQueryTypeAuthIdTf: authIdValue, - }, - ) -} - -func (q UserQuery) Type(ctx context.Context) attr.Type { - var authId AuthId - - return types.ObjectType{AttrTypes: map[string]attr.Type{ - userQueryTypeAuthIdTf: authId.Type(ctx), - }} -} - -func (q UserQuery) ToTerraformValue(ctx context.Context) (value tftypes.Value, err error) { - var authIdValue tftypes.Value - - q.Value.VisitUserQuery(UserQueryVisitor{ - AuthId: func(authId AuthId) { - authIdValue, err = authId.ToTerraformValue(ctx) - }, - }) - if err != nil { - return value, err - } - - return tftypes.NewValue( - UserQueryTfTypesType(), - map[string]tftypes.Value{ - userQueryTypeAuthIdTf: authIdValue, - }, - ), nil -} - -func (q UserQuery) Equal(value attr.Value) bool { - rhs, err := value.ToTerraformValue(context.Background()) - if err != nil { - return false - } - - lhs, err := q.ToTerraformValue(context.Background()) - if err != nil { - return false - } - - return lhs.Equal(rhs) -} - -func (q UserQuery) IsNull() (defined bool) { - return false -} - -func (q UserQuery) IsUnknown() (defined bool) { - return false -} - -func (q UserQuery) String() string { - var inner string - - q.Value.VisitUserQuery(UserQueryVisitor{ - func(authId AuthId) { - inner = authId.String() - }, - }) - - return fmt.Sprintf("UserQuery{%s}", inner) -} - -func AuthIdType() attr.Type { - return types.StringType -} - -func AuthIdTfTypesType() tftypes.Type { - return tftypes.String -} - -func (a AuthId) Type(context.Context) attr.Type { - return types.StringType -} - -func (a AuthId) ToTerraformValue(context.Context) (value tftypes.Value, err error) { - return tftypes.NewValue(tftypes.String, a.Value), nil -} - -func (a AuthId) Equal(value attr.Value) bool { - rhs, err := value.ToTerraformValue(context.Background()) - if err != nil { - return false - } - - lhs, err := a.ToTerraformValue(context.Background()) - if err != nil { - return false - } - - return lhs.Equal(rhs) -} - -func (a AuthId) IsNull() (defined bool) { - return false -} - -func (a AuthId) IsUnknown() (defined bool) { - return false -} - -func (a AuthId) String() string { - return a.Value -} diff --git a/internal/abbey/resources/requestable/user_query_type.go b/internal/abbey/resources/requestable/user_query_type.go deleted file mode 100644 index 318ecf3..0000000 --- a/internal/abbey/resources/requestable/user_query_type.go +++ /dev/null @@ -1 +0,0 @@ -package requestable diff --git a/internal/abbey/resources/requestable/workflow.go b/internal/abbey/resources/requestable/workflow.go deleted file mode 100644 index 02ec917..0000000 --- a/internal/abbey/resources/requestable/workflow.go +++ /dev/null @@ -1,435 +0,0 @@ -package requestable - -import ( - "encoding/json" - "fmt" - - "github.com/hashicorp/terraform-plugin-framework/attr" - "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/types" - - "abbey.io/terraform-provider-abbey/internal/abbey/entity" -) - -const ( - workflowTypeBuiltin = "Builtin" - - builtinWorkflowTypeAllOf = "AllOf" - builtinWorkflowTypeOneOf = "OneOf" -) - -type ( - Workflow struct { - Value WorkflowEnum - } - - WorkflowEnum interface { - VisitWorkflow(WorkflowVisitor) - } - - WorkflowVisitor struct { - Builtin func(BuiltinWorkflow) - GrantKit func(ReviewWorkflow) - } - - BuiltinWorkflow struct { - Value BuiltinWorkflowEnum - } - - BuiltinWorkflowEnum interface { - VisitBuiltinWorkflow(BuiltinWorkflowVisitor) - } - - BuiltinWorkflowVisitor struct { - AllOf func(BuiltinWorkflowAllOf) - OneOf func(BuiltinWorkflowOneOf) - } - - BuiltinWorkflowAllOf struct { - Reviewers []UserQuery `json:"reviewers"` - } - - BuiltinWorkflowOneOf struct { - Reviewers []UserQuery `json:"reviewers"` - } -) - -var ( - _ WorkflowEnum = (*BuiltinWorkflow)(nil) - _ BuiltinWorkflowEnum = (*BuiltinWorkflowAllOf)(nil) - _ BuiltinWorkflowEnum = (*BuiltinWorkflowOneOf)(nil) -) - -func (w Workflow) MarshalJSON() ([]byte, error) { - var ( - err error - type_ string - value json.RawMessage - ) - - w.Value.VisitWorkflow(WorkflowVisitor{ - Builtin: func(workflow BuiltinWorkflow) { - type_ = workflowTypeBuiltin - value, err = json.Marshal(workflow) - }, - GrantKit: func(workflow ReviewWorkflow) { - type_ = "GrantKit" - value, err = json.Marshal(workflow) - }, - }) - if err != nil { - return nil, err - } - - return json.Marshal(enum{ - Type: type_, - Value: value, - }) -} - -func (w *Workflow) UnmarshalJSON(b []byte) error { - var e enum - if err := json.Unmarshal(b, &e); err != nil { - return err - } - - var value WorkflowEnum - - switch e.Type { - case workflowTypeBuiltin: - var x BuiltinWorkflow - if err := json.Unmarshal(e.Value, &x); err != nil { - return err - } - - value = &x - case "GrantKit": - var x ReviewWorkflow - if err := json.Unmarshal(e.Value, &x); err != nil { - return err - } - - value = x - default: - return fmt.Errorf("unknown workflow type: %s", e.Type) - } - - *w = Workflow{Value: value} - - return nil -} - -func (b BuiltinWorkflow) VisitWorkflow(visitor WorkflowVisitor) { - visitor.Builtin(b) -} - -func (w BuiltinWorkflow) MarshalJSON() ([]byte, error) { - var ( - err error - type_ string - value json.RawMessage - ) - - w.Value.VisitBuiltinWorkflow(BuiltinWorkflowVisitor{ - AllOf: func(allOf BuiltinWorkflowAllOf) { - type_ = builtinWorkflowTypeAllOf - value, err = json.Marshal(allOf) - }, - OneOf: func(oneOf BuiltinWorkflowOneOf) { - type_ = builtinWorkflowTypeOneOf - value, err = json.Marshal(oneOf) - }, - }) - if err != nil { - return nil, err - } - - return json.Marshal(enum{ - Type: type_, - Value: value, - }) -} - -func (b *BuiltinWorkflow) UnmarshalJSON(bs []byte) error { - var e enum - if err := json.Unmarshal(bs, &e); err != nil { - return err - } - - var value BuiltinWorkflowEnum - - switch e.Type { - case builtinWorkflowTypeAllOf: - var x BuiltinWorkflowAllOf - if err := json.Unmarshal(e.Value, &x); err != nil { - return err - } - - value = &x - case builtinWorkflowTypeOneOf: - var x BuiltinWorkflowOneOf - if err := json.Unmarshal(e.Value, &x); err != nil { - return err - } - - value = &x - default: - return fmt.Errorf("unknown builtin workflow type: %s", e.Type) - } - - *b = BuiltinWorkflow{Value: value} - - return nil -} - -func (b BuiltinWorkflowAllOf) VisitBuiltinWorkflow(visitor BuiltinWorkflowVisitor) { - visitor.AllOf(b) -} - -func (b BuiltinWorkflowOneOf) VisitBuiltinWorkflow(visitor BuiltinWorkflowVisitor) { - visitor.OneOf(b) -} - -type ( - ReviewWorkflow struct { - Steps []ReviewStep `json:"steps"` - } - - ReviewStep struct { - Reviewers ReviewerQuantifierEnvelope `json:"reviewers"` - SkipIf []entity.Policy `json:"skip_if"` - } - - ReviewerQuantifierEnvelope struct { - Value ReviewerQuantifier - } - - ReviewerQuantifier interface { - VisitReviewerQuantifier(ReviewerQuantifierVisitor) - } - - ReviewerQuantifierVisitor struct { - AllOf func([]string) - OneOf func([]string) - } - - ReviewerQuantifierAllOf []string - ReviewerQuantifierOneOf []string -) - -func ReviewWorkflowAttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "steps": types.ListType{ - types.ObjectType{ - ReviewStepAttrTypes(), - }, - }, - } -} - -func (self ReviewerQuantifierEnvelope) ToObject() (obj types.Object, diags diag.Diagnostics) { - self.Value.VisitReviewerQuantifier(ReviewerQuantifierVisitor{ - AllOf: func(ss []string) { - var values []attr.Value - for _, s := range ss { - values = append(values, types.StringValue(s)) - } - - obj, diags = types.ObjectValue( - map[string]attr.Type{ - "all_of": types.ListType{ - ElemType: types.StringType, - }, - "one_of": types.ListType{ - ElemType: types.StringType, - }, - }, - map[string]attr.Value{ - "all_of": types.ListValueMust(types.StringType, values), - "one_of": types.ListNull(types.StringType), - }, - ) - }, - OneOf: func(ss []string) { - var values []attr.Value - for _, s := range ss { - values = append(values, types.StringValue(s)) - } - - obj, diags = types.ObjectValue( - map[string]attr.Type{ - "all_of": types.ListType{ - ElemType: types.StringType, - }, - "one_of": types.ListType{ - ElemType: types.StringType, - }, - }, - map[string]attr.Value{ - "one_of": types.ListValueMust(types.StringType, values), - "all_of": types.ListNull(types.StringType), - }, - ) - }, - }) - return obj, diags -} - -func (self ReviewWorkflow) ToObject() (invalid types.Object, diags diag.Diagnostics) { - var steps []attr.Value - for _, step := range self.Steps { - obj, diags_ := step.ToObject() - diags.Append(diags_...) - if diags.HasError() { - return invalid, diags - } - - steps = append(steps, obj) - } - - return types.ObjectValue( - ReviewWorkflowAttrTypes(), - map[string]attr.Value{ - "steps": types.ListValueMust(types.ObjectType{ReviewStepAttrTypes()}, steps), - }, - ) -} - -func ReviewStepAttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "reviewers": types.ObjectType{map[string]attr.Type{ - "all_of": types.ListType{ - ElemType: types.StringType, - }, - "one_of": types.ListType{ - ElemType: types.StringType, - }, - }}, - "skip_if": types.ListType{ - types.ObjectType{ - map[string]attr.Type{ - "bundle": types.StringType, - "query": types.StringType, - }, - }, - }, - } -} - -func (self ReviewStep) ToObject() (invalid types.Object, diags diag.Diagnostics) { - var ( - skipIfs []attr.Value - skipIfValue attr.Value - ) - - reviewers, diags_ := self.Reviewers.ToObject() - diags.Append(diags_...) - if diags.HasError() { - return invalid, diags - } - - policyType := types.ObjectType{ - map[string]attr.Type{ - "bundle": types.StringType, - "query": types.StringType, - }, - } - - for _, skipIf := range self.SkipIf { - obj, diags_ := types.ObjectValue( - map[string]attr.Type{ - "bundle": types.StringType, - "query": types.StringType, - }, - map[string]attr.Value{ - "bundle": types.StringValue(skipIf.Bundle.Unwrap()), - "query": types.StringValue(skipIf.Query.Unwrap()), - }, - ) - diags.Append(diags_...) - if diags.HasError() { - return invalid, diags - } - - skipIfs = append(skipIfs, obj) - } - - if skipIfs == nil { - skipIfValue = types.ListNull(policyType) - } else { - skipIfValue = types.ListValueMust(policyType, skipIfs) - } - - return types.ObjectValue( - ReviewStepAttrTypes(), - map[string]attr.Value{ - "reviewers": reviewers, - "skip_if": skipIfValue, - }, - ) -} - -func (self ReviewWorkflow) VisitWorkflow(visitor WorkflowVisitor) { - visitor.GrantKit(self) -} - -func (self ReviewerQuantifierAllOf) VisitReviewerQuantifier(visitor ReviewerQuantifierVisitor) { - visitor.AllOf(self) -} - -func (self ReviewerQuantifierOneOf) VisitReviewerQuantifier(visitor ReviewerQuantifierVisitor) { - visitor.OneOf(self) -} - -func (self ReviewerQuantifierEnvelope) MarshalJSON() ([]byte, error) { - var ( - tag string - ) - - self.Value.VisitReviewerQuantifier(ReviewerQuantifierVisitor{ - AllOf: func([]string) { tag = "AllOf" }, - OneOf: func([]string) { tag = "OneOf" }, - }) - - content, err := json.Marshal(self.Value) - if err != nil { - return nil, err - } - - return json.Marshal(enum{ - Type: tag, - Value: content, - }) -} - -func (self *ReviewerQuantifierEnvelope) UnmarshalJSON(data []byte) error { - var e enum - if err := json.Unmarshal(data, &e); err != nil { - return err - } - - var value ReviewerQuantifier - - switch e.Type { - case "AllOf": - var x ReviewerQuantifierAllOf - if err := json.Unmarshal(e.Value, &x); err != nil { - return err - } - - value = x - case "OneOf": - var x ReviewerQuantifierOneOf - if err := json.Unmarshal(e.Value, &x); err != nil { - return err - } - - value = x - default: - return fmt.Errorf("unknown review workflow type: %s", e.Type) - } - - *self = ReviewerQuantifierEnvelope{Value: value} - - return nil -} diff --git a/internal/abbey/resources/requestable/workflow_mapper.go b/internal/abbey/resources/requestable/workflow_mapper.go deleted file mode 100644 index 54df2c7..0000000 --- a/internal/abbey/resources/requestable/workflow_mapper.go +++ /dev/null @@ -1,148 +0,0 @@ -package requestable - -import ( - "errors" - "fmt" - - "github.com/hashicorp/terraform-plugin-go/tftypes" -) - -const ( - workflowTypeBuiltinTf = "builtin" - - builtinWorkflowTypeAllOfTf = "all_of" - builtinWorkflowTypeOneOfTf = "one_of" -) - -func BuiltinWorkflowFromTfTypesValue(value tftypes.Value) (ret *BuiltinWorkflow, err error) { - var m map[string]tftypes.Value - if err := value.As(&m); err != nil { - return nil, err - } - - if len(m) == 0 { - return nil, nil - } - - var inner BuiltinWorkflowEnum - - for key, val := range m { - switch key { - case builtinWorkflowTypeAllOfTf: - var inner_ *BuiltinWorkflowAllOf - inner_, err = AllOfFromGoValue(val) - if err != nil { - return nil, err - } - if inner_ == nil { - continue - } - - inner = inner_ - case builtinWorkflowTypeOneOfTf: - var inner_ *BuiltinWorkflowOneOf - inner_, err = OneOfFromGoValue(val) - if err != nil { - return nil, err - } - if inner_ == nil { - continue - } - - inner = inner_ - default: - return nil, fmt.Errorf("unknown key: %s", key) - } - if err != nil { - return nil, err - } - } - - return &BuiltinWorkflow{Value: inner}, nil -} - -func AllOfFromGoValue(value tftypes.Value) (*BuiltinWorkflowAllOf, error) { - var m map[string]tftypes.Value - if err := value.As(&m); err != nil { - return nil, err - } - - if len(m) == 0 { - return nil, nil - } - - reviewersValue, ok := m["reviewers"] - if !ok { - return nil, errors.New("missing reviewers field") - } - - reviewers, err := UserQueriesFromGoValue(reviewersValue) - if err != nil { - return nil, err - } - - return &BuiltinWorkflowAllOf{Reviewers: reviewers}, nil -} - -func OneOfFromGoValue(value tftypes.Value) (*BuiltinWorkflowOneOf, error) { - var m *map[string]tftypes.Value - if err := value.As(&m); err != nil { - return nil, err - } - - if m == nil { - return nil, nil - } - - reviewersValue, ok := (*m)["reviewers"] - if !ok { - return nil, errors.New("missing reviewers field") - } - - reviewers, err := UserQueriesFromGoValue(reviewersValue) - if err != nil { - return nil, err - } - - return &BuiltinWorkflowOneOf{Reviewers: reviewers}, nil -} - -func UserQueriesFromGoValue(value tftypes.Value) ([]UserQuery, error) { - var list []tftypes.Value - if err := value.As(&list); err != nil { - return nil, err - } - - userQueries := make([]UserQuery, 0, len(list)) - - for _, v := range list { - var m *map[string]tftypes.Value - if err := v.As(&m); err != nil { - return nil, err - } - - if m == nil { - return nil, errors.New("got nil user query") - } - - var inner UserQueryEnum - - for key, val := range *m { - switch key { - case userQueryTypeAuthIdTf: - var s string - if err := val.As(&s); err != nil { - return nil, err - } - - inner = &AuthId{Value: s} - default: - return nil, fmt.Errorf("unknowon key: %s", key) - } - } - - userQueries = append(userQueries, UserQuery{Value: inner}) - } - - return userQueries, nil -} diff --git a/internal/abbey/resources/requestable/workflow_tf.go b/internal/abbey/resources/requestable/workflow_tf.go deleted file mode 100644 index 3b34b85..0000000 --- a/internal/abbey/resources/requestable/workflow_tf.go +++ /dev/null @@ -1,491 +0,0 @@ -package requestable - -import ( - "context" - "fmt" - "strings" - - "github.com/hashicorp/terraform-plugin-framework/attr" - . "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" - - "abbey.io/terraform-provider-abbey/internal/abbey/value" -) - -var invalidWorkflow Workflow - -type WorkflowTf struct { - Workflow - - state value.State -} - -func NewWorkflow(w Workflow) WorkflowTf { - return WorkflowTf{Workflow: w, state: value.NewValidState()} -} - -func NewNullWorkflow() WorkflowTf { - return WorkflowTf{Workflow: invalidWorkflow, state: value.NewNullState()} -} - -func NewUnknownWorkflow() WorkflowTf { - return WorkflowTf{Workflow: invalidWorkflow, state: value.NewUnknownState()} -} - -func (w WorkflowTf) ToObjectValue(ctx context.Context) (object basetypes.ObjectValue, diags Diagnostics) { - w.state.Visit(value.StateVisitor{ - Null: func() { object = types.ObjectNull(w.AttrTypes(ctx)) }, - Unknown: func() { object = types.ObjectUnknown(w.AttrTypes(ctx)) }, - Valid: func() { - w.Value.VisitWorkflow(WorkflowVisitor{ - Builtin: func(workflow BuiltinWorkflow) { - var ( - diags_ Diagnostics - builtin BuiltinWorkflow - builtinValue attr.Value - ) - - builtinValue, diags = workflow.ToObjectValue(ctx) - if diags.HasError() { - return - } - - object, diags_ = types.ObjectValue( - map[string]attr.Type{ - workflowTypeBuiltinTf: builtin.Type(ctx), - }, - map[string]attr.Value{ - workflowTypeBuiltinTf: builtinValue, - }, - ) - diags.Append(diags_...) - }, - }) - }, - }) - - return object, diags -} - -func (w WorkflowTf) AttrTypes(ctx context.Context) map[string]attr.Type { - var builtin BuiltinWorkflow - - return map[string]attr.Type{ - workflowTypeBuiltinTf: builtin.Type(ctx), - } -} - -func (w WorkflowTf) Type(context.Context) attr.Type { - return WorkflowType{} -} - -func (w WorkflowTf) ToTerraformValue(ctx context.Context) (value tftypes.Value, err error) { - var ( - builtinValue tftypes.Value - type_ WorkflowType - ) - - w.Value.VisitWorkflow(WorkflowVisitor{ - Builtin: func(workflow BuiltinWorkflow) { - builtinValue, err = workflow.ToTerraformValue(ctx) - }, - }) - if err != nil { - return value, err - } - - return tftypes.NewValue( - type_.TerraformType(ctx), - map[string]tftypes.Value{ - workflowTypeBuiltinTf: builtinValue, - }, - ), nil -} - -func (w WorkflowTf) Equal(value attr.Value) bool { - rhs, err := value.ToTerraformValue(context.Background()) - if err != nil { - return false - } - - lhs, err := w.ToTerraformValue(context.Background()) - if err != nil { - return false - } - - return lhs.Equal(rhs) -} - -func (w WorkflowTf) IsNull() (null bool) { - w.state.Visit(value.StateVisitor{ - Null: func() { null = true }, - Unknown: func() {}, - Valid: func() {}, - }) - - return null -} - -func (w WorkflowTf) IsUnknown() (unknown bool) { - w.state.Visit(value.StateVisitor{ - Null: func() {}, - Unknown: func() { unknown = true }, - Valid: func() {}, - }) - - return unknown -} - -func (w WorkflowTf) String() string { - var inner string - - w.Value.VisitWorkflow(WorkflowVisitor{ - Builtin: func(workflow BuiltinWorkflow) { - inner = workflow.String() - }, - }) - - return fmt.Sprintf("Workflow{%s}", inner) -} - -func BuiltinWorkflowTfTypesType() tftypes.Type { - return tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - builtinWorkflowTypeAllOfTf: BuiltinWorkflowAllOfTfTypesType(), - builtinWorkflowTypeOneOfTf: BuiltinWorkflowOneOfTfTypesType(), - }, - OptionalAttributes: nil, - } -} - -func (w BuiltinWorkflow) ToObjectValue(ctx context.Context) (object basetypes.ObjectValue, diags Diagnostics) { - var ( - allOf BuiltinWorkflowAllOf - oneOf BuiltinWorkflowOneOf - allOfValue attr.Value = types.ObjectNull(allOf.AttrTypes(ctx)) - oneOfValue attr.Value = types.ObjectNull(oneOf.AttrTypes(ctx)) - ) - - w.Value.VisitBuiltinWorkflow(BuiltinWorkflowVisitor{ - AllOf: func(allOf BuiltinWorkflowAllOf) { - allOfValue, diags = allOf.ToObjectValue(ctx) - }, - OneOf: func(oneOf BuiltinWorkflowOneOf) { - oneOfValue, diags = oneOf.ToObjectValue(ctx) - }, - }) - if diags.HasError() { - return object, diags - } - - return types.ObjectValue( - map[string]attr.Type{ - builtinWorkflowTypeAllOfTf: allOf.Type(ctx), - builtinWorkflowTypeOneOfTf: oneOf.Type(ctx), - }, - map[string]attr.Value{ - builtinWorkflowTypeAllOfTf: allOfValue, - builtinWorkflowTypeOneOfTf: oneOfValue, - }, - ) -} - -func (w BuiltinWorkflow) Type(ctx context.Context) attr.Type { - var ( - allOf BuiltinWorkflowAllOf - oneOf BuiltinWorkflowOneOf - ) - - return types.ObjectType{AttrTypes: map[string]attr.Type{ - builtinWorkflowTypeAllOfTf: allOf.Type(ctx), - builtinWorkflowTypeOneOfTf: oneOf.Type(ctx), - }} -} - -func (w BuiltinWorkflow) ToTerraformValue(ctx context.Context) (value tftypes.Value, err error) { - var ( - allOfValue = tftypes.NewValue(BuiltinWorkflowAllOfTfTypesType(), nil) - oneOfValue = tftypes.NewValue(BuiltinWorkflowOneOfTfTypesType(), nil) - ) - - w.Value.VisitBuiltinWorkflow(BuiltinWorkflowVisitor{ - AllOf: func(allOf BuiltinWorkflowAllOf) { - allOfValue, err = allOf.ToTerraformValue(ctx) - }, - OneOf: func(oneOf BuiltinWorkflowOneOf) { - oneOfValue, err = oneOf.ToTerraformValue(ctx) - }, - }) - if err != nil { - return value, err - } - - return tftypes.NewValue( - BuiltinWorkflowTfTypesType(), - map[string]tftypes.Value{ - builtinWorkflowTypeAllOfTf: allOfValue, - builtinWorkflowTypeOneOfTf: oneOfValue, - }, - ), nil -} - -func (w BuiltinWorkflow) Equal(value attr.Value) bool { - rhs, err := value.ToTerraformValue(context.Background()) - if err != nil { - return false - } - - lhs, err := w.ToTerraformValue(context.Background()) - if err != nil { - return false - } - - return lhs.Equal(rhs) -} - -func (w BuiltinWorkflow) IsNull() (defined bool) { - w.Value.VisitBuiltinWorkflow(BuiltinWorkflowVisitor{ - AllOf: func(BuiltinWorkflowAllOf) { - defined = true - }, - OneOf: func(BuiltinWorkflowOneOf) { - defined = true - }, - }) - - return !defined -} - -func (w BuiltinWorkflow) IsUnknown() (defined bool) { - w.Value.VisitBuiltinWorkflow(BuiltinWorkflowVisitor{ - AllOf: func(BuiltinWorkflowAllOf) { - defined = true - }, - OneOf: func(BuiltinWorkflowOneOf) { - defined = true - }, - }) - - return !defined -} - -func (w BuiltinWorkflow) String() string { - var inner string - - w.Value.VisitBuiltinWorkflow(BuiltinWorkflowVisitor{ - AllOf: func(allOf BuiltinWorkflowAllOf) { - inner = allOf.String() - }, - OneOf: func(oneOf BuiltinWorkflowOneOf) { - inner = oneOf.String() - }, - }) - - return fmt.Sprintf("BuiltinWorkflow{%s}", inner) -} - -func BuiltinWorkflowAllOfTfTypesType() tftypes.Type { - return tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "reviewers": tftypes.List{ElementType: UserQueryTfTypesType()}, - }, - OptionalAttributes: nil, - } -} - -func (w BuiltinWorkflowAllOf) AttrTypes(ctx context.Context) map[string]attr.Type { - var userQuery UserQuery - - return map[string]attr.Type{ - "reviewers": types.ListType{ - ElemType: userQuery.Type(ctx), - }, - } -} - -func (w BuiltinWorkflowAllOf) Type(ctx context.Context) attr.Type { - return types.ObjectType{AttrTypes: w.AttrTypes(ctx)} -} - -func (w BuiltinWorkflowAllOf) ToTerraformValue(ctx context.Context) (value tftypes.Value, err error) { - reviewers := make([]tftypes.Value, 0, len(w.Reviewers)) - for _, reviewer := range w.Reviewers { - v, err := reviewer.ToTerraformValue(ctx) - if err != nil { - return value, err - } - - reviewers = append(reviewers, v) - } - - return tftypes.NewValue( - BuiltinWorkflowAllOfTfTypesType(), - map[string]tftypes.Value{ - "reviewers": tftypes.NewValue( - tftypes.List{ElementType: UserQueryTfTypesType()}, - reviewers, - ), - }, - ), nil -} - -func (w BuiltinWorkflowAllOf) Equal(value attr.Value) bool { - rhs, err := value.ToTerraformValue(context.Background()) - if err != nil { - return false - } - - lhs, err := w.ToTerraformValue(context.Background()) - if err != nil { - return false - } - - return lhs.Equal(rhs) -} - -func (w BuiltinWorkflowAllOf) IsNull() (defined bool) { - return false -} - -func (w BuiltinWorkflowAllOf) IsUnknown() (defined bool) { - return false -} - -func (w BuiltinWorkflowAllOf) String() string { - elemStrs := make([]string, 0, len(w.Reviewers)) - for _, reviewer := range w.Reviewers { - elemStrs = append(elemStrs, reviewer.String()) - } - - return fmt.Sprintf("BuiltinWorkflowAllOf{Reviewers: [%s]}", strings.Join(elemStrs, ", ")) -} - -func (w BuiltinWorkflowAllOf) ToObjectValue(context.Context) (object basetypes.ObjectValue, diags Diagnostics) { - reviewerValues := make([]attr.Value, 0, len(w.Reviewers)) - for _, reviewer := range w.Reviewers { - reviewerValues = append(reviewerValues, reviewer) - } - - reviewersValue, diags := basetypes.NewListValue(UserQueryType(), reviewerValues) - if diags.HasError() { - return object, diags - } - - return types.ObjectValue( - map[string]attr.Type{ - "reviewers": types.ListType{ - ElemType: UserQueryType(), - }, - }, - map[string]attr.Value{ - "reviewers": reviewersValue, - }, - ) -} - -func BuiltinWorkflowOneOfTfTypesType() tftypes.Type { - return tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "reviewers": tftypes.List{ElementType: UserQueryTfTypesType()}, - }, - OptionalAttributes: nil, - } -} - -func (w BuiltinWorkflowOneOf) AttrTypes(ctx context.Context) map[string]attr.Type { - var userQuery UserQuery - - return map[string]attr.Type{ - "reviewers": types.ListType{ - ElemType: userQuery.Type(ctx), - }, - } -} - -func (w BuiltinWorkflowOneOf) Type(ctx context.Context) attr.Type { - var userQuery UserQuery - - return types.ObjectType{AttrTypes: map[string]attr.Type{ - "reviewers": types.ListType{ - ElemType: userQuery.Type(ctx), - }, - }} -} - -func (w BuiltinWorkflowOneOf) ToTerraformValue(ctx context.Context) (value tftypes.Value, err error) { - reviewers := make([]tftypes.Value, 0, len(w.Reviewers)) - for _, reviewer := range w.Reviewers { - v, err := reviewer.ToTerraformValue(ctx) - if err != nil { - return value, err - } - - reviewers = append(reviewers, v) - } - - return tftypes.NewValue( - BuiltinWorkflowAllOfTfTypesType(), - map[string]tftypes.Value{ - "reviewers": tftypes.NewValue( - tftypes.List{ElementType: UserQueryTfTypesType()}, - reviewers, - ), - }, - ), nil -} - -func (w BuiltinWorkflowOneOf) Equal(value attr.Value) bool { - rhs, err := value.ToTerraformValue(context.Background()) - if err != nil { - return false - } - - lhs, err := w.ToTerraformValue(context.Background()) - if err != nil { - return false - } - - return lhs.Equal(rhs) -} - -func (w BuiltinWorkflowOneOf) IsNull() (defined bool) { - return false -} - -func (w BuiltinWorkflowOneOf) IsUnknown() (defined bool) { - return false -} - -func (w BuiltinWorkflowOneOf) String() string { - elemStrs := make([]string, 0, len(w.Reviewers)) - for _, reviewer := range w.Reviewers { - elemStrs = append(elemStrs, reviewer.String()) - } - - return fmt.Sprintf("BuiltinWorkflowOneOf{Reviewers: [%s]}", strings.Join(elemStrs, ", ")) -} - -func (w BuiltinWorkflowOneOf) ToObjectValue(context.Context) (object basetypes.ObjectValue, diags Diagnostics) { - reviewerValues := make([]attr.Value, 0, len(w.Reviewers)) - for _, reviewer := range w.Reviewers { - reviewerValues = append(reviewerValues, reviewer) - } - - reviewersValue, diags := basetypes.NewListValue(UserQueryType(), reviewerValues) - if diags.HasError() { - return object, diags - } - - return types.ObjectValue( - map[string]attr.Type{ - "reviewers": types.ListType{ - ElemType: UserQueryType(), - }, - }, - map[string]attr.Value{ - "reviewers": reviewersValue, - }, - ) -} diff --git a/internal/abbey/resources/requestable/workflow_type.go b/internal/abbey/resources/requestable/workflow_type.go deleted file mode 100644 index 41affaf..0000000 --- a/internal/abbey/resources/requestable/workflow_type.go +++ /dev/null @@ -1,106 +0,0 @@ -package requestable - -import ( - "context" - "fmt" - - "github.com/hashicorp/terraform-plugin-framework/attr" - . "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" -) - -type WorkflowType struct{} - -func (w WorkflowType) TerraformType(context.Context) tftypes.Type { - return tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - workflowTypeBuiltinTf: BuiltinWorkflowTfTypesType(), - }, - OptionalAttributes: nil, - } -} - -func (t WorkflowType) ValueFromTerraform(_ context.Context, value tftypes.Value) (value_ attr.Value, err error) { - if !value.IsKnown() { - return NewUnknownWorkflow(), nil - } - - var m *map[string]tftypes.Value - if err := value.As(&m); err != nil { - return nil, err - } - - if m == nil { - return NewNullWorkflow(), nil - } - - var inner WorkflowEnum - - for key, val := range *m { - switch key { - case workflowTypeBuiltinTf: - inner_, err := BuiltinWorkflowFromTfTypesValue(val) - if err != nil { - return nil, err - } - if inner_ == nil { - continue - } - - inner = inner_ - default: - return value_, fmt.Errorf("unknown key: %s", key) - } - } - if err != nil { - return value_, err - } - - return NewWorkflow(Workflow{Value: inner}), nil -} - -func (t WorkflowType) ValueType(context.Context) attr.Value { - var wtf WorkflowTf - return wtf -} - -func (t WorkflowType) Equal(ty attr.Type) bool { - _, ok := ty.(WorkflowType) - return ok -} - -func (WorkflowType) String() string { - return "Workflow" -} - -func (w WorkflowType) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { - attrName, ok := step.(tftypes.AttributeName) - if !ok { - return nil, fmt.Errorf("cannot apply step %T to WorkflowType", step) - } - - switch string(attrName) { - case workflowTypeBuiltinTf: - return BuiltinWorkflowTfTypesType(), nil - default: - return nil, fmt.Errorf("undefined attribute name %s in WorkflowType", attrName) - } -} - -func (t WorkflowType) ValueFromObject( - ctx context.Context, - value basetypes.ObjectValue, -) (basetypes.ObjectValuable, Diagnostics) { - var w WorkflowTf - - diags := value.As(ctx, &w, basetypes.ObjectAsOptions{ - UnhandledNullAsEmpty: false, - UnhandledUnknownAsEmpty: false, - }) - if diags.HasError() { - return nil, diags - } - - return w, diags -} diff --git a/internal/abbey/value/state.go b/internal/abbey/value/state.go deleted file mode 100644 index ed2d272..0000000 --- a/internal/abbey/value/state.go +++ /dev/null @@ -1,36 +0,0 @@ -package value - -type ( - State struct { - value state - } - - state interface { - VisitState(StateVisitor) - } - - StateVisitor struct { - Null func() - Unknown func() - Valid func() - } - - null struct{} - unknown struct{} - valid struct{} -) - -var ( - _ state = (*null)(nil) - _ state = (*unknown)(nil) - _ state = (*valid)(nil) -) - -func (null) VisitState(visitor StateVisitor) { visitor.Null() } -func (unknown) VisitState(visitor StateVisitor) { visitor.Unknown() } -func (valid) VisitState(visitor StateVisitor) { visitor.Valid() } -func (s State) Visit(visitor StateVisitor) { s.value.VisitState(visitor) } - -func NewNullState() State { return State{value: null{}} } -func NewUnknownState() State { return State{value: unknown{}} } -func NewValidState() State { return State{value: valid{}} } diff --git a/internal/planmodifiers/boolplanmodifier/suppress_diff.go b/internal/planmodifiers/boolplanmodifier/suppress_diff.go new file mode 100755 index 0000000..ec2d6b5 --- /dev/null +++ b/internal/planmodifiers/boolplanmodifier/suppress_diff.go @@ -0,0 +1,47 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package boolplanmodifier + +import ( + "abbey/internal/planmodifiers/utils" + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" +) + +// SuppressDiff returns a plan modifier that propagates a state value into the planned value, when it is Known, and the Plan Value is Unknown +func SuppressDiff() planmodifier.Bool { + return suppressDiff{} +} + +// suppressDiff implements the plan modifier. +type suppressDiff struct{} + +// Description returns a human-readable description of the plan modifier. +func (m suppressDiff) Description(_ context.Context) string { + return "Once set, the value of this attribute in state will not change." +} + +// MarkdownDescription returns a markdown description of the plan modifier. +func (m suppressDiff) MarkdownDescription(_ context.Context) string { + return "Once set, the value of this attribute in state will not change." +} + +// PlanModifyBool implements the plan modification logic. +func (m suppressDiff) PlanModifyBool(ctx context.Context, req planmodifier.BoolRequest, resp *planmodifier.BoolResponse) { + // Do nothing if there is a known planned value. + if !req.PlanValue.IsUnknown() { + return + } + + // Do nothing if there is an unknown configuration value + if req.ConfigValue.IsUnknown() { + return + } + + if utils.IsAllStateUnknown(ctx, req.State) { + return + } + + resp.PlanValue = req.StateValue +} diff --git a/internal/planmodifiers/float64planmodifier/suppress_diff.go b/internal/planmodifiers/float64planmodifier/suppress_diff.go new file mode 100755 index 0000000..5f92cb9 --- /dev/null +++ b/internal/planmodifiers/float64planmodifier/suppress_diff.go @@ -0,0 +1,47 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package float64planmodifier + +import ( + "abbey/internal/planmodifiers/utils" + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" +) + +// SuppressDiff returns a plan modifier that propagates a state value into the planned value, when it is Known, and the Plan Value is Unknown +func SuppressDiff() planmodifier.Float64 { + return suppressDiff{} +} + +// suppressDiff implements the plan modifier. +type suppressDiff struct{} + +// Description returns a human-readable description of the plan modifier. +func (m suppressDiff) Description(_ context.Context) string { + return "Once set, the value of this attribute in state will not change." +} + +// MarkdownDescription returns a markdown description of the plan modifier. +func (m suppressDiff) MarkdownDescription(_ context.Context) string { + return "Once set, the value of this attribute in state will not change." +} + +// PlanModifyFloat64 implements the plan modification logic. +func (m suppressDiff) PlanModifyFloat64(ctx context.Context, req planmodifier.Float64Request, resp *planmodifier.Float64Response) { + // Do nothing if there is a known planned value. + if !req.PlanValue.IsUnknown() { + return + } + + // Do nothing if there is an unknown configuration value + if req.ConfigValue.IsUnknown() { + return + } + + if utils.IsAllStateUnknown(ctx, req.State) { + return + } + + resp.PlanValue = req.StateValue +} diff --git a/internal/planmodifiers/int64planmodifier/suppress_diff.go b/internal/planmodifiers/int64planmodifier/suppress_diff.go new file mode 100755 index 0000000..8a39407 --- /dev/null +++ b/internal/planmodifiers/int64planmodifier/suppress_diff.go @@ -0,0 +1,47 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package int64planmodifier + +import ( + "abbey/internal/planmodifiers/utils" + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" +) + +// SuppressDiff returns a plan modifier that propagates a state value into the planned value, when it is Known, and the Plan Value is Unknown +func SuppressDiff() planmodifier.Int64 { + return suppressDiff{} +} + +// suppressDiff implements the plan modifier. +type suppressDiff struct{} + +// Description returns a human-readable description of the plan modifier. +func (m suppressDiff) Description(_ context.Context) string { + return "Once set, the value of this attribute in state will not change." +} + +// MarkdownDescription returns a markdown description of the plan modifier. +func (m suppressDiff) MarkdownDescription(_ context.Context) string { + return "Once set, the value of this attribute in state will not change." +} + +// PlanModifyInt64 implements the plan modification logic. +func (m suppressDiff) PlanModifyInt64(ctx context.Context, req planmodifier.Int64Request, resp *planmodifier.Int64Response) { + // Do nothing if there is a known planned value. + if !req.PlanValue.IsUnknown() { + return + } + + // Do nothing if there is an unknown configuration value + if req.ConfigValue.IsUnknown() { + return + } + + if utils.IsAllStateUnknown(ctx, req.State) { + return + } + + resp.PlanValue = req.StateValue +} diff --git a/internal/planmodifiers/listplanmodifier/suppress_diff.go b/internal/planmodifiers/listplanmodifier/suppress_diff.go new file mode 100755 index 0000000..8779191 --- /dev/null +++ b/internal/planmodifiers/listplanmodifier/suppress_diff.go @@ -0,0 +1,47 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package listplanmodifier + +import ( + "abbey/internal/planmodifiers/utils" + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" +) + +// SuppressDiff returns a plan modifier that propagates a state value into the planned value, when it is Known, and the Plan Value is Unknown +func SuppressDiff() planmodifier.List { + return suppressDiff{} +} + +// suppressDiff implements the plan modifier. +type suppressDiff struct{} + +// Description returns a human-readable description of the plan modifier. +func (m suppressDiff) Description(_ context.Context) string { + return "Once set, the value of this attribute in state will not change." +} + +// MarkdownDescription returns a markdown description of the plan modifier. +func (m suppressDiff) MarkdownDescription(_ context.Context) string { + return "Once set, the value of this attribute in state will not change." +} + +// PlanModifyList implements the plan modification logic. +func (m suppressDiff) PlanModifyList(ctx context.Context, req planmodifier.ListRequest, resp *planmodifier.ListResponse) { + // Do nothing if there is a known planned value. + if !req.PlanValue.IsUnknown() { + return + } + + // Do nothing if there is an unknown configuration value + if req.ConfigValue.IsUnknown() { + return + } + + if utils.IsAllStateUnknown(ctx, req.State) { + return + } + + resp.PlanValue = req.StateValue +} diff --git a/internal/planmodifiers/mapplanmodifier/suppress_diff.go b/internal/planmodifiers/mapplanmodifier/suppress_diff.go new file mode 100755 index 0000000..f41b260 --- /dev/null +++ b/internal/planmodifiers/mapplanmodifier/suppress_diff.go @@ -0,0 +1,47 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package mapplanmodifier + +import ( + "abbey/internal/planmodifiers/utils" + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" +) + +// SuppressDiff returns a plan modifier that propagates a state value into the planned value, when it is Known, and the Plan Value is Unknown +func SuppressDiff() planmodifier.Map { + return suppressDiff{} +} + +// suppressDiff implements the plan modifier. +type suppressDiff struct{} + +// Description returns a human-readable description of the plan modifier. +func (m suppressDiff) Description(_ context.Context) string { + return "Once set, the value of this attribute in state will not change." +} + +// MarkdownDescription returns a markdown description of the plan modifier. +func (m suppressDiff) MarkdownDescription(_ context.Context) string { + return "Once set, the value of this attribute in state will not change." +} + +// PlanModifyMap implements the plan modification logic. +func (m suppressDiff) PlanModifyMap(ctx context.Context, req planmodifier.MapRequest, resp *planmodifier.MapResponse) { + // Do nothing if there is a known planned value. + if !req.PlanValue.IsUnknown() { + return + } + + // Do nothing if there is an unknown configuration value + if req.ConfigValue.IsUnknown() { + return + } + + if utils.IsAllStateUnknown(ctx, req.State) { + return + } + + resp.PlanValue = req.StateValue +} diff --git a/internal/planmodifiers/numberplanmodifier/suppress_diff.go b/internal/planmodifiers/numberplanmodifier/suppress_diff.go new file mode 100755 index 0000000..0cf7096 --- /dev/null +++ b/internal/planmodifiers/numberplanmodifier/suppress_diff.go @@ -0,0 +1,47 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package numberplanmodifier + +import ( + "abbey/internal/planmodifiers/utils" + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" +) + +// SuppressDiff returns a plan modifier that propagates a state value into the planned value, when it is Known, and the Plan Value is Unknown +func SuppressDiff() planmodifier.Number { + return suppressDiff{} +} + +// suppressDiff implements the plan modifier. +type suppressDiff struct{} + +// Description returns a human-readable description of the plan modifier. +func (m suppressDiff) Description(_ context.Context) string { + return "Once set, the value of this attribute in state will not change." +} + +// MarkdownDescription returns a markdown description of the plan modifier. +func (m suppressDiff) MarkdownDescription(_ context.Context) string { + return "Once set, the value of this attribute in state will not change." +} + +// PlanModifyNumber implements the plan modification logic. +func (m suppressDiff) PlanModifyNumber(ctx context.Context, req planmodifier.NumberRequest, resp *planmodifier.NumberResponse) { + // Do nothing if there is a known planned value. + if !req.PlanValue.IsUnknown() { + return + } + + // Do nothing if there is an unknown configuration value + if req.ConfigValue.IsUnknown() { + return + } + + if utils.IsAllStateUnknown(ctx, req.State) { + return + } + + resp.PlanValue = req.StateValue +} diff --git a/internal/planmodifiers/objectplanmodifier/suppress_diff.go b/internal/planmodifiers/objectplanmodifier/suppress_diff.go new file mode 100755 index 0000000..b35891c --- /dev/null +++ b/internal/planmodifiers/objectplanmodifier/suppress_diff.go @@ -0,0 +1,47 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package objectplanmodifier + +import ( + "abbey/internal/planmodifiers/utils" + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" +) + +// SuppressDiff returns a plan modifier that propagates a state value into the planned value, when it is Known, and the Plan Value is Unknown +func SuppressDiff() planmodifier.Object { + return suppressDiff{} +} + +// suppressDiff implements the plan modifier. +type suppressDiff struct{} + +// Description returns a human-readable description of the plan modifier. +func (m suppressDiff) Description(_ context.Context) string { + return "Once set, the value of this attribute in state will not change." +} + +// MarkdownDescription returns a markdown description of the plan modifier. +func (m suppressDiff) MarkdownDescription(_ context.Context) string { + return "Once set, the value of this attribute in state will not change." +} + +// PlanModifyObject implements the plan modification logic. +func (m suppressDiff) PlanModifyObject(ctx context.Context, req planmodifier.ObjectRequest, resp *planmodifier.ObjectResponse) { + // Do nothing if there is a known planned value. + if !req.PlanValue.IsUnknown() { + return + } + + // Do nothing if there is an unknown configuration value + if req.ConfigValue.IsUnknown() { + return + } + + if utils.IsAllStateUnknown(ctx, req.State) { + return + } + + resp.PlanValue = req.StateValue +} diff --git a/internal/planmodifiers/setplanmodifier/suppress_diff.go b/internal/planmodifiers/setplanmodifier/suppress_diff.go new file mode 100755 index 0000000..68d6998 --- /dev/null +++ b/internal/planmodifiers/setplanmodifier/suppress_diff.go @@ -0,0 +1,47 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package setplanmodifier + +import ( + "abbey/internal/planmodifiers/utils" + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" +) + +// SuppressDiff returns a plan modifier that propagates a state value into the planned value, when it is Known, and the Plan Value is Unknown +func SuppressDiff() planmodifier.Set { + return suppressDiff{} +} + +// suppressDiff implements the plan modifier. +type suppressDiff struct{} + +// Description returns a human-readable description of the plan modifier. +func (m suppressDiff) Description(_ context.Context) string { + return "Once set, the value of this attribute in state will not change." +} + +// MarkdownDescription returns a markdown description of the plan modifier. +func (m suppressDiff) MarkdownDescription(_ context.Context) string { + return "Once set, the value of this attribute in state will not change." +} + +// PlanModifySet implements the plan modification logic. +func (m suppressDiff) PlanModifySet(ctx context.Context, req planmodifier.SetRequest, resp *planmodifier.SetResponse) { + // Do nothing if there is a known planned value. + if !req.PlanValue.IsUnknown() { + return + } + + // Do nothing if there is an unknown configuration value + if req.ConfigValue.IsUnknown() { + return + } + + if utils.IsAllStateUnknown(ctx, req.State) { + return + } + + resp.PlanValue = req.StateValue +} diff --git a/internal/planmodifiers/stringplanmodifier/suppress_diff.go b/internal/planmodifiers/stringplanmodifier/suppress_diff.go new file mode 100755 index 0000000..7050a35 --- /dev/null +++ b/internal/planmodifiers/stringplanmodifier/suppress_diff.go @@ -0,0 +1,47 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package stringplanmodifier + +import ( + "abbey/internal/planmodifiers/utils" + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" +) + +// SuppressDiff returns a plan modifier that propagates a state value into the planned value, when it is Known, and the Plan Value is Unknown +func SuppressDiff() planmodifier.String { + return suppressDiff{} +} + +// suppressDiff implements the plan modifier. +type suppressDiff struct{} + +// Description returns a human-readable description of the plan modifier. +func (m suppressDiff) Description(_ context.Context) string { + return "Once set, the value of this attribute in state will not change." +} + +// MarkdownDescription returns a markdown description of the plan modifier. +func (m suppressDiff) MarkdownDescription(_ context.Context) string { + return "Once set, the value of this attribute in state will not change." +} + +// PlanModifyString implements the plan modification logic. +func (m suppressDiff) PlanModifyString(ctx context.Context, req planmodifier.StringRequest, resp *planmodifier.StringResponse) { + // Do nothing if there is a known planned value. + if !req.PlanValue.IsUnknown() { + return + } + + // Do nothing if there is an unknown configuration value + if req.ConfigValue.IsUnknown() { + return + } + + if utils.IsAllStateUnknown(ctx, req.State) { + return + } + + resp.PlanValue = req.StateValue +} diff --git a/internal/planmodifiers/utils/state_check.go b/internal/planmodifiers/utils/state_check.go new file mode 100755 index 0000000..66d9150 --- /dev/null +++ b/internal/planmodifiers/utils/state_check.go @@ -0,0 +1,25 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package utils + +import ( + "context" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" +) + +func IsAllStateUnknown(ctx context.Context, state tfsdk.State) bool { + attrs := state.Schema.GetAttributes() + anyFound := false + for k, _ := range attrs { + attrValue := new(attr.Value) + state.GetAttribute(ctx, path.Root(k), attrValue) + if attrValue != nil && !(*attrValue).IsUnknown() && !(*attrValue).IsNull() { + anyFound = true + break + } + } + + return !anyFound +} diff --git a/internal/provider/grantkit_resource.go b/internal/provider/grantkit_resource.go new file mode 100755 index 0000000..4467b80 --- /dev/null +++ b/internal/provider/grantkit_resource.go @@ -0,0 +1,342 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package provider + +import ( + "abbey/internal/sdk" + "context" + "fmt" + + "abbey/internal/sdk/pkg/models/operations" + "abbey/internal/validators" + "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/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +// Ensure provider defined types fully satisfy framework interfaces. +var _ resource.Resource = &GrantKitResource{} +var _ resource.ResourceWithImportState = &GrantKitResource{} + +func NewGrantKitResource() resource.Resource { + return &GrantKitResource{} +} + +// GrantKitResource defines the resource implementation. +type GrantKitResource struct { + client *sdk.SDK +} + +// GrantKitResourceModel describes the resource data model. +type GrantKitResourceModel struct { + CreatedAt types.String `tfsdk:"created_at"` + CurrentVersionID types.String `tfsdk:"current_version_id"` + Description types.String `tfsdk:"description"` + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Output Output `tfsdk:"output"` + Policies []Policy `tfsdk:"policies"` + UpdatedAt types.String `tfsdk:"updated_at"` + Workflow *GrantWorkflow `tfsdk:"workflow"` +} + +func (r *GrantKitResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_grant_kit" +} + +func (r *GrantKitResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "GrantKit Resource", + + Attributes: map[string]schema.Attribute{ + "created_at": schema.StringAttribute{ + Computed: true, + Validators: []validator.String{ + validators.IsRFC3339(), + }, + }, + "current_version_id": schema.StringAttribute{ + Computed: true, + }, + "description": schema.StringAttribute{ + Required: true, + }, + "id": schema.StringAttribute{ + Computed: true, + }, + "name": schema.StringAttribute{ + Required: true, + }, + "output": schema.SingleNestedAttribute{ + Required: true, + Attributes: map[string]schema.Attribute{ + "append": schema.StringAttribute{ + Computed: true, + Optional: true, + }, + "location": schema.StringAttribute{ + Required: true, + }, + "overwrite": schema.StringAttribute{ + Computed: true, + Optional: true, + }, + }, + }, + "policies": schema.ListNestedAttribute{ + Computed: true, + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "bundle": schema.StringAttribute{ + Computed: true, + Optional: true, + }, + "query": schema.StringAttribute{ + Computed: true, + Optional: true, + }, + }, + }, + }, + "updated_at": schema.StringAttribute{ + Computed: true, + Validators: []validator.String{ + validators.IsRFC3339(), + }, + }, + "workflow": schema.SingleNestedAttribute{ + Computed: true, + Optional: true, + Attributes: map[string]schema.Attribute{ + "steps": schema.ListNestedAttribute{ + Required: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "reviewers": schema.SingleNestedAttribute{ + Required: true, + Attributes: map[string]schema.Attribute{ + "all_of": schema.ListAttribute{ + Computed: true, + Optional: true, + ElementType: types.StringType, + }, + "one_of": schema.ListAttribute{ + Computed: true, + Optional: true, + ElementType: types.StringType, + }, + }, + }, + "skip_if": schema.ListNestedAttribute{ + Computed: true, + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "bundle": schema.StringAttribute{ + Computed: true, + Optional: true, + }, + "query": schema.StringAttribute{ + Computed: true, + Optional: true, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func (r *GrantKitResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + // Prevent panic if the provider has not been configured. + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*sdk.SDK) + + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *sdk.SDK, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + r.client = client +} + +func (r *GrantKitResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data *GrantKitResourceModel + var item types.Object + + resp.Diagnostics.Append(req.Plan.Get(ctx, &item)...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(item.As(ctx, &data, basetypes.ObjectAsOptions{ + UnhandledNullAsEmpty: true, + UnhandledUnknownAsEmpty: true, + })...) + + if resp.Diagnostics.HasError() { + return + } + + request := *data.ToCreateSDKType() + res, err := r.client.GrantKits.CreateGrantKit(ctx, request) + if err != nil { + resp.Diagnostics.AddError("failure to invoke API", err.Error()) + return + } + if res == nil { + resp.Diagnostics.AddError("unexpected response from API", fmt.Sprintf("%v", res)) + return + } + if res.StatusCode != 201 { + resp.Diagnostics.AddError(fmt.Sprintf("unexpected response from API. Got an unexpected response code %v", res.StatusCode), debugResponse(res.RawResponse)) + return + } + if res.GrantKit == nil { + resp.Diagnostics.AddError("unexpected response from API. No response body", debugResponse(res.RawResponse)) + return + } + data.RefreshFromCreateResponse(res.GrantKit) + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *GrantKitResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data *GrantKitResourceModel + var item types.Object + + resp.Diagnostics.Append(req.State.Get(ctx, &item)...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(item.As(ctx, &data, basetypes.ObjectAsOptions{ + UnhandledNullAsEmpty: true, + UnhandledUnknownAsEmpty: true, + })...) + + if resp.Diagnostics.HasError() { + return + } + + grantKitIDOrName := data.ID.ValueString() + request := operations.GetGrantKitByIDRequest{ + GrantKitIDOrName: grantKitIDOrName, + } + res, err := r.client.GrantKits.GetGrantKitByID(ctx, request) + if err != nil { + resp.Diagnostics.AddError("failure to invoke API", err.Error()) + return + } + if res == nil { + resp.Diagnostics.AddError("unexpected response from API", fmt.Sprintf("%v", res)) + return + } + if res.StatusCode != 200 { + resp.Diagnostics.AddError(fmt.Sprintf("unexpected response from API. Got an unexpected response code %v", res.StatusCode), debugResponse(res.RawResponse)) + return + } + if res.GrantKit == nil { + resp.Diagnostics.AddError("unexpected response from API. No response body", debugResponse(res.RawResponse)) + return + } + data.RefreshFromGetResponse(res.GrantKit) + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *GrantKitResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var data *GrantKitResourceModel + merge(ctx, req, resp, &data) + if resp.Diagnostics.HasError() { + return + } + + grantKitUpdateParams := *data.ToUpdateSDKType() + grantKitIDOrName := data.ID.ValueString() + request := operations.UpdateGrantKitRequest{ + GrantKitUpdateParams: grantKitUpdateParams, + GrantKitIDOrName: grantKitIDOrName, + } + res, err := r.client.GrantKits.UpdateGrantKit(ctx, request) + if err != nil { + resp.Diagnostics.AddError("failure to invoke API", err.Error()) + return + } + if res == nil { + resp.Diagnostics.AddError("unexpected response from API", fmt.Sprintf("%v", res)) + return + } + if res.StatusCode != 200 { + resp.Diagnostics.AddError(fmt.Sprintf("unexpected response from API. Got an unexpected response code %v", res.StatusCode), debugResponse(res.RawResponse)) + return + } + if res.GrantKit == nil { + resp.Diagnostics.AddError("unexpected response from API. No response body", debugResponse(res.RawResponse)) + return + } + data.RefreshFromUpdateResponse(res.GrantKit) + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *GrantKitResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data *GrantKitResourceModel + var item types.Object + + resp.Diagnostics.Append(req.State.Get(ctx, &item)...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(item.As(ctx, &data, basetypes.ObjectAsOptions{ + UnhandledNullAsEmpty: true, + UnhandledUnknownAsEmpty: true, + })...) + + if resp.Diagnostics.HasError() { + return + } + + grantKitIDOrName := data.ID.ValueString() + request := operations.DeleteGrantKitRequest{ + GrantKitIDOrName: grantKitIDOrName, + } + res, err := r.client.GrantKits.DeleteGrantKit(ctx, request) + if err != nil { + resp.Diagnostics.AddError("failure to invoke API", err.Error()) + return + } + if res == nil { + resp.Diagnostics.AddError("unexpected response from API", fmt.Sprintf("%v", res)) + return + } + if res.StatusCode != 200 { + resp.Diagnostics.AddError(fmt.Sprintf("unexpected response from API. Got an unexpected response code %v", res.StatusCode), debugResponse(res.RawResponse)) + return + } + +} + +func (r *GrantKitResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} diff --git a/internal/provider/grantkit_resource_sdk.go b/internal/provider/grantkit_resource_sdk.go new file mode 100755 index 0000000..a6e993b --- /dev/null +++ b/internal/provider/grantkit_resource_sdk.go @@ -0,0 +1,286 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package provider + +import ( + "abbey/internal/sdk/pkg/models/shared" + "github.com/hashicorp/terraform-plugin-framework/types" + "time" +) + +func (r *GrantKitResourceModel) ToCreateSDKType() *shared.GrantKitCreateParams { + description := r.Description.ValueString() + name := r.Name.ValueString() + append1 := new(string) + if !r.Output.Append.IsUnknown() && !r.Output.Append.IsNull() { + *append1 = r.Output.Append.ValueString() + } else { + append1 = nil + } + location := r.Output.Location.ValueString() + overwrite := new(string) + if !r.Output.Overwrite.IsUnknown() && !r.Output.Overwrite.IsNull() { + *overwrite = r.Output.Overwrite.ValueString() + } else { + overwrite = nil + } + output := shared.Output{ + Append: append1, + Location: location, + Overwrite: overwrite, + } + policies := make([]shared.Policy, 0) + for _, policiesItem := range r.Policies { + bundle := new(string) + if !policiesItem.Bundle.IsUnknown() && !policiesItem.Bundle.IsNull() { + *bundle = policiesItem.Bundle.ValueString() + } else { + bundle = nil + } + query := new(string) + if !policiesItem.Query.IsUnknown() && !policiesItem.Query.IsNull() { + *query = policiesItem.Query.ValueString() + } else { + query = nil + } + policies = append(policies, shared.Policy{ + Bundle: bundle, + Query: query, + }) + } + var workflow *shared.GrantWorkflow + if r.Workflow != nil { + steps := make([]shared.Step, 0) + for _, stepsItem := range r.Workflow.Steps { + allOf := make([]string, 0) + for _, allOfItem := range stepsItem.Reviewers.AllOf { + allOf = append(allOf, allOfItem.ValueString()) + } + oneOf := make([]string, 0) + for _, oneOfItem := range stepsItem.Reviewers.OneOf { + oneOf = append(oneOf, oneOfItem.ValueString()) + } + reviewers := shared.Reviewers{ + AllOf: allOf, + OneOf: oneOf, + } + skipIf := make([]shared.Policy, 0) + for _, skipIfItem := range stepsItem.SkipIf { + bundle1 := new(string) + if !skipIfItem.Bundle.IsUnknown() && !skipIfItem.Bundle.IsNull() { + *bundle1 = skipIfItem.Bundle.ValueString() + } else { + bundle1 = nil + } + query1 := new(string) + if !skipIfItem.Query.IsUnknown() && !skipIfItem.Query.IsNull() { + *query1 = skipIfItem.Query.ValueString() + } else { + query1 = nil + } + skipIf = append(skipIf, shared.Policy{ + Bundle: bundle1, + Query: query1, + }) + } + steps = append(steps, shared.Step{ + Reviewers: reviewers, + SkipIf: skipIf, + }) + } + workflow = &shared.GrantWorkflow{ + Steps: steps, + } + } + out := shared.GrantKitCreateParams{ + Description: description, + Name: name, + Output: output, + Policies: policies, + Workflow: workflow, + } + return &out +} + +func (r *GrantKitResourceModel) ToGetSDKType() *shared.GrantKitCreateParams { + out := r.ToCreateSDKType() + return out +} + +func (r *GrantKitResourceModel) ToUpdateSDKType() *shared.GrantKitUpdateParams { + description := r.Description.ValueString() + name := r.Name.ValueString() + append1 := new(string) + if !r.Output.Append.IsUnknown() && !r.Output.Append.IsNull() { + *append1 = r.Output.Append.ValueString() + } else { + append1 = nil + } + location := r.Output.Location.ValueString() + overwrite := new(string) + if !r.Output.Overwrite.IsUnknown() && !r.Output.Overwrite.IsNull() { + *overwrite = r.Output.Overwrite.ValueString() + } else { + overwrite = nil + } + output := shared.Output{ + Append: append1, + Location: location, + Overwrite: overwrite, + } + policies := make([]shared.Policy, 0) + for _, policiesItem := range r.Policies { + bundle := new(string) + if !policiesItem.Bundle.IsUnknown() && !policiesItem.Bundle.IsNull() { + *bundle = policiesItem.Bundle.ValueString() + } else { + bundle = nil + } + query := new(string) + if !policiesItem.Query.IsUnknown() && !policiesItem.Query.IsNull() { + *query = policiesItem.Query.ValueString() + } else { + query = nil + } + policies = append(policies, shared.Policy{ + Bundle: bundle, + Query: query, + }) + } + var workflow *shared.GrantWorkflow + if r.Workflow != nil { + steps := make([]shared.Step, 0) + for _, stepsItem := range r.Workflow.Steps { + allOf := make([]string, 0) + for _, allOfItem := range stepsItem.Reviewers.AllOf { + allOf = append(allOf, allOfItem.ValueString()) + } + oneOf := make([]string, 0) + for _, oneOfItem := range stepsItem.Reviewers.OneOf { + oneOf = append(oneOf, oneOfItem.ValueString()) + } + reviewers := shared.Reviewers{ + AllOf: allOf, + OneOf: oneOf, + } + skipIf := make([]shared.Policy, 0) + for _, skipIfItem := range stepsItem.SkipIf { + bundle1 := new(string) + if !skipIfItem.Bundle.IsUnknown() && !skipIfItem.Bundle.IsNull() { + *bundle1 = skipIfItem.Bundle.ValueString() + } else { + bundle1 = nil + } + query1 := new(string) + if !skipIfItem.Query.IsUnknown() && !skipIfItem.Query.IsNull() { + *query1 = skipIfItem.Query.ValueString() + } else { + query1 = nil + } + skipIf = append(skipIf, shared.Policy{ + Bundle: bundle1, + Query: query1, + }) + } + steps = append(steps, shared.Step{ + Reviewers: reviewers, + SkipIf: skipIf, + }) + } + workflow = &shared.GrantWorkflow{ + Steps: steps, + } + } + out := shared.GrantKitUpdateParams{ + Description: description, + Name: name, + Output: output, + Policies: policies, + Workflow: workflow, + } + return &out +} + +func (r *GrantKitResourceModel) ToDeleteSDKType() *shared.GrantKitCreateParams { + out := r.ToCreateSDKType() + return out +} + +func (r *GrantKitResourceModel) RefreshFromGetResponse(resp *shared.GrantKit) { + r.CreatedAt = types.StringValue(resp.CreatedAt.Format(time.RFC3339)) + r.CurrentVersionID = types.StringValue(resp.CurrentVersionID) + r.Description = types.StringValue(resp.Description) + r.ID = types.StringValue(resp.ID) + r.Name = types.StringValue(resp.Name) + if resp.Output.Append != nil { + r.Output.Append = types.StringValue(*resp.Output.Append) + } else { + r.Output.Append = types.StringNull() + } + r.Output.Location = types.StringValue(resp.Output.Location) + if resp.Output.Overwrite != nil { + r.Output.Overwrite = types.StringValue(*resp.Output.Overwrite) + } else { + r.Output.Overwrite = types.StringNull() + } + r.Policies = nil + for _, policiesItem := range resp.Policies { + var policies1 Policy + if policiesItem.Bundle != nil { + policies1.Bundle = types.StringValue(*policiesItem.Bundle) + } else { + policies1.Bundle = types.StringNull() + } + if policiesItem.Query != nil { + policies1.Query = types.StringValue(*policiesItem.Query) + } else { + policies1.Query = types.StringNull() + } + r.Policies = append(r.Policies, policies1) + } + r.UpdatedAt = types.StringValue(resp.UpdatedAt.Format(time.RFC3339)) + if r.Workflow == nil { + r.Workflow = &GrantWorkflow{} + } + if resp.Workflow == nil { + r.Workflow = nil + } else { + r.Workflow = &GrantWorkflow{} + r.Workflow.Steps = nil + for _, stepsItem := range resp.Workflow.Steps { + var steps1 Step + steps1.Reviewers.AllOf = nil + for _, v := range stepsItem.Reviewers.AllOf { + steps1.Reviewers.AllOf = append(steps1.Reviewers.AllOf, types.StringValue(v)) + } + steps1.Reviewers.OneOf = nil + for _, v := range stepsItem.Reviewers.OneOf { + steps1.Reviewers.OneOf = append(steps1.Reviewers.OneOf, types.StringValue(v)) + } + steps1.SkipIf = nil + for _, skipIfItem := range stepsItem.SkipIf { + var skipIf1 Policy + if skipIfItem.Bundle != nil { + skipIf1.Bundle = types.StringValue(*skipIfItem.Bundle) + } else { + skipIf1.Bundle = types.StringNull() + } + if skipIfItem.Query != nil { + skipIf1.Query = types.StringValue(*skipIfItem.Query) + } else { + skipIf1.Query = types.StringNull() + } + steps1.SkipIf = append(steps1.SkipIf, skipIf1) + } + r.Workflow.Steps = append(r.Workflow.Steps, steps1) + } + } +} + +func (r *GrantKitResourceModel) RefreshFromCreateResponse(resp *shared.GrantKit) { + r.RefreshFromGetResponse(resp) +} + +func (r *GrantKitResourceModel) RefreshFromUpdateResponse(resp *shared.GrantKit) { + r.RefreshFromGetResponse(resp) +} diff --git a/internal/provider/identity_resource.go b/internal/provider/identity_resource.go new file mode 100755 index 0000000..6006277 --- /dev/null +++ b/internal/provider/identity_resource.go @@ -0,0 +1,238 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package provider + +import ( + "abbey/internal/sdk" + "context" + "fmt" + + "abbey/internal/sdk/pkg/models/operations" + "abbey/internal/validators" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +// Ensure provider defined types fully satisfy framework interfaces. +var _ resource.Resource = &IdentityResource{} +var _ resource.ResourceWithImportState = &IdentityResource{} + +func NewIdentityResource() resource.Resource { + return &IdentityResource{} +} + +// IdentityResource defines the resource implementation. +type IdentityResource struct { + client *sdk.SDK +} + +// IdentityResourceModel describes the resource data model. +type IdentityResourceModel struct { + CreatedAt types.String `tfsdk:"created_at"` + ID types.String `tfsdk:"id"` + Linked map[string][]types.String `tfsdk:"linked"` + Name types.String `tfsdk:"name"` +} + +func (r *IdentityResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_identity" +} + +func (r *IdentityResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "Identity Resource", + + Attributes: map[string]schema.Attribute{ + "created_at": schema.StringAttribute{ + Computed: true, + Validators: []validator.String{ + validators.IsRFC3339(), + }, + }, + "id": schema.StringAttribute{ + Computed: true, + }, + "linked": schema.MapAttribute{ + PlanModifiers: []planmodifier.Map{ + mapplanmodifier.RequiresReplace(), + }, + Required: true, + }, + "name": schema.StringAttribute{ + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Required: true, + }, + }, + } +} + +func (r *IdentityResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + // Prevent panic if the provider has not been configured. + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*sdk.SDK) + + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *sdk.SDK, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + r.client = client +} + +func (r *IdentityResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data *IdentityResourceModel + var item types.Object + + resp.Diagnostics.Append(req.Plan.Get(ctx, &item)...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(item.As(ctx, &data, basetypes.ObjectAsOptions{ + UnhandledNullAsEmpty: true, + UnhandledUnknownAsEmpty: true, + })...) + + if resp.Diagnostics.HasError() { + return + } + + request := *data.ToCreateSDKType() + res, err := r.client.Identities.CreateIdentity(ctx, request) + if err != nil { + resp.Diagnostics.AddError("failure to invoke API", err.Error()) + return + } + if res == nil { + resp.Diagnostics.AddError("unexpected response from API", fmt.Sprintf("%v", res)) + return + } + if res.StatusCode != 201 { + resp.Diagnostics.AddError(fmt.Sprintf("unexpected response from API. Got an unexpected response code %v", res.StatusCode), debugResponse(res.RawResponse)) + return + } + if res.Identity == nil { + resp.Diagnostics.AddError("unexpected response from API. No response body", debugResponse(res.RawResponse)) + return + } + data.RefreshFromCreateResponse(res.Identity) + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *IdentityResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data *IdentityResourceModel + var item types.Object + + resp.Diagnostics.Append(req.State.Get(ctx, &item)...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(item.As(ctx, &data, basetypes.ObjectAsOptions{ + UnhandledNullAsEmpty: true, + UnhandledUnknownAsEmpty: true, + })...) + + if resp.Diagnostics.HasError() { + return + } + + identityID := data.ID.ValueString() + request := operations.GetIdentityRequest{ + IdentityID: identityID, + } + res, err := r.client.Identities.GetIdentity(ctx, request) + if err != nil { + resp.Diagnostics.AddError("failure to invoke API", err.Error()) + return + } + if res == nil { + resp.Diagnostics.AddError("unexpected response from API", fmt.Sprintf("%v", res)) + return + } + if res.StatusCode != 200 { + resp.Diagnostics.AddError(fmt.Sprintf("unexpected response from API. Got an unexpected response code %v", res.StatusCode), debugResponse(res.RawResponse)) + return + } + if res.Identity == nil { + resp.Diagnostics.AddError("unexpected response from API. No response body", debugResponse(res.RawResponse)) + return + } + data.RefreshFromGetResponse(res.Identity) + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *IdentityResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var data *IdentityResourceModel + merge(ctx, req, resp, &data) + if resp.Diagnostics.HasError() { + return + } + + // Not Implemented; all attributes marked as RequiresReplace + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *IdentityResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data *IdentityResourceModel + var item types.Object + + resp.Diagnostics.Append(req.State.Get(ctx, &item)...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(item.As(ctx, &data, basetypes.ObjectAsOptions{ + UnhandledNullAsEmpty: true, + UnhandledUnknownAsEmpty: true, + })...) + + if resp.Diagnostics.HasError() { + return + } + + identityID := data.ID.ValueString() + request := operations.DeleteIdentityRequest{ + IdentityID: identityID, + } + res, err := r.client.Identities.DeleteIdentity(ctx, request) + if err != nil { + resp.Diagnostics.AddError("failure to invoke API", err.Error()) + return + } + if res == nil { + resp.Diagnostics.AddError("unexpected response from API", fmt.Sprintf("%v", res)) + return + } + if res.StatusCode != 204 { + resp.Diagnostics.AddError(fmt.Sprintf("unexpected response from API. Got an unexpected response code %v", res.StatusCode), debugResponse(res.RawResponse)) + return + } + +} + +func (r *IdentityResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} diff --git a/internal/provider/identity_resource_sdk.go b/internal/provider/identity_resource_sdk.go new file mode 100755 index 0000000..292b391 --- /dev/null +++ b/internal/provider/identity_resource_sdk.go @@ -0,0 +1,41 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package provider + +import ( + "abbey/internal/sdk/pkg/models/shared" + "github.com/hashicorp/terraform-plugin-framework/types" + "time" +) + +func (r *IdentityResourceModel) ToCreateSDKType() *shared.IdentityParams { + linked := make(map[string][]interface{}) + // Warning. This is a map, but the source tf var is not a map. This might indicate a bug. + name := r.Name.ValueString() + out := shared.IdentityParams{ + Linked: linked, + Name: name, + } + return &out +} + +func (r *IdentityResourceModel) ToGetSDKType() *shared.IdentityParams { + out := r.ToCreateSDKType() + return out +} + +func (r *IdentityResourceModel) ToDeleteSDKType() *shared.IdentityParams { + out := r.ToCreateSDKType() + return out +} + +func (r *IdentityResourceModel) RefreshFromGetResponse(resp *shared.Identity) { + r.CreatedAt = types.StringValue(resp.CreatedAt.Format(time.RFC3339)) + r.ID = types.StringValue(resp.ID) + // Not Implemented resp.Linked, {"AssociatedTypes":[],"Enum":null,"RefType":"","Truncated":false,"Output":false,"Name":"","ItemType":{"Input":false,"Examples":[],"Name":"","ItemType":{"Enum":null,"RefType":"","Discriminator":null,"Type":"any","ItemType":null,"Scope":"","Output":false,"Extensions":{"x-untouched":true,"x-speakeasy-param-computed":true,"x-speakeasy-param-force-new":true,"Symbol":""},"Examples":[],"AdditionalProperties":null,"AssociatedTypes":[],"Input":false,"Format":"","Name":"","Fields":[],"BaseName":"","Truncated":false,"Comments":null},"Scope":"","BaseName":"","Truncated":false,"Comments":null,"Output":false,"Extensions":{"x-untouched":true,"x-speakeasy-param-computed":true,"x-speakeasy-param-force-new":true},"AdditionalProperties":null,"Discriminator":null,"Type":"array","Enum":null,"Fields":[],"AssociatedTypes":[],"RefType":"","Format":""},"Scope":"","Comments":null,"Format":"","Type":"map","Fields":[],"AdditionalProperties":null,"BaseName":"","Input":false,"Extensions":{"x-untouched":true,"x-speakeasy-param-computed":true,"x-speakeasy-param-force-new":true},"Examples":[],"Discriminator":null}, false, , , r.Linked + r.Name = types.StringValue(resp.Name) +} + +func (r *IdentityResourceModel) RefreshFromCreateResponse(resp *shared.Identity) { + r.RefreshFromGetResponse(resp) +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go new file mode 100755 index 0000000..45c5331 --- /dev/null +++ b/internal/provider/provider.go @@ -0,0 +1,89 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package provider + +import ( + "abbey/internal/sdk" + "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" +) + +var _ provider.Provider = &AbbeyProvider{} + +type AbbeyProvider struct { + // version is set to the provider version on release, "dev" when the + // provider is built and ran locally, and "test" when running acceptance + // testing. + version string +} + +// AbbeyProviderModel describes the provider data model. +type AbbeyProviderModel struct { + ServerURL types.String `tfsdk:"server_url"` +} + +func (p *AbbeyProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) { + resp.TypeName = "abbey" + resp.Version = p.version +} + +func (p *AbbeyProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: `Edge API: The front door to Abbey Labs.`, + Attributes: map[string]schema.Attribute{ + "server_url": schema.StringAttribute{ + MarkdownDescription: "Server URL (defaults to https://api.abbey.io/v1)", + Optional: true, + Required: false, + }, + }, + } +} + +func (p *AbbeyProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { + var data AbbeyProviderModel + + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + ServerURL := data.ServerURL.ValueString() + + if ServerURL == "" { + ServerURL = "https://api.abbey.io/v1" + } + + opts := []sdk.SDKOption{ + sdk.WithServerURL(ServerURL), + } + client := sdk.New(opts...) + + resp.DataSourceData = client + resp.ResourceData = client +} + +func (p *AbbeyProvider) Resources(ctx context.Context) []func() resource.Resource { + return []func() resource.Resource{ + NewGrantKitResource, + NewIdentityResource, + } +} + +func (p *AbbeyProvider) DataSources(ctx context.Context) []func() datasource.DataSource { + return []func() datasource.DataSource{} +} + +func New(version string) func() provider.Provider { + return func() provider.Provider { + return &AbbeyProvider{ + version: version, + } + } +} diff --git a/internal/provider/reflect/diags.go b/internal/provider/reflect/diags.go new file mode 100755 index 0000000..a91bba9 --- /dev/null +++ b/internal/provider/reflect/diags.go @@ -0,0 +1,115 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package reflect + +import ( + "fmt" + "reflect" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +func toTerraform5ValueErrorDiag(err error, path path.Path) diag.DiagnosticWithPath { + return diag.NewAttributeErrorDiagnostic( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert into a Terraform value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) +} + +func toTerraformValueErrorDiag(err error, path path.Path) diag.DiagnosticWithPath { + return diag.NewAttributeErrorDiagnostic( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert the Attribute value into a Terraform value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) +} + +func validateValueErrorDiag(err error, path path.Path) diag.DiagnosticWithPath { + return diag.NewAttributeErrorDiagnostic( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to validate the Terraform value type. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) +} + +func valueFromTerraformErrorDiag(err error, path path.Path) diag.DiagnosticWithPath { + return diag.NewAttributeErrorDiagnostic( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert the Terraform value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) +} + +type DiagIntoIncompatibleType struct { + Val tftypes.Value + TargetType reflect.Type + Err error +} + +func (d DiagIntoIncompatibleType) Severity() diag.Severity { + return diag.SeverityError +} + +func (d DiagIntoIncompatibleType) Summary() string { + return "Value Conversion Error" +} + +func (d DiagIntoIncompatibleType) Detail() string { + return fmt.Sprintf("An unexpected error was encountered trying to convert %T into %s. This is always an error in the provider. Please report the following to the provider developer:\n\n%s", d.Val, d.TargetType, d.Err.Error()) +} + +func (d DiagIntoIncompatibleType) Equal(o diag.Diagnostic) bool { + od, ok := o.(DiagIntoIncompatibleType) + if !ok { + return false + } + if !d.Val.Equal(od.Val) { + return false + } + if d.TargetType != od.TargetType { + return false + } + if d.Err.Error() != od.Err.Error() { + return false + } + return true +} + +type DiagNewAttributeValueIntoWrongType struct { + ValType reflect.Type + TargetType reflect.Type + SchemaType attr.Type +} + +func (d DiagNewAttributeValueIntoWrongType) Severity() diag.Severity { + return diag.SeverityError +} + +func (d DiagNewAttributeValueIntoWrongType) Summary() string { + return "Value Conversion Error" +} + +func (d DiagNewAttributeValueIntoWrongType) Detail() string { + return fmt.Sprintf("An unexpected error was encountered trying to convert into a Terraform value. This is always an error in the provider. Please report the following to the provider developer:\n\nCannot use attr.Value %s, only %s is supported because %T is the type in the schema", d.TargetType, d.ValType, d.SchemaType) +} + +func (d DiagNewAttributeValueIntoWrongType) Equal(o diag.Diagnostic) bool { + od, ok := o.(DiagNewAttributeValueIntoWrongType) + if !ok { + return false + } + if d.ValType != od.ValType { + return false + } + if d.TargetType != od.TargetType { + return false + } + if !d.SchemaType.Equal(od.SchemaType) { + return false + } + return true +} diff --git a/internal/provider/reflect/doc.go b/internal/provider/reflect/doc.go new file mode 100755 index 0000000..ec91111 --- /dev/null +++ b/internal/provider/reflect/doc.go @@ -0,0 +1,9 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +// Package reflect is a forked version of https://github.com/hashicorp/terraform-plugin-framework/tree/main/internal/reflect +// that has been modified to support speakeasy's terraform generator. +// In particular, behaviour differs in that it is intended to support merging Terraform State and Terraform Plan structures +// into a single data structure, with Known Plan values overriding State values. This allows for code to be written +// that drives API calls from a single point of truth. +// Fork Commit hash is 99f28445b60580b6e39afda88a4bb469461f9bbb +package reflect diff --git a/internal/provider/reflect/generic_attr_value.go b/internal/provider/reflect/generic_attr_value.go new file mode 100755 index 0000000..430ce2f --- /dev/null +++ b/internal/provider/reflect/generic_attr_value.go @@ -0,0 +1,14 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package reflect + +import ( + "context" + "reflect" + + "github.com/hashicorp/terraform-plugin-framework/attr" +) + +func IsGenericAttrValue(ctx context.Context, target interface{}) bool { + return reflect.TypeOf((*attr.Value)(nil)) == reflect.TypeOf(target) +} diff --git a/internal/provider/reflect/helpers.go b/internal/provider/reflect/helpers.go new file mode 100755 index 0000000..82b9515 --- /dev/null +++ b/internal/provider/reflect/helpers.go @@ -0,0 +1,98 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package reflect + +import ( + "context" + "fmt" + "reflect" + "regexp" + "strings" + + "github.com/hashicorp/terraform-plugin-framework/path" +) + +// trueReflectValue returns the reflect.Value for `in` after derefencing all +// the pointers and unwrapping all the interfaces. It's the concrete value +// beneath it all. +func trueReflectValue(val reflect.Value) reflect.Value { + kind := val.Type().Kind() + for kind == reflect.Interface || kind == reflect.Ptr { + innerVal := val.Elem() + if !innerVal.IsValid() { + break + } + val = innerVal + kind = val.Type().Kind() + } + return val +} + +// commaSeparatedString returns an English joining of the strings in `in`, +// using "and" and commas as appropriate. +func commaSeparatedString(in []string) string { + switch len(in) { + case 0: + return "" + case 1: + return in[0] + case 2: + return strings.Join(in, " and ") + default: + in[len(in)-1] = "and " + in[len(in)-1] + return strings.Join(in, ", ") + } +} + +// getStructTags returns a map of Terraform field names to their position in +// the tags of the struct `in`. `in` must be a struct. +func getStructTags(_ context.Context, in reflect.Value, path path.Path) (map[string]int, error) { + tags := map[string]int{} + typ := trueReflectValue(in).Type() + if typ.Kind() != reflect.Struct { + return nil, fmt.Errorf("%s: can't get struct tags of %s, is not a struct", path, in.Type()) + } + for i := 0; i < typ.NumField(); i++ { + field := typ.Field(i) + if field.PkgPath != "" { + // skip unexported fields + continue + } + tag := field.Tag.Get(`tfsdk`) + if tag == "-" { + // skip explicitly excluded fields + continue + } + if tag == "" { + return nil, fmt.Errorf(`%s: need a struct tag for "tfsdk" on %s`, path, field.Name) + } + path := path.AtName(tag) + if !isValidFieldName(tag) { + return nil, fmt.Errorf("%s: invalid field name, must only use lowercase letters, underscores, and numbers, and must start with a letter", path) + } + if other, ok := tags[tag]; ok { + return nil, fmt.Errorf("%s: can't use field name for both %s and %s", path, typ.Field(other).Name, field.Name) + } + tags[tag] = i + } + return tags, nil +} + +// isValidFieldName returns true if `name` can be used as a field name in a +// Terraform resource or data source. +func isValidFieldName(name string) bool { + re := regexp.MustCompile("^[a-z][a-z0-9_]*$") + return re.MatchString(name) +} + +// canBeNil returns true if `target`'s type can hold a nil value +func canBeNil(target reflect.Value) bool { + switch target.Kind() { + case reflect.Ptr, reflect.Slice, reflect.Map, reflect.Interface: + // these types can all hold nils + return true + default: + // nothing else can be set to nil + return false + } +} diff --git a/internal/provider/reflect/interfaces.go b/internal/provider/reflect/interfaces.go new file mode 100755 index 0000000..364ff86 --- /dev/null +++ b/internal/provider/reflect/interfaces.go @@ -0,0 +1,353 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package reflect + +import ( + "context" + "fmt" + "reflect" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/attr/xattr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Unknownable is an interface for types that can be explicitly set to known or +// unknown. +type Unknownable interface { + SetUnknown(context.Context, bool) error + SetValue(context.Context, interface{}) error + GetUnknown(context.Context) bool + GetValue(context.Context) interface{} +} + +// NewUnknownable creates a zero value of `target` (or the concrete type it's +// referencing, if it's a pointer) and calls its SetUnknown method. +// +// It is meant to be called through Into, not directly. +func NewUnknownable(ctx context.Context, typ attr.Type, val tftypes.Value, target reflect.Value, opts Options, path path.Path) (reflect.Value, diag.Diagnostics) { + var diags diag.Diagnostics + receiver := pointerSafeZeroValue(ctx, target) + method := receiver.MethodByName("SetUnknown") + if !method.IsValid() { + err := fmt.Errorf("cannot find SetUnknown method on type %s", receiver.Type().String()) + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return target, diags + } + results := method.Call([]reflect.Value{ + reflect.ValueOf(ctx), + reflect.ValueOf(!val.IsKnown()), + }) + err := results[0].Interface() + if err != nil { + var underlyingErr error + switch e := err.(type) { + case error: + underlyingErr = e + default: + underlyingErr = fmt.Errorf("unknown error type %T: %v", e, e) + } + underlyingErr = fmt.Errorf("reflection error: %w", underlyingErr) + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert into a value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+underlyingErr.Error(), + ) + return target, diags + } + return receiver, diags +} + +// FromUnknownable creates an attr.Value from the data in an Unknownable. +// +// It is meant to be called through FromValue, not directly. +func FromUnknownable(ctx context.Context, typ attr.Type, val Unknownable, path path.Path) (attr.Value, diag.Diagnostics) { + var diags diag.Diagnostics + + if val.GetUnknown(ctx) { + tfVal := tftypes.NewValue(typ.TerraformType(ctx), tftypes.UnknownValue) + + if typeWithValidate, ok := typ.(xattr.TypeWithValidate); ok { + diags.Append(typeWithValidate.Validate(ctx, tfVal, path)...) + + if diags.HasError() { + return nil, diags + } + } + + res, err := typ.ValueFromTerraform(ctx, tfVal) + if err != nil { + return nil, append(diags, valueFromTerraformErrorDiag(err, path)) + } + return res, nil + } + err := tftypes.ValidateValue(typ.TerraformType(ctx), val.GetValue(ctx)) + if err != nil { + return nil, append(diags, validateValueErrorDiag(err, path)) + } + + tfVal := tftypes.NewValue(typ.TerraformType(ctx), val.GetValue(ctx)) + + if typeWithValidate, ok := typ.(xattr.TypeWithValidate); ok { + diags.Append(typeWithValidate.Validate(ctx, tfVal, path)...) + + if diags.HasError() { + return nil, diags + } + } + + res, err := typ.ValueFromTerraform(ctx, tfVal) + if err != nil { + return nil, append(diags, valueFromTerraformErrorDiag(err, path)) + } + return res, nil +} + +// Nullable is an interface for types that can be explicitly set to null. +type Nullable interface { + SetNull(context.Context, bool) error + SetValue(context.Context, interface{}) error + GetNull(context.Context) bool + GetValue(context.Context) interface{} +} + +// NewNullable creates a zero value of `target` (or the concrete type it's +// referencing, if it's a pointer) and calls its SetNull method. +// +// It is meant to be called through Into, not directly. +func NewNullable(ctx context.Context, typ attr.Type, val tftypes.Value, target reflect.Value, opts Options, path path.Path) (reflect.Value, diag.Diagnostics) { + var diags diag.Diagnostics + receiver := pointerSafeZeroValue(ctx, target) + method := receiver.MethodByName("SetNull") + if !method.IsValid() { + err := fmt.Errorf("cannot find SetNull method on type %s", receiver.Type().String()) + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return target, diags + } + results := method.Call([]reflect.Value{ + reflect.ValueOf(ctx), + reflect.ValueOf(val.IsNull()), + }) + err := results[0].Interface() + if err != nil { + var underlyingErr error + switch e := err.(type) { + case error: + underlyingErr = e + default: + underlyingErr = fmt.Errorf("unknown error type: %T", e) + } + underlyingErr = fmt.Errorf("reflection error: %w", underlyingErr) + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert into a value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+underlyingErr.Error(), + ) + return target, diags + } + return receiver, diags +} + +// FromNullable creates an attr.Value from the data in a Nullable. +// +// It is meant to be called through FromValue, not directly. +func FromNullable(ctx context.Context, typ attr.Type, val Nullable, path path.Path) (attr.Value, diag.Diagnostics) { + var diags diag.Diagnostics + + if val.GetNull(ctx) { + tfVal := tftypes.NewValue(typ.TerraformType(ctx), nil) + + if typeWithValidate, ok := typ.(xattr.TypeWithValidate); ok { + diags.Append(typeWithValidate.Validate(ctx, tfVal, path)...) + + if diags.HasError() { + return nil, diags + } + } + + res, err := typ.ValueFromTerraform(ctx, tfVal) + if err != nil { + return nil, append(diags, valueFromTerraformErrorDiag(err, path)) + } + return res, nil + } + err := tftypes.ValidateValue(typ.TerraformType(ctx), val.GetValue(ctx)) + if err != nil { + return nil, append(diags, validateValueErrorDiag(err, path)) + } + + tfVal := tftypes.NewValue(typ.TerraformType(ctx), val.GetValue(ctx)) + + if typeWithValidate, ok := typ.(xattr.TypeWithValidate); ok { + diags.Append(typeWithValidate.Validate(ctx, tfVal, path)...) + + if diags.HasError() { + return nil, diags + } + } + + res, err := typ.ValueFromTerraform(ctx, tfVal) + if err != nil { + return nil, append(diags, valueFromTerraformErrorDiag(err, path)) + } + return res, diags +} + +// NewValueConverter creates a zero value of `target` (or the concrete type +// it's referencing, if it's a pointer) and calls its FromTerraform5Value +// method. +// +// It is meant to be called through Into, not directly. +func NewValueConverter(ctx context.Context, typ attr.Type, val tftypes.Value, target reflect.Value, opts Options, path path.Path) (reflect.Value, diag.Diagnostics) { + var diags diag.Diagnostics + receiver := pointerSafeZeroValue(ctx, target) + method := receiver.MethodByName("FromTerraform5Value") + if !method.IsValid() { + err := fmt.Errorf("could not find FromTerraform5Type method on type %s", receiver.Type().String()) + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert into a value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return target, diags + } + results := method.Call([]reflect.Value{reflect.ValueOf(val)}) + err := results[0].Interface() + if err != nil { + var underlyingErr error + switch e := err.(type) { + case error: + underlyingErr = e + default: + underlyingErr = fmt.Errorf("unknown error type: %T", e) + } + underlyingErr = fmt.Errorf("reflection error: %w", underlyingErr) + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert into a value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+underlyingErr.Error(), + ) + return target, diags + } + return receiver, diags +} + +// FromValueCreator creates an attr.Value from the data in a +// tftypes.ValueCreator, calling its ToTerraform5Value method and converting +// the result to an attr.Value using `typ`. +// +// It is meant to be called from FromValue, not directly. +func FromValueCreator(ctx context.Context, typ attr.Type, val tftypes.ValueCreator, path path.Path) (attr.Value, diag.Diagnostics) { + var diags diag.Diagnostics + raw, err := val.ToTerraform5Value() + if err != nil { + return nil, append(diags, toTerraform5ValueErrorDiag(err, path)) + } + err = tftypes.ValidateValue(typ.TerraformType(ctx), raw) + if err != nil { + return nil, append(diags, validateValueErrorDiag(err, path)) + } + tfVal := tftypes.NewValue(typ.TerraformType(ctx), raw) + + if typeWithValidate, ok := typ.(xattr.TypeWithValidate); ok { + diags.Append(typeWithValidate.Validate(ctx, tfVal, path)...) + + if diags.HasError() { + return nil, diags + } + } + + res, err := typ.ValueFromTerraform(ctx, tfVal) + if err != nil { + return nil, append(diags, valueFromTerraformErrorDiag(err, path)) + } + return res, diags +} + +// NewAttributeValue creates a new reflect.Value by calling the +// ValueFromTerraform method on `typ`. It will return an error if the returned +// `attr.Value` is not the same type as `target`. +// +// It is meant to be called through Into, not directly. +func NewAttributeValue(ctx context.Context, typ attr.Type, val tftypes.Value, target reflect.Value, opts Options, path path.Path) (reflect.Value, diag.Diagnostics) { + var diags diag.Diagnostics + + if typeWithValidate, ok := typ.(xattr.TypeWithValidate); ok { + diags.Append(typeWithValidate.Validate(ctx, val, path)...) + + if diags.HasError() { + return target, diags + } + } + + res, err := typ.ValueFromTerraform(ctx, val) + if err != nil { + return target, append(diags, valueFromTerraformErrorDiag(err, path)) + } + if reflect.TypeOf(res) != target.Type() { + diags.Append(diag.WithPath(path, DiagNewAttributeValueIntoWrongType{ + ValType: reflect.TypeOf(res), + TargetType: target.Type(), + SchemaType: typ, + })) + return target, diags + } + return reflect.ValueOf(res), diags +} + +// FromAttributeValue creates an attr.Value from an attr.Value. It just returns +// the attr.Value it is passed or an error if there is an unexpected mismatch +// between the attr.Type and attr.Value. +// +// It is meant to be called through FromValue, not directly. +func FromAttributeValue(ctx context.Context, typ attr.Type, val attr.Value, path path.Path) (attr.Value, diag.Diagnostics) { + var diags diag.Diagnostics + + // Since the reflection logic is a generic Go type implementation with + // user input, it is possible to get into awkward situations where + // the logic is expecting a certain type while a value may not be + // compatible. This check will ensure the framework raises its own + // error is there is a mismatch, rather than a terraform-plugin-go + // error or worse a panic. + // + // If this validation causes major issues, another option is to + // validate via tftypes.Type for both the type and value type. + if !typ.Equal(val.Type(ctx)) { + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered while verifying an attribute value matched its expected type to prevent unexpected behavior or panics. "+ + "This is always an error in the provider. Please report the following to the provider developer:\n\n"+ + fmt.Sprintf("Expected type: %s\n", typ)+ + fmt.Sprintf("Value type: %s\n", val.Type(ctx))+ + fmt.Sprintf("Path: %s", path), + ) + + return nil, diags + } + + if typeWithValidate, ok := typ.(xattr.TypeWithValidate); ok { + tfVal, err := val.ToTerraformValue(ctx) + if err != nil { + return val, append(diags, toTerraformValueErrorDiag(err, path)) + } + + diags.Append(typeWithValidate.Validate(ctx, tfVal, path)...) + + if diags.HasError() { + return val, diags + } + } + + return val, diags +} diff --git a/internal/provider/reflect/into.go b/internal/provider/reflect/into.go new file mode 100755 index 0000000..c53d1be --- /dev/null +++ b/internal/provider/reflect/into.go @@ -0,0 +1,216 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package reflect + +import ( + "context" + "fmt" + "math/big" + "reflect" + + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" +) + +// Into uses the data in `val` to populate `target`, using the reflection +// package to recursively reflect into structs and slices. If `target` is an +// attr.Value, its assignment method will be used instead of reflecting. If +// `target` is a tftypes.ValueConverter, the FromTerraformValue method will be +// used instead of using reflection. Primitives are set using the val.As +// method. Structs use reflection: each exported struct field must have a +// "tfsdk" tag with the name of the field in the tftypes.Value, and all fields +// in the tftypes.Value must have a corresponding property in the struct. Into +// will be called for each struct field. Slices will have Into called for each +// element. +func Into(ctx context.Context, typ attr.Type, val tftypes.Value, target interface{}, opts Options, path path.Path) diag.Diagnostics { + var diags diag.Diagnostics + + v := reflect.ValueOf(target) + if v.Kind() != reflect.Ptr { + err := fmt.Errorf("target must be a pointer, got %T, which is a %s", target, v.Kind()) + diags.AddAttributeError( + path, + "Value Conversion Error", + fmt.Sprintf("An unexpected error was encountered trying to convert the value. This is always an error in the provider. Please report the following to the provider developer:\n\nPath: %s\nError: %s", path.String(), err.Error()), + ) + return diags + } + result, diags := BuildValue(ctx, typ, val, v.Elem(), opts, path) + if diags.HasError() { + return diags + } + v.Elem().Set(result) + return diags +} + +// BuildValue constructs a reflect.Value of the same type as `target`, +// populated with the data in `val`. It will defensively instantiate new values +// to set, making it safe for use with pointer types which may be nil. It tries +// to give consumers the ability to override its default behaviors wherever +// possible. +func BuildValue(ctx context.Context, typ attr.Type, val tftypes.Value, target reflect.Value, opts Options, path path.Path) (reflect.Value, diag.Diagnostics) { + var diags diag.Diagnostics + + // if this isn't a valid reflect.Value, bail before we accidentally + // panic + if !target.IsValid() { + err := fmt.Errorf("invalid target") + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to build a value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return target, diags + } + + // ------------------------- + // -- Fork Start ----------- + // ------------------------- + + if !val.IsKnown() { + return target, diags + } + + // ------------------------- + // -- Fork End - ----------- + // ------------------------- + + // if this is an attr.Value, build the type from that + if target.Type().Implements(reflect.TypeOf((*attr.Value)(nil)).Elem()) { + return NewAttributeValue(ctx, typ, val, target, opts, path) + } + // if this tells tftypes how to build an instance of it out of a + // tftypes.Value, well, that's what we want, so do that instead of our + // default logic. + if target.Type().Implements(reflect.TypeOf((*tftypes.ValueConverter)(nil)).Elem()) { + return NewValueConverter(ctx, typ, val, target, opts, path) + } + // if this can explicitly be set to unknown, do that + if target.Type().Implements(reflect.TypeOf((*Unknownable)(nil)).Elem()) { + res, unknownableDiags := NewUnknownable(ctx, typ, val, target, opts, path) + diags.Append(unknownableDiags...) + if diags.HasError() { + return target, diags + } + target = res + // only return if it's unknown; we want to call SetUnknown + // either way, but if the value is unknown, there's nothing + // else to do, so bail + if !val.IsKnown() { + return target, nil + } + } + // if this can explicitly be set to null, do that + if target.Type().Implements(reflect.TypeOf((*Nullable)(nil)).Elem()) { + res, nullableDiags := NewNullable(ctx, typ, val, target, opts, path) + diags.Append(nullableDiags...) + if diags.HasError() { + return target, diags + } + target = res + // only return if it's null; we want to call SetNull either + // way, but if the value is null, there's nothing else to do, + // so bail + if val.IsNull() { + return target, nil + } + } + // ------------------------- + // -- Fork Start ----------- + // ------------------------- + //if !val.IsKnown() { + // // we already handled unknown the only ways we can + // // we checked that target doesn't have a SetUnknown method we + // // can call + // // we checked that target isn't an attr.Value + // // all that's left to us now is to set it as an empty value or + // // throw an error, depending on what's in opts + // if !opts.UnhandledUnknownAsEmpty { + // diags.AddAttributeError( + // path, + // "Value Conversion Error", + // "An unexpected error was encountered trying to build a value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+ + // "Received unknown value, however the target type cannot handle unknown values. Use the corresponding `types` package type or a custom type that handles unknown values.\n\n"+ + // fmt.Sprintf("Path: %s\nTarget Type: %s\nSuggested Type: %s", path.String(), target.Type(), reflect.TypeOf(typ.ValueType(ctx))), + // ) + // return target, diags + // } + // // we want to set unhandled unknowns to the empty value + // return reflect.Zero(target.Type()), diags + //} + // ------------------------- + // -- Fork End - ----------- + // ------------------------- + + if val.IsNull() { + // we already handled null the only ways we can + // we checked that target doesn't have a SetNull method we can + // call + // we checked that target isn't an attr.Value + // all that's left to us now is to set it as an empty value or + // throw an error, depending on what's in opts + if canBeNil(target) || opts.UnhandledNullAsEmpty { + return reflect.Zero(target.Type()), nil + } + + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to build a value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+ + "Received null value, however the target type cannot handle null values. Use the corresponding `types` package type, a pointer type or a custom type that handles null values.\n\n"+ + fmt.Sprintf("Path: %s\nTarget Type: %s\nSuggested `types` Type: %s\nSuggested Pointer Type: *%s", path.String(), target.Type(), reflect.TypeOf(typ.ValueType(ctx)), target.Type()), + ) + + return target, diags + } + // *big.Float and *big.Int are technically pointers, but we want them + // handled as numbers + if target.Type() == reflect.TypeOf(big.NewFloat(0)) || target.Type() == reflect.TypeOf(big.NewInt(0)) { + return Number(ctx, typ, val, target, opts, path) + } + switch target.Kind() { + case reflect.Struct: + val, valDiags := Struct(ctx, typ, val, target, opts, path) + diags.Append(valDiags...) + return val, diags + case reflect.Bool, reflect.String: + val, valDiags := Primitive(ctx, typ, val, target, path) + diags.Append(valDiags...) + return val, diags + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, + reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, + reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64: + // numbers are the wooooorst and need their own special handling + // because we can't just hand them off to tftypes and also + // because we can't just make people use *big.Floats, because a + // nil *big.Float will crash everything if we don't handle it + // as a special case, so let's just special case numbers and + // let people use the types they want + val, valDiags := Number(ctx, typ, val, target, opts, path) + diags.Append(valDiags...) + return val, diags + case reflect.Slice: + val, valDiags := reflectSlice(ctx, typ, val, target, opts, path) + diags.Append(valDiags...) + return val, diags + case reflect.Map: + val, valDiags := Map(ctx, typ, val, target, opts, path) + diags.Append(valDiags...) + return val, diags + case reflect.Ptr: + val, valDiags := Pointer(ctx, typ, val, target, opts, path) + diags.Append(valDiags...) + return val, diags + default: + err := fmt.Errorf("don't know how to reflect %s into %s", val.Type(), target.Type()) + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to build a value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return target, diags + } +} diff --git a/internal/provider/reflect/map.go b/internal/provider/reflect/map.go new file mode 100755 index 0000000..3310d42 --- /dev/null +++ b/internal/provider/reflect/map.go @@ -0,0 +1,188 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package reflect + +import ( + "context" + "fmt" + "reflect" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/attr/xattr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Map creates a map value that matches the type of `target`, and populates it +// with the contents of `val`. +func Map(ctx context.Context, typ attr.Type, val tftypes.Value, target reflect.Value, opts Options, path path.Path) (reflect.Value, diag.Diagnostics) { + var diags diag.Diagnostics + underlyingValue := trueReflectValue(target) + + // this only works with maps, so check that out first + if underlyingValue.Kind() != reflect.Map { + diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{ + Val: val, + TargetType: target.Type(), + Err: fmt.Errorf("expected a map type, got %s", target.Type()), + })) + return target, diags + } + if !val.Type().Is(tftypes.Map{}) { + diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{ + Val: val, + TargetType: target.Type(), + Err: fmt.Errorf("cannot reflect %s into a map, must be a map", val.Type().String()), + })) + return target, diags + } + elemTyper, ok := typ.(attr.TypeWithElementType) + if !ok { + diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{ + Val: val, + TargetType: target.Type(), + Err: fmt.Errorf("cannot reflect map using type information provided by %T, %T must be an attr.TypeWithElementType", typ, typ), + })) + return target, diags + } + + // we need our value to become a map of values so we can iterate over + // them and handle them individually + values := map[string]tftypes.Value{} + err := val.As(&values) + if err != nil { + diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{ + Val: val, + TargetType: target.Type(), + Err: err, + })) + return target, diags + } + + // we need to know the type the slice is wrapping + elemType := underlyingValue.Type().Elem() + elemAttrType := elemTyper.ElementType() + + // we want an empty version of the map + m := reflect.MakeMapWithSize(underlyingValue.Type(), len(values)) + + // go over each of the values passed in, create a Go value of the right + // type for them, and add it to our new map + for key, value := range values { + // create a new Go value of the type that can go in the map + targetValue := reflect.Zero(elemType) + + // update our path so we can have nice errors + path := path.AtMapKey(key) + + // reflect the value into our new target + result, elemDiags := BuildValue(ctx, elemAttrType, value, targetValue, opts, path) + diags.Append(elemDiags...) + + if diags.HasError() { + return target, diags + } + + m.SetMapIndex(reflect.ValueOf(key), result) + } + + return m, diags +} + +// FromMap returns an attr.Value representing the data contained in `val`. +// `val` must be a map type with keys that are a string type. The attr.Value +// will be of the type produced by `typ`. +// +// It is meant to be called through FromValue, not directly. +func FromMap(ctx context.Context, typ attr.TypeWithElementType, val reflect.Value, path path.Path) (attr.Value, diag.Diagnostics) { + var diags diag.Diagnostics + tfType := typ.TerraformType(ctx) + + if val.IsNil() { + tfVal := tftypes.NewValue(tfType, nil) + + if typeWithValidate, ok := typ.(xattr.TypeWithValidate); ok { + diags.Append(typeWithValidate.Validate(ctx, tfVal, path)...) + + if diags.HasError() { + return nil, diags + } + } + + attrVal, err := typ.ValueFromTerraform(ctx, tfVal) + + if err != nil { + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert from map value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return nil, diags + } + + return attrVal, diags + } + + elemType := typ.ElementType() + tfElems := map[string]tftypes.Value{} + for _, key := range val.MapKeys() { + if key.Kind() != reflect.String { + err := fmt.Errorf("map keys must be strings, got %s", key.Type()) + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert into a Terraform value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return nil, diags + } + val, valDiags := FromValue(ctx, elemType, val.MapIndex(key).Interface(), path.AtMapKey(key.String())) + diags.Append(valDiags...) + + if diags.HasError() { + return nil, diags + } + tfVal, err := val.ToTerraformValue(ctx) + if err != nil { + return nil, append(diags, toTerraformValueErrorDiag(err, path)) + } + + if typeWithValidate, ok := elemType.(xattr.TypeWithValidate); ok { + diags.Append(typeWithValidate.Validate(ctx, tfVal, path.AtMapKey(key.String()))...) + + if diags.HasError() { + return nil, diags + } + } + + tfElems[key.String()] = tfVal + } + + err := tftypes.ValidateValue(tfType, tfElems) + if err != nil { + return nil, append(diags, validateValueErrorDiag(err, path)) + } + + tfVal := tftypes.NewValue(tfType, tfElems) + + if typeWithValidate, ok := typ.(xattr.TypeWithValidate); ok { + diags.Append(typeWithValidate.Validate(ctx, tfVal, path)...) + + if diags.HasError() { + return nil, diags + } + } + + attrVal, err := typ.ValueFromTerraform(ctx, tfVal) + + if err != nil { + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert to map value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return nil, diags + } + + return attrVal, diags +} diff --git a/internal/provider/reflect/number.go b/internal/provider/reflect/number.go new file mode 100755 index 0000000..a0e4126 --- /dev/null +++ b/internal/provider/reflect/number.go @@ -0,0 +1,374 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package reflect + +import ( + "context" + "fmt" + "math" + "math/big" + "reflect" + "strconv" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/attr/xattr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Number creates a *big.Float and populates it with the data in `val`. It then +// gets converted to the type of `target`, as long as `target` is a valid +// number type (any of the built-in int, uint, or float types, *big.Float, and +// *big.Int). +// +// Number will loudly fail when a number cannot be losslessly represented using +// the requested type, unless opts.AllowRoundingNumbers is set to true. This +// setting is mildly dangerous, because Terraform does not like when you round +// things, as a general rule of thumb. +// +// It is meant to be called through Into, not directly. +func Number(ctx context.Context, typ attr.Type, val tftypes.Value, target reflect.Value, opts Options, path path.Path) (reflect.Value, diag.Diagnostics) { + var diags diag.Diagnostics + result := big.NewFloat(0) + err := val.As(&result) + if err != nil { + diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{ + Err: err, + TargetType: target.Type(), + Val: val, + })) + return target, diags + } + roundingError := fmt.Errorf("cannot store %s in %s", result.String(), target.Type()) + roundingErrorDiag := diag.NewAttributeErrorDiagnostic( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert to number. This is always an error in the provider. Please report the following to the provider developer:\n\n"+roundingError.Error(), + ) + + switch target.Type() { + case reflect.TypeOf(big.NewFloat(0)): + return reflect.ValueOf(result), diags + case reflect.TypeOf(big.NewInt(0)): + intResult, acc := result.Int(nil) + if acc != big.Exact && !opts.AllowRoundingNumbers { + return reflect.ValueOf(result), append(diags, roundingErrorDiag) + } + return reflect.ValueOf(intResult), diags + } + switch target.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, + reflect.Int64: + intResult, acc := result.Int64() + if acc != big.Exact && !opts.AllowRoundingNumbers { + return target, append(diags, roundingErrorDiag) + } + switch target.Kind() { + case reflect.Int: + if strconv.IntSize == 32 && intResult > math.MaxInt32 { + if !opts.AllowRoundingNumbers { + return target, append(diags, roundingErrorDiag) + } + intResult = math.MaxInt32 + } + if strconv.IntSize == 32 && intResult < math.MinInt32 { + if !opts.AllowRoundingNumbers { + return target, append(diags, roundingErrorDiag) + } + intResult = math.MinInt32 + } + return reflect.ValueOf(int(intResult)), diags + case reflect.Int8: + if intResult > math.MaxInt8 { + if !opts.AllowRoundingNumbers { + return target, append(diags, roundingErrorDiag) + } + intResult = math.MaxInt8 + } + if intResult < math.MinInt8 { + if !opts.AllowRoundingNumbers { + return target, append(diags, roundingErrorDiag) + } + intResult = math.MinInt8 + } + return reflect.ValueOf(int8(intResult)), diags + case reflect.Int16: + if intResult > math.MaxInt16 { + if !opts.AllowRoundingNumbers { + return target, append(diags, roundingErrorDiag) + } + intResult = math.MaxInt16 + } + if intResult < math.MinInt16 { + if !opts.AllowRoundingNumbers { + return target, append(diags, roundingErrorDiag) + } + intResult = math.MinInt16 + } + return reflect.ValueOf(int16(intResult)), diags + case reflect.Int32: + if intResult > math.MaxInt32 { + if !opts.AllowRoundingNumbers { + return target, append(diags, roundingErrorDiag) + } + intResult = math.MaxInt32 + } + if intResult < math.MinInt32 { + if !opts.AllowRoundingNumbers { + return target, append(diags, roundingErrorDiag) + } + intResult = math.MinInt32 + } + return reflect.ValueOf(int32(intResult)), diags + case reflect.Int64: + return reflect.ValueOf(intResult), diags + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, + reflect.Uint64: + uintResult, acc := result.Uint64() + if acc != big.Exact && !opts.AllowRoundingNumbers { + return target, append(diags, roundingErrorDiag) + } + switch target.Kind() { + case reflect.Uint: + if strconv.IntSize == 32 && uintResult > math.MaxUint32 { + if !opts.AllowRoundingNumbers { + return target, append(diags, roundingErrorDiag) + } + uintResult = math.MaxUint32 + } + return reflect.ValueOf(uint(uintResult)), diags + case reflect.Uint8: + if uintResult > math.MaxUint8 { + if !opts.AllowRoundingNumbers { + return target, append(diags, roundingErrorDiag) + } + uintResult = math.MaxUint8 + } + return reflect.ValueOf(uint8(uintResult)), diags + case reflect.Uint16: + if uintResult > math.MaxUint16 { + if !opts.AllowRoundingNumbers { + return target, append(diags, roundingErrorDiag) + } + uintResult = math.MaxUint16 + } + return reflect.ValueOf(uint16(uintResult)), diags + case reflect.Uint32: + if uintResult > math.MaxUint32 { + if !opts.AllowRoundingNumbers { + return target, append(diags, roundingErrorDiag) + } + uintResult = math.MaxUint32 + } + return reflect.ValueOf(uint32(uintResult)), diags + case reflect.Uint64: + return reflect.ValueOf(uintResult), diags + } + case reflect.Float32: + floatResult, acc := result.Float32() + if acc != big.Exact && !opts.AllowRoundingNumbers { + return target, append(diags, roundingErrorDiag) + } else if acc == big.Above { + floatResult = math.MaxFloat32 + } else if acc == big.Below { + floatResult = math.SmallestNonzeroFloat32 + } else if acc != big.Exact { + err := fmt.Errorf("unsure how to round %s and %f", acc, floatResult) + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert to number. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return target, diags + } + return reflect.ValueOf(floatResult), diags + case reflect.Float64: + floatResult, acc := result.Float64() + if acc != big.Exact && !opts.AllowRoundingNumbers { + return target, append(diags, roundingErrorDiag) + } + if acc == big.Above { + if floatResult == math.Inf(1) || floatResult == math.MaxFloat64 { + floatResult = math.MaxFloat64 + } else if floatResult == 0.0 || floatResult == math.SmallestNonzeroFloat64 { + floatResult = -math.SmallestNonzeroFloat64 + } else { + err := fmt.Errorf("not sure how to round %s and %f", acc, floatResult) + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert to number. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return target, diags + } + } else if acc == big.Below { + if floatResult == math.Inf(-1) || floatResult == -math.MaxFloat64 { + floatResult = -math.MaxFloat64 + } else if floatResult == -0.0 || floatResult == -math.SmallestNonzeroFloat64 { //nolint:staticcheck + floatResult = math.SmallestNonzeroFloat64 + } else { + err := fmt.Errorf("not sure how to round %s and %f", acc, floatResult) + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert to number. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return target, diags + } + } else if acc != big.Exact { + err := fmt.Errorf("not sure how to round %s and %f", acc, floatResult) + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert to number. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return target, diags + } + return reflect.ValueOf(floatResult), diags + } + err = fmt.Errorf("cannot convert number to %s", target.Type()) + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert to number. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return target, diags +} + +// FromInt creates an attr.Value using `typ` from an int64. +// +// It is meant to be called through FromValue, not directly. +func FromInt(ctx context.Context, typ attr.Type, val int64, path path.Path) (attr.Value, diag.Diagnostics) { + var diags diag.Diagnostics + err := tftypes.ValidateValue(tftypes.Number, val) + if err != nil { + return nil, append(diags, validateValueErrorDiag(err, path)) + } + tfNum := tftypes.NewValue(tftypes.Number, val) + + if typeWithValidate, ok := typ.(xattr.TypeWithValidate); ok { + diags.Append(typeWithValidate.Validate(ctx, tfNum, path)...) + + if diags.HasError() { + return nil, diags + } + } + + num, err := typ.ValueFromTerraform(ctx, tfNum) + if err != nil { + return nil, append(diags, valueFromTerraformErrorDiag(err, path)) + } + + return num, diags +} + +// FromUint creates an attr.Value using `typ` from a uint64. +// +// It is meant to be called through FromValue, not directly. +func FromUint(ctx context.Context, typ attr.Type, val uint64, path path.Path) (attr.Value, diag.Diagnostics) { + var diags diag.Diagnostics + err := tftypes.ValidateValue(tftypes.Number, val) + if err != nil { + return nil, append(diags, validateValueErrorDiag(err, path)) + } + tfNum := tftypes.NewValue(tftypes.Number, val) + + if typeWithValidate, ok := typ.(xattr.TypeWithValidate); ok { + diags.Append(typeWithValidate.Validate(ctx, tfNum, path)...) + + if diags.HasError() { + return nil, diags + } + } + + num, err := typ.ValueFromTerraform(ctx, tfNum) + if err != nil { + return nil, append(diags, valueFromTerraformErrorDiag(err, path)) + } + + return num, diags +} + +// FromFloat creates an attr.Value using `typ` from a float64. +// +// It is meant to be called through FromValue, not directly. +func FromFloat(ctx context.Context, typ attr.Type, val float64, path path.Path) (attr.Value, diag.Diagnostics) { + var diags diag.Diagnostics + err := tftypes.ValidateValue(tftypes.Number, val) + if err != nil { + return nil, append(diags, validateValueErrorDiag(err, path)) + } + tfNum := tftypes.NewValue(tftypes.Number, val) + + if typeWithValidate, ok := typ.(xattr.TypeWithValidate); ok { + diags.Append(typeWithValidate.Validate(ctx, tfNum, path)...) + + if diags.HasError() { + return nil, diags + } + } + + num, err := typ.ValueFromTerraform(ctx, tfNum) + if err != nil { + return nil, append(diags, valueFromTerraformErrorDiag(err, path)) + } + + return num, diags +} + +// FromBigFloat creates an attr.Value using `typ` from a *big.Float. +// +// It is meant to be called through FromValue, not directly. +func FromBigFloat(ctx context.Context, typ attr.Type, val *big.Float, path path.Path) (attr.Value, diag.Diagnostics) { + var diags diag.Diagnostics + err := tftypes.ValidateValue(tftypes.Number, val) + if err != nil { + return nil, append(diags, validateValueErrorDiag(err, path)) + } + tfNum := tftypes.NewValue(tftypes.Number, val) + + if typeWithValidate, ok := typ.(xattr.TypeWithValidate); ok { + diags.Append(typeWithValidate.Validate(ctx, tfNum, path)...) + + if diags.HasError() { + return nil, diags + } + } + + num, err := typ.ValueFromTerraform(ctx, tfNum) + if err != nil { + return nil, append(diags, valueFromTerraformErrorDiag(err, path)) + } + + return num, diags +} + +// FromBigInt creates an attr.Value using `typ` from a *big.Int. +// +// It is meant to be called through FromValue, not directly. +func FromBigInt(ctx context.Context, typ attr.Type, val *big.Int, path path.Path) (attr.Value, diag.Diagnostics) { + var diags diag.Diagnostics + fl := big.NewFloat(0).SetInt(val) + err := tftypes.ValidateValue(tftypes.Number, fl) + if err != nil { + return nil, append(diags, validateValueErrorDiag(err, path)) + } + tfNum := tftypes.NewValue(tftypes.Number, fl) + + if typeWithValidate, ok := typ.(xattr.TypeWithValidate); ok { + diags.Append(typeWithValidate.Validate(ctx, tfNum, path)...) + + if diags.HasError() { + return nil, diags + } + } + + num, err := typ.ValueFromTerraform(ctx, tfNum) + if err != nil { + return nil, append(diags, valueFromTerraformErrorDiag(err, path)) + } + + return num, diags +} diff --git a/internal/provider/reflect/options.go b/internal/provider/reflect/options.go new file mode 100755 index 0000000..490c5f1 --- /dev/null +++ b/internal/provider/reflect/options.go @@ -0,0 +1,22 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package reflect + +// Options provides configuration settings for how the reflection behavior +// works, letting callers tweak different behaviors based on their needs. +type Options struct { + // UnhandledNullAsEmpty controls whether null values should be + // translated into empty values without provider interaction, or if + // they must be explicitly handled. + UnhandledNullAsEmpty bool + + // UnhandledUnknownAsEmpty controls whether null values should be + // translated into empty values without provider interaction, or if + // they must be explicitly handled. + UnhandledUnknownAsEmpty bool + + // AllowRoundingNumbers silently rounds numbers that don't fit + // perfectly in the types they're being stored in, rather than + // returning errors. Numbers will always be rounded towards 0. + AllowRoundingNumbers bool +} diff --git a/internal/provider/reflect/outof.go b/internal/provider/reflect/outof.go new file mode 100755 index 0000000..7852781 --- /dev/null +++ b/internal/provider/reflect/outof.go @@ -0,0 +1,94 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package reflect + +import ( + "context" + "fmt" + "math/big" + "reflect" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// FromValue is the inverse of Into, taking a Go value (`val`) and transforming it +// into an attr.Value using the attr.Type supplied. `val` will first be +// transformed into a tftypes.Value, then passed to `typ`'s ValueFromTerraform +// method. +func FromValue(ctx context.Context, typ attr.Type, val interface{}, path path.Path) (attr.Value, diag.Diagnostics) { + var diags diag.Diagnostics + + if v, ok := val.(attr.Value); ok { + return FromAttributeValue(ctx, typ, v, path) + } + if v, ok := val.(tftypes.ValueCreator); ok { + return FromValueCreator(ctx, typ, v, path) + } + if v, ok := val.(Unknownable); ok { + return FromUnknownable(ctx, typ, v, path) + } + if v, ok := val.(Nullable); ok { + return FromNullable(ctx, typ, v, path) + } + if bf, ok := val.(*big.Float); ok { + return FromBigFloat(ctx, typ, bf, path) + } + if bi, ok := val.(*big.Int); ok { + return FromBigInt(ctx, typ, bi, path) + } + value := reflect.ValueOf(val) + kind := value.Kind() + switch kind { + case reflect.Struct: + t, ok := typ.(attr.TypeWithAttributeTypes) + if !ok { + err := fmt.Errorf("cannot use type %T as schema type %T; %T must be an attr.TypeWithAttributeTypes to hold %T", val, typ, typ, val) + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert from value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return nil, diags + } + return FromStruct(ctx, t, value, path) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, + reflect.Int64: + return FromInt(ctx, typ, value.Int(), path) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, + reflect.Uint64: + return FromUint(ctx, typ, value.Uint(), path) + case reflect.Float32, reflect.Float64: + return FromFloat(ctx, typ, value.Float(), path) + case reflect.Bool: + return FromBool(ctx, typ, value.Bool(), path) + case reflect.String: + return FromString(ctx, typ, value.String(), path) + case reflect.Slice: + return FromSlice(ctx, typ, value, path) + case reflect.Map: + t, ok := typ.(attr.TypeWithElementType) + if !ok { + err := fmt.Errorf("cannot use type %T as schema type %T; %T must be an attr.TypeWithElementType to hold %T", val, typ, typ, val) + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert from value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return nil, diags + } + return FromMap(ctx, t, value, path) + case reflect.Ptr: + return FromPointer(ctx, typ, value, path) + default: + err := fmt.Errorf("cannot construct attr.Type from %T (%s)", val, kind) + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert from value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return nil, diags + } +} diff --git a/internal/provider/reflect/pointer.go b/internal/provider/reflect/pointer.go new file mode 100755 index 0000000..e02c9eb --- /dev/null +++ b/internal/provider/reflect/pointer.go @@ -0,0 +1,126 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package reflect + +import ( + "context" + "fmt" + "reflect" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/attr/xattr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Pointer builds a new zero value of the concrete type that `target` +// references, populates it with BuildValue, and takes a pointer to it. +// +// It is meant to be called through Into, not directly. +func Pointer(ctx context.Context, typ attr.Type, val tftypes.Value, target reflect.Value, opts Options, path path.Path) (reflect.Value, diag.Diagnostics) { + var diags diag.Diagnostics + + if target.Kind() != reflect.Ptr { + diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{ + Val: val, + TargetType: target.Type(), + Err: fmt.Errorf("cannot dereference pointer, not a pointer, is a %s (%s)", target.Type(), target.Kind()), + })) + return target, diags + } + // we may have gotten a nil pointer, so we need to create our own that + // we can set + // FORK START + pointer := target + if target.IsNil() { + pointer = reflect.New(target.Type().Elem()) + } + // FORK END + // build out whatever the pointer is pointing to + pointed, pointedDiags := BuildValue(ctx, typ, val, pointer.Elem(), opts, path) + diags.Append(pointedDiags...) + + if diags.HasError() { + return target, diags + } + // to be able to set the pointer to our new pointer, we need to create + // a pointer to the pointer + pointerPointer := reflect.New(pointer.Type()) + // we set the pointer we created on the pointer to the pointer + pointerPointer.Elem().Set(pointer) + // then it's settable, so we can now set the concrete value we created + // on the pointer + pointerPointer.Elem().Elem().Set(pointed) + // return the pointer we created + return pointerPointer.Elem(), diags +} + +// create a zero value of concrete type underlying any number of pointers, then +// wrap it in that number of pointers again. The end result is to wind up with +// the same exact type, except now you can be sure it's pointing to actual data +// and will not give you a nil pointer dereference panic unexpectedly. +func pointerSafeZeroValue(_ context.Context, target reflect.Value) reflect.Value { + pointer := target.Type() + var pointers int + for pointer.Kind() == reflect.Ptr { + pointer = pointer.Elem() + pointers++ + } + receiver := reflect.Zero(pointer) + for i := 0; i < pointers; i++ { + newReceiver := reflect.New(receiver.Type()) + newReceiver.Elem().Set(receiver) + receiver = newReceiver + } + return receiver +} + +// FromPointer turns a pointer into an attr.Value using `typ`. If the pointer +// is nil, the attr.Value will use its null representation. If it is not nil, +// it will recurse into FromValue to find the attr.Value of the type the value +// the pointer is referencing. +// +// It is meant to be called through FromValue, not directly. +func FromPointer(ctx context.Context, typ attr.Type, value reflect.Value, path path.Path) (attr.Value, diag.Diagnostics) { + var diags diag.Diagnostics + + if value.Kind() != reflect.Ptr { + err := fmt.Errorf("cannot use type %s as a pointer", value.Type()) + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert from pointer value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return nil, diags + } + if value.IsNil() { + tfVal := tftypes.NewValue(typ.TerraformType(ctx), nil) + + if typeWithValidate, ok := typ.(xattr.TypeWithValidate); ok { + diags.Append(typeWithValidate.Validate(ctx, tfVal, path)...) + + if diags.HasError() { + return nil, diags + } + } + + attrVal, err := typ.ValueFromTerraform(ctx, tfVal) + + if err != nil { + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert from pointer value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return nil, diags + } + + return attrVal, diags + } + + attrVal, attrValDiags := FromValue(ctx, typ, value.Elem().Interface(), path) + diags.Append(attrValDiags...) + + return attrVal, diags +} diff --git a/internal/provider/reflect/primitive.go b/internal/provider/reflect/primitive.go new file mode 100755 index 0000000..3113c39 --- /dev/null +++ b/internal/provider/reflect/primitive.go @@ -0,0 +1,111 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package reflect + +import ( + "context" + "errors" + "reflect" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/attr/xattr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Primitive builds a string or boolean, depending on the type of `target`, and +// populates it with the data in `val`. +// +// It is meant to be called through `Into`, not directly. +func Primitive(ctx context.Context, typ attr.Type, val tftypes.Value, target reflect.Value, path path.Path) (reflect.Value, diag.Diagnostics) { + var diags diag.Diagnostics + + switch target.Kind() { + case reflect.Bool: + var b bool + err := val.As(&b) + if err != nil { + diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{ + Val: val, + TargetType: target.Type(), + Err: err, + })) + return target, diags + } + return reflect.ValueOf(b).Convert(target.Type()), nil + case reflect.String: + var s string + err := val.As(&s) + if err != nil { + diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{ + Val: val, + TargetType: target.Type(), + Err: err, + })) + return target, diags + } + return reflect.ValueOf(s).Convert(target.Type()), nil + default: + diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{ + Val: val, + TargetType: target.Type(), + Err: errors.New("unknown type"), + })) + return target, diags + } +} + +// FromString returns an attr.Value as produced by `typ` from a string. +// +// It is meant to be called through FromValue, not directly. +func FromString(ctx context.Context, typ attr.Type, val string, path path.Path) (attr.Value, diag.Diagnostics) { + var diags diag.Diagnostics + err := tftypes.ValidateValue(tftypes.String, val) + if err != nil { + return nil, append(diags, validateValueErrorDiag(err, path)) + } + tfStr := tftypes.NewValue(tftypes.String, val) + + if typeWithValidate, ok := typ.(xattr.TypeWithValidate); ok { + diags.Append(typeWithValidate.Validate(ctx, tfStr, path)...) + + if diags.HasError() { + return nil, diags + } + } + + str, err := typ.ValueFromTerraform(ctx, tfStr) + if err != nil { + return nil, append(diags, valueFromTerraformErrorDiag(err, path)) + } + + return str, diags +} + +// FromBool returns an attr.Value as produced by `typ` from a bool. +// +// It is meant to be called through FromValue, not directly. +func FromBool(ctx context.Context, typ attr.Type, val bool, path path.Path) (attr.Value, diag.Diagnostics) { + var diags diag.Diagnostics + err := tftypes.ValidateValue(tftypes.Bool, val) + if err != nil { + return nil, append(diags, validateValueErrorDiag(err, path)) + } + tfBool := tftypes.NewValue(tftypes.Bool, val) + + if typeWithValidate, ok := typ.(xattr.TypeWithValidate); ok { + diags.Append(typeWithValidate.Validate(ctx, tfBool, path)...) + + if diags.HasError() { + return nil, diags + } + } + + b, err := typ.ValueFromTerraform(ctx, tfBool) + if err != nil { + return nil, append(diags, valueFromTerraformErrorDiag(err, path)) + } + + return b, diags +} diff --git a/internal/provider/reflect/slice.go b/internal/provider/reflect/slice.go new file mode 100755 index 0000000..b91f729 --- /dev/null +++ b/internal/provider/reflect/slice.go @@ -0,0 +1,214 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package reflect + +import ( + "context" + "fmt" + "reflect" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/attr/xattr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// build a slice of elements, matching the type of `target`, and fill it with +// the data in `val`. +func reflectSlice(ctx context.Context, typ attr.Type, val tftypes.Value, target reflect.Value, opts Options, path path.Path) (reflect.Value, diag.Diagnostics) { + var diags diag.Diagnostics + + // this only works with slices, so check that out first + if target.Kind() != reflect.Slice { + diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{ + Val: val, + TargetType: target.Type(), + Err: fmt.Errorf("expected a slice type, got %s", target.Type()), + })) + return target, diags + } + // TODO: check that the val is a list or set or tuple + elemTyper, ok := typ.(attr.TypeWithElementType) + if !ok { + diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{ + Val: val, + TargetType: target.Type(), + Err: fmt.Errorf("cannot reflect %s using type information provided by %T, %T must be an attr.TypeWithElementType", val.Type(), typ, typ), + })) + return target, diags + } + + // we need our value to become a list of values so we can iterate over + // them and handle them individually + var values []tftypes.Value + err := val.As(&values) + if err != nil { + diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{ + Val: val, + TargetType: target.Type(), + Err: err, + })) + return target, diags + } + + // we need to know the type the slice is wrapping + elemType := target.Type().Elem() + elemAttrType := elemTyper.ElementType() + + // we want an empty version of the slice + slice := reflect.MakeSlice(target.Type(), 0, len(values)) + + // go over each of the values passed in, create a Go value of the right + // type for them, and add it to our new slice + for pos, value := range values { + // create a new Go value of the type that can go in the slice + targetValue := reflect.Zero(elemType) + + // update our path so we can have nice errors + valPath := path.AtListIndex(pos) + + if typ.TerraformType(ctx).Is(tftypes.Set{}) { + attrVal, err := elemAttrType.ValueFromTerraform(ctx, value) + + if err != nil { + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert to slice value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return target, diags + } + + valPath = path.AtSetValue(attrVal) + } + + // reflect the value into our new target + val, valDiags := BuildValue(ctx, elemAttrType, value, targetValue, opts, valPath) + diags.Append(valDiags...) + + if diags.HasError() { + return target, diags + } + + // add the new target to our slice + slice = reflect.Append(slice, val) + } + + return slice, diags +} + +// FromSlice returns an attr.Value as produced by `typ` using the data in +// `val`. `val` must be a slice. `typ` must be an attr.TypeWithElementType or +// attr.TypeWithElementTypes. If the slice is nil, the representation of null +// for `typ` will be returned. Otherwise, FromSlice will recurse into FromValue +// for each element in the slice, using the element type or types defined on +// `typ` to construct values for them. +// +// It is meant to be called through FromValue, not directly. +func FromSlice(ctx context.Context, typ attr.Type, val reflect.Value, path path.Path) (attr.Value, diag.Diagnostics) { + var diags diag.Diagnostics + + // TODO: support tuples, which are attr.TypeWithElementTypes + tfType := typ.TerraformType(ctx) + + if val.IsNil() { + tfVal := tftypes.NewValue(tfType, nil) + + if typeWithValidate, ok := typ.(xattr.TypeWithValidate); ok { + diags.Append(typeWithValidate.Validate(ctx, tfVal, path)...) + + if diags.HasError() { + return nil, diags + } + } + + attrVal, err := typ.ValueFromTerraform(ctx, tfVal) + + if err != nil { + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert from slice value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return nil, diags + } + + return attrVal, diags + } + + t, ok := typ.(attr.TypeWithElementType) + if !ok { + err := fmt.Errorf("cannot use type %T as schema type %T; %T must be an attr.TypeWithElementType to hold %T", val, typ, typ, val) + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert from slice value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return nil, diags + } + + elemType := t.ElementType() + tfElems := make([]tftypes.Value, 0, val.Len()) + for i := 0; i < val.Len(); i++ { + // The underlying reflect.Slice is fetched by Index(). For set types, + // the path is value-based instead of index-based. Since there is only + // the index until the value is retrieved, this will pass the + // technically incorrect index-based path at first for framework + // debugging purposes, then correct the path afterwards. + valPath := path.AtListIndex(i) + + val, valDiags := FromValue(ctx, elemType, val.Index(i).Interface(), valPath) + diags.Append(valDiags...) + + if diags.HasError() { + return nil, diags + } + + tfVal, err := val.ToTerraformValue(ctx) + if err != nil { + return nil, append(diags, toTerraformValueErrorDiag(err, path)) + } + + if tfType.Is(tftypes.Set{}) { + valPath = path.AtSetValue(val) + } + + if typeWithValidate, ok := elemType.(xattr.TypeWithValidate); ok { + diags.Append(typeWithValidate.Validate(ctx, tfVal, valPath)...) + if diags.HasError() { + return nil, diags + } + } + + tfElems = append(tfElems, tfVal) + } + + err := tftypes.ValidateValue(tfType, tfElems) + if err != nil { + return nil, append(diags, validateValueErrorDiag(err, path)) + } + + tfVal := tftypes.NewValue(tfType, tfElems) + + if typeWithValidate, ok := typ.(xattr.TypeWithValidate); ok { + diags.Append(typeWithValidate.Validate(ctx, tfVal, path)...) + + if diags.HasError() { + return nil, diags + } + } + + attrVal, err := typ.ValueFromTerraform(ctx, tfVal) + + if err != nil { + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert from slice value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return nil, diags + } + + return attrVal, diags +} diff --git a/internal/provider/reflect/struct.go b/internal/provider/reflect/struct.go new file mode 100755 index 0000000..b670228 --- /dev/null +++ b/internal/provider/reflect/struct.go @@ -0,0 +1,266 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package reflect + +import ( + "context" + "fmt" + "reflect" + "strings" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/attr/xattr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Struct builds a new struct using the data in `object`, as long as `object` +// is a `tftypes.Object`. It will take the struct type from `target`, which +// must be a struct type. +// +// The properties on `target` must be tagged with a "tfsdk" label containing +// the field name to map to that property. Every property must be tagged, and +// every property must be present in the type of `object`, and all the +// attributes in the type of `object` must have a corresponding property. +// Properties that don't map to object attributes must have a `tfsdk:"-"` tag, +// explicitly defining them as not part of the object. This is to catch typos +// and other mistakes early. +// +// Struct is meant to be called from Into, not directly. +func Struct(ctx context.Context, typ attr.Type, object tftypes.Value, target reflect.Value, opts Options, path path.Path) (reflect.Value, diag.Diagnostics) { + var diags diag.Diagnostics + + // this only works with object values, so make sure that constraint is + // met + if target.Kind() != reflect.Struct { + diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{ + Val: object, + TargetType: target.Type(), + Err: fmt.Errorf("expected a struct type, got %s", target.Type()), + })) + return target, diags + } + if !object.Type().Is(tftypes.Object{}) { + diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{ + Val: object, + TargetType: target.Type(), + Err: fmt.Errorf("cannot reflect %s into a struct, must be an object", object.Type().String()), + })) + return target, diags + } + attrsType, ok := typ.(attr.TypeWithAttributeTypes) + if !ok { + diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{ + Val: object, + TargetType: target.Type(), + Err: fmt.Errorf("cannot reflect object using type information provided by %T, %T must be an attr.TypeWithAttributeTypes", typ, typ), + })) + return target, diags + } + + // collect a map of fields that are in the object passed in + var objectFields map[string]tftypes.Value + err := object.As(&objectFields) + if err != nil { + diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{ + Val: object, + TargetType: target.Type(), + Err: err, + })) + return target, diags + } + + // collect a map of fields that are defined in the tags of the struct + // passed in + targetFields, err := getStructTags(ctx, target, path) + if err != nil { + diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{ + Val: object, + TargetType: target.Type(), + Err: fmt.Errorf("error retrieving field names from struct tags: %w", err), + })) + return target, diags + } + + // we require an exact, 1:1 match of these fields to avoid typos + // leading to surprises, so let's ensure they have the exact same + // fields defined + var objectMissing, targetMissing []string + for field := range targetFields { + if _, ok := objectFields[field]; !ok { + objectMissing = append(objectMissing, field) + } + } + for field := range objectFields { + if _, ok := targetFields[field]; !ok { + targetMissing = append(targetMissing, field) + } + } + if len(objectMissing) > 0 || len(targetMissing) > 0 { + var missing []string + if len(objectMissing) > 0 { + missing = append(missing, fmt.Sprintf("Struct defines fields not found in object: %s.", commaSeparatedString(objectMissing))) + } + if len(targetMissing) > 0 { + missing = append(missing, fmt.Sprintf("Object defines fields not found in struct: %s.", commaSeparatedString(targetMissing))) + } + diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{ + Val: object, + TargetType: target.Type(), + Err: fmt.Errorf("mismatch between struct and object: %s", strings.Join(missing, " ")), + })) + return target, diags + } + + attrTypes := attrsType.AttributeTypes() + + // now that we know they match perfectly, fill the struct with the + // values in the object + // Fork start + //result := reflect.New(target.Type()).Elem() + var result reflect.Value + if target.CanSet() { + result = target + } else { + result = reflect.New(target.Type()).Elem() + } + // Fork End + for field, structFieldPos := range targetFields { + attrType, ok := attrTypes[field] + if !ok { + diags.Append(diag.WithPath(path, DiagIntoIncompatibleType{ + Val: object, + TargetType: target.Type(), + Err: fmt.Errorf("could not find type information for attribute in supplied attr.Type %T", typ), + })) + return target, diags + } + structField := result.Field(structFieldPos) + fieldVal, fieldValDiags := BuildValue(ctx, attrType, objectFields[field], structField, opts, path.AtName(field)) + diags.Append(fieldValDiags...) + + if diags.HasError() { + return target, diags + } + structField.Set(fieldVal) + } + return result, diags +} + +// FromStruct builds an attr.Value as produced by `typ` from the data in `val`. +// `val` must be a struct type, and must have all its properties tagged and be +// a 1:1 match with the attributes reported by `typ`. FromStruct will recurse +// into FromValue for each attribute, using the type of the attribute as +// reported by `typ`. +// +// It is meant to be called through FromValue, not directly. +func FromStruct(ctx context.Context, typ attr.TypeWithAttributeTypes, val reflect.Value, path path.Path) (attr.Value, diag.Diagnostics) { + var diags diag.Diagnostics + objTypes := map[string]tftypes.Type{} + objValues := map[string]tftypes.Value{} + + // collect a map of fields that are defined in the tags of the struct + // passed in + targetFields, err := getStructTags(ctx, val, path) + if err != nil { + err = fmt.Errorf("error retrieving field names from struct tags: %w", err) + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert from struct value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return nil, diags + } + + attrTypes := typ.AttributeTypes() + + var objectMissing, structMissing []string + + for field := range targetFields { + if _, ok := attrTypes[field]; !ok { + objectMissing = append(objectMissing, field) + } + } + + for attrName, attrType := range attrTypes { + if attrType == nil { + objectMissing = append(objectMissing, attrName) + } + + if _, ok := targetFields[attrName]; !ok { + structMissing = append(structMissing, attrName) + } + } + + if len(objectMissing) > 0 || len(structMissing) > 0 { + missing := make([]string, 0, len(objectMissing)+len(structMissing)) + + if len(objectMissing) > 0 { + missing = append(missing, fmt.Sprintf("Struct defines fields not found in object: %s.", commaSeparatedString(objectMissing))) + } + + if len(structMissing) > 0 { + missing = append(missing, fmt.Sprintf("Object defines fields not found in struct: %s.", commaSeparatedString(structMissing))) + } + + diags.AddAttributeError( + path, + "Value Conversion Error", + "An unexpected error was encountered trying to convert from struct into an object. "+ + "This is always an error in the provider. Please report the following to the provider developer:\n\n"+ + fmt.Sprintf("Mismatch between struct and object type: %s\n", strings.Join(missing, " "))+ + fmt.Sprintf("Struct: %s\n", val.Type())+ + fmt.Sprintf("Object type: %s", typ), + ) + + return nil, diags + } + + for name, fieldNo := range targetFields { + path := path.AtName(name) + fieldValue := val.Field(fieldNo) + + attrVal, attrValDiags := FromValue(ctx, attrTypes[name], fieldValue.Interface(), path) + diags.Append(attrValDiags...) + + if diags.HasError() { + return nil, diags + } + + tfObjVal, err := attrVal.ToTerraformValue(ctx) + if err != nil { + return nil, append(diags, toTerraformValueErrorDiag(err, path)) + } + + if typeWithValidate, ok := typ.(xattr.TypeWithValidate); ok { + diags.Append(typeWithValidate.Validate(ctx, tfObjVal, path)...) + + if diags.HasError() { + return nil, diags + } + } + + objValues[name] = tfObjVal + objTypes[name] = tfObjVal.Type() + } + + tfVal := tftypes.NewValue(tftypes.Object{ + AttributeTypes: objTypes, + }, objValues) + + if typeWithValidate, ok := typ.(xattr.TypeWithValidate); ok { + diags.Append(typeWithValidate.Validate(ctx, tfVal, path)...) + + if diags.HasError() { + return nil, diags + } + } + + ret, err := typ.ValueFromTerraform(ctx, tfVal) + if err != nil { + return nil, append(diags, valueFromTerraformErrorDiag(err, path)) + } + + return ret, diags +} diff --git a/internal/provider/type_grant_workflow.go b/internal/provider/type_grant_workflow.go new file mode 100755 index 0000000..64c3c70 --- /dev/null +++ b/internal/provider/type_grant_workflow.go @@ -0,0 +1,7 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package provider + +type GrantWorkflow struct { + Steps []Step `tfsdk:"steps"` +} diff --git a/internal/provider/type_output.go b/internal/provider/type_output.go new file mode 100755 index 0000000..08088cd --- /dev/null +++ b/internal/provider/type_output.go @@ -0,0 +1,11 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package provider + +import "github.com/hashicorp/terraform-plugin-framework/types" + +type Output struct { + Append types.String `tfsdk:"append"` + Location types.String `tfsdk:"location"` + Overwrite types.String `tfsdk:"overwrite"` +} diff --git a/internal/provider/type_policy.go b/internal/provider/type_policy.go new file mode 100755 index 0000000..e75dd6e --- /dev/null +++ b/internal/provider/type_policy.go @@ -0,0 +1,10 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package provider + +import "github.com/hashicorp/terraform-plugin-framework/types" + +type Policy struct { + Bundle types.String `tfsdk:"bundle"` + Query types.String `tfsdk:"query"` +} diff --git a/internal/provider/type_reviewers.go b/internal/provider/type_reviewers.go new file mode 100755 index 0000000..b063f44 --- /dev/null +++ b/internal/provider/type_reviewers.go @@ -0,0 +1,10 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package provider + +import "github.com/hashicorp/terraform-plugin-framework/types" + +type Reviewers struct { + AllOf []types.String `tfsdk:"all_of"` + OneOf []types.String `tfsdk:"one_of"` +} diff --git a/internal/provider/type_step.go b/internal/provider/type_step.go new file mode 100755 index 0000000..4657448 --- /dev/null +++ b/internal/provider/type_step.go @@ -0,0 +1,8 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package provider + +type Step struct { + Reviewers Reviewers `tfsdk:"reviewers"` + SkipIf []Policy `tfsdk:"skip_if"` +} diff --git a/internal/provider/utils.go b/internal/provider/utils.go new file mode 100755 index 0000000..c7f759d --- /dev/null +++ b/internal/provider/utils.go @@ -0,0 +1,81 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package provider + +import ( + tfReflect "abbey/internal/provider/reflect" + "context" + "encoding/json" + "fmt" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "net/http" + "net/http/httputil" + "reflect" +) + +func debugResponse(response *http.Response) string { + dumpReq, err := httputil.DumpRequest(response.Request, false) + if err != nil { + return err.Error() + } + dumpRes, err := httputil.DumpResponse(response, true) + if err != nil { + dumpRes, err = httputil.DumpResponse(response, false) + if err != nil { + return err.Error() + } + } + return fmt.Sprintf("**Request**:\n%s\n**Response**:\n%s", string(dumpReq), string(dumpRes)) +} + +func reflectJSONKey(data any, key string) reflect.Value { + jsonIfied, err := json.Marshal(data) + if err != nil { + panic(fmt.Errorf("failed to marshal data: %w", err)) + } + var jsonMap map[string]interface{} + err = json.Unmarshal(jsonIfied, &jsonMap) + if err != nil { + panic(fmt.Errorf("failed to unmarshal data: %w", err)) + } + return reflect.ValueOf(jsonMap[key]) +} + +func merge(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse, target interface{}) { + var plan types.Object + var state types.Object + + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(state.As(ctx, target, basetypes.ObjectAsOptions{ + UnhandledNullAsEmpty: true, + UnhandledUnknownAsEmpty: true, + })...) + if resp.Diagnostics.HasError() { + return + } + + // we need a tftypes.Value for this Object to be able to use it with + // our reflection code + obj := types.ObjectType{AttrTypes: plan.AttributeTypes(ctx)} + val, err := plan.ToTerraformValue(ctx) + if err != nil { + resp.Diagnostics.Append(diag.NewErrorDiagnostic("Object Conversion Error", "An unexpected error was encountered trying to convert object. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error())) + return + } + resp.Diagnostics.Append(tfReflect.Into(ctx, obj, val, target, tfReflect.Options{ + UnhandledNullAsEmpty: true, + UnhandledUnknownAsEmpty: true, + }, path.Empty())...) +} diff --git a/internal/sdk/apikeys.go b/internal/sdk/apikeys.go new file mode 100755 index 0000000..05e6d7e --- /dev/null +++ b/internal/sdk/apikeys.go @@ -0,0 +1,239 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package sdk + +import ( + "abbey/internal/sdk/pkg/models/operations" + "abbey/internal/sdk/pkg/models/shared" + "abbey/internal/sdk/pkg/utils" + "bytes" + "context" + "fmt" + "io" + "net/http" + "strings" +) + +// apiKeys - API Keys are used to authenticate to the Abbey API. +// +// https://docs.abbey.io/product/managing-api-keys +type apiKeys struct { + sdkConfiguration sdkConfiguration +} + +func newAPIKeys(sdkConfig sdkConfiguration) *apiKeys { + return &apiKeys{ + sdkConfiguration: sdkConfig, + } +} + +// CreateAPIKey - Create an API Key +// Creates a new API Key +func (s *apiKeys) CreateAPIKey(ctx context.Context, request shared.APIKeysCreateParams) (*operations.CreateAPIKeyResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url := strings.TrimSuffix(baseURL, "/") + "/api-keys" + + bodyReader, reqContentType, err := utils.SerializeRequestBody(ctx, request, "Request", "json") + if err != nil { + return nil, fmt.Errorf("error serializing request body: %w", err) + } + if bodyReader == nil { + return nil, fmt.Errorf("request body is required") + } + + req, err := http.NewRequestWithContext(ctx, "POST", url, bodyReader) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json;q=1, application/json;q=0") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + req.Header.Set("Content-Type", reqContentType) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.CreateAPIKeyResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 201: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.APIKey + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.APIKey = out + } + case httpRes.StatusCode == 400: + fallthrough + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 409: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} + +// GetAPIKeys - List API Keys +// Returns a list of a user's API Keys. +func (s *apiKeys) GetAPIKeys(ctx context.Context) (*operations.GetAPIKeysResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url := strings.TrimSuffix(baseURL, "/") + "/api-keys" + + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json;q=1, application/json;q=0") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.GetAPIKeysResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 200: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.APIKeys + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.APIKeys = out + } + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} + +// DeleteAPIKey - Delete an API Key +// Delete a specified API Key. +func (s *apiKeys) DeleteAPIKey(ctx context.Context, request operations.DeleteAPIKeyRequest) (*operations.DeleteAPIKeyResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url, err := utils.GenerateURL(ctx, baseURL, "/api-keys/{api_key}", request, nil) + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "DELETE", url, nil) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.DeleteAPIKeyResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 204: + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 404: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} diff --git a/internal/sdk/connections.go b/internal/sdk/connections.go new file mode 100755 index 0000000..b58f4f2 --- /dev/null +++ b/internal/sdk/connections.go @@ -0,0 +1,337 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package sdk + +import ( + "abbey/internal/sdk/pkg/models/operations" + "abbey/internal/sdk/pkg/models/shared" + "abbey/internal/sdk/pkg/utils" + "bytes" + "context" + "fmt" + "io" + "net/http" + "strings" +) + +// connections - Connections are authenticated, with scopes if available, and made available to Abbey Grant Kits at runtime. +// +// https://docs.abbey.io +type connections struct { + sdkConfiguration sdkConfiguration +} + +func newConnections(sdkConfig sdkConfiguration) *connections { + return &connections{ + sdkConfiguration: sdkConfig, + } +} + +// CreateConnection - Create a Connection +// Creates a new connection. +// +// Connections are authenticated, with scopes if available, and made available to Abbey Grant Kits at runtime. +func (s *connections) CreateConnection(ctx context.Context, request shared.ConnectionParams) (*operations.CreateConnectionResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url := strings.TrimSuffix(baseURL, "/") + "/connections" + + bodyReader, reqContentType, err := utils.SerializeRequestBody(ctx, request, "Request", "json") + if err != nil { + return nil, fmt.Errorf("error serializing request body: %w", err) + } + if bodyReader == nil { + return nil, fmt.Errorf("request body is required") + } + + req, err := http.NewRequestWithContext(ctx, "POST", url, bodyReader) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json;q=1, application/json;q=0") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + req.Header.Set("Content-Type", reqContentType) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.CreateConnectionResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 201: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Connection + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Connection = out + } + case httpRes.StatusCode == 400: + fallthrough + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 409: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} + +// GetConnection - Retrieve a Connection by ID +// Returns the details of a connection. +func (s *connections) GetConnection(ctx context.Context, request operations.GetConnectionRequest) (*operations.GetConnectionResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url, err := utils.GenerateURL(ctx, baseURL, "/connections/{connection_id}", request, nil) + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json;q=1, application/json;q=0") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.GetConnectionResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 200: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Connection + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Connection = out + } + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 404: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} + +// ListConnections - List Connections +// Returns a list of connections. +// The connections are returned sorted by creation date, descending. +func (s *connections) ListConnections(ctx context.Context) (*operations.ListConnectionsResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url := strings.TrimSuffix(baseURL, "/") + "/connections" + + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json;q=1, application/json;q=0") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.ListConnectionsResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 200: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.ConnectionListing + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.ConnectionListing = out + } + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} + +// UpdateConnection - Update a Connection +// Updates the specified connection. +func (s *connections) UpdateConnection(ctx context.Context, request operations.UpdateConnectionRequest) (*operations.UpdateConnectionResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url, err := utils.GenerateURL(ctx, baseURL, "/connections/{connection_id}", request, nil) + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + bodyReader, reqContentType, err := utils.SerializeRequestBody(ctx, request, "ConnectionUpdateParams", "json") + if err != nil { + return nil, fmt.Errorf("error serializing request body: %w", err) + } + if bodyReader == nil { + return nil, fmt.Errorf("request body is required") + } + + req, err := http.NewRequestWithContext(ctx, "PUT", url, bodyReader) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json;q=1, application/json;q=0") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + req.Header.Set("Content-Type", reqContentType) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.UpdateConnectionResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 200: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Connection + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Connection = out + } + case httpRes.StatusCode == 400: + fallthrough + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 404: + fallthrough + case httpRes.StatusCode == 409: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} diff --git a/internal/sdk/connectionspecs.go b/internal/sdk/connectionspecs.go new file mode 100755 index 0000000..d1cc8c9 --- /dev/null +++ b/internal/sdk/connectionspecs.go @@ -0,0 +1,97 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package sdk + +import ( + "abbey/internal/sdk/pkg/models/operations" + "abbey/internal/sdk/pkg/models/shared" + "abbey/internal/sdk/pkg/utils" + "bytes" + "context" + "fmt" + "io" + "net/http" + "strings" +) + +// connectionSpecs - Connection Specs are the templates for creating connections. +// They are used to validate connection parameters and to provide a UI for creating connections. +// +// https://docs.abbey.io +type connectionSpecs struct { + sdkConfiguration sdkConfiguration +} + +func newConnectionSpecs(sdkConfig sdkConfiguration) *connectionSpecs { + return &connectionSpecs{ + sdkConfiguration: sdkConfig, + } +} + +// ListConnectionSpecs - List Connection Specs +// Returns a list of connection specs. +// The connection specs are returned sorted alphabetically. +func (s *connectionSpecs) ListConnectionSpecs(ctx context.Context) (*operations.ListConnectionSpecsResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url := strings.TrimSuffix(baseURL, "/") + "/connection-specs" + + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json;q=1, application/json;q=0") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.ListConnectionSpecsResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 200: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.ConnectionSpecListing + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.ConnectionSpecListing = out + } + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} diff --git a/internal/sdk/grantkits.go b/internal/sdk/grantkits.go new file mode 100755 index 0000000..b37ec6c --- /dev/null +++ b/internal/sdk/grantkits.go @@ -0,0 +1,487 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package sdk + +import ( + "abbey/internal/sdk/pkg/models/operations" + "abbey/internal/sdk/pkg/models/shared" + "abbey/internal/sdk/pkg/utils" + "bytes" + "context" + "fmt" + "io" + "net/http" + "strings" +) + +// grantKits - Grant Kits are what you configure in code to control and automatically right-size permissions for resources. +// A Grant Kit has 3 components: +// +// 1. Workflow to configure how someone should get access. +// 2. Policies to configure if someone should get access. +// 3. Output to configure how and where Grants should materialize. +// +// https://docs.abbey.io/getting-started/concepts#grant-kits +type grantKits struct { + sdkConfiguration sdkConfiguration +} + +func newGrantKits(sdkConfig sdkConfiguration) *grantKits { + return &grantKits{ + sdkConfiguration: sdkConfig, + } +} + +// CreateGrantKit - Create a Grant Kit +// Creates a new Grant Kit +func (s *grantKits) CreateGrantKit(ctx context.Context, request shared.GrantKitCreateParams) (*operations.CreateGrantKitResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url := strings.TrimSuffix(baseURL, "/") + "/grant-kits" + + bodyReader, reqContentType, err := utils.SerializeRequestBody(ctx, request, "Request", "json") + if err != nil { + return nil, fmt.Errorf("error serializing request body: %w", err) + } + if bodyReader == nil { + return nil, fmt.Errorf("request body is required") + } + + req, err := http.NewRequestWithContext(ctx, "POST", url, bodyReader) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json;q=1, application/json;q=0") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + req.Header.Set("Content-Type", reqContentType) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.CreateGrantKitResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 201: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.GrantKit + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.GrantKit = out + } + case httpRes.StatusCode == 400: + fallthrough + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 409: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} + +// GetGrantKits - List Grant Kits +// Returns a list of the latest versions of each grant kit in the organization. +// +// Grant Kits are sorted by creation date, descending. +func (s *grantKits) GetGrantKits(ctx context.Context) (*operations.GetGrantKitsResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url := strings.TrimSuffix(baseURL, "/") + "/grant-kits" + + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json;q=1, application/json;q=0") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.GetGrantKitsResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 200: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out []shared.GrantKit + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.GrantKits = out + } + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} + +// DeleteGrantKit - Delete a Grant Kit +// Deletes the specified grant kit. +func (s *grantKits) DeleteGrantKit(ctx context.Context, request operations.DeleteGrantKitRequest) (*operations.DeleteGrantKitResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url, err := utils.GenerateURL(ctx, baseURL, "/grant-kits/{grant_kit_id_or_name}", request, nil) + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "DELETE", url, nil) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json;q=1, application/json;q=0") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.DeleteGrantKitResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 200: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.GrantKit + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.GrantKit = out + } + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 404: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} + +// GetGrantKitByID - Retrieve a Grant Kit by ID +// Returns the details of a Grant Kit. +func (s *grantKits) GetGrantKitByID(ctx context.Context, request operations.GetGrantKitByIDRequest) (*operations.GetGrantKitByIDResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url, err := utils.GenerateURL(ctx, baseURL, "/grant-kits/{grant_kit_id_or_name}", request, nil) + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json;q=1, application/json;q=0") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.GetGrantKitByIDResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 200: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.GrantKit + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.GrantKit = out + } + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 404: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} + +// ListGrantKitVersionsByID - List Grant Kit Versions of a Grant Kit ID +// Returns all versions of a grant kit. +// +// Grant Kits are sorted by creation date, descending. +func (s *grantKits) ListGrantKitVersionsByID(ctx context.Context, request operations.ListGrantKitVersionsByIDRequest) (*operations.ListGrantKitVersionsByIDResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url, err := utils.GenerateURL(ctx, baseURL, "/grant-kits/{grant_kit_id_or_name}/versions", request, nil) + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json;q=1, application/json;q=0") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.ListGrantKitVersionsByIDResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 200: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out []shared.GrantKitVersion + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.GrantKitVersions = out + } + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 404: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} + +// UpdateGrantKit - Update a Grant Kit +// Updates the specified grant kit. +func (s *grantKits) UpdateGrantKit(ctx context.Context, request operations.UpdateGrantKitRequest) (*operations.UpdateGrantKitResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url, err := utils.GenerateURL(ctx, baseURL, "/grant-kits/{grant_kit_id_or_name}", request, nil) + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + bodyReader, reqContentType, err := utils.SerializeRequestBody(ctx, request, "GrantKitUpdateParams", "json") + if err != nil { + return nil, fmt.Errorf("error serializing request body: %w", err) + } + if bodyReader == nil { + return nil, fmt.Errorf("request body is required") + } + + req, err := http.NewRequestWithContext(ctx, "PUT", url, bodyReader) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json;q=1, application/json;q=0") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + req.Header.Set("Content-Type", reqContentType) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.UpdateGrantKitResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 200: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.GrantKit + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.GrantKit = out + } + case httpRes.StatusCode == 400: + fallthrough + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 404: + fallthrough + case httpRes.StatusCode == 409: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} diff --git a/internal/sdk/grants.go b/internal/sdk/grants.go new file mode 100755 index 0000000..9013e38 --- /dev/null +++ b/internal/sdk/grants.go @@ -0,0 +1,249 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package sdk + +import ( + "abbey/internal/sdk/pkg/models/operations" + "abbey/internal/sdk/pkg/models/shared" + "abbey/internal/sdk/pkg/utils" + "bytes" + "context" + "fmt" + "io" + "net/http" + "strings" +) + +// grants - Grants are permissions that reflect the result of an access request going through the process of evaluating +// policies and approval workflows where all approval conditions are met. +// +// Grants may be revoked manually by a user or automatically if a time-based or attribute-based policy is +// included in the corresponding Grant Kit's policy. +// +// https://docs.abbey.io/getting-started/concepts#grants +type grants struct { + sdkConfiguration sdkConfiguration +} + +func newGrants(sdkConfig sdkConfiguration) *grants { + return &grants{ + sdkConfiguration: sdkConfig, + } +} + +// ListGrants - List Grants +// Returns a list of all the grants belonging to a user. +// +// Grants are sorted by creation date, descending. Creation date effectively means when the grant was approved. +func (s *grants) ListGrants(ctx context.Context) (*operations.ListGrantsResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url := strings.TrimSuffix(baseURL, "/") + "/grants" + + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json;q=1, application/json;q=0") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.ListGrantsResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 200: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out []shared.Grant + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Grants = out + } + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} + +// GetGrantByID - Retrieve a Grant by ID +// Returns the details of a grant. +func (s *grants) GetGrantByID(ctx context.Context, request operations.GetGrantByIDRequest) (*operations.GetGrantByIDResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url, err := utils.GenerateURL(ctx, baseURL, "/grants/{grant_id}", request, nil) + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json;q=1, application/json;q=0") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.GetGrantByIDResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 200: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Grant + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Grant = out + } + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 404: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} + +// RevokeGrant - Revoke a Grant by ID +// Revokes the specified grant. +func (s *grants) RevokeGrant(ctx context.Context, request operations.RevokeGrantRequest) (*operations.RevokeGrantResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url, err := utils.GenerateURL(ctx, baseURL, "/grants/{grant_id}", request, nil) + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "DELETE", url, nil) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json;q=1, application/json;q=0") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.RevokeGrantResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 200: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Grant + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Grant = out + } + case httpRes.StatusCode == 400: + fallthrough + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 404: + fallthrough + case httpRes.StatusCode == 409: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} diff --git a/internal/sdk/identities.go b/internal/sdk/identities.go new file mode 100755 index 0000000..b06e8af --- /dev/null +++ b/internal/sdk/identities.go @@ -0,0 +1,247 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package sdk + +import ( + "abbey/internal/sdk/pkg/models/operations" + "abbey/internal/sdk/pkg/models/shared" + "abbey/internal/sdk/pkg/utils" + "bytes" + "context" + "fmt" + "io" + "net/http" + "strings" +) + +// identities - User metadata used for enriching data. +// Enriched data is used to write richer policies, workflows, and outputs. +// +// https://docs.abbey.io +type identities struct { + sdkConfiguration sdkConfiguration +} + +func newIdentities(sdkConfig sdkConfiguration) *identities { + return &identities{ + sdkConfiguration: sdkConfig, + } +} + +// CreateIdentity - Create an Identity +// Creates a new identity. +// +// An identity represents a human, service, or workload. +func (s *identities) CreateIdentity(ctx context.Context, request shared.IdentityParams) (*operations.CreateIdentityResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url := strings.TrimSuffix(baseURL, "/") + "/identities" + + bodyReader, reqContentType, err := utils.SerializeRequestBody(ctx, request, "Request", "json") + if err != nil { + return nil, fmt.Errorf("error serializing request body: %w", err) + } + if bodyReader == nil { + return nil, fmt.Errorf("request body is required") + } + + req, err := http.NewRequestWithContext(ctx, "POST", url, bodyReader) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json;q=1, application/json;q=0") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + req.Header.Set("Content-Type", reqContentType) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.CreateIdentityResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 201: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Identity + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Identity = out + } + case httpRes.StatusCode == 400: + fallthrough + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 409: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} + +// DeleteIdentity - Delete an Identity +// Deletes the specified identity. +func (s *identities) DeleteIdentity(ctx context.Context, request operations.DeleteIdentityRequest) (*operations.DeleteIdentityResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url, err := utils.GenerateURL(ctx, baseURL, "/identities/{identity_id}", request, nil) + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "DELETE", url, nil) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.DeleteIdentityResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 204: + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 404: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} + +// GetIdentity - Retrieve an Identity +// Returns the details of an identity. +func (s *identities) GetIdentity(ctx context.Context, request operations.GetIdentityRequest) (*operations.GetIdentityResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url, err := utils.GenerateURL(ctx, baseURL, "/identities/{identity_id}", request, nil) + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json;q=1, application/json;q=0") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.GetIdentityResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 200: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Identity + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Identity = out + } + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 404: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} diff --git a/internal/sdk/pkg/models/operations/approvereview.go b/internal/sdk/pkg/models/operations/approvereview.go new file mode 100755 index 0000000..cd299e3 --- /dev/null +++ b/internal/sdk/pkg/models/operations/approvereview.go @@ -0,0 +1,24 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type ApproveReviewRequest struct { + ReviewUpdateParams shared.ReviewUpdateParams `request:"mediaType=application/json"` + // The ID of the review to approve + ReviewID string `pathParam:"style=simple,explode=false,name=review_id"` +} + +type ApproveReviewResponse struct { + ContentType string + // Request Failed + Error *shared.Error + // Success + Review *shared.Review + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/cancelrequestbyid.go b/internal/sdk/pkg/models/operations/cancelrequestbyid.go new file mode 100755 index 0000000..5159f3b --- /dev/null +++ b/internal/sdk/pkg/models/operations/cancelrequestbyid.go @@ -0,0 +1,24 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type CancelRequestByIDRequest struct { + RequestCancelParams shared.RequestCancelParams `request:"mediaType=application/json"` + // The ID of the request to cancel + RequestID string `pathParam:"style=simple,explode=false,name=request_id"` +} + +type CancelRequestByIDResponse struct { + ContentType string + // Request Failed + Error *shared.Error + // Success + Request *shared.Request + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/createapikey.go b/internal/sdk/pkg/models/operations/createapikey.go new file mode 100755 index 0000000..c7b89b3 --- /dev/null +++ b/internal/sdk/pkg/models/operations/createapikey.go @@ -0,0 +1,18 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type CreateAPIKeyResponse struct { + // Created + APIKey *shared.APIKey + ContentType string + // Request Failed + Error *shared.Error + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/createconnection.go b/internal/sdk/pkg/models/operations/createconnection.go new file mode 100755 index 0000000..0b71788 --- /dev/null +++ b/internal/sdk/pkg/models/operations/createconnection.go @@ -0,0 +1,18 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type CreateConnectionResponse struct { + // Created + Connection *shared.Connection + ContentType string + // Request Failed + Error *shared.Error + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/creategrantkit.go b/internal/sdk/pkg/models/operations/creategrantkit.go new file mode 100755 index 0000000..13ef0a2 --- /dev/null +++ b/internal/sdk/pkg/models/operations/creategrantkit.go @@ -0,0 +1,18 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type CreateGrantKitResponse struct { + ContentType string + // Request Failed + Error *shared.Error + // Created + GrantKit *shared.GrantKit + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/createidentity.go b/internal/sdk/pkg/models/operations/createidentity.go new file mode 100755 index 0000000..3a726d9 --- /dev/null +++ b/internal/sdk/pkg/models/operations/createidentity.go @@ -0,0 +1,18 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type CreateIdentityResponse struct { + ContentType string + // Request Failed + Error *shared.Error + // Created + Identity *shared.Identity + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/createrequest.go b/internal/sdk/pkg/models/operations/createrequest.go new file mode 100755 index 0000000..c16d4fb --- /dev/null +++ b/internal/sdk/pkg/models/operations/createrequest.go @@ -0,0 +1,18 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type CreateRequestResponse struct { + ContentType string + // Request Failed + Error *shared.Error + // Created + Request *shared.Request + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/deleteapikey.go b/internal/sdk/pkg/models/operations/deleteapikey.go new file mode 100755 index 0000000..edd48f5 --- /dev/null +++ b/internal/sdk/pkg/models/operations/deleteapikey.go @@ -0,0 +1,21 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type DeleteAPIKeyRequest struct { + // The API Key to delete. + APIKey string `pathParam:"style=simple,explode=false,name=api_key"` +} + +type DeleteAPIKeyResponse struct { + ContentType string + // Authentication Failed + Error *shared.Error + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/deletegrantkit.go b/internal/sdk/pkg/models/operations/deletegrantkit.go new file mode 100755 index 0000000..3726dab --- /dev/null +++ b/internal/sdk/pkg/models/operations/deletegrantkit.go @@ -0,0 +1,23 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type DeleteGrantKitRequest struct { + // The ID of the grant kit or resource to delete + GrantKitIDOrName string `pathParam:"style=simple,explode=false,name=grant_kit_id_or_name"` +} + +type DeleteGrantKitResponse struct { + ContentType string + // Authentication Failed + Error *shared.Error + // Success + GrantKit *shared.GrantKit + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/deleteidentity.go b/internal/sdk/pkg/models/operations/deleteidentity.go new file mode 100755 index 0000000..39b29a6 --- /dev/null +++ b/internal/sdk/pkg/models/operations/deleteidentity.go @@ -0,0 +1,21 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type DeleteIdentityRequest struct { + // The ID of the identity to delete + IdentityID string `pathParam:"style=simple,explode=false,name=identity_id"` +} + +type DeleteIdentityResponse struct { + ContentType string + // Authentication Failed + Error *shared.Error + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/denyreview.go b/internal/sdk/pkg/models/operations/denyreview.go new file mode 100755 index 0000000..208e1b8 --- /dev/null +++ b/internal/sdk/pkg/models/operations/denyreview.go @@ -0,0 +1,24 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type DenyReviewRequest struct { + ReviewUpdateParams shared.ReviewUpdateParams `request:"mediaType=application/json"` + // The ID of the review to deny + ReviewID string `pathParam:"style=simple,explode=false,name=review_id"` +} + +type DenyReviewResponse struct { + ContentType string + // Request Failed + Error *shared.Error + // Success + Review *shared.Review + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/getapikeys.go b/internal/sdk/pkg/models/operations/getapikeys.go new file mode 100755 index 0000000..f5ee8e1 --- /dev/null +++ b/internal/sdk/pkg/models/operations/getapikeys.go @@ -0,0 +1,18 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type GetAPIKeysResponse struct { + // Success + APIKeys *shared.APIKeys + ContentType string + // Authentication Failed + Error *shared.Error + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/getconnection.go b/internal/sdk/pkg/models/operations/getconnection.go new file mode 100755 index 0000000..1b45f7b --- /dev/null +++ b/internal/sdk/pkg/models/operations/getconnection.go @@ -0,0 +1,23 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type GetConnectionRequest struct { + // The ID of the connection to retrieve + ConnectionID string `pathParam:"style=simple,explode=false,name=connection_id"` +} + +type GetConnectionResponse struct { + // Success + Connection *shared.Connection + ContentType string + // Authentication Failed + Error *shared.Error + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/getgrantbyid.go b/internal/sdk/pkg/models/operations/getgrantbyid.go new file mode 100755 index 0000000..19e3d0a --- /dev/null +++ b/internal/sdk/pkg/models/operations/getgrantbyid.go @@ -0,0 +1,23 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type GetGrantByIDRequest struct { + // The ID of the grant to retrieve + GrantID string `pathParam:"style=simple,explode=false,name=grant_id"` +} + +type GetGrantByIDResponse struct { + ContentType string + // Authentication Failed + Error *shared.Error + // Success + Grant *shared.Grant + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/getgrantkitbyid.go b/internal/sdk/pkg/models/operations/getgrantkitbyid.go new file mode 100755 index 0000000..fe81aaf --- /dev/null +++ b/internal/sdk/pkg/models/operations/getgrantkitbyid.go @@ -0,0 +1,23 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type GetGrantKitByIDRequest struct { + // The ID of the grant kit or resource to retrieve. + GrantKitIDOrName string `pathParam:"style=simple,explode=false,name=grant_kit_id_or_name"` +} + +type GetGrantKitByIDResponse struct { + ContentType string + // Authentication Failed + Error *shared.Error + // Success + GrantKit *shared.GrantKit + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/getgrantkits.go b/internal/sdk/pkg/models/operations/getgrantkits.go new file mode 100755 index 0000000..7d68e0d --- /dev/null +++ b/internal/sdk/pkg/models/operations/getgrantkits.go @@ -0,0 +1,18 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type GetGrantKitsResponse struct { + ContentType string + // Authentication Failed + Error *shared.Error + // Success + GrantKits []shared.GrantKit + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/getidentity.go b/internal/sdk/pkg/models/operations/getidentity.go new file mode 100755 index 0000000..76c39ad --- /dev/null +++ b/internal/sdk/pkg/models/operations/getidentity.go @@ -0,0 +1,23 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type GetIdentityRequest struct { + // The ID of the identity to retrieve + IdentityID string `pathParam:"style=simple,explode=false,name=identity_id"` +} + +type GetIdentityResponse struct { + ContentType string + // Authentication Failed + Error *shared.Error + // Success + Identity *shared.Identity + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/getrequestbyid.go b/internal/sdk/pkg/models/operations/getrequestbyid.go new file mode 100755 index 0000000..57cda31 --- /dev/null +++ b/internal/sdk/pkg/models/operations/getrequestbyid.go @@ -0,0 +1,23 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type GetRequestByIDRequest struct { + // The ID of the request to retrieve + RequestID string `pathParam:"style=simple,explode=false,name=request_id"` +} + +type GetRequestByIDResponse struct { + ContentType string + // Authentication Failed + Error *shared.Error + // Success + Request *shared.Request + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/getreviewbyid.go b/internal/sdk/pkg/models/operations/getreviewbyid.go new file mode 100755 index 0000000..9a730ee --- /dev/null +++ b/internal/sdk/pkg/models/operations/getreviewbyid.go @@ -0,0 +1,23 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type GetReviewByIDRequest struct { + // The ID of the review to retrieve. + ReviewID string `pathParam:"style=simple,explode=false,name=review_id"` +} + +type GetReviewByIDResponse struct { + ContentType string + // Authentication Failed + Error *shared.Error + // Success + Review *shared.Review + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/listconnections.go b/internal/sdk/pkg/models/operations/listconnections.go new file mode 100755 index 0000000..15c1b6e --- /dev/null +++ b/internal/sdk/pkg/models/operations/listconnections.go @@ -0,0 +1,18 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type ListConnectionsResponse struct { + // Success + ConnectionListing *shared.ConnectionListing + ContentType string + // Authentication Failed + Error *shared.Error + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/listconnectionspecs.go b/internal/sdk/pkg/models/operations/listconnectionspecs.go new file mode 100755 index 0000000..642683e --- /dev/null +++ b/internal/sdk/pkg/models/operations/listconnectionspecs.go @@ -0,0 +1,18 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type ListConnectionSpecsResponse struct { + // Success + ConnectionSpecListing *shared.ConnectionSpecListing + ContentType string + // Authentication Failed + Error *shared.Error + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/listgrantkitversionsbyid.go b/internal/sdk/pkg/models/operations/listgrantkitversionsbyid.go new file mode 100755 index 0000000..de864ce --- /dev/null +++ b/internal/sdk/pkg/models/operations/listgrantkitversionsbyid.go @@ -0,0 +1,23 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type ListGrantKitVersionsByIDRequest struct { + // The ID of the grant kit or resource to retrieve. + GrantKitIDOrName string `pathParam:"style=simple,explode=false,name=grant_kit_id_or_name"` +} + +type ListGrantKitVersionsByIDResponse struct { + ContentType string + // Authentication Failed + Error *shared.Error + // Success + GrantKitVersions []shared.GrantKitVersion + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/listgrants.go b/internal/sdk/pkg/models/operations/listgrants.go new file mode 100755 index 0000000..b9ff39f --- /dev/null +++ b/internal/sdk/pkg/models/operations/listgrants.go @@ -0,0 +1,18 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type ListGrantsResponse struct { + ContentType string + // Authentication Failed + Error *shared.Error + // Success + Grants []shared.Grant + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/listrequests.go b/internal/sdk/pkg/models/operations/listrequests.go new file mode 100755 index 0000000..929ba87 --- /dev/null +++ b/internal/sdk/pkg/models/operations/listrequests.go @@ -0,0 +1,18 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type ListRequestsResponse struct { + ContentType string + // Authentication Failed + Error *shared.Error + // Success + Requests []shared.Request + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/listreviews.go b/internal/sdk/pkg/models/operations/listreviews.go new file mode 100755 index 0000000..ab66a32 --- /dev/null +++ b/internal/sdk/pkg/models/operations/listreviews.go @@ -0,0 +1,18 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type ListReviewsResponse struct { + ContentType string + // Authentication Failed + Error *shared.Error + // Success + Reviews []shared.Review + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/revokegrant.go b/internal/sdk/pkg/models/operations/revokegrant.go new file mode 100755 index 0000000..a630b80 --- /dev/null +++ b/internal/sdk/pkg/models/operations/revokegrant.go @@ -0,0 +1,23 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type RevokeGrantRequest struct { + // The ID of the grant to revoke + GrantID string `pathParam:"style=simple,explode=false,name=grant_id"` +} + +type RevokeGrantResponse struct { + ContentType string + // Request Failed + Error *shared.Error + // Success + Grant *shared.Grant + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/updateconnection.go b/internal/sdk/pkg/models/operations/updateconnection.go new file mode 100755 index 0000000..e2ecfbb --- /dev/null +++ b/internal/sdk/pkg/models/operations/updateconnection.go @@ -0,0 +1,24 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type UpdateConnectionRequest struct { + ConnectionUpdateParams shared.ConnectionUpdateParams `request:"mediaType=application/json"` + // The ID of the connection to update + ConnectionID string `pathParam:"style=simple,explode=false,name=connection_id"` +} + +type UpdateConnectionResponse struct { + // Success + Connection *shared.Connection + ContentType string + // Request Failed + Error *shared.Error + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/operations/updategrantkit.go b/internal/sdk/pkg/models/operations/updategrantkit.go new file mode 100755 index 0000000..8c96e29 --- /dev/null +++ b/internal/sdk/pkg/models/operations/updategrantkit.go @@ -0,0 +1,24 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package operations + +import ( + "abbey/internal/sdk/pkg/models/shared" + "net/http" +) + +type UpdateGrantKitRequest struct { + GrantKitUpdateParams shared.GrantKitUpdateParams `request:"mediaType=application/json"` + // The ID of the grant kit or resource to update + GrantKitIDOrName string `pathParam:"style=simple,explode=false,name=grant_kit_id_or_name"` +} + +type UpdateGrantKitResponse struct { + ContentType string + // Request Failed + Error *shared.Error + // Success + GrantKit *shared.GrantKit + StatusCode int + RawResponse *http.Response +} diff --git a/internal/sdk/pkg/models/shared/apikey.go b/internal/sdk/pkg/models/shared/apikey.go new file mode 100755 index 0000000..b9d1654 --- /dev/null +++ b/internal/sdk/pkg/models/shared/apikey.go @@ -0,0 +1,9 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +// APIKey - Created +type APIKey struct { + APIKey string `json:"api_key"` + Name string `json:"name"` +} diff --git a/internal/sdk/pkg/models/shared/apikeys.go b/internal/sdk/pkg/models/shared/apikeys.go new file mode 100755 index 0000000..b165eff --- /dev/null +++ b/internal/sdk/pkg/models/shared/apikeys.go @@ -0,0 +1,8 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +// APIKeys - Success +type APIKeys struct { + Items []APIKey `json:"items"` +} diff --git a/internal/sdk/pkg/models/shared/apikeyscreateparams.go b/internal/sdk/pkg/models/shared/apikeyscreateparams.go new file mode 100755 index 0000000..cf7e880 --- /dev/null +++ b/internal/sdk/pkg/models/shared/apikeyscreateparams.go @@ -0,0 +1,7 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +type APIKeysCreateParams struct { + Name string `json:"name"` +} diff --git a/internal/sdk/pkg/models/shared/connection.go b/internal/sdk/pkg/models/shared/connection.go new file mode 100755 index 0000000..2501a0d --- /dev/null +++ b/internal/sdk/pkg/models/shared/connection.go @@ -0,0 +1,15 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +import ( + "time" +) + +// Connection - Created +type Connection struct { + CreatedAt time.Time `json:"created_at"` + ID string `json:"id"` + Name *string `json:"name,omitempty"` + Type ConnectionType `json:"type"` +} diff --git a/internal/sdk/pkg/models/shared/connectionauth.go b/internal/sdk/pkg/models/shared/connectionauth.go new file mode 100755 index 0000000..c9a2792 --- /dev/null +++ b/internal/sdk/pkg/models/shared/connectionauth.go @@ -0,0 +1,69 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" +) + +type ConnectionAuthType string + +const ( + ConnectionAuthTypeOauth2 ConnectionAuthType = "Oauth2" +) + +type ConnectionAuth struct { + Oauth2Flow *Oauth2Flow + + Type ConnectionAuthType +} + +func CreateConnectionAuthOauth2(oauth2 Oauth2Flow) ConnectionAuth { + typ := ConnectionAuthTypeOauth2 + typStr := ConnectionAuthTypeEnum(typ) + oauth2.ConnectionAuthTypeEnum = &typStr + + return ConnectionAuth{ + Oauth2Flow: &oauth2, + Type: typ, + } +} + +func (u *ConnectionAuth) UnmarshalJSON(data []byte) error { + var d *json.Decoder + + type discriminator struct { + ConnectionAuthTypeEnum string + } + + dis := new(discriminator) + if err := json.Unmarshal(data, &dis); err != nil { + return fmt.Errorf("could not unmarshal discriminator: %w", err) + } + + switch dis.ConnectionAuthTypeEnum { + case "Oauth2": + d = json.NewDecoder(bytes.NewReader(data)) + oauth2Flow := new(Oauth2Flow) + if err := d.Decode(&oauth2Flow); err != nil { + return fmt.Errorf("could not unmarshal expected type: %w", err) + } + + u.Oauth2Flow = oauth2Flow + u.Type = ConnectionAuthTypeOauth2 + return nil + } + + return errors.New("could not unmarshal into supported union types") +} + +func (u ConnectionAuth) MarshalJSON() ([]byte, error) { + if u.Oauth2Flow != nil { + return json.Marshal(u.Oauth2Flow) + } + + return nil, nil +} diff --git a/internal/sdk/pkg/models/shared/connectionauthtypeenum.go b/internal/sdk/pkg/models/shared/connectionauthtypeenum.go new file mode 100755 index 0000000..d02a993 --- /dev/null +++ b/internal/sdk/pkg/models/shared/connectionauthtypeenum.go @@ -0,0 +1,32 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +import ( + "encoding/json" + "fmt" +) + +type ConnectionAuthTypeEnum string + +const ( + ConnectionAuthTypeEnumOauth2 ConnectionAuthTypeEnum = "Oauth2" +) + +func (e ConnectionAuthTypeEnum) ToPointer() *ConnectionAuthTypeEnum { + return &e +} + +func (e *ConnectionAuthTypeEnum) UnmarshalJSON(data []byte) error { + var v string + if err := json.Unmarshal(data, &v); err != nil { + return err + } + switch v { + case "Oauth2": + *e = ConnectionAuthTypeEnum(v) + return nil + default: + return fmt.Errorf("invalid value for ConnectionAuthTypeEnum: %v", v) + } +} diff --git a/internal/sdk/pkg/models/shared/connectionlisting.go b/internal/sdk/pkg/models/shared/connectionlisting.go new file mode 100755 index 0000000..3bfe855 --- /dev/null +++ b/internal/sdk/pkg/models/shared/connectionlisting.go @@ -0,0 +1,10 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +// ConnectionListing - Success +type ConnectionListing struct { + Items []Connection `json:"items"` + Next *string `json:"next,omitempty"` + Prev *string `json:"prev,omitempty"` +} diff --git a/internal/sdk/pkg/models/shared/connectionparams.go b/internal/sdk/pkg/models/shared/connectionparams.go new file mode 100755 index 0000000..8b7fe66 --- /dev/null +++ b/internal/sdk/pkg/models/shared/connectionparams.go @@ -0,0 +1,96 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" +) + +type ConnectionParamsType string + +const ( + ConnectionParamsTypeGithub ConnectionParamsType = "Github" + ConnectionParamsTypePagerduty ConnectionParamsType = "Pagerduty" +) + +type ConnectionParams struct { + ConnectionParamsGithubVariant *ConnectionParamsGithubVariant + PagerdutyConnectionValue *PagerdutyConnectionValue + + Type ConnectionParamsType +} + +func CreateConnectionParamsGithub(github ConnectionParamsGithubVariant) ConnectionParams { + typ := ConnectionParamsTypeGithub + typStr := ConnectionType(typ) + github.Type = typStr + + return ConnectionParams{ + ConnectionParamsGithubVariant: &github, + Type: typ, + } +} + +func CreateConnectionParamsPagerduty(pagerduty PagerdutyConnectionValue) ConnectionParams { + typ := ConnectionParamsTypePagerduty + typStr := ConnectionType(typ) + pagerduty.Type = typStr + + return ConnectionParams{ + PagerdutyConnectionValue: &pagerduty, + Type: typ, + } +} + +func (u *ConnectionParams) UnmarshalJSON(data []byte) error { + var d *json.Decoder + + type discriminator struct { + Type string + } + + dis := new(discriminator) + if err := json.Unmarshal(data, &dis); err != nil { + return fmt.Errorf("could not unmarshal discriminator: %w", err) + } + + switch dis.Type { + case "Github": + d = json.NewDecoder(bytes.NewReader(data)) + connectionParamsGithubVariant := new(ConnectionParamsGithubVariant) + if err := d.Decode(&connectionParamsGithubVariant); err != nil { + return fmt.Errorf("could not unmarshal expected type: %w", err) + } + + u.ConnectionParamsGithubVariant = connectionParamsGithubVariant + u.Type = ConnectionParamsTypeGithub + return nil + case "Pagerduty": + d = json.NewDecoder(bytes.NewReader(data)) + pagerdutyConnectionValue := new(PagerdutyConnectionValue) + if err := d.Decode(&pagerdutyConnectionValue); err != nil { + return fmt.Errorf("could not unmarshal expected type: %w", err) + } + + u.PagerdutyConnectionValue = pagerdutyConnectionValue + u.Type = ConnectionParamsTypePagerduty + return nil + } + + return errors.New("could not unmarshal into supported union types") +} + +func (u ConnectionParams) MarshalJSON() ([]byte, error) { + if u.ConnectionParamsGithubVariant != nil { + return json.Marshal(u.ConnectionParamsGithubVariant) + } + + if u.PagerdutyConnectionValue != nil { + return json.Marshal(u.PagerdutyConnectionValue) + } + + return nil, nil +} diff --git a/internal/sdk/pkg/models/shared/connectionparamsgithub.go b/internal/sdk/pkg/models/shared/connectionparamsgithub.go new file mode 100755 index 0000000..afd535d --- /dev/null +++ b/internal/sdk/pkg/models/shared/connectionparamsgithub.go @@ -0,0 +1,7 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +type ConnectionParamsGithub struct { + InstallationID string `json:"installation_id"` +} diff --git a/internal/sdk/pkg/models/shared/connectionparamsgithubvariant.go b/internal/sdk/pkg/models/shared/connectionparamsgithubvariant.go new file mode 100755 index 0000000..dfbc8e8 --- /dev/null +++ b/internal/sdk/pkg/models/shared/connectionparamsgithubvariant.go @@ -0,0 +1,9 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +type ConnectionParamsGithubVariant struct { + Name *string `json:"name,omitempty"` + Type ConnectionType `json:"type"` + Value ConnectionParamsGithub `json:"value"` +} diff --git a/internal/sdk/pkg/models/shared/connectionrequestspec.go b/internal/sdk/pkg/models/shared/connectionrequestspec.go new file mode 100755 index 0000000..8e75192 --- /dev/null +++ b/internal/sdk/pkg/models/shared/connectionrequestspec.go @@ -0,0 +1,9 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +type ConnectionRequestSpec struct { + // A value conforming to the official JSON Schema core/validation dialect 2020-12 meta-schema. + // + Body interface{} `json:"body,omitempty"` +} diff --git a/internal/sdk/pkg/models/shared/connectionspec.go b/internal/sdk/pkg/models/shared/connectionspec.go new file mode 100755 index 0000000..b8ba2fb --- /dev/null +++ b/internal/sdk/pkg/models/shared/connectionspec.go @@ -0,0 +1,11 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +type ConnectionSpec struct { + Auth ConnectionAuth `json:"auth"` + Icon string `json:"icon"` + ID string `json:"id"` + Name string `json:"name"` + Request ConnectionRequestSpec `json:"request"` +} diff --git a/internal/sdk/pkg/models/shared/connectionspeclisting.go b/internal/sdk/pkg/models/shared/connectionspeclisting.go new file mode 100755 index 0000000..b0f46f3 --- /dev/null +++ b/internal/sdk/pkg/models/shared/connectionspeclisting.go @@ -0,0 +1,10 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +// ConnectionSpecListing - Success +type ConnectionSpecListing struct { + Items []ConnectionSpec `json:"items"` + Next *string `json:"next,omitempty"` + Prev *string `json:"prev,omitempty"` +} diff --git a/internal/sdk/pkg/models/shared/connectiontype.go b/internal/sdk/pkg/models/shared/connectiontype.go new file mode 100755 index 0000000..1ba1d91 --- /dev/null +++ b/internal/sdk/pkg/models/shared/connectiontype.go @@ -0,0 +1,35 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +import ( + "encoding/json" + "fmt" +) + +type ConnectionType string + +const ( + ConnectionTypeGithub ConnectionType = "Github" + ConnectionTypePagerduty ConnectionType = "Pagerduty" +) + +func (e ConnectionType) ToPointer() *ConnectionType { + return &e +} + +func (e *ConnectionType) UnmarshalJSON(data []byte) error { + var v string + if err := json.Unmarshal(data, &v); err != nil { + return err + } + switch v { + case "Github": + fallthrough + case "Pagerduty": + *e = ConnectionType(v) + return nil + default: + return fmt.Errorf("invalid value for ConnectionType: %v", v) + } +} diff --git a/internal/sdk/pkg/models/shared/connectionupdateparams.go b/internal/sdk/pkg/models/shared/connectionupdateparams.go new file mode 100755 index 0000000..a1e216c --- /dev/null +++ b/internal/sdk/pkg/models/shared/connectionupdateparams.go @@ -0,0 +1,8 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +type ConnectionUpdateParams struct { + // The name of the connection + Name *string `json:"name,omitempty"` +} diff --git a/internal/sdk/pkg/models/shared/error.go b/internal/sdk/pkg/models/shared/error.go new file mode 100755 index 0000000..b8ec694 --- /dev/null +++ b/internal/sdk/pkg/models/shared/error.go @@ -0,0 +1,12 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +// Error - Authentication Failed +type Error struct { + Code string `json:"code"` + DocURL *string `json:"doc_url,omitempty"` + Message string `json:"message"` + Param *string `json:"param,omitempty"` + Type string `json:"type"` +} diff --git a/internal/sdk/pkg/models/shared/grant.go b/internal/sdk/pkg/models/shared/grant.go new file mode 100755 index 0000000..8e54d58 --- /dev/null +++ b/internal/sdk/pkg/models/shared/grant.go @@ -0,0 +1,20 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +import ( + "time" +) + +// Grant - Success +type Grant struct { + CreatedAt time.Time `json:"created_at"` + Deleted bool `json:"deleted"` + GrantKitID string `json:"grant_kit_id"` + GrantKitVersionID string `json:"grant_kit_version_id"` + ID string `json:"id"` + OrganizationID string `json:"organization_id"` + RequestID string `json:"request_id"` + UpdatedAt time.Time `json:"updated_at"` + UserID string `json:"user_id"` +} diff --git a/internal/sdk/pkg/models/shared/grantkit.go b/internal/sdk/pkg/models/shared/grantkit.go new file mode 100755 index 0000000..c025fc4 --- /dev/null +++ b/internal/sdk/pkg/models/shared/grantkit.go @@ -0,0 +1,20 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +import ( + "time" +) + +// GrantKit - Created +type GrantKit struct { + CreatedAt time.Time `json:"created_at"` + CurrentVersionID string `json:"current_version_id"` + Description string `json:"description"` + ID string `json:"id"` + Name string `json:"name"` + Output Output `json:"output"` + Policies []Policy `json:"policies,omitempty"` + UpdatedAt time.Time `json:"updated_at"` + Workflow *GrantWorkflow `json:"workflow,omitempty"` +} diff --git a/internal/sdk/pkg/models/shared/grantkitcreateparams.go b/internal/sdk/pkg/models/shared/grantkitcreateparams.go new file mode 100755 index 0000000..58abeee --- /dev/null +++ b/internal/sdk/pkg/models/shared/grantkitcreateparams.go @@ -0,0 +1,11 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +type GrantKitCreateParams struct { + Description string `json:"description"` + Name string `json:"name"` + Output Output `json:"output"` + Policies []Policy `json:"policies,omitempty"` + Workflow *GrantWorkflow `json:"workflow,omitempty"` +} diff --git a/internal/sdk/pkg/models/shared/grantkitupdateparams.go b/internal/sdk/pkg/models/shared/grantkitupdateparams.go new file mode 100755 index 0000000..7ee2100 --- /dev/null +++ b/internal/sdk/pkg/models/shared/grantkitupdateparams.go @@ -0,0 +1,12 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +type GrantKitUpdateParams struct { + Description string `json:"description"` + // The name of the connection + Name string `json:"name"` + Output Output `json:"output"` + Policies []Policy `json:"policies,omitempty"` + Workflow *GrantWorkflow `json:"workflow,omitempty"` +} diff --git a/internal/sdk/pkg/models/shared/grantkitversion.go b/internal/sdk/pkg/models/shared/grantkitversion.go new file mode 100755 index 0000000..d21388c --- /dev/null +++ b/internal/sdk/pkg/models/shared/grantkitversion.go @@ -0,0 +1,20 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +import ( + "time" +) + +type GrantKitVersion struct { + CreatedAt time.Time `json:"created_at"` + Description string `json:"description"` + GrantKitID string `json:"grant_kit_id"` + GrantKitName string `json:"grant_kit_name"` + ID string `json:"id"` + Output Output `json:"output"` + Policies []Policy `json:"policies"` + UpdatedAt time.Time `json:"updated_at"` + Version int64 `json:"version"` + Workflow GrantWorkflow `json:"workflow"` +} diff --git a/internal/sdk/pkg/models/shared/grantworkflow.go b/internal/sdk/pkg/models/shared/grantworkflow.go new file mode 100755 index 0000000..d022d68 --- /dev/null +++ b/internal/sdk/pkg/models/shared/grantworkflow.go @@ -0,0 +1,7 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +type GrantWorkflow struct { + Steps []Step `json:"steps"` +} diff --git a/internal/sdk/pkg/models/shared/httpmethod.go b/internal/sdk/pkg/models/shared/httpmethod.go new file mode 100755 index 0000000..348430e --- /dev/null +++ b/internal/sdk/pkg/models/shared/httpmethod.go @@ -0,0 +1,35 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +import ( + "encoding/json" + "fmt" +) + +type HTTPMethod string + +const ( + HTTPMethodGet HTTPMethod = "GET" + HTTPMethodPost HTTPMethod = "POST" +) + +func (e HTTPMethod) ToPointer() *HTTPMethod { + return &e +} + +func (e *HTTPMethod) UnmarshalJSON(data []byte) error { + var v string + if err := json.Unmarshal(data, &v); err != nil { + return err + } + switch v { + case "GET": + fallthrough + case "POST": + *e = HTTPMethod(v) + return nil + default: + return fmt.Errorf("invalid value for HTTPMethod: %v", v) + } +} diff --git a/internal/sdk/pkg/models/shared/identity.go b/internal/sdk/pkg/models/shared/identity.go new file mode 100755 index 0000000..a0a4e7b --- /dev/null +++ b/internal/sdk/pkg/models/shared/identity.go @@ -0,0 +1,15 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +import ( + "time" +) + +// Identity - Created +type Identity struct { + CreatedAt time.Time `json:"created_at"` + ID string `json:"id"` + Linked map[string][]interface{} `json:"linked"` + Name string `json:"name"` +} diff --git a/internal/sdk/pkg/models/shared/identityparams.go b/internal/sdk/pkg/models/shared/identityparams.go new file mode 100755 index 0000000..eaa7b9b --- /dev/null +++ b/internal/sdk/pkg/models/shared/identityparams.go @@ -0,0 +1,8 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +type IdentityParams struct { + Linked map[string][]interface{} `json:"linked"` + Name string `json:"name"` +} diff --git a/internal/sdk/pkg/models/shared/keyvaluepair.go b/internal/sdk/pkg/models/shared/keyvaluepair.go new file mode 100755 index 0000000..a73a53c --- /dev/null +++ b/internal/sdk/pkg/models/shared/keyvaluepair.go @@ -0,0 +1,10 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +type KeyValuePair struct { + Key string `json:"key"` + // A meta-schema conforming to the official JSON Schema core/validation dialect 2020-12 meta-schema. + // + Value interface{} `json:"value,omitempty"` +} diff --git a/internal/sdk/pkg/models/shared/oauth2flow.go b/internal/sdk/pkg/models/shared/oauth2flow.go new file mode 100755 index 0000000..5611dfd --- /dev/null +++ b/internal/sdk/pkg/models/shared/oauth2flow.go @@ -0,0 +1,14 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +type Oauth2Flow struct { + CallbackQueryParams []string `json:"callback_query_params,omitempty"` + ConnectionAuthTypeEnum *ConnectionAuthTypeEnum `json:"type,omitempty"` + // Describes how the client should conduct the authorization code exchange. + // + Exchange *Oauth2FlowExchange `json:"exchange,omitempty"` + Pkce *Oauth2FlowPkce `json:"pkce,omitempty"` + QueryParams []KeyValuePair `json:"query_params,omitempty"` + URL string `json:"url"` +} diff --git a/internal/sdk/pkg/models/shared/oauth2flowexchange.go b/internal/sdk/pkg/models/shared/oauth2flowexchange.go new file mode 100755 index 0000000..4375691 --- /dev/null +++ b/internal/sdk/pkg/models/shared/oauth2flowexchange.go @@ -0,0 +1,9 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +// Oauth2FlowExchange - Describes how the client should conduct the authorization code exchange. +type Oauth2FlowExchange struct { + Enabled bool `json:"enabled"` + Request RequestSpec `json:"request"` +} diff --git a/internal/sdk/pkg/models/shared/oauth2flowpkce.go b/internal/sdk/pkg/models/shared/oauth2flowpkce.go new file mode 100755 index 0000000..2ab7ef2 --- /dev/null +++ b/internal/sdk/pkg/models/shared/oauth2flowpkce.go @@ -0,0 +1,8 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +type Oauth2FlowPkce struct { + Enabled bool `json:"enabled"` + Method PkceMethod `json:"method"` +} diff --git a/internal/sdk/pkg/models/shared/output.go b/internal/sdk/pkg/models/shared/output.go new file mode 100755 index 0000000..c3a23ca --- /dev/null +++ b/internal/sdk/pkg/models/shared/output.go @@ -0,0 +1,9 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +type Output struct { + Append *string `json:"append,omitempty"` + Location string `json:"location"` + Overwrite *string `json:"overwrite,omitempty"` +} diff --git a/internal/sdk/pkg/models/shared/pagerdutyconnection.go b/internal/sdk/pkg/models/shared/pagerdutyconnection.go new file mode 100755 index 0000000..f1acd1d --- /dev/null +++ b/internal/sdk/pkg/models/shared/pagerdutyconnection.go @@ -0,0 +1,11 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +type PagerdutyConnection struct { + ClientInfo string `json:"client_info"` + IDToken string `json:"id_token"` + RefreshToken string `json:"refresh_token"` + Scope string `json:"scope"` + TokenType *string `json:"token_type,omitempty"` +} diff --git a/internal/sdk/pkg/models/shared/pagerdutyconnectionvalue.go b/internal/sdk/pkg/models/shared/pagerdutyconnectionvalue.go new file mode 100755 index 0000000..b2fb471 --- /dev/null +++ b/internal/sdk/pkg/models/shared/pagerdutyconnectionvalue.go @@ -0,0 +1,9 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +type PagerdutyConnectionValue struct { + Name *string `json:"name,omitempty"` + Type ConnectionType `json:"type"` + Value PagerdutyConnection `json:"value"` +} diff --git a/internal/sdk/pkg/models/shared/pkcemethod.go b/internal/sdk/pkg/models/shared/pkcemethod.go new file mode 100755 index 0000000..3422bf4 --- /dev/null +++ b/internal/sdk/pkg/models/shared/pkcemethod.go @@ -0,0 +1,32 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +import ( + "encoding/json" + "fmt" +) + +type PkceMethod string + +const ( + PkceMethodS256 PkceMethod = "S256" +) + +func (e PkceMethod) ToPointer() *PkceMethod { + return &e +} + +func (e *PkceMethod) UnmarshalJSON(data []byte) error { + var v string + if err := json.Unmarshal(data, &v); err != nil { + return err + } + switch v { + case "S256": + *e = PkceMethod(v) + return nil + default: + return fmt.Errorf("invalid value for PkceMethod: %v", v) + } +} diff --git a/internal/sdk/pkg/models/shared/policy.go b/internal/sdk/pkg/models/shared/policy.go new file mode 100755 index 0000000..56bb6d8 --- /dev/null +++ b/internal/sdk/pkg/models/shared/policy.go @@ -0,0 +1,8 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +type Policy struct { + Bundle *string `json:"bundle,omitempty"` + Query *string `json:"query,omitempty"` +} diff --git a/internal/sdk/pkg/models/shared/request.go b/internal/sdk/pkg/models/shared/request.go new file mode 100755 index 0000000..35f989a --- /dev/null +++ b/internal/sdk/pkg/models/shared/request.go @@ -0,0 +1,20 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +import ( + "time" +) + +// Request - Created +type Request struct { + CreatedAt time.Time `json:"created_at"` + GrantID string `json:"grant_id"` + GrantKitVersionID string `json:"grant_kit_version_id"` + ID string `json:"id"` + Reason string `json:"reason"` + Reviews []Review `json:"reviews"` + Status RequestStatus `json:"status"` + UpdatedAt time.Time `json:"updated_at"` + UserID string `json:"user_id"` +} diff --git a/internal/sdk/pkg/models/shared/requestcancelparams.go b/internal/sdk/pkg/models/shared/requestcancelparams.go new file mode 100755 index 0000000..6085afb --- /dev/null +++ b/internal/sdk/pkg/models/shared/requestcancelparams.go @@ -0,0 +1,8 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +type RequestCancelParams struct { + // The reason for canceling the request + Reason *string `json:"reason,omitempty"` +} diff --git a/internal/sdk/pkg/models/shared/requestcontenttype.go b/internal/sdk/pkg/models/shared/requestcontenttype.go new file mode 100755 index 0000000..1e00e24 --- /dev/null +++ b/internal/sdk/pkg/models/shared/requestcontenttype.go @@ -0,0 +1,35 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +import ( + "encoding/json" + "fmt" +) + +type RequestContentType string + +const ( + RequestContentTypeApplicationJSON RequestContentType = "application/json" + RequestContentTypeApplicationXWwwFormUrlencoded RequestContentType = "application/x-www-form-urlencoded" +) + +func (e RequestContentType) ToPointer() *RequestContentType { + return &e +} + +func (e *RequestContentType) UnmarshalJSON(data []byte) error { + var v string + if err := json.Unmarshal(data, &v); err != nil { + return err + } + switch v { + case "application/json": + fallthrough + case "application/x-www-form-urlencoded": + *e = RequestContentType(v) + return nil + default: + return fmt.Errorf("invalid value for RequestContentType: %v", v) + } +} diff --git a/internal/sdk/pkg/models/shared/requestparams.go b/internal/sdk/pkg/models/shared/requestparams.go new file mode 100755 index 0000000..ca345f0 --- /dev/null +++ b/internal/sdk/pkg/models/shared/requestparams.go @@ -0,0 +1,8 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +type RequestParams struct { + GrantKitID *string `json:"grant_kit_id,omitempty"` + Reason string `json:"reason"` +} diff --git a/internal/sdk/pkg/models/shared/requestspec.go b/internal/sdk/pkg/models/shared/requestspec.go new file mode 100755 index 0000000..130dfb2 --- /dev/null +++ b/internal/sdk/pkg/models/shared/requestspec.go @@ -0,0 +1,12 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +type RequestSpec struct { + // A value conforming to the official JSON Schema core/validation dialect 2020-12 meta-schema. + // + Body interface{} `json:"body,omitempty"` + ContentType *RequestContentType `json:"content_type,omitempty"` + Method HTTPMethod `json:"method"` + URL string `json:"url"` +} diff --git a/internal/sdk/pkg/models/shared/requeststatus.go b/internal/sdk/pkg/models/shared/requeststatus.go new file mode 100755 index 0000000..3a6b543 --- /dev/null +++ b/internal/sdk/pkg/models/shared/requeststatus.go @@ -0,0 +1,41 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +import ( + "encoding/json" + "fmt" +) + +type RequestStatus string + +const ( + RequestStatusPending RequestStatus = "Pending" + RequestStatusDenied RequestStatus = "Denied" + RequestStatusApproved RequestStatus = "Approved" + RequestStatusCanceled RequestStatus = "Canceled" +) + +func (e RequestStatus) ToPointer() *RequestStatus { + return &e +} + +func (e *RequestStatus) UnmarshalJSON(data []byte) error { + var v string + if err := json.Unmarshal(data, &v); err != nil { + return err + } + switch v { + case "Pending": + fallthrough + case "Denied": + fallthrough + case "Approved": + fallthrough + case "Canceled": + *e = RequestStatus(v) + return nil + default: + return fmt.Errorf("invalid value for RequestStatus: %v", v) + } +} diff --git a/internal/sdk/pkg/models/shared/review.go b/internal/sdk/pkg/models/shared/review.go new file mode 100755 index 0000000..8d0158a --- /dev/null +++ b/internal/sdk/pkg/models/shared/review.go @@ -0,0 +1,20 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +import ( + "time" +) + +// Review - Success +type Review struct { + CreatedAt time.Time `json:"created_at"` + GrantID string `json:"grant_id"` + GrantKitVersionID string `json:"grant_kit_version_id"` + ID string `json:"id"` + Reason string `json:"reason"` + RequestID string `json:"request_id"` + Status ReviewStatus `json:"status"` + UpdatedAt time.Time `json:"updated_at"` + UserID string `json:"user_id"` +} diff --git a/internal/sdk/pkg/models/shared/reviewers.go b/internal/sdk/pkg/models/shared/reviewers.go new file mode 100755 index 0000000..2e493d8 --- /dev/null +++ b/internal/sdk/pkg/models/shared/reviewers.go @@ -0,0 +1,8 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +type Reviewers struct { + AllOf []string `json:"all_of,omitempty"` + OneOf []string `json:"one_of,omitempty"` +} diff --git a/internal/sdk/pkg/models/shared/reviewstatus.go b/internal/sdk/pkg/models/shared/reviewstatus.go new file mode 100755 index 0000000..b5aef5b --- /dev/null +++ b/internal/sdk/pkg/models/shared/reviewstatus.go @@ -0,0 +1,41 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +import ( + "encoding/json" + "fmt" +) + +type ReviewStatus string + +const ( + ReviewStatusPending ReviewStatus = "Pending" + ReviewStatusDenied ReviewStatus = "Denied" + ReviewStatusApproved ReviewStatus = "Approved" + ReviewStatusCanceled ReviewStatus = "Canceled" +) + +func (e ReviewStatus) ToPointer() *ReviewStatus { + return &e +} + +func (e *ReviewStatus) UnmarshalJSON(data []byte) error { + var v string + if err := json.Unmarshal(data, &v); err != nil { + return err + } + switch v { + case "Pending": + fallthrough + case "Denied": + fallthrough + case "Approved": + fallthrough + case "Canceled": + *e = ReviewStatus(v) + return nil + default: + return fmt.Errorf("invalid value for ReviewStatus: %v", v) + } +} diff --git a/internal/sdk/pkg/models/shared/reviewupdateparams.go b/internal/sdk/pkg/models/shared/reviewupdateparams.go new file mode 100755 index 0000000..fba52ff --- /dev/null +++ b/internal/sdk/pkg/models/shared/reviewupdateparams.go @@ -0,0 +1,7 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +type ReviewUpdateParams struct { + Reason string `json:"reason"` +} diff --git a/internal/sdk/pkg/models/shared/step.go b/internal/sdk/pkg/models/shared/step.go new file mode 100755 index 0000000..473b742 --- /dev/null +++ b/internal/sdk/pkg/models/shared/step.go @@ -0,0 +1,8 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package shared + +type Step struct { + Reviewers Reviewers `json:"reviewers"` + SkipIf []Policy `json:"skip_if,omitempty"` +} diff --git a/internal/sdk/pkg/types/bigint.go b/internal/sdk/pkg/types/bigint.go new file mode 100755 index 0000000..b37a415 --- /dev/null +++ b/internal/sdk/pkg/types/bigint.go @@ -0,0 +1,48 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package types + +import ( + "fmt" + "math/big" +) + +type BigInt struct { + big.Int +} + +func (b BigInt) MarshalJSON() ([]byte, error) { + return []byte(`"` + b.String() + `"`), nil +} + +func (b *BigInt) UnmarshalJSON(p []byte) error { + if string(p) == "null" { + return nil + } + + stringVal := string(p) + if len(stringVal) > 2 && stringVal[0] == '"' && stringVal[len(stringVal)-1] == '"' { + stringVal = stringVal[1 : len(stringVal)-1] + } + + var z big.Int + _, ok := z.SetString(string(stringVal), 10) + if !ok { + return fmt.Errorf("not a valid big integer: %s", p) + } + b.Int = z + return nil +} + +// MustBigIntFromString provides a helper function to return a big.Int from a string +// The string is assumed to be base 10 and if it is not a valid big.Int +// then the function will return nil +func MustBigIntFromString(s string) *BigInt { + i, ok := new(big.Int).SetString(s, 10) + if !ok { + return nil + } + return &BigInt{ + Int: *i, + } +} diff --git a/internal/sdk/pkg/types/date.go b/internal/sdk/pkg/types/date.go new file mode 100755 index 0000000..01c69b7 --- /dev/null +++ b/internal/sdk/pkg/types/date.go @@ -0,0 +1,86 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package types + +import ( + "encoding/json" + "fmt" + "strings" + "time" +) + +// Date is a wrapper around time.Time that allows for JSON marshaling a date string formatted as "2006-01-02". +type Date struct { + time.Time +} + +var ( + _ json.Marshaler = &Date{} + _ json.Unmarshaler = &Date{} + _ fmt.Stringer = &Date{} +) + +// NewDate returns an instance of Date from a time.Time. +func NewDate(t time.Time) *Date { + d := DateFromTime(t) + return &d +} + +// DateFromTime returns a Date from a time.Time. +func DateFromTime(t time.Time) Date { + return Date{t} +} + +// NewDateFromString returns an instance of Date from a string formatted as "2006-01-02". +func NewDateFromString(str string) (*Date, error) { + d, err := DateFromString(str) + if err != nil { + return nil, err + } + + return &d, nil +} + +// DateFromString returns a Date from a string formatted as "2006-01-02". +func DateFromString(str string) (Date, error) { + var d Date + var err error + + d.Time, err = time.Parse("2006-01-02", str) + return d, err +} + +// MustNewDateFromString returns an instance of Date from a string formatted as "2006-01-02" or panics. +// Avoid using this function in production code. +func MustNewDateFromString(str string) *Date { + d := MustDateFromString(str) + return &d +} + +// MustDateFromString returns a Date from a string formatted as "2006-01-02" or panics. +// Avoid using this function in production code. +func MustDateFromString(str string) Date { + d, err := DateFromString(str) + if err != nil { + panic(err) + } + return d +} + +func (d Date) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, d.Time.Format("2006-01-02"))), nil +} + +func (d *Date) UnmarshalJSON(data []byte) error { + var err error + + str := string(data) + str = strings.Trim(str, `"`) + + d.Time, err = time.Parse("2006-01-02", str) + return err +} + +func (d Date) String() string { + return d.Time.Format("2006-01-02") +} diff --git a/internal/sdk/pkg/types/datetime.go b/internal/sdk/pkg/types/datetime.go new file mode 100755 index 0000000..0529b25 --- /dev/null +++ b/internal/sdk/pkg/types/datetime.go @@ -0,0 +1,23 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package types + +import "time" + +// MustTimeFromString returns a time.Time from a string formatted as "2006-01-02T15:04:05Z07:00" or panics. +// Avoid using this function in production code. +func MustTimeFromString(str string) time.Time { + t, err := time.Parse(time.RFC3339, str) + if err != nil { + panic(err) + } + + return t +} + +// MustNewTimeFromString returns an instance of time.Time from a string formatted as "2006-01-02T15:04:05Z07:00" or panics. +// Avoid using this function in production code. +func MustNewTimeFromString(str string) *time.Time { + t := MustTimeFromString(str) + return &t +} diff --git a/internal/sdk/pkg/utils/contenttype.go b/internal/sdk/pkg/utils/contenttype.go new file mode 100755 index 0000000..8ed13e2 --- /dev/null +++ b/internal/sdk/pkg/utils/contenttype.go @@ -0,0 +1,33 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package utils + +import ( + "fmt" + "mime" + "strings" +) + +func MatchContentType(contentType string, pattern string) bool { + if contentType == pattern || pattern == "*" || pattern == "*/*" { + return true + } + + mediaType, _, err := mime.ParseMediaType(contentType) + if err != nil { + return false + } + + if mediaType == pattern { + return true + } + + parts := strings.Split(mediaType, "/") + if len(parts) == 2 { + if fmt.Sprintf("%s/*", parts[0]) == pattern || fmt.Sprintf("*/%s", parts[1]) == pattern { + return true + } + } + + return false +} diff --git a/internal/sdk/pkg/utils/form.go b/internal/sdk/pkg/utils/form.go new file mode 100755 index 0000000..76ef728 --- /dev/null +++ b/internal/sdk/pkg/utils/form.go @@ -0,0 +1,114 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package utils + +import ( + "fmt" + "math/big" + "net/url" + "reflect" + "strings" + "time" + + "abbey/internal/sdk/pkg/types" +) + +func populateForm(paramName string, explode bool, objType reflect.Type, objValue reflect.Value, delimiter string, getFieldName func(reflect.StructField) string) url.Values { + + formValues := url.Values{} + + if objType.Kind() == reflect.Pointer { + if objValue.IsNil() { + return formValues + } + objType = objType.Elem() + objValue = objValue.Elem() + } + + switch objType.Kind() { + case reflect.Struct: + switch objValue.Interface().(type) { + case time.Time: + formValues.Add(paramName, valToString(objValue.Interface())) + case types.Date: + formValues.Add(paramName, valToString(objValue.Interface())) + case types.BigInt: + formValues.Add(paramName, valToString(objValue.Interface())) + case big.Int: + formValues.Add(paramName, valToString(objValue.Interface())) + default: + var items []string + + for i := 0; i < objType.NumField(); i++ { + fieldType := objType.Field(i) + valType := objValue.Field(i) + + if valType.Kind() == reflect.Pointer { + if valType.IsNil() { + continue + } + + valType = valType.Elem() + } + + fieldName := getFieldName(fieldType) + if fieldName == "" { + continue + } + + if explode { + formValues.Add(fieldName, valToString(valType.Interface())) + } else { + items = append(items, fmt.Sprintf("%s%s%s", fieldName, delimiter, valToString(valType.Interface()))) + } + } + + if len(items) > 0 { + formValues.Add(paramName, strings.Join(items, delimiter)) + } + } + case reflect.Map: + items := []string{} + + iter := objValue.MapRange() + for iter.Next() { + if explode { + formValues.Add(iter.Key().String(), valToString(iter.Value().Interface())) + } else { + items = append(items, fmt.Sprintf("%s%s%s", iter.Key().String(), delimiter, valToString(iter.Value().Interface()))) + } + } + + if len(items) > 0 { + formValues.Add(paramName, strings.Join(items, delimiter)) + } + case reflect.Slice, reflect.Array: + values := parseDelimitedArray(explode, objValue, delimiter) + for _, v := range values { + formValues.Add(paramName, v) + } + default: + formValues.Add(paramName, valToString(objValue.Interface())) + } + + return formValues +} + +func parseDelimitedArray(explode bool, objValue reflect.Value, delimiter string) []string { + values := []string{} + items := []string{} + + for i := 0; i < objValue.Len(); i++ { + if explode { + values = append(values, valToString(objValue.Index(i).Interface())) + } else { + items = append(items, valToString(objValue.Index(i).Interface())) + } + } + + if len(items) > 0 { + values = append(values, strings.Join(items, delimiter)) + } + + return values +} diff --git a/internal/sdk/pkg/utils/headers.go b/internal/sdk/pkg/utils/headers.go new file mode 100755 index 0000000..d8ca9b1 --- /dev/null +++ b/internal/sdk/pkg/utils/headers.go @@ -0,0 +1,100 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package utils + +import ( + "context" + "fmt" + "net/http" + "reflect" + "strings" +) + +func PopulateHeaders(ctx context.Context, req *http.Request, headers interface{}) { + headerParamsStructType := reflect.TypeOf(headers) + headerParamsValType := reflect.ValueOf(headers) + + for i := 0; i < headerParamsStructType.NumField(); i++ { + fieldType := headerParamsStructType.Field(i) + valType := headerParamsValType.Field(i) + + tag := parseParamTag(headerParamTagKey, fieldType, "simple", false) + if tag == nil { + continue + } + + value := serializeHeader(fieldType.Type, valType, tag.Explode) + if value != "" { + req.Header.Add(tag.ParamName, value) + } + } +} + +func serializeHeader(objType reflect.Type, objValue reflect.Value, explode bool) string { + if objType.Kind() == reflect.Pointer { + if objValue.IsNil() { + return "" + } + objType = objType.Elem() + objValue = objValue.Elem() + } + + switch objType.Kind() { + case reflect.Struct: + items := []string{} + + for i := 0; i < objType.NumField(); i++ { + fieldType := objType.Field(i) + valType := objValue.Field(i) + + if fieldType.Type.Kind() == reflect.Pointer { + if valType.IsNil() { + continue + } + valType = valType.Elem() + } + + tag := parseParamTag(headerParamTagKey, fieldType, "simple", false) + if tag == nil { + continue + } + + fieldName := tag.ParamName + + if fieldName == "" { + continue + } + + if explode { + items = append(items, fmt.Sprintf("%s=%s", fieldName, valToString(valType.Interface()))) + } else { + items = append(items, fieldName, valToString(valType.Interface())) + } + } + + return strings.Join(items, ",") + case reflect.Map: + items := []string{} + + iter := objValue.MapRange() + for iter.Next() { + if explode { + items = append(items, fmt.Sprintf("%s=%s", iter.Key().String(), valToString(iter.Value().Interface()))) + } else { + items = append(items, iter.Key().String(), valToString(iter.Value().Interface())) + } + } + + return strings.Join(items, ",") + case reflect.Slice, reflect.Array: + items := []string{} + + for i := 0; i < objValue.Len(); i++ { + items = append(items, valToString(objValue.Index(i).Interface())) + } + + return strings.Join(items, ",") + default: + return valToString(objValue.Interface()) + } +} diff --git a/internal/sdk/pkg/utils/pathparams.go b/internal/sdk/pkg/utils/pathparams.go new file mode 100755 index 0000000..c2fd728 --- /dev/null +++ b/internal/sdk/pkg/utils/pathparams.go @@ -0,0 +1,126 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package utils + +import ( + "context" + "fmt" + "net/url" + "reflect" + "strings" +) + +func GenerateURL(ctx context.Context, serverURL, path string, pathParams interface{}, globals map[string]map[string]map[string]interface{}) (string, error) { + uri := strings.TrimSuffix(serverURL, "/") + path + + pathParamsStructType := reflect.TypeOf(pathParams) + pathParamsValType := reflect.ValueOf(pathParams) + + parsedParameters := map[string]string{} + + for i := 0; i < pathParamsStructType.NumField(); i++ { + fieldType := pathParamsStructType.Field(i) + valType := pathParamsValType.Field(i) + + requestTag := getRequestTag(fieldType) + if requestTag != nil { + continue + } + + ppTag := parseParamTag(pathParamTagKey, fieldType, "simple", false) + if ppTag == nil { + continue + } + + valType = populateFromGlobals(fieldType, valType, "pathParam", globals) + + if ppTag.Serialization != "" { + vals, err := populateSerializedParams(ppTag, fieldType.Type, valType) + if err != nil { + return "", err + } + for k, v := range vals { + parsedParameters[k] = url.PathEscape(v) + } + } else { + // TODO: support other styles + switch ppTag.Style { + case "simple": + simpleParams := getSimplePathParams(ctx, ppTag.ParamName, fieldType.Type, valType, ppTag.Explode) + for k, v := range simpleParams { + parsedParameters[k] = v + } + } + } + } + + // TODO should we handle the case where there are no matching path params? + return ReplaceParameters(uri, parsedParameters), nil +} + +func getSimplePathParams(ctx context.Context, parentName string, objType reflect.Type, objValue reflect.Value, explode bool) map[string]string { + pathParams := make(map[string]string) + + if objType.Kind() == reflect.Ptr { + if objValue.IsNil() { + return nil + } + objType = objType.Elem() + objValue = objValue.Elem() + } + + switch objType.Kind() { + case reflect.Array, reflect.Slice: + if objValue.Len() == 0 { + return nil + } + var ppVals []string + for i := 0; i < objValue.Len(); i++ { + ppVals = append(ppVals, valToString(objValue.Index(i).Interface())) + } + pathParams[parentName] = strings.Join(ppVals, ",") + case reflect.Map: + if objValue.Len() == 0 { + return nil + } + var ppVals []string + objMap := objValue.MapRange() + for objMap.Next() { + if explode { + ppVals = append(ppVals, fmt.Sprintf("%s=%s", objMap.Key().String(), valToString(objMap.Value().Interface()))) + } else { + ppVals = append(ppVals, fmt.Sprintf("%s,%s", objMap.Key().String(), valToString(objMap.Value().Interface()))) + } + } + pathParams[parentName] = strings.Join(ppVals, ",") + case reflect.Struct: + var ppVals []string + for i := 0; i < objType.NumField(); i++ { + fieldType := objType.Field(i) + valType := objValue.Field(i) + + ppTag := parseParamTag(pathParamTagKey, fieldType, "simple", explode) + if ppTag == nil { + continue + } + + if fieldType.Type.Kind() == reflect.Pointer { + if valType.IsNil() { + continue + } + valType = valType.Elem() + } + + if explode { + ppVals = append(ppVals, fmt.Sprintf("%s=%s", ppTag.ParamName, valToString(valType.Interface()))) + } else { + ppVals = append(ppVals, fmt.Sprintf("%s,%s", ppTag.ParamName, valToString(valType.Interface()))) + } + } + pathParams[parentName] = strings.Join(ppVals, ",") + default: + pathParams[parentName] = valToString(objValue.Interface()) + } + + return pathParams +} diff --git a/internal/sdk/pkg/utils/queryparams.go b/internal/sdk/pkg/utils/queryparams.go new file mode 100755 index 0000000..87ecef8 --- /dev/null +++ b/internal/sdk/pkg/utils/queryparams.go @@ -0,0 +1,178 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package utils + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/url" + "reflect" +) + +func PopulateQueryParams(ctx context.Context, req *http.Request, queryParams interface{}, globals map[string]map[string]map[string]interface{}) error { + queryParamsStructType := reflect.TypeOf(queryParams) + queryParamsValType := reflect.ValueOf(queryParams) + + values := url.Values{} + + for i := 0; i < queryParamsStructType.NumField(); i++ { + fieldType := queryParamsStructType.Field(i) + valType := queryParamsValType.Field(i) + + requestTag := getRequestTag(fieldType) + if requestTag != nil { + continue + } + + qpTag := parseQueryParamTag(fieldType) + if qpTag == nil { + continue + } + + valType = populateFromGlobals(fieldType, valType, "queryParam", globals) + + if qpTag.Serialization != "" { + vals, err := populateSerializedParams(qpTag, fieldType.Type, valType) + if err != nil { + return err + } + for k, v := range vals { + values.Add(k, v) + } + } else { + switch qpTag.Style { + case "deepObject": + vals := populateDeepObjectParams(req, qpTag, fieldType.Type, valType) + for k, v := range vals { + for _, vv := range v { + values.Add(k, vv) + } + } + case "form": + vals := populateFormParams(req, qpTag, fieldType.Type, valType, ",") + for k, v := range vals { + for _, vv := range v { + values.Add(k, vv) + } + } + case "pipeDelimited": + vals := populateFormParams(req, qpTag, fieldType.Type, valType, "|") + for k, v := range vals { + for _, vv := range v { + values.Add(k, vv) + } + } + default: + return fmt.Errorf("unsupported style: %s", qpTag.Style) + } + } + } + + req.URL.RawQuery = values.Encode() + + return nil +} + +func populateSerializedParams(tag *paramTag, objType reflect.Type, objValue reflect.Value) (map[string]string, error) { + if objType.Kind() == reflect.Pointer { + if objValue.IsNil() { + return nil, nil + } + objValue = objValue.Elem() + } + if objValue.Interface() == nil { + return nil, nil + } + + values := map[string]string{} + + switch tag.Serialization { + case "json": + data, err := json.Marshal(objValue.Interface()) + if err != nil { + return nil, fmt.Errorf("error marshaling json: %v", err) + } + values[tag.ParamName] = string(data) + } + + return values, nil +} + +func populateDeepObjectParams(req *http.Request, tag *paramTag, objType reflect.Type, objValue reflect.Value) url.Values { + values := url.Values{} + + if objType.Kind() == reflect.Pointer { + if objValue.IsNil() { + return values + } + objType = objType.Elem() + objValue = objValue.Elem() + } + + switch objType.Kind() { + case reflect.Struct: + for i := 0; i < objType.NumField(); i++ { + fieldType := objType.Field(i) + valType := objValue.Field(i) + + if fieldType.Type.Kind() == reflect.Pointer { + if valType.IsNil() { + continue + } + valType = valType.Elem() + } + + qpTag := parseQueryParamTag(fieldType) + if qpTag == nil { + continue + } + + switch valType.Kind() { + case reflect.Array, reflect.Slice: + for i := 0; i < valType.Len(); i++ { + values.Add(fmt.Sprintf("%s[%s]", tag.ParamName, qpTag.ParamName), valToString(valType.Index(i).Interface())) + } + default: + values.Add(fmt.Sprintf("%s[%s]", tag.ParamName, qpTag.ParamName), valToString(valType.Interface())) + } + } + case reflect.Map: + iter := objValue.MapRange() + for iter.Next() { + switch iter.Value().Kind() { + case reflect.Array, reflect.Slice: + for i := 0; i < iter.Value().Len(); i++ { + values.Add(fmt.Sprintf("%s[%s]", tag.ParamName, iter.Key().String()), valToString(iter.Value().Index(i).Interface())) + } + default: + values.Add(fmt.Sprintf("%s[%s]", tag.ParamName, iter.Key().String()), valToString(iter.Value().Interface())) + } + } + } + + return values +} + +func populateFormParams(req *http.Request, tag *paramTag, objType reflect.Type, objValue reflect.Value, delimiter string) url.Values { + return populateForm(tag.ParamName, tag.Explode, objType, objValue, delimiter, func(fieldType reflect.StructField) string { + qpTag := parseQueryParamTag(fieldType) + if qpTag == nil { + return "" + } + + return qpTag.ParamName + }) +} + +type paramTag struct { + Style string + Explode bool + ParamName string + Serialization string +} + +func parseQueryParamTag(field reflect.StructField) *paramTag { + return parseParamTag(queryParamTagKey, field, "form", true) +} diff --git a/internal/sdk/pkg/utils/requestbody.go b/internal/sdk/pkg/utils/requestbody.go new file mode 100755 index 0000000..5c1d263 --- /dev/null +++ b/internal/sdk/pkg/utils/requestbody.go @@ -0,0 +1,373 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package utils + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "mime/multipart" + "net/url" + "reflect" + "regexp" +) + +const ( + requestTagKey = "request" + multipartFormTagKey = "multipartForm" + formTagKey = "form" +) + +var ( + jsonEncodingRegex = regexp.MustCompile(`(application|text)\/.*?\+*json.*`) + multipartEncodingRegex = regexp.MustCompile(`multipart\/.*`) + urlEncodedEncodingRegex = regexp.MustCompile(`application\/x-www-form-urlencoded.*`) +) + +func SerializeRequestBody(ctx context.Context, request interface{}, requestFieldName string, serializationMethod string) (*bytes.Buffer, string, error) { + requestStructType := reflect.TypeOf(request) + requestValType := reflect.ValueOf(request) + + if requestStructType.Kind() == reflect.Pointer { + if requestValType.IsNil() { + return nil, "", nil + } + + requestStructType = requestStructType.Elem() + requestValType = requestValType.Elem() + } + + if requestStructType.Kind() != reflect.Struct { + return serializeContentType(requestFieldName, SerializationMethodToContentType[serializationMethod], requestValType) + } + + requestField, ok := requestStructType.FieldByName(requestFieldName) + + if ok { + tag := getRequestTag(requestField) + if tag != nil { + // request object (non-flattened) + requestVal := requestValType.FieldByName(requestFieldName) + if requestField.Type.Kind() == reflect.Pointer && requestVal.IsNil() { + return nil, "", nil + } + + return serializeContentType(requestFieldName, tag.MediaType, requestVal) + } + } + + // flattened request object + return serializeContentType(requestFieldName, SerializationMethodToContentType[serializationMethod], requestValType) +} + +func serializeContentType(fieldName string, mediaType string, val reflect.Value) (*bytes.Buffer, string, error) { + buf := &bytes.Buffer{} + + switch { + case jsonEncodingRegex.MatchString(mediaType): + if err := json.NewEncoder(buf).Encode(val.Interface()); err != nil { + return nil, "", err + } + case multipartEncodingRegex.MatchString(mediaType): + var err error + mediaType, err = encodeMultipartFormData(buf, val.Interface()) + if err != nil { + return nil, "", err + } + case urlEncodedEncodingRegex.MatchString(mediaType): + if err := encodeFormData(fieldName, buf, val.Interface()); err != nil { + return nil, "", err + } + default: + val = reflect.Indirect(val) + + switch { + case val.Type().Kind() == reflect.String: + if _, err := buf.WriteString(valToString(val.Interface())); err != nil { + return nil, "", err + } + case val.Type() == reflect.TypeOf([]byte(nil)): + if _, err := buf.Write(val.Bytes()); err != nil { + return nil, "", err + } + default: + return nil, "", fmt.Errorf("invalid request body type %s for mediaType %s", val.Type(), mediaType) + } + } + + return buf, mediaType, nil +} + +func encodeMultipartFormData(w io.Writer, data interface{}) (string, error) { + requestStructType := reflect.TypeOf(data) + requestValType := reflect.ValueOf(data) + + if requestStructType.Kind() == reflect.Pointer { + requestStructType = requestStructType.Elem() + requestValType = requestValType.Elem() + } + + writer := multipart.NewWriter(w) + + for i := 0; i < requestStructType.NumField(); i++ { + field := requestStructType.Field(i) + fieldType := field.Type + valType := requestValType.Field(i) + + if fieldType.Kind() == reflect.Pointer { + if valType.IsNil() { + continue + } + + fieldType = fieldType.Elem() + valType = valType.Elem() + } + + tag := parseMultipartFormTag(field) + if tag.File { + if err := encodeMultipartFormDataFile(writer, fieldType, valType); err != nil { + writer.Close() + return "", err + } + } else if tag.JSON { + jw, err := writer.CreateFormField(tag.Name) + if err != nil { + writer.Close() + return "", err + } + d, err := json.Marshal(valType.Interface()) + if err != nil { + writer.Close() + return "", err + } + if _, err := jw.Write(d); err != nil { + writer.Close() + return "", err + } + } else { + switch fieldType.Kind() { + case reflect.Slice, reflect.Array: + values := parseDelimitedArray(true, valType, ",") + for _, v := range values { + if err := writer.WriteField(tag.Name+"[]", v); err != nil { + writer.Close() + return "", err + } + } + default: + if err := writer.WriteField(tag.Name, valToString(valType.Interface())); err != nil { + writer.Close() + return "", err + } + } + } + } + + if err := writer.Close(); err != nil { + return "", err + } + + return writer.FormDataContentType(), nil +} + +func encodeMultipartFormDataFile(w *multipart.Writer, fieldType reflect.Type, valType reflect.Value) error { + if fieldType.Kind() != reflect.Struct { + return fmt.Errorf("invalid type %s for multipart/form-data file", valType.Type()) + } + + var fieldName string + var fileName string + var content []byte + + for i := 0; i < fieldType.NumField(); i++ { + field := fieldType.Field(i) + val := valType.Field(i) + + tag := parseMultipartFormTag(field) + if !tag.Content && tag.Name == "" { + continue + } + + if tag.Content { + content = val.Bytes() + } else { + fieldName = tag.Name + fileName = val.String() + } + } + + if fieldName == "" || fileName == "" || content == nil { + return fmt.Errorf("invalid multipart/form-data file") + } + + fw, err := w.CreateFormFile(fieldName, fileName) + if err != nil { + return err + } + if _, err := fw.Write(content); err != nil { + return err + } + + return nil +} + +func encodeFormData(fieldName string, w io.Writer, data interface{}) error { + requestType := reflect.TypeOf(data) + requestValType := reflect.ValueOf(data) + + if requestType.Kind() == reflect.Pointer { + requestType = requestType.Elem() + requestValType = requestValType.Elem() + } + + dataValues := url.Values{} + + switch requestType.Kind() { + case reflect.Struct: + for i := 0; i < requestType.NumField(); i++ { + field := requestType.Field(i) + fieldType := field.Type + valType := requestValType.Field(i) + + if fieldType.Kind() == reflect.Pointer { + if valType.IsNil() { + continue + } + + fieldType = fieldType.Elem() + valType = valType.Elem() + } + + tag := parseFormTag(field) + if tag.JSON { + data, err := json.Marshal(valType.Interface()) + if err != nil { + return err + } + dataValues.Set(tag.Name, string(data)) + } else { + switch tag.Style { + // TODO: support other styles + case "form": + values := populateForm(tag.Name, tag.Explode, fieldType, valType, ",", func(sf reflect.StructField) string { + tag := parseFormTag(field) + if tag == nil { + return "" + } + + return tag.Name + }) + for k, v := range values { + for _, vv := range v { + dataValues.Add(k, vv) + } + } + } + } + } + case reflect.Map: + for _, k := range requestValType.MapKeys() { + v := requestValType.MapIndex(k) + dataValues.Set(fmt.Sprintf("%v", k.Interface()), valToString(v.Interface())) + } + case reflect.Slice, reflect.Array: + for i := 0; i < requestValType.Len(); i++ { + v := requestValType.Index(i) + dataValues.Set(fieldName, valToString(v.Interface())) + } + } + + if _, err := w.Write([]byte(dataValues.Encode())); err != nil { + return err + } + + return nil +} + +type requestTag struct { + MediaType string +} + +func getRequestTag(field reflect.StructField) *requestTag { + // example `request:"mediaType=multipart/form-data"` + values := parseStructTag(requestTagKey, field) + if values == nil { + return nil + } + + tag := &requestTag{ + MediaType: "application/octet-stream", + } + + for k, v := range values { + switch k { + case "mediaType": + tag.MediaType = v + } + } + + return tag +} + +type multipartFormTag struct { + File bool + Content bool + JSON bool + Name string +} + +func parseMultipartFormTag(field reflect.StructField) *multipartFormTag { + // example `multipartForm:"name=file"` + values := parseStructTag(multipartFormTagKey, field) + + tag := &multipartFormTag{} + + for k, v := range values { + switch k { + case "file": + tag.File = v == "true" + case "content": + tag.Content = v == "true" + case "name": + tag.Name = v + case "json": + tag.JSON = v == "true" + } + } + + return tag +} + +type formTag struct { + Name string + JSON bool + Style string + Explode bool +} + +func parseFormTag(field reflect.StructField) *formTag { + // example `form:"name=propName,style=spaceDelimited,explode"` + values := parseStructTag(formTagKey, field) + + tag := &formTag{ + Style: "form", + Explode: true, + } + + for k, v := range values { + switch k { + case "name": + tag.Name = v + case "json": + tag.JSON = v == "true" + case "style": + tag.Style = v + case "explode": + tag.Explode = v == "true" + } + } + + return tag +} diff --git a/internal/sdk/pkg/utils/retries.go b/internal/sdk/pkg/utils/retries.go new file mode 100755 index 0000000..ff39d0d --- /dev/null +++ b/internal/sdk/pkg/utils/retries.go @@ -0,0 +1,117 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package utils + +import ( + "context" + "errors" + "fmt" + "net/http" + "net/url" + "strconv" + "strings" + "time" + + "github.com/cenkalti/backoff/v4" +) + +var errRequestFailed = errors.New("request failed") + +type BackoffStrategy struct { + InitialInterval int + MaxInterval int + Exponent float64 + MaxElapsedTime int +} + +type RetryConfig struct { + Strategy string + Backoff *BackoffStrategy + RetryConnectionErrors bool +} + +type Retries struct { + Config *RetryConfig + StatusCodes []string +} + +func Retry(ctx context.Context, r Retries, action func() (*http.Response, error)) (*http.Response, error) { + switch r.Config.Strategy { + case "backoff": + if r.Config.Backoff == nil { + return action() + } + + config := backoff.NewExponentialBackOff() + config.InitialInterval = time.Duration(r.Config.Backoff.InitialInterval) * time.Millisecond + config.MaxInterval = time.Duration(r.Config.Backoff.MaxInterval) * time.Millisecond + config.Multiplier = r.Config.Backoff.Exponent + config.MaxElapsedTime = time.Duration(r.Config.Backoff.MaxElapsedTime) * time.Millisecond + config.Reset() + + var resp *http.Response + + err := backoff.Retry(func() error { + if resp != nil { + resp.Body.Close() + } + + select { + case <-ctx.Done(): + return backoff.Permanent(ctx.Err()) + default: + } + + res, err := action() + if err != nil { + urlError := new(url.Error) + if errors.As(err, &urlError) { + if (urlError.Temporary() || urlError.Timeout()) && r.Config.RetryConnectionErrors { + return err + } + } + + return backoff.Permanent(err) + } + resp = res + if res == nil { + return fmt.Errorf("no response") + } + + for _, code := range r.StatusCodes { + if strings.Contains(strings.ToUpper(code), "X") { + codeRange, err := strconv.Atoi(code[:1]) + if err != nil { + continue + } + + s := res.StatusCode / 100 + + if s >= codeRange && s < codeRange+1 { + return errRequestFailed + } + } else { + parsedCode, err := strconv.Atoi(code) + if err != nil { + continue + } + + if res.StatusCode == parsedCode { + return errRequestFailed + } + } + } + + resp = res + + return nil + }, config) + if err != nil && !errors.Is(err, errRequestFailed) { + return nil, err + } + + return resp, nil + default: + return action() + } +} diff --git a/internal/sdk/pkg/utils/security.go b/internal/sdk/pkg/utils/security.go new file mode 100755 index 0000000..ec837d7 --- /dev/null +++ b/internal/sdk/pkg/utils/security.go @@ -0,0 +1,292 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package utils + +import ( + "encoding/base64" + "fmt" + "net/http" + "reflect" + "strings" +) + +type HTTPClient interface { + Do(req *http.Request) (*http.Response, error) +} + +const ( + securityTagKey = "security" +) + +type securityTag struct { + Option bool + Scheme bool + Name string + Type string + SubType string +} + +type SecurityClient struct { + client HTTPClient + headers map[string]string + queryParams map[string]string +} + +func newSecurityClient(client HTTPClient) *SecurityClient { + return &SecurityClient{ + client: client, + headers: make(map[string]string), + queryParams: make(map[string]string), + } +} + +func (c *SecurityClient) Do(req *http.Request) (*http.Response, error) { + for k, v := range c.headers { + req.Header.Set(k, v) + } + + queryParams := req.URL.Query() + + for k, v := range c.queryParams { + queryParams.Set(k, v) + } + + req.URL.RawQuery = queryParams.Encode() + + return c.client.Do(req) +} + +func ConfigureSecurityClient(c HTTPClient, security interface{}) *SecurityClient { + client := parseSecurityStruct(c, security) + if client != nil { + return client + } + + return newSecurityClient(c) +} + +func parseSecurityStruct(c HTTPClient, security interface{}) *SecurityClient { + securityStructType := reflect.TypeOf(security) + securityValType := reflect.ValueOf(security) + + if securityStructType.Kind() == reflect.Ptr { + if securityValType.IsNil() { + return nil + } + + securityStructType = securityStructType.Elem() + securityValType = securityValType.Elem() + } + + client := newSecurityClient(c) + + for i := 0; i < securityStructType.NumField(); i++ { + fieldType := securityStructType.Field(i) + valType := securityValType.Field(i) + + kind := valType.Kind() + + if fieldType.Type.Kind() == reflect.Pointer { + if valType.IsNil() { + continue + } + + kind = valType.Elem().Kind() + } + + secTag := parseSecurityTag(fieldType) + if secTag != nil { + if secTag.Option { + return parseSecurityOption(c, valType.Interface()) + } else if secTag.Scheme { + // Special case for basic auth which could be a flattened struct + if secTag.SubType == "basic" && kind != reflect.Struct { + parseSecurityScheme(client, secTag, security) + return client + } else { + parseSecurityScheme(client, secTag, valType.Interface()) + } + } + } + } + + return client +} + +func parseSecurityOption(c HTTPClient, option interface{}) *SecurityClient { + optionStructType := reflect.TypeOf(option) + optionValType := reflect.ValueOf(option) + + if optionStructType.Kind() == reflect.Ptr { + if optionValType.IsNil() { + return nil + } + + optionStructType = optionStructType.Elem() + optionValType = optionValType.Elem() + } + + client := newSecurityClient(c) + + for i := 0; i < optionStructType.NumField(); i++ { + fieldType := optionStructType.Field(i) + valType := optionValType.Field(i) + + secTag := parseSecurityTag(fieldType) + if secTag != nil && secTag.Scheme { + parseSecurityScheme(client, secTag, valType.Interface()) + } + } + + return client +} + +func parseSecurityScheme(client *SecurityClient, schemeTag *securityTag, scheme interface{}) { + schemeType := reflect.TypeOf(scheme) + schemeVal := reflect.ValueOf(scheme) + + if schemeType.Kind() == reflect.Ptr { + if schemeVal.IsNil() { + return + } + + schemeType = schemeType.Elem() + schemeVal = schemeVal.Elem() + } + + if schemeType.Kind() == reflect.Struct { + if schemeTag.Type == "http" && schemeTag.SubType == "basic" { + parseBasicAuthScheme(client, schemeVal.Interface()) + return + } + + for i := 0; i < schemeType.NumField(); i++ { + fieldType := schemeType.Field(i) + valType := schemeVal.Field(i) + + if fieldType.Type.Kind() == reflect.Ptr { + if valType.IsNil() { + continue + } + + valType = valType.Elem() + } + + secTag := parseSecurityTag(fieldType) + if secTag == nil || secTag.Name == "" { + return + } + + parseSecuritySchemeValue(client, schemeTag, secTag, valType.Interface()) + } + } else { + parseSecuritySchemeValue(client, schemeTag, schemeTag, schemeVal.Interface()) + } +} + +func parseSecuritySchemeValue(client *SecurityClient, schemeTag *securityTag, secTag *securityTag, val interface{}) { + switch schemeTag.Type { + case "apiKey": + switch schemeTag.SubType { + case "header": + client.headers[secTag.Name] = valToString(val) + case "query": + client.queryParams[secTag.Name] = valToString(val) + case "cookie": + client.headers["Cookie"] = fmt.Sprintf("%s=%s", secTag.Name, valToString(val)) + default: + panic("not supported") + } + case "openIdConnect": + client.headers[secTag.Name] = valToString(val) + case "oauth2": + client.headers[secTag.Name] = valToString(val) + case "http": + switch schemeTag.SubType { + case "bearer": + client.headers[secTag.Name] = prefixBearer(valToString(val)) + default: + panic("not supported") + } + default: + panic("not supported") + } +} + +func prefixBearer(authHeaderValue string) string { + if strings.HasPrefix(strings.ToLower(authHeaderValue), "bearer ") { + return authHeaderValue + } + + return fmt.Sprintf("Bearer %s", authHeaderValue) +} + +func parseBasicAuthScheme(client *SecurityClient, scheme interface{}) { + schemeStructType := reflect.TypeOf(scheme) + schemeValType := reflect.ValueOf(scheme) + + var username, password string + + for i := 0; i < schemeStructType.NumField(); i++ { + fieldType := schemeStructType.Field(i) + valType := schemeValType.Field(i) + + secTag := parseSecurityTag(fieldType) + if secTag == nil || secTag.Name == "" { + continue + } + + switch secTag.Name { + case "username": + username = valType.String() + case "password": + password = valType.String() + } + } + + client.headers["Authorization"] = fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password)))) +} + +func parseSecurityTag(field reflect.StructField) *securityTag { + tag := field.Tag.Get(securityTagKey) + if tag == "" { + return nil + } + + option := false + scheme := false + name := "" + securityType := "" + securitySubType := "" + + options := strings.Split(tag, ",") + for _, optionConf := range options { + parts := strings.Split(optionConf, "=") + if len(parts) < 1 || len(parts) > 2 { + continue + } + + switch parts[0] { + case "name": + name = parts[1] + case "type": + securityType = parts[1] + case "subtype": + securitySubType = parts[1] + case "option": + option = true + case "scheme": + scheme = true + } + } + + // TODO: validate tag? + + return &securityTag{ + Option: option, + Scheme: scheme, + Name: name, + Type: securityType, + SubType: securitySubType, + } +} diff --git a/internal/sdk/pkg/utils/utils.go b/internal/sdk/pkg/utils/utils.go new file mode 100755 index 0000000..513aa28 --- /dev/null +++ b/internal/sdk/pkg/utils/utils.go @@ -0,0 +1,152 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package utils + +import ( + "encoding/json" + "fmt" + "io" + "math/big" + "reflect" + "regexp" + "strings" + "time" + + "abbey/internal/sdk/pkg/types" +) + +const ( + queryParamTagKey = "queryParam" + headerParamTagKey = "header" + pathParamTagKey = "pathParam" +) + +var ( + paramRegex = regexp.MustCompile(`({.*?})`) + SerializationMethodToContentType = map[string]string{ + "json": "application/json", + "form": "application/x-www-form-urlencoded", + "multipart": "multipart/form-data", + "raw": "application/octet-stream", + "string": "text/plain", + } +) + +func UnmarshalJsonFromResponseBody(body io.Reader, out interface{}) error { + data, err := io.ReadAll(body) + if err != nil { + return fmt.Errorf("error reading response body: %w", err) + } + if err := json.Unmarshal(data, &out); err != nil { + return fmt.Errorf("error unmarshalling json response body: %w", err) + } + + return nil +} + +func ReplaceParameters(stringWithParams string, params map[string]string) string { + return paramRegex.ReplaceAllStringFunc(stringWithParams, func(match string) string { + match = match[1 : len(match)-1] + return params[match] + }) +} + +func Contains(slice []string, item string) bool { + for _, s := range slice { + if s == item { + return true + } + } + return false +} + +func parseStructTag(tagKey string, field reflect.StructField) map[string]string { + tag := field.Tag.Get(tagKey) + if tag == "" { + return nil + } + + values := map[string]string{} + + options := strings.Split(tag, ",") + for _, optionConf := range options { + parts := strings.Split(optionConf, "=") + + switch len(parts) { + case 1: + // flag option + parts = append(parts, "true") + case 2: + // key=value option + break + default: + // invalid option + continue + } + + values[parts[0]] = parts[1] + } + + return values +} + +func parseParamTag(tagKey string, field reflect.StructField, defaultStyle string, defaultExplode bool) *paramTag { + // example `{tagKey}:"style=simple,explode=false,name=apiID"` + values := parseStructTag(tagKey, field) + if values == nil { + return nil + } + + tag := ¶mTag{ + Style: defaultStyle, + Explode: defaultExplode, + ParamName: strings.ToLower(field.Name), + } + + for k, v := range values { + switch k { + case "style": + tag.Style = v + case "explode": + tag.Explode = v == "true" + case "name": + tag.ParamName = v + case "serialization": + tag.Serialization = v + } + } + + return tag +} + +func valToString(val interface{}) string { + switch v := val.(type) { + case time.Time: + return v.Format(time.RFC3339Nano) + case types.BigInt: + return v.String() + case big.Int: + return v.String() + default: + return fmt.Sprintf("%v", v) + } +} + +func populateFromGlobals(fieldType reflect.StructField, valType reflect.Value, paramType string, globals map[string]map[string]map[string]interface{}) reflect.Value { + if globals != nil && fieldType.Type.Kind() == reflect.Ptr { + parameters, ok := globals["parameters"] + if ok { + paramsOfType, ok := parameters[paramType] + if ok { + globalVal, ok := paramsOfType[fieldType.Name] + if ok { + if reflect.TypeOf(globalVal).Kind() == fieldType.Type.Elem().Kind() && valType.IsNil() { + valType = reflect.ValueOf(&globalVal) + } + } + } + } + } + + return valType +} diff --git a/internal/sdk/requests.go b/internal/sdk/requests.go new file mode 100755 index 0000000..540fdc4 --- /dev/null +++ b/internal/sdk/requests.go @@ -0,0 +1,342 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package sdk + +import ( + "abbey/internal/sdk/pkg/models/operations" + "abbey/internal/sdk/pkg/models/shared" + "abbey/internal/sdk/pkg/utils" + "bytes" + "context" + "fmt" + "io" + "net/http" + "strings" +) + +// requests - Requests are Access Requests that users make to get access to a resource. +// +// https://docs.abbey.io/getting-started/concepts#access-requests +type requests struct { + sdkConfiguration sdkConfiguration +} + +func newRequests(sdkConfig sdkConfiguration) *requests { + return &requests{ + sdkConfiguration: sdkConfig, + } +} + +// CancelRequestByID - Cancel a Request by ID +// Cancels the specified request. +func (s *requests) CancelRequestByID(ctx context.Context, request operations.CancelRequestByIDRequest) (*operations.CancelRequestByIDResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url, err := utils.GenerateURL(ctx, baseURL, "/requests/{request_id}/cancel", request, nil) + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + bodyReader, reqContentType, err := utils.SerializeRequestBody(ctx, request, "RequestCancelParams", "json") + if err != nil { + return nil, fmt.Errorf("error serializing request body: %w", err) + } + if bodyReader == nil { + return nil, fmt.Errorf("request body is required") + } + + req, err := http.NewRequestWithContext(ctx, "PUT", url, bodyReader) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json;q=1, application/json;q=0") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + req.Header.Set("Content-Type", reqContentType) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.CancelRequestByIDResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 200: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Request + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Request = out + } + case httpRes.StatusCode == 400: + fallthrough + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 404: + fallthrough + case httpRes.StatusCode == 409: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} + +// CreateRequest - Create a Request +// Creates a new request. +// +// You will need to pass in a Grant Kit ID as the target of this request. This will create a request +// against the latest version of the Grant Kit. +// +// Grant Kit Versions are immutable and you won't be able to create a request against an older Grant Kit Version. +// If you want to do this, you will have to roll forward by creating a new Grant Kit Version. +func (s *requests) CreateRequest(ctx context.Context, request shared.RequestParams) (*operations.CreateRequestResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url := strings.TrimSuffix(baseURL, "/") + "/requests" + + bodyReader, reqContentType, err := utils.SerializeRequestBody(ctx, request, "Request", "json") + if err != nil { + return nil, fmt.Errorf("error serializing request body: %w", err) + } + if bodyReader == nil { + return nil, fmt.Errorf("request body is required") + } + + req, err := http.NewRequestWithContext(ctx, "POST", url, bodyReader) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json;q=1, application/json;q=0") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + req.Header.Set("Content-Type", reqContentType) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.CreateRequestResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 201: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Request + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Request = out + } + case httpRes.StatusCode == 400: + fallthrough + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 409: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} + +// GetRequestByID - Retrieve a Request by ID +// Returns the details of a request. +func (s *requests) GetRequestByID(ctx context.Context, request operations.GetRequestByIDRequest) (*operations.GetRequestByIDResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url, err := utils.GenerateURL(ctx, baseURL, "/requests/{request_id}", request, nil) + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json;q=1, application/json;q=0") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.GetRequestByIDResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 200: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Request + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Request = out + } + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 404: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} + +// ListRequests - List Requests +// Returns a list of requests. +// +// Requests are sorted by creation date, descending. +func (s *requests) ListRequests(ctx context.Context) (*operations.ListRequestsResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url := strings.TrimSuffix(baseURL, "/") + "/requests" + + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json;q=1, application/json;q=0") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.ListRequestsResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 200: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out []shared.Request + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Requests = out + } + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} diff --git a/internal/sdk/reviews.go b/internal/sdk/reviews.go new file mode 100755 index 0000000..953b15c --- /dev/null +++ b/internal/sdk/reviews.go @@ -0,0 +1,343 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package sdk + +import ( + "abbey/internal/sdk/pkg/models/operations" + "abbey/internal/sdk/pkg/models/shared" + "abbey/internal/sdk/pkg/utils" + "bytes" + "context" + "fmt" + "io" + "net/http" + "strings" +) + +// reviews - Reviews are decisions made by a reviewer on an Access Request. +// +// A Reviewer might approve or deny a request. +// +// https://docs.abbey.io/product/approving-or-denying-access-requests +type reviews struct { + sdkConfiguration sdkConfiguration +} + +func newReviews(sdkConfig sdkConfiguration) *reviews { + return &reviews{ + sdkConfiguration: sdkConfig, + } +} + +// ListReviews - List Reviews +// Returns a list of all the reviews sent to the user. +// +// Reviews are sorted by creation date, descending. +func (s *reviews) ListReviews(ctx context.Context) (*operations.ListReviewsResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url := strings.TrimSuffix(baseURL, "/") + "/reviews" + + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json;q=1, application/json;q=0") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.ListReviewsResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 200: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out []shared.Review + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Reviews = out + } + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} + +// ApproveReview - Approve a Review +// Updates the specified review with an approval decision. +func (s *reviews) ApproveReview(ctx context.Context, request operations.ApproveReviewRequest) (*operations.ApproveReviewResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url, err := utils.GenerateURL(ctx, baseURL, "/reviews/{review_id}/approve", request, nil) + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + bodyReader, reqContentType, err := utils.SerializeRequestBody(ctx, request, "ReviewUpdateParams", "json") + if err != nil { + return nil, fmt.Errorf("error serializing request body: %w", err) + } + if bodyReader == nil { + return nil, fmt.Errorf("request body is required") + } + + req, err := http.NewRequestWithContext(ctx, "PUT", url, bodyReader) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json;q=1, application/json;q=0") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + req.Header.Set("Content-Type", reqContentType) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.ApproveReviewResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 200: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Review + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Review = out + } + case httpRes.StatusCode == 400: + fallthrough + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 404: + fallthrough + case httpRes.StatusCode == 409: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} + +// DenyReview - Deny a Review +// Updates the specified review with a deny decision. +func (s *reviews) DenyReview(ctx context.Context, request operations.DenyReviewRequest) (*operations.DenyReviewResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url, err := utils.GenerateURL(ctx, baseURL, "/reviews/{review_id}/deny", request, nil) + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + bodyReader, reqContentType, err := utils.SerializeRequestBody(ctx, request, "ReviewUpdateParams", "json") + if err != nil { + return nil, fmt.Errorf("error serializing request body: %w", err) + } + if bodyReader == nil { + return nil, fmt.Errorf("request body is required") + } + + req, err := http.NewRequestWithContext(ctx, "PUT", url, bodyReader) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json;q=1, application/json;q=0") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + req.Header.Set("Content-Type", reqContentType) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.DenyReviewResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 200: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Review + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Review = out + } + case httpRes.StatusCode == 400: + fallthrough + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 404: + fallthrough + case httpRes.StatusCode == 409: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} + +// GetReviewByID - Retrieve a Review by ID +// Returns the details of a review +func (s *reviews) GetReviewByID(ctx context.Context, request operations.GetReviewByIDRequest) (*operations.GetReviewByIDResponse, error) { + baseURL := utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + url, err := utils.GenerateURL(ctx, baseURL, "/reviews/{review_id}", request, nil) + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json;q=1, application/json;q=0") + req.Header.Set("user-agent", fmt.Sprintf("speakeasy-sdk/%s %s %s %s", s.sdkConfiguration.Language, s.sdkConfiguration.SDKVersion, s.sdkConfiguration.GenVersion, s.sdkConfiguration.OpenAPIDocVersion)) + + client := s.sdkConfiguration.DefaultClient + + httpRes, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error sending request: %w", err) + } + if httpRes == nil { + return nil, fmt.Errorf("error sending request: no response") + } + + rawBody, err := io.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + httpRes.Body.Close() + httpRes.Body = io.NopCloser(bytes.NewBuffer(rawBody)) + + contentType := httpRes.Header.Get("Content-Type") + + res := &operations.GetReviewByIDResponse{ + StatusCode: httpRes.StatusCode, + ContentType: contentType, + RawResponse: httpRes, + } + switch { + case httpRes.StatusCode == 200: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Review + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Review = out + } + case httpRes.StatusCode == 401: + fallthrough + case httpRes.StatusCode == 404: + fallthrough + case httpRes.StatusCode == 429: + fallthrough + default: + switch { + case utils.MatchContentType(contentType, `application/json`): + var out *shared.Error + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out); err != nil { + return nil, err + } + + res.Error = out + } + } + + return res, nil +} diff --git a/internal/sdk/sdk.go b/internal/sdk/sdk.go new file mode 100755 index 0000000..d435c73 --- /dev/null +++ b/internal/sdk/sdk.go @@ -0,0 +1,201 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package sdk + +import ( + "abbey/internal/sdk/pkg/utils" + "fmt" + "net/http" + "time" +) + +const ( + // ServerProd - prod + ServerProd string = "prod" + // ServerDev - dev + ServerDev string = "dev" +) + +// ServerList contains the list of servers available to the SDK +var ServerList = map[string]string{ + ServerProd: "https://api.abbey.io/v1", + ServerDev: "http://localhost:8080/v1", +} + +// HTTPClient provides an interface for suplying the SDK with a custom HTTP client +type HTTPClient interface { + Do(req *http.Request) (*http.Response, error) +} + +// String provides a helper function to return a pointer to a string +func String(s string) *string { return &s } + +// Bool provides a helper function to return a pointer to a bool +func Bool(b bool) *bool { return &b } + +// Int provides a helper function to return a pointer to an int +func Int(i int) *int { return &i } + +// Int64 provides a helper function to return a pointer to an int64 +func Int64(i int64) *int64 { return &i } + +// Float32 provides a helper function to return a pointer to a float32 +func Float32(f float32) *float32 { return &f } + +// Float64 provides a helper function to return a pointer to a float64 +func Float64(f float64) *float64 { return &f } + +type sdkConfiguration struct { + DefaultClient HTTPClient + SecurityClient HTTPClient + + ServerURL string + Server string + Language string + OpenAPIDocVersion string + SDKVersion string + GenVersion string +} + +func (c *sdkConfiguration) GetServerDetails() (string, map[string]string) { + if c.ServerURL != "" { + return c.ServerURL, nil + } + + if c.Server == "" { + c.Server = "prod" + } + + return ServerList[c.Server], nil +} + +// SDK - Edge API: The front door to Abbey Labs. +type SDK struct { + // APIKeys - API Keys are used to authenticate to the Abbey API. + // + // https://docs.abbey.io/product/managing-api-keys + APIKeys *apiKeys + // ConnectionSpecs - Connection Specs are the templates for creating connections. + // They are used to validate connection parameters and to provide a UI for creating connections. + // + // https://docs.abbey.io + ConnectionSpecs *connectionSpecs + // Connections - Connections are authenticated, with scopes if available, and made available to Abbey Grant Kits at runtime. + // + // https://docs.abbey.io + Connections *connections + // GrantKits - Grant Kits are what you configure in code to control and automatically right-size permissions for resources. + // A Grant Kit has 3 components: + // + // 1. Workflow to configure how someone should get access. + // 2. Policies to configure if someone should get access. + // 3. Output to configure how and where Grants should materialize. + // + // https://docs.abbey.io/getting-started/concepts#grant-kits + GrantKits *grantKits + // Grants - Grants are permissions that reflect the result of an access request going through the process of evaluating + // policies and approval workflows where all approval conditions are met. + // + // Grants may be revoked manually by a user or automatically if a time-based or attribute-based policy is + // included in the corresponding Grant Kit's policy. + // + // https://docs.abbey.io/getting-started/concepts#grants + Grants *grants + // Identities - User metadata used for enriching data. + // Enriched data is used to write richer policies, workflows, and outputs. + // + // https://docs.abbey.io + Identities *identities + // Requests - Requests are Access Requests that users make to get access to a resource. + // + // https://docs.abbey.io/getting-started/concepts#access-requests + Requests *requests + // Reviews - Reviews are decisions made by a reviewer on an Access Request. + // + // A Reviewer might approve or deny a request. + // + // https://docs.abbey.io/product/approving-or-denying-access-requests + Reviews *reviews + + sdkConfiguration sdkConfiguration +} + +type SDKOption func(*SDK) + +// WithServerURL allows the overriding of the default server URL +func WithServerURL(serverURL string) SDKOption { + return func(sdk *SDK) { + sdk.sdkConfiguration.ServerURL = serverURL + } +} + +// WithTemplatedServerURL allows the overriding of the default server URL with a templated URL populated with the provided parameters +func WithTemplatedServerURL(serverURL string, params map[string]string) SDKOption { + return func(sdk *SDK) { + if params != nil { + serverURL = utils.ReplaceParameters(serverURL, params) + } + + sdk.sdkConfiguration.ServerURL = serverURL + } +} + +// WithServer allows the overriding of the default server by name +func WithServer(server string) SDKOption { + return func(sdk *SDK) { + _, ok := ServerList[server] + if !ok { + panic(fmt.Errorf("server %s not found", server)) + } + + sdk.sdkConfiguration.Server = server + } +} + +// WithClient allows the overriding of the default HTTP client used by the SDK +func WithClient(client HTTPClient) SDKOption { + return func(sdk *SDK) { + sdk.sdkConfiguration.DefaultClient = client + } +} + +// New creates a new instance of the SDK with the provided options +func New(opts ...SDKOption) *SDK { + sdk := &SDK{ + sdkConfiguration: sdkConfiguration{ + Language: "terraform", + OpenAPIDocVersion: "0.1.0", + SDKVersion: "1.1.1", + GenVersion: "2.59.0", + }, + } + for _, opt := range opts { + opt(sdk) + } + + // Use WithClient to override the default client if you would like to customize the timeout + if sdk.sdkConfiguration.DefaultClient == nil { + sdk.sdkConfiguration.DefaultClient = &http.Client{Timeout: 60 * time.Second} + } + if sdk.sdkConfiguration.SecurityClient == nil { + sdk.sdkConfiguration.SecurityClient = sdk.sdkConfiguration.DefaultClient + } + + sdk.APIKeys = newAPIKeys(sdk.sdkConfiguration) + + sdk.ConnectionSpecs = newConnectionSpecs(sdk.sdkConfiguration) + + sdk.Connections = newConnections(sdk.sdkConfiguration) + + sdk.GrantKits = newGrantKits(sdk.sdkConfiguration) + + sdk.Grants = newGrants(sdk.sdkConfiguration) + + sdk.Identities = newIdentities(sdk.sdkConfiguration) + + sdk.Requests = newRequests(sdk.sdkConfiguration) + + sdk.Reviews = newReviews(sdk.sdkConfiguration) + + return sdk +} diff --git a/internal/validators/DateValidator.go b/internal/validators/DateValidator.go new file mode 100755 index 0000000..6444cab --- /dev/null +++ b/internal/validators/DateValidator.go @@ -0,0 +1,50 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package validators + +import ( + "abbey/internal/sdk/pkg/types" + "context" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +var _ validator.String = DateValidator{} + +type DateValidator struct { +} + +func (validator DateValidator) Description(ctx context.Context) string { + return "value must be a string in YYYY-MM-DD format" +} + +func (validator DateValidator) MarkdownDescription(ctx context.Context) string { + return validator.Description(ctx) +} + +func (validator DateValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { + // Only validate the attribute configuration value if it is known. + if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() { + return + } + + if _, err := types.NewDateFromString(req.ConfigValue.ValueString()); err != nil { + resp.Diagnostics.Append(validatordiag.InvalidAttributeTypeDiagnostic( + req.Path, + validator.MarkdownDescription(ctx), + req.ConfigValue.ValueString(), + )) + return + } +} + +// IsDate returns an AttributeValidator which ensures that any configured +// attribute value: +// +// - Is a String. +// - Is in YYYY-MM-DD Format. +// +// Null (unconfigured) and unknown (known after apply) values are skipped. +func IsValidDate() validator.String { + return DateValidator{} +} diff --git a/internal/validators/ExactlyOneChild.go b/internal/validators/ExactlyOneChild.go new file mode 100755 index 0000000..839ef01 --- /dev/null +++ b/internal/validators/ExactlyOneChild.go @@ -0,0 +1,49 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package validators + +import ( + "context" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +var _ validator.Object = exactlyOneChild{} + +// exactlyOneChild validates if the provided value is of type string and can be parsed as JSON. +type exactlyOneChild struct { +} + +func (validator exactlyOneChild) ValidateObject(ctx context.Context, req validator.ObjectRequest, resp *validator.ObjectResponse) { + defined := make(map[string]bool) + count := 0 + for key, attr := range req.ConfigValue.Attributes() { + if attr.IsUnknown() || attr.IsNull() { + continue + } + defined[key] = true + count++ + } + if count != 1 { + resp.Diagnostics.Append(validatordiag.InvalidAttributeTypeDiagnostic( + req.Path, + validator.MarkdownDescription(ctx), + req.ConfigValue.String(), + )) + } +} + +func (validator exactlyOneChild) Description(ctx context.Context) string { + return "value must have exactly one child attribute defined" +} + +func (validator exactlyOneChild) MarkdownDescription(ctx context.Context) string { + return validator.Description(ctx) +} + +// ExactlyOneChild returns an AttributeValidator which ensures that any configured +// attribute object has only one child attribute. +// Null (unconfigured) and unknown values are skipped. +func ExactlyOneChild() validator.Object { + return exactlyOneChild{} +} diff --git a/internal/validators/JSONParseValidator.go b/internal/validators/JSONParseValidator.go new file mode 100755 index 0000000..3a341e1 --- /dev/null +++ b/internal/validators/JSONParseValidator.go @@ -0,0 +1,51 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package validators + +import ( + "context" + "encoding/json" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +var _ validator.String = JSONParseValidator{} + +// JSONParseValidator validates if the provided value is of type string and can be parsed as JSON. +type JSONParseValidator struct { +} + +func (validator JSONParseValidator) Description(ctx context.Context) string { + return "value must be parsable as JSON" +} + +func (validator JSONParseValidator) MarkdownDescription(ctx context.Context) string { + return validator.Description(ctx) +} + +func (validator JSONParseValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { + // Only validate the attribute configuration value if it is known. + if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() { + return + } + + if !json.Valid([]byte(req.ConfigValue.ValueString())) { + resp.Diagnostics.Append(validatordiag.InvalidAttributeTypeDiagnostic( + req.Path, + validator.MarkdownDescription(ctx), + req.ConfigValue.ValueString(), + )) + return + } +} + +// IsValidJSON returns an AttributeValidator which ensures that any configured +// attribute value: +// +// - Is a String. +// - Is considered valid JSON. +// +// Null (unconfigured) and unknown (known after apply) values are skipped. +func IsValidJSON() validator.String { + return JSONParseValidator{} +} diff --git a/internal/validators/RFC3339Validator.go b/internal/validators/RFC3339Validator.go new file mode 100755 index 0000000..42c90dc --- /dev/null +++ b/internal/validators/RFC3339Validator.go @@ -0,0 +1,50 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +package validators + +import ( + "context" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "time" +) + +var _ validator.String = RFC3339TimeValidator{} + +type RFC3339TimeValidator struct { +} + +func (validator RFC3339TimeValidator) Description(ctx context.Context) string { + return "value must be a string in RFC3339 format" +} + +func (validator RFC3339TimeValidator) MarkdownDescription(ctx context.Context) string { + return validator.Description(ctx) +} + +func (validator RFC3339TimeValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { + // Only validate the attribute configuration value if it is known. + if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() { + return + } + + if _, err := time.Parse(time.RFC3339, req.ConfigValue.ValueString()); err != nil { + resp.Diagnostics.Append(validatordiag.InvalidAttributeTypeDiagnostic( + req.Path, + validator.MarkdownDescription(ctx), + req.ConfigValue.ValueString(), + )) + return + } +} + +// IsRFC3339 returns an AttributeValidator which ensures that any configured +// attribute value: +// +// - Is a String. +// - Is in RFC3339 Format. +// +// Null (unconfigured) and unknown (known after apply) values are skipped. +func IsRFC3339() validator.String { + return RFC3339TimeValidator{} +} diff --git a/main.go b/main.go old mode 100644 new mode 100755 index 141ddad..9e63be8 --- a/main.go +++ b/main.go @@ -1,32 +1,46 @@ -package main +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. -//go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs generate +package main import ( "context" + "flag" "log" + "abbey/internal/provider" "github.com/hashicorp/terraform-plugin-framework/providerserver" - - "abbey.io/terraform-provider-abbey/internal/abbey" - "abbey.io/terraform-provider-abbey/internal/abbey/provider" ) -// GoReleaser sets the version in compiled binaries. -// https://goreleaser.com/cookbooks/using-main.version/ -var version = "dev" +// Run "go generate" to format example terraform files and generate the docs for the registry/website + +// If you do not have terraform installed, you can remove the formatting command, but its suggested to +// ensure the documentation is formatted properly. +//go:generate terraform fmt -recursive ./examples/ + +// Run the docs generation tool, check its repository for more information on how it works and how docs +// can be customized. +//go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs + +var ( + // these will be set by the goreleaser configuration + // to appropriate values for the compiled binary + version string = "dev" +) func main() { - err := providerserver.Serve( - context.Background(), - abbey.New(version, provider.DefaultHost), - providerserver.ServeOpts{ - Address: "registry.terraform.io/abbeylabs/abbey", - ProtocolVersion: 6, - Debug: false, - }, - ) + 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/abbeylabs/abbey", + Debug: debug, + } + + err := providerserver.Serve(context.Background(), provider.New(version), opts) + if err != nil { - log.Fatal(err) + log.Fatal(err.Error()) } } diff --git a/terraform-registry-manifest.json b/terraform-registry-manifest.json old mode 100644 new mode 100755 index 295001a..fec2a56 --- a/terraform-registry-manifest.json +++ b/terraform-registry-manifest.json @@ -1,6 +1,6 @@ { - "version": 1, - "metadata": { - "protocol_versions": ["6.0"] - } + "version": 1, + "metadata": { + "protocol_versions": ["6.0"] + } } diff --git a/tools/tools.go b/tools/tools.go new file mode 100755 index 0000000..4f42a8c --- /dev/null +++ b/tools/tools.go @@ -0,0 +1,10 @@ +// Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT. + +//go:build tools + +package tools + +import ( + // Documentation generation + _ "github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs" +)