// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 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 { return &coffeesDataSource{} } type coffeesDataSource struct { client *hashicups.Client } 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 (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"` } type coffeeIngredientsModel struct { ID types.Int64 `tfsdk:"id"` }