Kaynağa Gözat

Tutorial 3: Data sources.

Frédéric G. MARAND 5 ay önce
ebeveyn
işleme
208414067a

+ 3 - 2
.gitignore

@@ -2,8 +2,9 @@
 *.exe
 .DS_Store
 example.tf
-terraform.tfplan
-terraform.tfstate
+*.tfplan
+*.tfstate
+.terraform.lock.hcl
 bin/
 dist/
 modules-dev/

+ 19 - 0
examples/coffees/main.tf

@@ -0,0 +1,19 @@
+terraform {
+  required_providers {
+    hashicups = {
+      source = "hashicorp.com/edu/hashicups"
+    }
+  }
+}
+
+provider "hashicups" {
+  host     = "http://localhost:19090" // See docker-compose/docker-compose.yml
+  username = "education"              // We used: curl -X POST localhost:19090/signup -d '{"username":"education", "password":"test123"}'
+  password = "test123"                // We used: curl -X POST localhost:19090/signup -d '{"username":"education", "password":"test123"}'
+}
+
+data "hashicups_coffees" "edu" {}
+
+output "edu_coffees" {
+  value = data.hashicups_coffees.edu
+}

+ 127 - 5
internal/provider/coffees_data_source.go

@@ -2,9 +2,18 @@ package provider
 
 import (
 	"context"
+	"fmt"
 
+	"github.com/hashicorp-demoapp/hashicups-client-go"
 	"github.com/hashicorp/terraform-plugin-framework/datasource"
 	"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ datasource.DataSource              = &coffeesDataSource{}
+	_ datasource.DataSourceWithConfigure = &coffeesDataSource{}
 )
 
 func NewCoffeesDataSource() datasource.DataSource {
@@ -12,16 +21,129 @@ func NewCoffeesDataSource() datasource.DataSource {
 }
 
 type coffeesDataSource struct {
+	client *hashicups.Client
 }
 
-func (c coffeesDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+func (d *coffeesDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
+	// Add a nil check when handling ProviderData because Terraform
+	// sets that data after it calls the ConfigureProvider RPC.
+	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
+	}
+	d.client = client
+}
+
+func (d *coffeesDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
 	resp.TypeName = req.ProviderTypeName + "_coffees"
 }
 
-func (c coffeesDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
-	resp.Schema = schema.Schema{}
+func (d *coffeesDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+	resp.Schema = schema.Schema{
+		Attributes: map[string]schema.Attribute{
+			"coffees": schema.ListNestedAttribute{
+				Computed: true,
+				NestedObject: schema.NestedAttributeObject{
+					Attributes: map[string]schema.Attribute{
+						"id": schema.Int64Attribute{
+							Computed: 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,
+						},
+						"ingredients": schema.ListNestedAttribute{
+							Computed: true,
+							NestedObject: schema.NestedAttributeObject{
+								Attributes: map[string]schema.Attribute{
+									"id": schema.Int64Attribute{
+										Computed: true,
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+}
+
+// Read is used by the data source to refresh the Terraform state based on the schema data.
+func (d *coffeesDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+	// 1. Reads coffees list. The method invokes the API client's GetCoffees method.
+	var state coffeesDataSourceModel
+	coffees, err := d.client.GetCoffees()
+	if err != nil {
+		resp.Diagnostics.AddError(
+			"Unable to Read HashiCups Coffees",
+			err.Error(),
+		)
+		return
+	}
+
+	// 2. Maps response body to schema attributes.
+	// After the method reads the coffees, it maps the []hashicups.Coffee response
+	// to coffeesModel so the data source can set the Terraform state.
+	for _, coffee := range coffees {
+		coffeeState := coffeesModel{
+			ID:          types.Int64Value(int64(coffee.ID)),
+			Name:        types.StringValue(coffee.Name),
+			Teaser:      types.StringValue(coffee.Teaser),
+			Description: types.StringValue(coffee.Description),
+			Price:       types.Float64Value(coffee.Price),
+			Image:       types.StringValue(coffee.Image),
+			Ingredients: make([]coffeeIngredientsModel, 0, len(coffee.Ingredient)), // Not in tutorial, why ?
+		}
+		for _, ingredient := range coffee.Ingredient {
+			coffeeState.Ingredients = append(coffeeState.Ingredients, coffeeIngredientsModel{
+				ID: types.Int64Value(int64(ingredient.ID)),
+			})
+		}
+		state.Coffees = append(state.Coffees, coffeeState)
+	}
+
+	// 3. Sets state with coffees list.
+	diags := resp.State.Set(ctx, state)
+	resp.Diagnostics.Append(diags...)
+
+	// Useless code from tutorial, why ?
+	if resp.Diagnostics.HasError() {
+		return
+	}
+}
+
+type coffeesDataSourceModel struct {
+	Coffees []coffeesModel `tfsdk:"coffees"`
+}
+
+type coffeesModel 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"`
+	Ingredients []coffeeIngredientsModel `tfsdk:"ingredients"`
 }
 
-func (c coffeesDataSource) Read(ctx context.Context, request datasource.ReadRequest, response *datasource.ReadResponse) {
-	return
+type coffeeIngredientsModel struct {
+	ID types.Int64 `tfsdk:"id"`
 }

+ 4 - 4
internal/provider/provider.go

@@ -183,10 +183,10 @@ func (p *hashicupsProvider) Configure(ctx context.Context, req provider.Configur
 }
 
 func (p *hashicupsProvider) DataSources(_ context.Context) []func() datasource.DataSource {
-	return nil
-	//return []func() datasource.DataSource{
-	//	NewCoffeesDataSource,
-	//}
+	// return nil
+	return []func() datasource.DataSource{
+		NewCoffeesDataSource,
+	}
 }
 
 func (p *hashicupsProvider) Resources(_ context.Context) []func() resource.Resource {