diff --git a/docs/data-sources/connection.md b/docs/data-sources/connection.md new file mode 100644 index 0000000..6611144 --- /dev/null +++ b/docs/data-sources/connection.md @@ -0,0 +1,124 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "hookdeck_connection Data Source - terraform-provider-hookdeck" +subcategory: "" +description: |- + Connection Data Source +--- + +# hookdeck_connection (Data Source) + +Connection Data Source + + + + +## Schema + +### Required + +- `id` (String) ID of the connection + +### Read-Only + +- `created_at` (String) Date the connection was created +- `description` (String) Description for the connection +- `destination_id` (String) ID of a destination to bind to the connection +- `disabled_at` (String) Date the connection was disabled +- `name` (String) A unique, human-friendly name for the connection +- `paused_at` (String) Date the connection was paused +- `rules` (Attributes Set) (see [below for nested schema](#nestedatt--rules)) +- `source_id` (String) ID of a source to bind to the connection +- `team_id` (String) ID of the workspace +- `updated_at` (String) Date the connection was last updated + + +### Nested Schema for `rules` + +Read-Only: + +- `delay_rule` (Attributes) (see [below for nested schema](#nestedatt--rules--delay_rule)) +- `filter_rule` (Attributes) (see [below for nested schema](#nestedatt--rules--filter_rule)) +- `retry_rule` (Attributes) (see [below for nested schema](#nestedatt--rules--retry_rule)) +- `transform_rule` (Attributes) (see [below for nested schema](#nestedatt--rules--transform_rule)) + + +### Nested Schema for `rules.delay_rule` + +Read-Only: + +- `delay` (Number) Delay to introduce in MS + + + +### Nested Schema for `rules.filter_rule` + +Read-Only: + +- `body` (Attributes) (see [below for nested schema](#nestedatt--rules--filter_rule--body)) +- `headers` (Attributes) (see [below for nested schema](#nestedatt--rules--filter_rule--headers)) +- `path` (Attributes) (see [below for nested schema](#nestedatt--rules--filter_rule--path)) +- `query` (Attributes) (see [below for nested schema](#nestedatt--rules--filter_rule--query)) + + +### Nested Schema for `rules.filter_rule.body` + +Read-Only: + +- `boolean` (Boolean) +- `json` (String) Stringied JSON using our filter syntax to filter on request headers +- `number` (Number) +- `string` (String) + + + +### Nested Schema for `rules.filter_rule.headers` + +Read-Only: + +- `boolean` (Boolean) +- `json` (String) Stringied JSON using our filter syntax to filter on request headers +- `number` (Number) +- `string` (String) + + + +### Nested Schema for `rules.filter_rule.path` + +Read-Only: + +- `boolean` (Boolean) +- `json` (String) Stringied JSON using our filter syntax to filter on request headers +- `number` (Number) +- `string` (String) + + + +### Nested Schema for `rules.filter_rule.query` + +Read-Only: + +- `boolean` (Boolean) +- `json` (String) Stringied JSON using our filter syntax to filter on request headers +- `number` (Number) +- `string` (String) + + + + +### Nested Schema for `rules.retry_rule` + +Read-Only: + +- `count` (Number) Maximum number of retries to attempt +- `interval` (Number) Time in MS between each retry +- `strategy` (String) must be one of ["linear", "exponential"] +Algorithm to use when calculating delay between retries + + + +### Nested Schema for `rules.transform_rule` + +Read-Only: + +- `transformation_id` (String) ID of the attached transformation object. diff --git a/docs/data-sources/destination.md b/docs/data-sources/destination.md new file mode 100644 index 0000000..142f449 --- /dev/null +++ b/docs/data-sources/destination.md @@ -0,0 +1,136 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "hookdeck_destination Data Source - terraform-provider-hookdeck" +subcategory: "" +description: |- + Destination Data Source +--- + +# hookdeck_destination (Data Source) + +Destination Data Source + + + + +## Schema + +### Required + +- `id` (String) ID of the destination + +### Read-Only + +- `auth_method` (Attributes) Config for the destination's auth method (see [below for nested schema](#nestedatt--auth_method)) +- `cli_path` (String) Path for the CLI destination +- `created_at` (String) Date the destination was created +- `description` (String) Description for the destination +- `disabled_at` (String) Date the destination was disabled +- `http_method` (String) must be one of ["GET", "POST", "PUT", "PATCH", "DELETE"] +HTTP method used on requests sent to the destination, overrides the method used on requests sent to the source. +- `name` (String) A unique, human-friendly name for the destination +- `path_forwarding_disabled` (Boolean) +- `rate_limit` (Attributes) Rate limit (see [below for nested schema](#nestedatt--rate_limit)) +- `team_id` (String) ID of the workspace +- `updated_at` (String) Date the destination was last updated +- `url` (String) HTTP endpoint of the destination + + +### Nested Schema for `auth_method` + +Read-Only: + +- `api_key` (Attributes) API Key (see [below for nested schema](#nestedatt--auth_method--api_key)) +- `aws_signature` (Attributes) AWS Signature (see [below for nested schema](#nestedatt--auth_method--aws_signature)) +- `basic_auth` (Attributes) Basic Auth (see [below for nested schema](#nestedatt--auth_method--basic_auth)) +- `bearer_token` (Attributes) Bearer Token (see [below for nested schema](#nestedatt--auth_method--bearer_token)) +- `custom_signature` (Attributes) Custom Signature (see [below for nested schema](#nestedatt--auth_method--custom_signature)) +- `hookdeck_signature` (Attributes) Hookdeck Signature (see [below for nested schema](#nestedatt--auth_method--hookdeck_signature)) +- `json` (String, Sensitive) Stringified JSON value for destination payload, used when Terraform provider hasn't supported the destination method on Hookdeck yet +- `oauth2_authorization_code` (Attributes) OAuth2 Client Credentials (see [below for nested schema](#nestedatt--auth_method--oauth2_authorization_code)) +- `oauth2_client_credentials` (Attributes) OAuth2 Client Credentials (see [below for nested schema](#nestedatt--auth_method--oauth2_client_credentials)) + + +### Nested Schema for `auth_method.api_key` + +Read-Only: + +- `api_key` (String, Sensitive) API key for the API key auth +- `key` (String) Key for the API key auth +- `to` (String) must be one of ["header", "query"] +Whether the API key should be sent as a header or a query parameter + + + +### Nested Schema for `auth_method.aws_signature` + +Read-Only: + +- `access_key_id` (String, Sensitive) AWS access key id +- `secret_access_key` (String, Sensitive) AWS secret access key + + + +### Nested Schema for `auth_method.basic_auth` + +Read-Only: + +- `password` (String, Sensitive) Password for basic auth +- `username` (String) Username for basic auth + + + +### Nested Schema for `auth_method.bearer_token` + +Read-Only: + +- `token` (String, Sensitive) Token for the bearer token auth + + + +### Nested Schema for `auth_method.custom_signature` + +Read-Only: + +- `key` (String) Key for the custom signature auth +- `signing_secret` (String, Sensitive) Signing secret for the custom signature auth. If left empty a secret will be generated for you. + + + +### Nested Schema for `auth_method.hookdeck_signature` + + + +### Nested Schema for `auth_method.oauth2_authorization_code` + +Read-Only: + +- `auth_server` (String) URL of the auth server +- `client_id` (String) Client id in the auth server +- `client_secret` (String, Sensitive) Client secret in the auth server +- `refresh_token` (String, Sensitive) Refresh token already returned by the auth server +- `scope` (String) Scope to access + + + +### Nested Schema for `auth_method.oauth2_client_credentials` + +Read-Only: + +- `auth_server` (String) URL of the auth server +- `authentication_type` (String) must be one of [basic, bearer] +Basic (default) or Bearer Authentication +- `client_id` (String) Client id in the auth server +- `client_secret` (String, Sensitive) Client secret in the auth server +- `scope` (String) Scope to access + + + + +### Nested Schema for `rate_limit` + +Read-Only: + +- `limit` (Number) Limit event attempts to receive per period. Max value is workspace plan's max attempts thoughput. +- `period` (String) must be one of ["second", "minute", "hour"] +Period to rate limit attempts diff --git a/docs/data-sources/source.md b/docs/data-sources/source.md new file mode 100644 index 0000000..04576fd --- /dev/null +++ b/docs/data-sources/source.md @@ -0,0 +1,41 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "hookdeck_source Data Source - terraform-provider-hookdeck" +subcategory: "" +description: |- + Source Data Source +--- + +# hookdeck_source (Data Source) + +Source Data Source + + + + +## Schema + +### Required + +- `id` (String) ID of the source + +### Read-Only + +- `allowed_http_methods` (List of String) List of allowed HTTP methods. Defaults to PUT, POST, PATCH, DELETE. +- `created_at` (String) Date the source was created +- `custom_response` (Attributes) Custom response object (see [below for nested schema](#nestedatt--custom_response)) +- `description` (String) Description for the source +- `disabled_at` (String) Date the source was disabled +- `name` (String) A unique, human-friendly name for the source +- `team_id` (String) ID of the workspace +- `updated_at` (String) Date the source was last updated +- `url` (String) A unique URL that must be supplied to your webhook's provider + + +### Nested Schema for `custom_response` + +Read-Only: + +- `body` (String) Body of the custom response +- `content_type` (String) must be one of [json, text, xml] +Content type of the custom response diff --git a/docs/resources/webhook_registration.md b/docs/resources/webhook_registration.md index 2449596..bcdfe61 100644 --- a/docs/resources/webhook_registration.md +++ b/docs/resources/webhook_registration.md @@ -3,12 +3,12 @@ page_title: "hookdeck_webhook_registration Resource - terraform-provider-hookdeck" subcategory: "" description: |- - Webhook Resource + Webhook Registration Resource --- # hookdeck_webhook_registration (Resource) -Webhook Resource +Webhook Registration Resource ## Example Usage diff --git a/internal/provider/connection/datasource.go b/internal/provider/connection/datasource.go new file mode 100644 index 0000000..6729b52 --- /dev/null +++ b/internal/provider/connection/datasource.go @@ -0,0 +1,81 @@ +package connection + +import ( + "context" + "fmt" + schemaHelpers "terraform-provider-hookdeck/internal/schemahelpers" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + hookdeckClient "github.com/hookdeck/hookdeck-go-sdk/client" +) + +// Ensure the implementation satisfies the expected interfaces. +var ( + _ datasource.DataSource = &connectionDataSource{} + _ datasource.DataSourceWithConfigure = &connectionDataSource{} +) + +// NewConnectionDataSource is a helper function to simplify the provider implementation. +func NewConnectionDataSource() datasource.DataSource { + return &connectionDataSource{} +} + +// connectionDataSource is the datasource implementation. +type connectionDataSource struct { + client hookdeckClient.Client +} + +// Metadata returns the datasource type name. +func (r *connectionDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_connection" +} + +// Schema returns the data source schema. +func (r *connectionDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Connection Data Source", + Attributes: schemaHelpers.DataSourceSchemaFromResourceSchema(schemaAttributes(), "id"), + } +} + +// Configure adds the provider configured client to the datasource. +func (r *connectionDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*hookdeckClient.Client) + + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *hookdeckClient.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + r.client = *client +} + +// Read refreshes the Terraform state with the latest data. +func (r *connectionDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + // Get data from Terraform state + var data *connectionResourceModel + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + // Get refreshed datasource value + connection, err := r.client.Connection.Retrieve(context.Background(), data.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Error reading connection", err.Error()) + return + } + + // Save refreshed data into Terraform state + data.Refresh(connection) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/internal/provider/connection/resource.go b/internal/provider/connection/resource.go index ddd3a98..49616cb 100644 --- a/internal/provider/connection/resource.go +++ b/internal/provider/connection/resource.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" hookdeckClient "github.com/hookdeck/hookdeck-go-sdk/client" ) @@ -31,6 +32,14 @@ func (r *connectionResource) Metadata(_ context.Context, req resource.MetadataRe resp.TypeName = req.ProviderTypeName + "_connection" } +// Schema returns the resource schema. +func (r *connectionResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "Connection Resource", + Attributes: schemaAttributes(), + } +} + // Configure adds the provider configured client to the resource. func (r *connectionResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { if req.ProviderData == nil { diff --git a/internal/provider/connection/schema.go b/internal/provider/connection/schema.go index 17cfd14..9908d8f 100644 --- a/internal/provider/connection/schema.go +++ b/internal/provider/connection/schema.go @@ -1,19 +1,16 @@ package connection import ( - "context" - "terraform-provider-hookdeck/internal/validators" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" - "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/schema/validator" ) -func (r *connectionResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { +func schemaAttributes() map[string]schema.Attribute { filterRulePropertySchema := schema.SingleNestedAttribute{ Optional: true, Attributes: map[string]schema.Attribute{ @@ -36,148 +33,144 @@ func (r *connectionResource) Schema(_ context.Context, _ resource.SchemaRequest, }, } - resp.Schema = schema.Schema{ - MarkdownDescription: "Connection Resource", - - Attributes: map[string]schema.Attribute{ - "created_at": schema.StringAttribute{ - Computed: true, - Validators: []validator.String{ - validators.IsRFC3339(), - }, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Description: "Date the connection was created", + return map[string]schema.Attribute{ + "created_at": schema.StringAttribute{ + Computed: true, + Validators: []validator.String{ + validators.IsRFC3339(), }, - "description": schema.StringAttribute{ - Optional: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Description: "Description for the connection", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "destination_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - }, - Description: "ID of a destination to bind to the connection", + Description: "Date the connection was created", + }, + "description": schema.StringAttribute{ + Optional: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "disabled_at": schema.StringAttribute{ - Computed: true, - Optional: true, - Validators: []validator.String{ - validators.IsRFC3339(), - }, - Description: "Date the connection was disabled", + Description: "Description for the connection", + }, + "destination_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), }, - "id": schema.StringAttribute{ - Computed: true, - Description: `ID of the connection`, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, + Description: "ID of a destination to bind to the connection", + }, + "disabled_at": schema.StringAttribute{ + Computed: true, + Optional: true, + Validators: []validator.String{ + validators.IsRFC3339(), }, - "name": schema.StringAttribute{ - Optional: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Description: `A unique, human-friendly name for the connection`, + Description: "Date the connection was disabled", + }, + "id": schema.StringAttribute{ + Computed: true, + Description: `ID of the connection`, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "paused_at": schema.StringAttribute{ - Computed: true, - Validators: []validator.String{ - validators.IsRFC3339(), - }, - Description: "Date the connection was paused", + }, + "name": schema.StringAttribute{ + Optional: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "rules": schema.SetNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "delay_rule": schema.SingleNestedAttribute{ - Optional: true, - Attributes: map[string]schema.Attribute{ - "delay": schema.Int64Attribute{ - Required: true, - Description: `Delay to introduce in MS`, - }, + Description: `A unique, human-friendly name for the connection`, + }, + "paused_at": schema.StringAttribute{ + Computed: true, + Validators: []validator.String{ + validators.IsRFC3339(), + }, + Description: "Date the connection was paused", + }, + "rules": schema.SetNestedAttribute{ + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "delay_rule": schema.SingleNestedAttribute{ + Optional: true, + Attributes: map[string]schema.Attribute{ + "delay": schema.Int64Attribute{ + Required: true, + Description: `Delay to introduce in MS`, }, }, - "filter_rule": schema.SingleNestedAttribute{ - Optional: true, - Attributes: map[string]schema.Attribute{ - "body": filterRulePropertySchema, - "headers": filterRulePropertySchema, - "path": filterRulePropertySchema, - "query": filterRulePropertySchema, + }, + "filter_rule": schema.SingleNestedAttribute{ + Optional: true, + Attributes: map[string]schema.Attribute{ + "body": filterRulePropertySchema, + "headers": filterRulePropertySchema, + "path": filterRulePropertySchema, + "query": filterRulePropertySchema, + }, + Validators: []validator.Object{ + validators.AtLeastOneChild(), + }, + }, + "retry_rule": schema.SingleNestedAttribute{ + Optional: true, + Attributes: map[string]schema.Attribute{ + "count": schema.Int64Attribute{ + Optional: true, + Description: `Maximum number of retries to attempt`, }, - Validators: []validator.Object{ - validators.AtLeastOneChild(), + "interval": schema.Int64Attribute{ + Optional: true, + Description: `Time in MS between each retry`, }, - }, - "retry_rule": schema.SingleNestedAttribute{ - Optional: true, - Attributes: map[string]schema.Attribute{ - "count": schema.Int64Attribute{ - Optional: true, - Description: `Maximum number of retries to attempt`, - }, - "interval": schema.Int64Attribute{ - Optional: true, - Description: `Time in MS between each retry`, - }, - "strategy": schema.StringAttribute{ - Required: true, - Validators: []validator.String{ - stringvalidator.OneOf( - "linear", - "exponential", - ), - }, - MarkdownDescription: `must be one of ["linear", "exponential"]` + "\n" + - `Algorithm to use when calculating delay between retries`, + "strategy": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.OneOf( + "linear", + "exponential", + ), }, + MarkdownDescription: `must be one of ["linear", "exponential"]` + "\n" + + `Algorithm to use when calculating delay between retries`, }, }, - "transform_rule": schema.SingleNestedAttribute{ - Optional: true, - Attributes: map[string]schema.Attribute{ - "transformation_id": schema.StringAttribute{ - Required: true, - Description: `ID of the attached transformation object.`, - }, + }, + "transform_rule": schema.SingleNestedAttribute{ + Optional: true, + Attributes: map[string]schema.Attribute{ + "transformation_id": schema.StringAttribute{ + Required: true, + Description: `ID of the attached transformation object.`, }, }, }, - Validators: []validator.Object{ - validators.ExactlyOneChild(), - }, }, - }, - "source_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), + Validators: []validator.Object{ + validators.ExactlyOneChild(), }, - Description: `ID of a source to bind to the connection`, }, - "team_id": schema.StringAttribute{ - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Description: `ID of the workspace`, + }, + "source_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), }, - "updated_at": schema.StringAttribute{ - Computed: true, - Validators: []validator.String{ - validators.IsRFC3339(), - }, - Description: `Date the connection was last updated`, + Description: `ID of a source to bind to the connection`, + }, + "team_id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + Description: `ID of the workspace`, + }, + "updated_at": schema.StringAttribute{ + Computed: true, + Validators: []validator.String{ + validators.IsRFC3339(), }, + Description: `Date the connection was last updated`, }, } } diff --git a/internal/provider/destination/datasource.go b/internal/provider/destination/datasource.go new file mode 100644 index 0000000..3bc54cc --- /dev/null +++ b/internal/provider/destination/datasource.go @@ -0,0 +1,81 @@ +package destination + +import ( + "context" + "fmt" + schemaHelpers "terraform-provider-hookdeck/internal/schemahelpers" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + hookdeckClient "github.com/hookdeck/hookdeck-go-sdk/client" +) + +// Ensure the implementation satisfies the expected interfaces. +var ( + _ datasource.DataSource = &destinationDataSource{} + _ datasource.DataSourceWithConfigure = &destinationDataSource{} +) + +// NewDestinationDataSource is a helper function to simplify the provider implementation. +func NewDestinationDataSource() datasource.DataSource { + return &destinationDataSource{} +} + +// destinationDataSource is the datasource implementation. +type destinationDataSource struct { + client hookdeckClient.Client +} + +// Metadata returns the datasource type name. +func (r *destinationDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_destination" +} + +// Schema returns the data source schema. +func (r *destinationDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Destination Data Source", + Attributes: schemaHelpers.DataSourceSchemaFromResourceSchema(schemaAttributes(), "id"), + } +} + +// Configure adds the provider configured client to the datasource. +func (r *destinationDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*hookdeckClient.Client) + + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *hookdeckClient.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + r.client = *client +} + +// Read refreshes the Terraform state with the latest data. +func (r *destinationDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + // Get data from Terraform state + var data *destinationResourceModel + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + // Get refreshed datasource value + destination, err := r.client.Destination.Retrieve(context.Background(), data.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Error reading destination", err.Error()) + return + } + + // Save refreshed data into Terraform state + data.Refresh(destination) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/internal/provider/destination/resource.go b/internal/provider/destination/resource.go index 02377db..4825a69 100644 --- a/internal/provider/destination/resource.go +++ b/internal/provider/destination/resource.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" hookdeckClient "github.com/hookdeck/hookdeck-go-sdk/client" ) @@ -31,6 +32,14 @@ func (r *destinationResource) Metadata(_ context.Context, req resource.MetadataR resp.TypeName = req.ProviderTypeName + "_destination" } +// Schema returns the resource schema. +func (r *destinationResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "Destination Resource", + Attributes: schemaAttributes(), + } +} + // Configure adds the provider configured client to the resource. func (r *destinationResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { if req.ProviderData == nil { diff --git a/internal/provider/destination/schema.go b/internal/provider/destination/schema.go index 2694520..b5491f2 100644 --- a/internal/provider/destination/schema.go +++ b/internal/provider/destination/schema.go @@ -1,12 +1,9 @@ package destination import ( - "context" - "terraform-provider-hookdeck/internal/validators" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" - "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" @@ -17,134 +14,130 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" ) -func (r *destinationResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { - resp.Schema = schema.Schema{ - MarkdownDescription: "Destination Resource", - - Attributes: map[string]schema.Attribute{ - "auth_method": schema.SingleNestedAttribute{ - Computed: true, - Optional: true, - Attributes: getAuthenticationMethodSchemaAttributes(), - Validators: []validator.Object{ - validators.ExactlyOneChild(), - }, - Default: objectdefault.StaticValue( - types.ObjectValueMust( - getAuthenticationMethodSchemaAttrTypes(), - getAuthenticationMethodSchemaDefaultValue(), - ), +func schemaAttributes() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "auth_method": schema.SingleNestedAttribute{ + Computed: true, + Optional: true, + Attributes: getAuthenticationMethodSchemaAttributes(), + Validators: []validator.Object{ + validators.ExactlyOneChild(), + }, + Default: objectdefault.StaticValue( + types.ObjectValueMust( + getAuthenticationMethodSchemaAttrTypes(), + getAuthenticationMethodSchemaDefaultValue(), ), - Description: `Config for the destination's auth method`, + ), + Description: `Config for the destination's auth method`, + }, + "cli_path": schema.StringAttribute{ + Optional: true, + Description: `Path for the CLI destination`, + }, + "created_at": schema.StringAttribute{ + Computed: true, + Validators: []validator.String{ + validators.IsRFC3339(), }, - "cli_path": schema.StringAttribute{ - Optional: true, - Description: `Path for the CLI destination`, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "created_at": schema.StringAttribute{ - Computed: true, - Validators: []validator.String{ - validators.IsRFC3339(), - }, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Description: `Date the destination was created`, - }, - "description": schema.StringAttribute{ - Optional: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Description: "Description for the destination", + Description: `Date the destination was created`, + }, + "description": schema.StringAttribute{ + Optional: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "disabled_at": schema.StringAttribute{ - Computed: true, - Optional: true, - Validators: []validator.String{ - validators.IsRFC3339(), - }, - Description: `Date the destination was disabled`, + Description: "Description for the destination", + }, + "disabled_at": schema.StringAttribute{ + Computed: true, + Optional: true, + Validators: []validator.String{ + validators.IsRFC3339(), }, - "http_method": schema.StringAttribute{ - // Computed: true, - Optional: true, - Validators: []validator.String{ - stringvalidator.OneOf( - "GET", - "POST", - "PUT", - "PATCH", - "DELETE", - ), - }, - MarkdownDescription: `must be one of ["GET", "POST", "PUT", "PATCH", "DELETE"]` + "\n" + - `HTTP method used on requests sent to the destination, overrides the method used on requests sent to the source.`, + Description: `Date the destination was disabled`, + }, + "http_method": schema.StringAttribute{ + // Computed: true, + Optional: true, + Validators: []validator.String{ + stringvalidator.OneOf( + "GET", + "POST", + "PUT", + "PATCH", + "DELETE", + ), }, - "id": schema.StringAttribute{ - Computed: true, - Description: `ID of the destination`, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, + MarkdownDescription: `must be one of ["GET", "POST", "PUT", "PATCH", "DELETE"]` + "\n" + + `HTTP method used on requests sent to the destination, overrides the method used on requests sent to the source.`, + }, + "id": schema.StringAttribute{ + Computed: true, + Description: `ID of the destination`, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "name": schema.StringAttribute{ - Required: true, - Description: `A unique, human-friendly name for the destination`, + }, + "name": schema.StringAttribute{ + Required: true, + Description: `A unique, human-friendly name for the destination`, + }, + "path_forwarding_disabled": schema.BoolAttribute{ + Computed: true, + Optional: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.UseStateForUnknown(), }, - "path_forwarding_disabled": schema.BoolAttribute{ - Computed: true, - Optional: true, - PlanModifiers: []planmodifier.Bool{ - boolplanmodifier.UseStateForUnknown(), + }, + "rate_limit": schema.SingleNestedAttribute{ + Optional: true, + Attributes: map[string]schema.Attribute{ + "limit": schema.Int64Attribute{ + Required: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.UseStateForUnknown(), + }, + Description: `Limit event attempts to receive per period. Max value is workspace plan's max attempts thoughput.`, }, - }, - "rate_limit": schema.SingleNestedAttribute{ - Optional: true, - Attributes: map[string]schema.Attribute{ - "limit": schema.Int64Attribute{ - Required: true, - PlanModifiers: []planmodifier.Int64{ - int64planmodifier.UseStateForUnknown(), - }, - Description: `Limit event attempts to receive per period. Max value is workspace plan's max attempts thoughput.`, + "period": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.OneOf( + "second", + "minute", + "hour", + ), }, - "period": schema.StringAttribute{ - Required: true, - Validators: []validator.String{ - stringvalidator.OneOf( - "second", - "minute", - "hour", - ), - }, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - MarkdownDescription: `must be one of ["second", "minute", "hour"]` + "\n" + - `Period to rate limit attempts`, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, + MarkdownDescription: `must be one of ["second", "minute", "hour"]` + "\n" + + `Period to rate limit attempts`, }, - Description: "Rate limit", - }, - "team_id": schema.StringAttribute{ - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Description: `ID of the workspace`, }, - "updated_at": schema.StringAttribute{ - Computed: true, - Validators: []validator.String{ - validators.IsRFC3339(), - }, - Description: `Date the destination was last updated`, + Description: "Rate limit", + }, + "team_id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "url": schema.StringAttribute{ - Optional: true, - Description: `HTTP endpoint of the destination`, + Description: `ID of the workspace`, + }, + "updated_at": schema.StringAttribute{ + Computed: true, + Validators: []validator.String{ + validators.IsRFC3339(), }, + Description: `Date the destination was last updated`, + }, + "url": schema.StringAttribute{ + Optional: true, + Description: `HTTP endpoint of the destination`, }, } } diff --git a/internal/provider/provider.go b/internal/provider/provider.go index b88cfc7..73e1de6 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -157,7 +157,11 @@ func (p *hookdeckProvider) Resources(ctx context.Context) []func() resource.Reso } func (p *hookdeckProvider) DataSources(_ context.Context) []func() datasource.DataSource { - return []func() datasource.DataSource{} + return []func() datasource.DataSource{ + connection.NewConnectionDataSource, + destination.NewDestinationDataSource, + source.NewSourceDataSource, + } } func New(version string) func() provider.Provider { diff --git a/internal/provider/source/datasource.go b/internal/provider/source/datasource.go new file mode 100644 index 0000000..19b16dd --- /dev/null +++ b/internal/provider/source/datasource.go @@ -0,0 +1,82 @@ +package source + +import ( + "context" + "fmt" + schemaHelpers "terraform-provider-hookdeck/internal/schemahelpers" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + hookdeck "github.com/hookdeck/hookdeck-go-sdk" + hookdeckClient "github.com/hookdeck/hookdeck-go-sdk/client" +) + +// Ensure the implementation satisfies the expected interfaces. +var ( + _ datasource.DataSource = &sourceDataSource{} + _ datasource.DataSourceWithConfigure = &sourceDataSource{} +) + +// NewSourceDataSource is a helper function to simplify the provider implementation. +func NewSourceDataSource() datasource.DataSource { + return &sourceDataSource{} +} + +// sourceDataSource is the datasource implementation. +type sourceDataSource struct { + client hookdeckClient.Client +} + +// Metadata returns the datasource type name. +func (r *sourceDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_source" +} + +// Schema returns the data source schema. +func (r *sourceDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Source Data Source", + Attributes: schemaHelpers.DataSourceSchemaFromResourceSchema(schemaAttributes(), "id"), + } +} + +// Configure adds the provider configured client to the datasource. +func (r *sourceDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*hookdeckClient.Client) + + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *hookdeckClient.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + r.client = *client +} + +// Read refreshes the Terraform state with the latest data. +func (r *sourceDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + // Get data from Terraform state + var data *sourceResourceModel + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + // Get refreshed datasource value + source, err := r.client.Source.Retrieve(context.Background(), data.ID.ValueString(), &hookdeck.SourceRetrieveRequest{}) + if err != nil { + resp.Diagnostics.AddError("Error reading source", err.Error()) + return + } + + // Save refreshed data into Terraform state + data.Refresh(source) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/internal/provider/source/resource.go b/internal/provider/source/resource.go index 3aad547..85cd5be 100644 --- a/internal/provider/source/resource.go +++ b/internal/provider/source/resource.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" hookdeck "github.com/hookdeck/hookdeck-go-sdk" hookdeckClient "github.com/hookdeck/hookdeck-go-sdk/client" ) @@ -32,6 +33,14 @@ func (r *sourceResource) Metadata(_ context.Context, req resource.MetadataReques resp.TypeName = req.ProviderTypeName + "_source" } +// Schema returns the resource schema. +func (r *sourceResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Source Resource", + Attributes: schemaAttributes(), + } +} + // Configure adds the provider configured client to the resource. func (r *sourceResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { if req.ProviderData == nil { diff --git a/internal/provider/source/schema.go b/internal/provider/source/schema.go index 5395d65..6ae66e3 100644 --- a/internal/provider/source/schema.go +++ b/internal/provider/source/schema.go @@ -7,7 +7,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" - "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/listdefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier" @@ -17,110 +16,107 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" ) -func (r *sourceResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { +func schemaAttributes() map[string]schema.Attribute { defaultAllowedHttpMethods, _ := types.ListValueFrom(context.Background(), types.StringType, []string{"POST", "PUT", "PATCH", "DELETE"}) - resp.Schema = schema.Schema{ - Description: "Source Resource", - Attributes: map[string]schema.Attribute{ - "allowed_http_methods": schema.ListAttribute{ - Computed: true, - Optional: true, - ElementType: types.StringType, - Validators: []validator.List{ - listvalidator.ValueStringsAre(stringvalidator.OneOf( - "GET", - "POST", - "PUT", - "PATCH", - "DELETE", - )), - }, - Default: listdefault.StaticValue(defaultAllowedHttpMethods), - Description: "List of allowed HTTP methods. Defaults to PUT, POST, PATCH, DELETE.", + return map[string]schema.Attribute{ + "allowed_http_methods": schema.ListAttribute{ + Computed: true, + Optional: true, + ElementType: types.StringType, + Validators: []validator.List{ + listvalidator.ValueStringsAre(stringvalidator.OneOf( + "GET", + "POST", + "PUT", + "PATCH", + "DELETE", + )), }, - "created_at": schema.StringAttribute{ - Computed: true, - Validators: []validator.String{ - validators.IsRFC3339(), - }, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Description: "Date the source was created", + Default: listdefault.StaticValue(defaultAllowedHttpMethods), + Description: "List of allowed HTTP methods. Defaults to PUT, POST, PATCH, DELETE.", + }, + "created_at": schema.StringAttribute{ + Computed: true, + Validators: []validator.String{ + validators.IsRFC3339(), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "custom_response": schema.SingleNestedAttribute{ - Optional: true, - PlanModifiers: []planmodifier.Object{ - objectplanmodifier.UseStateForUnknown(), + Description: "Date the source was created", + }, + "custom_response": schema.SingleNestedAttribute{ + Optional: true, + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.UseStateForUnknown(), + }, + Attributes: map[string]schema.Attribute{ + "body": schema.StringAttribute{ + Required: true, + Description: "Body of the custom response", }, - Attributes: map[string]schema.Attribute{ - "body": schema.StringAttribute{ - Required: true, - Description: "Body of the custom response", - }, - "content_type": schema.StringAttribute{ - Required: true, - Validators: []validator.String{ - stringvalidator.OneOf( - "json", - "text", - "xml", - ), - }, - MarkdownDescription: "must be one of [json, text, xml]" + "\n" + - "Content type of the custom response", + "content_type": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.OneOf( + "json", + "text", + "xml", + ), }, + MarkdownDescription: "must be one of [json, text, xml]" + "\n" + + "Content type of the custom response", }, - Description: "Custom response object", }, - "description": schema.StringAttribute{ - Optional: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Description: "Description for the source", - }, - "disabled_at": schema.StringAttribute{ - Computed: true, - Optional: true, - Validators: []validator.String{ - validators.IsRFC3339(), - }, - Description: "Date the source was disabled", + Description: "Custom response object", + }, + "description": schema.StringAttribute{ + Optional: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "id": schema.StringAttribute{ - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Description: "ID of the source", + Description: "Description for the source", + }, + "disabled_at": schema.StringAttribute{ + Computed: true, + Optional: true, + Validators: []validator.String{ + validators.IsRFC3339(), }, - "name": schema.StringAttribute{ - Required: true, - Description: "A unique, human-friendly name for the source", + Description: "Date the source was disabled", + }, + "id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "team_id": schema.StringAttribute{ - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Description: "ID of the workspace", + Description: "ID of the source", + }, + "name": schema.StringAttribute{ + Required: true, + Description: "A unique, human-friendly name for the source", + }, + "team_id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "updated_at": schema.StringAttribute{ - Computed: true, - Validators: []validator.String{ - validators.IsRFC3339(), - }, - Description: "Date the source was last updated", + Description: "ID of the workspace", + }, + "updated_at": schema.StringAttribute{ + Computed: true, + Validators: []validator.String{ + validators.IsRFC3339(), }, - "url": schema.StringAttribute{ - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Description: "A unique URL that must be supplied to your webhook's provider", + Description: "Date the source was last updated", + }, + "url": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, + Description: "A unique URL that must be supplied to your webhook's provider", }, } } diff --git a/internal/provider/sourceverification/resource.go b/internal/provider/sourceverification/resource.go index 0bc0fae..91fed43 100644 --- a/internal/provider/sourceverification/resource.go +++ b/internal/provider/sourceverification/resource.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" hookdeck "github.com/hookdeck/hookdeck-go-sdk" hookdeckClient "github.com/hookdeck/hookdeck-go-sdk/client" ) @@ -32,6 +33,14 @@ func (r *sourceVerificationResource) Metadata(_ context.Context, req resource.Me resp.TypeName = req.ProviderTypeName + "_source_verification" } +// Schema returns the resource schema. +func (r *sourceVerificationResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Transformation Resource", + Attributes: schemaAttributes(), + } +} + // Configure adds the provider configured client to the resource. func (r *sourceVerificationResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { if req.ProviderData == nil { diff --git a/internal/provider/sourceverification/schema.go b/internal/provider/sourceverification/schema.go index 1657ef6..e8a34ca 100644 --- a/internal/provider/sourceverification/schema.go +++ b/internal/provider/sourceverification/schema.go @@ -1,95 +1,90 @@ package sourceverification import ( - "context" "terraform-provider-hookdeck/internal/validators" - "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/schema/validator" ) -func (r *sourceVerificationResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { - resp.Schema = schema.Schema{ - Description: "Transformation Resource", - Attributes: map[string]schema.Attribute{ - "source_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - }, - Description: "ID of the source", +func schemaAttributes() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "source_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), }, - "verification": schema.SingleNestedAttribute{ - Required: true, - Attributes: map[string]schema.Attribute{ - // generic - "api_key": apiKeyConfigSchema(), - "basic_auth": basicAuthConfigSchema(), - "hmac": hmacConfigSchema(), - "json": jsonConfigSchema(), - // providers - "adyen": adyenConfigSchema(), - "akeneo": akeneoConfigSchema(), - "aws_sns": awsSNSConfigSchema(), - "bondsmith": bondsmithConfigSchema(), - "cloudsignal": cloudsignalConfigSchema(), - "commercelayer": commercelayerConfigSchema(), - "courier": courierConfigSchema(), - "discord": discordConfigSchema(), - "ebay": ebayConfigSchema(), - "enode": enodeConfigSchema(), - "favro": favroConfigSchema(), - "fiserv": fiservConfigSchema(), - "frontapp": frontAppConfigSchema(), - "github": githubConfigSchema(), - "gitlab": gitlabConfigSchema(), - "linear": linearConfigSchema(), - "mailgun": mailgunConfigSchema(), - "nmi": nmiConfigSchema(), - "orb": orbConfigSchema(), - "oura": ouraConfigSchema(), - "persona": personaConfigSchema(), - "pipedrive": pipedriveConfigSchema(), - "postmark": postmarkConfigSchema(), - "property_finder": propertyFinderConfigSchema(), - "pylon": pylonConfigSchema(), - "razorpay": razorpayConfigSchema(), - "recharge": rechargeConfigSchema(), - "repay": repayConfigSchema(), - "sanity": sanityConfigSchema(), - "sendgrid": sendgridConfigSchema(), - "shopify": shopifyConfigSchema(), - "shopline": shoplineConfigSchema(), - "slack": slackConfigSchema(), - "solidgate": solidgateConfigSchema(), - "square": squareConfigSchema(), - "stripe": stripeConfigSchema(), - "svix": svixConfigSchema(), - "synctera": syncteraConfigSchema(), - "tebex": tebexConfigSchema(), - "telnyx": telnyxConfigSchema(), - "threedeye": threeDEyeConfigSchema(), - "tokenio": tokenIoConfigSchema(), - "trello": trelloConfigSchema(), - "twitch": twitchConfigSchema(), - "twitter": twitterConfigSchema(), - "typeform": typeformConfigSchema(), - "vercel": vercelConfigSchema(), - "vercel_log_drains": vercelLogDrainsConfigSchema(), - "wix": wixConfigSchema(), - "woocommerce": woocommerceConfigSchema(), - "workos": workOSConfigSchema(), - "xero": xeroConfigSchema(), - "zoom": zoomConfigSchema(), - }, - Validators: []validator.Object{ - validators.ExactlyOneChild(), - }, - Description: "The verification configs for the specific verification type", + Description: "ID of the source", + }, + "verification": schema.SingleNestedAttribute{ + Required: true, + Attributes: map[string]schema.Attribute{ + // generic + "api_key": apiKeyConfigSchema(), + "basic_auth": basicAuthConfigSchema(), + "hmac": hmacConfigSchema(), + "json": jsonConfigSchema(), + // providers + "adyen": adyenConfigSchema(), + "akeneo": akeneoConfigSchema(), + "aws_sns": awsSNSConfigSchema(), + "bondsmith": bondsmithConfigSchema(), + "cloudsignal": cloudsignalConfigSchema(), + "commercelayer": commercelayerConfigSchema(), + "courier": courierConfigSchema(), + "discord": discordConfigSchema(), + "ebay": ebayConfigSchema(), + "enode": enodeConfigSchema(), + "favro": favroConfigSchema(), + "fiserv": fiservConfigSchema(), + "frontapp": frontAppConfigSchema(), + "github": githubConfigSchema(), + "gitlab": gitlabConfigSchema(), + "linear": linearConfigSchema(), + "mailgun": mailgunConfigSchema(), + "nmi": nmiConfigSchema(), + "orb": orbConfigSchema(), + "oura": ouraConfigSchema(), + "persona": personaConfigSchema(), + "pipedrive": pipedriveConfigSchema(), + "postmark": postmarkConfigSchema(), + "property_finder": propertyFinderConfigSchema(), + "pylon": pylonConfigSchema(), + "razorpay": razorpayConfigSchema(), + "recharge": rechargeConfigSchema(), + "repay": repayConfigSchema(), + "sanity": sanityConfigSchema(), + "sendgrid": sendgridConfigSchema(), + "shopify": shopifyConfigSchema(), + "shopline": shoplineConfigSchema(), + "slack": slackConfigSchema(), + "solidgate": solidgateConfigSchema(), + "square": squareConfigSchema(), + "stripe": stripeConfigSchema(), + "svix": svixConfigSchema(), + "synctera": syncteraConfigSchema(), + "tebex": tebexConfigSchema(), + "telnyx": telnyxConfigSchema(), + "threedeye": threeDEyeConfigSchema(), + "tokenio": tokenIoConfigSchema(), + "trello": trelloConfigSchema(), + "twitch": twitchConfigSchema(), + "twitter": twitterConfigSchema(), + "typeform": typeformConfigSchema(), + "vercel": vercelConfigSchema(), + "vercel_log_drains": vercelLogDrainsConfigSchema(), + "wix": wixConfigSchema(), + "woocommerce": woocommerceConfigSchema(), + "workos": workOSConfigSchema(), + "xero": xeroConfigSchema(), + "zoom": zoomConfigSchema(), + }, + Validators: []validator.Object{ + validators.ExactlyOneChild(), }, + Description: "The verification configs for the specific verification type", }, } } diff --git a/internal/provider/transformation/resource.go b/internal/provider/transformation/resource.go index 0c80883..156db38 100644 --- a/internal/provider/transformation/resource.go +++ b/internal/provider/transformation/resource.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" hookdeck "github.com/hookdeck/hookdeck-go-sdk" hookdeckClient "github.com/hookdeck/hookdeck-go-sdk/client" ) @@ -33,6 +34,14 @@ func (r *transformationResource) Metadata(_ context.Context, req resource.Metada resp.TypeName = req.ProviderTypeName + "_transformation" } +// Schema returns the resource schema. +func (r *transformationResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Transformation Resource", + Attributes: schemaAttributes(), + } +} + // Configure adds the provider configured client to the resource. func (r *transformationResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { if req.ProviderData == nil { diff --git a/internal/provider/transformation/schema.go b/internal/provider/transformation/schema.go index e1e7fec..571e63a 100644 --- a/internal/provider/transformation/schema.go +++ b/internal/provider/transformation/schema.go @@ -1,64 +1,59 @@ package transformation import ( - "context" "terraform-provider-hookdeck/internal/validators" - "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/schema/validator" ) -func (r *transformationResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { - resp.Schema = schema.Schema{ - Description: "Transformation Resource", - Attributes: map[string]schema.Attribute{ - "code": schema.StringAttribute{ - Required: true, - Description: "JavaScript code to be executed", - }, - "created_at": schema.StringAttribute{ - Computed: true, - Validators: []validator.String{ - validators.IsRFC3339(), - }, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Description: "Date the transformation was created", - }, - "env": schema.StringAttribute{ - Optional: true, - Sensitive: true, - Description: "Key-value environment variables to be passed to the transformation", +func schemaAttributes() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "code": schema.StringAttribute{ + Required: true, + Description: "JavaScript code to be executed", + }, + "created_at": schema.StringAttribute{ + Computed: true, + Validators: []validator.String{ + validators.IsRFC3339(), }, - "id": schema.StringAttribute{ - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Description: "ID of the transformation", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "name": schema.StringAttribute{ - Required: true, - Description: "A unique, human-friendly name for the transformation", + Description: "Date the transformation was created", + }, + "env": schema.StringAttribute{ + Optional: true, + Sensitive: true, + Description: "Key-value environment variables to be passed to the transformation", + }, + "id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "team_id": schema.StringAttribute{ - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Description: "ID of the workspace", + Description: "ID of the transformation", + }, + "name": schema.StringAttribute{ + Required: true, + Description: "A unique, human-friendly name for the transformation", + }, + "team_id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "updated_at": schema.StringAttribute{ - Computed: true, - Validators: []validator.String{ - validators.IsRFC3339(), - }, - Description: "Date the transformation was last updated", + Description: "ID of the workspace", + }, + "updated_at": schema.StringAttribute{ + Computed: true, + Validators: []validator.String{ + validators.IsRFC3339(), }, + Description: "Date the transformation was last updated", }, } } diff --git a/internal/provider/webhookregistration/resource.go b/internal/provider/webhookregistration/resource.go index c7dd375..6122f65 100644 --- a/internal/provider/webhookregistration/resource.go +++ b/internal/provider/webhookregistration/resource.go @@ -5,6 +5,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" ) // Ensure the implementation satisfies the expected interfaces. @@ -28,6 +29,14 @@ func (r *webhookRegistrationResource) Metadata(_ context.Context, req resource.M resp.TypeName = req.ProviderTypeName + "_webhook_registration" } +// Schema returns the resource schema. +func (r *webhookRegistrationResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Webhook Registration Resource", + Attributes: schemaAttributes(), + } +} + // Configure adds the provider configured client to the resource. func (r *webhookRegistrationResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { } diff --git a/internal/provider/webhookregistration/schema.go b/internal/provider/webhookregistration/schema.go index c8a947a..8a6cf31 100644 --- a/internal/provider/webhookregistration/schema.go +++ b/internal/provider/webhookregistration/schema.go @@ -1,16 +1,13 @@ package webhookregistration import ( - "context" - - "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" ) -func (r *webhookRegistrationResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { +func schemaAttributes() map[string]schema.Attribute { requestSchemaAttributes := map[string]schema.Attribute{ "body": schema.StringAttribute{ Optional: true, @@ -26,34 +23,31 @@ func (r *webhookRegistrationResource) Schema(_ context.Context, _ resource.Schem }, } - resp.Schema = schema.Schema{ - Description: "Webhook Resource", - Attributes: map[string]schema.Attribute{ - "register": schema.SingleNestedAttribute{ - Required: true, - Attributes: map[string]schema.Attribute{ - "request": schema.SingleNestedAttribute{ - Required: true, - Attributes: requestSchemaAttributes, - }, - "response": schema.StringAttribute{ - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - }, + return map[string]schema.Attribute{ + "register": schema.SingleNestedAttribute{ + Required: true, + Attributes: map[string]schema.Attribute{ + "request": schema.SingleNestedAttribute{ + Required: true, + Attributes: requestSchemaAttributes, }, - PlanModifiers: []planmodifier.Object{ - objectplanmodifier.RequiresReplace(), + "response": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, }, }, - "unregister": schema.SingleNestedAttribute{ - Optional: true, - Attributes: map[string]schema.Attribute{ - "request": schema.SingleNestedAttribute{ - Required: true, - Attributes: requestSchemaAttributes, - }, + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.RequiresReplace(), + }, + }, + "unregister": schema.SingleNestedAttribute{ + Optional: true, + Attributes: map[string]schema.Attribute{ + "request": schema.SingleNestedAttribute{ + Required: true, + Attributes: requestSchemaAttributes, }, }, }, diff --git a/internal/schemahelpers/schemahelpers.go b/internal/schemahelpers/schemahelpers.go new file mode 100644 index 0000000..19e9064 --- /dev/null +++ b/internal/schemahelpers/schemahelpers.go @@ -0,0 +1,167 @@ +package schemahelpers + +import ( + "log" + + datasource_schema "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + resource_schema "github.com/hashicorp/terraform-plugin-framework/resource/schema" +) + +func DataSourceSchemaFromResourceSchema(resourceSchema map[string]resource_schema.Attribute, idField string) map[string]datasource_schema.Attribute { + foundIdField := false + + datasourceSchema := make(map[string]datasource_schema.Attribute) + for name, srcAttr := range resourceSchema { + optional := false + required := false + computed := true + + if name == idField { + required = true + computed = false + foundIdField = true + } + + switch srcAttrTyped := srcAttr.(type) { + case resource_schema.StringAttribute: + datasourceSchema[name] = datasource_schema.StringAttribute{ + Validators: srcAttrTyped.Validators, + Description: srcAttrTyped.Description, + MarkdownDescription: srcAttrTyped.MarkdownDescription, + CustomType: srcAttrTyped.CustomType, + Sensitive: srcAttrTyped.Sensitive, + Optional: optional, + Required: required, + Computed: computed, + } + case resource_schema.BoolAttribute: + datasourceSchema[name] = datasource_schema.BoolAttribute{ + Validators: srcAttrTyped.Validators, + Description: srcAttrTyped.Description, + MarkdownDescription: srcAttrTyped.MarkdownDescription, + CustomType: srcAttrTyped.CustomType, + Sensitive: srcAttrTyped.Sensitive, + Optional: optional, + Required: required, + Computed: computed, + } + case resource_schema.NumberAttribute: + datasourceSchema[name] = datasource_schema.NumberAttribute{ + Validators: srcAttrTyped.Validators, + Description: srcAttrTyped.Description, + MarkdownDescription: srcAttrTyped.MarkdownDescription, + CustomType: srcAttrTyped.CustomType, + Sensitive: srcAttrTyped.Sensitive, + Optional: optional, + Required: required, + Computed: computed, + } + case resource_schema.Int64Attribute: + datasourceSchema[name] = datasource_schema.Int64Attribute{ + Validators: srcAttrTyped.Validators, + Description: srcAttrTyped.Description, + MarkdownDescription: srcAttrTyped.MarkdownDescription, + CustomType: srcAttrTyped.CustomType, + Sensitive: srcAttrTyped.Sensitive, + Optional: optional, + Required: required, + Computed: computed, + } + case resource_schema.Int32Attribute: + datasourceSchema[name] = datasource_schema.Int32Attribute{ + Validators: srcAttrTyped.Validators, + Description: srcAttrTyped.Description, + MarkdownDescription: srcAttrTyped.MarkdownDescription, + CustomType: srcAttrTyped.CustomType, + Sensitive: srcAttrTyped.Sensitive, + Optional: optional, + Required: required, + Computed: computed, + } + case resource_schema.ListAttribute: + datasourceSchema[name] = datasource_schema.ListAttribute{ + Validators: srcAttrTyped.Validators, + Description: srcAttrTyped.Description, + MarkdownDescription: srcAttrTyped.MarkdownDescription, + CustomType: srcAttrTyped.CustomType, + Sensitive: srcAttrTyped.Sensitive, + ElementType: srcAttrTyped.ElementType, + Optional: optional, + Required: required, + Computed: computed, + } + case resource_schema.MapAttribute: + datasourceSchema[name] = datasource_schema.MapAttribute{ + Validators: srcAttrTyped.Validators, + Description: srcAttrTyped.Description, + MarkdownDescription: srcAttrTyped.MarkdownDescription, + CustomType: srcAttrTyped.CustomType, + Sensitive: srcAttrTyped.Sensitive, + ElementType: srcAttrTyped.ElementType, + Optional: optional, + Required: required, + Computed: computed, + } + case resource_schema.SetAttribute: + datasourceSchema[name] = datasource_schema.SetAttribute{ + Validators: srcAttrTyped.Validators, + Description: srcAttrTyped.Description, + MarkdownDescription: srcAttrTyped.MarkdownDescription, + CustomType: srcAttrTyped.CustomType, + Sensitive: srcAttrTyped.Sensitive, + ElementType: srcAttrTyped.ElementType, + Optional: optional, + Required: required, + Computed: computed, + } + case resource_schema.SetNestedAttribute: + datasourceSchema[name] = datasource_schema.SetNestedAttribute{ + NestedObject: datasource_schema.NestedAttributeObject{ + Attributes: DataSourceSchemaFromResourceSchema(srcAttrTyped.NestedObject.Attributes, ""), + CustomType: srcAttrTyped.NestedObject.CustomType, + Validators: srcAttrTyped.NestedObject.Validators, + }, + Validators: srcAttrTyped.Validators, + Description: srcAttrTyped.Description, + MarkdownDescription: srcAttrTyped.MarkdownDescription, + CustomType: srcAttrTyped.CustomType, + Sensitive: srcAttrTyped.Sensitive, + Optional: optional, + Required: required, + Computed: computed, + } + case resource_schema.ObjectAttribute: + datasourceSchema[name] = datasource_schema.ObjectAttribute{ + AttributeTypes: srcAttrTyped.AttributeTypes, + Validators: srcAttrTyped.Validators, + Description: srcAttrTyped.Description, + MarkdownDescription: srcAttrTyped.MarkdownDescription, + CustomType: srcAttrTyped.CustomType, + Sensitive: srcAttrTyped.Sensitive, + Optional: optional, + Required: required, + Computed: computed, + } + case resource_schema.SingleNestedAttribute: + datasourceSchema[name] = datasource_schema.SingleNestedAttribute{ + Attributes: DataSourceSchemaFromResourceSchema(srcAttrTyped.Attributes, ""), + Validators: srcAttrTyped.Validators, + Description: srcAttrTyped.Description, + MarkdownDescription: srcAttrTyped.MarkdownDescription, + CustomType: srcAttrTyped.CustomType, + Sensitive: srcAttrTyped.Sensitive, + Optional: optional, + Required: required, + Computed: computed, + } + default: + log.Panicf("unknown attribute type: %v", srcAttr.GetType().String()) + } + } + + if !foundIdField && idField != "" { + log.Panicf("id field \"%s\" not found in resource schema", idField) + } + + return datasourceSchema +}