Skip to content

Commit

Permalink
terraform provider 04
Browse files Browse the repository at this point in the history
  • Loading branch information
mouuii committed Nov 30, 2023
1 parent 6e51e56 commit 76c0b3b
Show file tree
Hide file tree
Showing 3 changed files with 228 additions and 1 deletion.
33 changes: 33 additions & 0 deletions examples/order/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
terraform {
required_providers {
hashicups = {
source = "hashicorp.com/edu/hashicups"
}
}
required_version = ">= 1.1.0"
}

provider "hashicups" {
username = "education"
password = "test123"
host = "http://localhost:19090"
}

resource "hashicups_order" "edu" {
items = [{
coffee = {
id = 3
}
quantity = 2
}, {
coffee = {
id = 1
}
quantity = 2
}
]
}

output "edu_order" {
value = hashicups_order.edu
}
192 changes: 192 additions & 0 deletions internal/provider/order_resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
package provider

import (
"context"
"fmt"
"strconv"
"time"

"github.com/hashicorp-demoapp/hashicups-client-go"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
)

// Ensure the implementation satisfies the expected interfaces.
var (
_ resource.Resource = &orderResource{}
_ resource.ResourceWithConfigure = &orderResource{}
)

// NewOrderResource is a helper function to simplify the provider implementation.
func NewOrderResource() resource.Resource {
return &orderResource{}
}

// orderResource is the resource implementation.
type orderResource struct {
client *hashicups.Client
}

// Metadata returns the resource type name.
func (r *orderResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_order"
}

// Schema defines the schema for the resource.
func (r *orderResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
},
"last_updated": schema.StringAttribute{
Computed: true,
},
"items": schema.ListNestedAttribute{
Required: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"quantity": schema.Int64Attribute{
Required: true,
},
"coffee": schema.SingleNestedAttribute{
Required: true,
Attributes: map[string]schema.Attribute{
"id": schema.Int64Attribute{
Required: true,
},
"name": schema.StringAttribute{
Computed: true,
},
"teaser": schema.StringAttribute{
Computed: true,
},
"description": schema.StringAttribute{
Computed: true,
},
"price": schema.Float64Attribute{
Computed: true,
},
"image": schema.StringAttribute{
Computed: true,
},
},
},
},
},
},
},
}
}

// Create a new resource.
func (r *orderResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
// Retrieve values from plan
var plan orderResourceModel
diags := req.Plan.Get(ctx, &plan)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

// Generate API request body from plan
var items []hashicups.OrderItem
for _, item := range plan.Items {
items = append(items, hashicups.OrderItem{
Coffee: hashicups.Coffee{
ID: int(item.Coffee.ID.ValueInt64()),
},
Quantity: int(item.Quantity.ValueInt64()),
})
}

// Create new order
order, err := r.client.CreateOrder(items)
if err != nil {
resp.Diagnostics.AddError(
"Error creating order",
"Could not create order, unexpected error: "+err.Error(),
)
return
}

// Map response body to schema and populate Computed attribute values
plan.ID = types.StringValue(strconv.Itoa(order.ID))
for orderItemIndex, orderItem := range order.Items {
plan.Items[orderItemIndex] = orderItemModel{
Coffee: orderItemCoffeeModel{
ID: types.Int64Value(int64(orderItem.Coffee.ID)),
Name: types.StringValue(orderItem.Coffee.Name),
Teaser: types.StringValue(orderItem.Coffee.Teaser),
Description: types.StringValue(orderItem.Coffee.Description),
Price: types.Float64Value(orderItem.Coffee.Price),
Image: types.StringValue(orderItem.Coffee.Image),
},
Quantity: types.Int64Value(int64(orderItem.Quantity)),
}
}
plan.LastUpdated = types.StringValue(time.Now().Format(time.RFC850))

// Set state to fully populated data
diags = resp.State.Set(ctx, plan)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}

// Read refreshes the Terraform state with the latest data.
func (r *orderResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
}

// Update updates the resource and sets the updated Terraform state on success.
func (r *orderResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
}

// Delete deletes the resource and removes the Terraform state on success.
func (r *orderResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
}

// Configure adds the provider configured client to the resource.
func (r *orderResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
if req.ProviderData == nil {
return
}

client, ok := req.ProviderData.(*hashicups.Client)

if !ok {
resp.Diagnostics.AddError(
"Unexpected Data Source Configure Type",
fmt.Sprintf("Expected *hashicups.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)

return
}

r.client = client
}

// orderResourceModel maps the resource schema data.
type orderResourceModel struct {
ID types.String `tfsdk:"id"`
Items []orderItemModel `tfsdk:"items"`
LastUpdated types.String `tfsdk:"last_updated"`
}

// orderItemModel maps order item data.
type orderItemModel struct {
Coffee orderItemCoffeeModel `tfsdk:"coffee"`
Quantity types.Int64 `tfsdk:"quantity"`
}

// orderItemCoffeeModel maps coffee order item data.
type orderItemCoffeeModel struct {
ID types.Int64 `tfsdk:"id"`
Name types.String `tfsdk:"name"`
Teaser types.String `tfsdk:"teaser"`
Description types.String `tfsdk:"description"`
Price types.Float64 `tfsdk:"price"`
Image types.String `tfsdk:"image"`
}
4 changes: 3 additions & 1 deletion internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,9 @@ func (p *hashicupsProvider) DataSources(_ context.Context) []func() datasource.D

// Resources defines the resources implemented in the provider.
func (p *hashicupsProvider) Resources(_ context.Context) []func() resource.Resource {
return nil
return []func() resource.Resource{
NewOrderResource,
}
}

// hashicupsProviderModel maps provider schema data to a Go type.
Expand Down

0 comments on commit 76c0b3b

Please sign in to comment.