Przeglądaj źródła

Tutorial 6: update.

Frédéric G. MARAND 5 miesięcy temu
rodzic
commit
0c9c17aecb

+ 3 - 0
.gitignore

@@ -34,3 +34,6 @@ website/vendor
 
 # Keep windows files with windows line endings
 *.winfile eol=crlf
+
+# Symlinks
+terraformrc-*.tf

+ 1 - 1
examples/order/main.tf

@@ -19,7 +19,7 @@ resource "hashicups_order" "edu" {
       quantity = 2
     },
     {
-      coffee = { id = 1 }
+      coffee = { id = 2 }
       quantity = 2
     },
   ]

+ 50 - 36
internal/provider/coffees_data_source.go

@@ -20,35 +20,44 @@ var (
 	_ datasource.DataSourceWithConfigure = &coffeesDataSource{}
 )
 
+// NewCoffeesDataSource is a helper function to simplify the provider implementation.
 func NewCoffeesDataSource() datasource.DataSource {
 	return &coffeesDataSource{}
 }
 
+// coffeesDataSource is the data source implementation.
 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
-	}
+// coffeesDataSourceModel maps the data source schema data.
+type coffeesDataSourceModel struct {
+	Coffees []coffeesModel `tfsdk:"coffees"`
+}
 
-	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
+// coffeesModel maps coffees schema data.
+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 []coffeesIngredientsModel `tfsdk:"ingredients"`
+}
+
+// coffeesIngredientsModel maps coffee ingredients data.
+type coffeesIngredientsModel struct {
+	ID types.Int64 `tfsdk:"id"`
 }
 
-func (d *coffeesDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+// Metadata returns the data source type name.
+func (d *coffeesDataSource) Metadata(_ 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) {
+// Schema defines the schema for the data source.
+func (d *coffeesDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
 	resp.Schema = schema.Schema{
 		Attributes: map[string]schema.Attribute{
 			"coffees": schema.ListNestedAttribute{
@@ -90,8 +99,30 @@ func (d *coffeesDataSource) Schema(ctx context.Context, req datasource.SchemaReq
 	}
 }
 
+// Configure adds the provider configured client to the data source.
+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
+}
+
 // 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) {
+	var state coffeesDataSourceModel
 	var diags diag.Diagnostics
 
 	// 0. Checks whether the API Client is configured.
@@ -104,7 +135,6 @@ func (d *coffeesDataSource) Read(ctx context.Context, req datasource.ReadRequest
 	}
 
 	// 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(
@@ -125,13 +155,15 @@ func (d *coffeesDataSource) Read(ctx context.Context, req datasource.ReadRequest
 			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 ?
+			Ingredients: make([]coffeesIngredientsModel, 0, len(coffee.Ingredient)), // Not in tutorial, why ?
 		}
+
 		for _, ingredient := range coffee.Ingredient {
-			coffeeState.Ingredients = append(coffeeState.Ingredients, coffeeIngredientsModel{
+			coffeeState.Ingredients = append(coffeeState.Ingredients, coffeesIngredientsModel{
 				ID: types.Int64Value(int64(ingredient.ID)),
 			})
 		}
+
 		state.Coffees = append(state.Coffees, coffeeState)
 	}
 
@@ -144,21 +176,3 @@ func (d *coffeesDataSource) Read(ctx context.Context, req datasource.ReadRequest
 		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"`
-}

+ 0 - 3
internal/provider/example_data_source.go

@@ -1,6 +1,3 @@
-// Copyright (c) HashiCorp, Inc.
-// SPDX-License-Identifier: MPL-2.0
-
 package provider
 
 import (

+ 0 - 3
internal/provider/example_data_source_test.go

@@ -1,6 +1,3 @@
-// Copyright (c) HashiCorp, Inc.
-// SPDX-License-Identifier: MPL-2.0
-
 package provider
 
 import (

+ 4 - 7
internal/provider/example_resource.go

@@ -1,6 +1,3 @@
-// Copyright (c) HashiCorp, Inc.
-// SPDX-License-Identifier: MPL-2.0
-
 package provider
 
 import (
@@ -90,7 +87,7 @@ func (r *ExampleResource) Configure(ctx context.Context, req resource.ConfigureR
 }
 
 func (r *ExampleResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
-	var data ExampleResourceModel
+	var data *ExampleResourceModel
 
 	// Read Terraform plan data into the model
 	resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
@@ -120,7 +117,7 @@ func (r *ExampleResource) Create(ctx context.Context, req resource.CreateRequest
 }
 
 func (r *ExampleResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
-	var data ExampleResourceModel
+	var data *ExampleResourceModel
 
 	// Read Terraform prior state data into the model
 	resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
@@ -142,7 +139,7 @@ func (r *ExampleResource) Read(ctx context.Context, req resource.ReadRequest, re
 }
 
 func (r *ExampleResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
-	var data ExampleResourceModel
+	var data *ExampleResourceModel
 
 	// Read Terraform plan data into the model
 	resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
@@ -164,7 +161,7 @@ func (r *ExampleResource) Update(ctx context.Context, req resource.UpdateRequest
 }
 
 func (r *ExampleResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
-	var data ExampleResourceModel
+	var data *ExampleResourceModel
 
 	// Read Terraform prior state data into the model
 	resp.Diagnostics.Append(req.State.Get(ctx, &data)...)

+ 0 - 3
internal/provider/example_resource_test.go

@@ -1,6 +1,3 @@
-// Copyright (c) HashiCorp, Inc.
-// SPDX-License-Identifier: MPL-2.0
-
 package provider
 
 import (

+ 135 - 46
internal/provider/order_resource.go

@@ -9,6 +9,8 @@ import (
 	"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/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
 	"github.com/hashicorp/terraform-plugin-framework/types"
 )
 
@@ -23,35 +25,50 @@ func NewOrderResource() resource.Resource {
 	return &orderResource{}
 }
 
+// orderResource is the resource implementation.
 type orderResource struct {
 	client *hashicups.Client
 }
 
-func (r *orderResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
-	// Add a nil check when handling ProviderData because Terraform
-	// sets that data after it calls the ConfigureProvider RPC.
-	if req.ProviderData == nil {
-		return
-	}
+// The *Model types describe the types known by the API.
+// They are basically DTOs for the TF provider to talk to the API, while
+// its native type is the Plan with its Schema.
+type orderResourceModel struct {
+	ID          types.String     `tfsdk:"id"`
+	Items       []orderItemModel `tfsdk:"items"`
+	LastUpdated types.String     `tfsdk:"last_updated"`
+}
 
-	client, ok := req.ProviderData.(*hashicups.Client)
-	if !ok {
-		resp.Diagnostics.AddError("Unexpected Resource Configure Type",
-			fmt.Sprintf("Expected *hashicups.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData))
-		return
-	}
-	r.client = client
+// 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"`
 }
 
-func (r *orderResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
+// Metadata returns the resource type name.
+func (r *orderResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
 	resp.TypeName = req.ProviderTypeName + "_order"
 }
 
-func (r *orderResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
+// 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,
+				PlanModifiers: []planmodifier.String{
+					stringplanmodifier.UseStateForUnknown(),
+				},
 			},
 			"last_updated": schema.StringAttribute{
 				Computed: true,
@@ -93,6 +110,27 @@ func (r *orderResource) Schema(ctx context.Context, req resource.SchemaRequest,
 	}
 }
 
+// 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 Resource Configure Type",
+			fmt.Sprintf("Expected *hashicups.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+		)
+
+		return
+	}
+
+	r.client = client
+}
+
+// Create a new resource.
 func (r *orderResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
 	var plan orderResourceModel
 
@@ -114,7 +152,7 @@ func (r *orderResource) Create(ctx context.Context, req resource.CreateRequest,
 		return
 	}
 
-	// 3. Generates an API request body from the plan values.
+	// 3. Generates API request body from  plan
 	// The function loops through each plan item and maps it to a hashicups.OrderItem.
 	// This is what the API client needs to create a new order.
 	var items []hashicups.OrderItem
@@ -133,12 +171,12 @@ func (r *orderResource) Create(ctx context.Context, req resource.CreateRequest,
 	if err != nil {
 		resp.Diagnostics.AddError(
 			"Error creating order",
-			err.Error(),
+			"Could not create order, unexpected error: "+err.Error(),
 		)
 		return
 	}
 
-	// 5. Maps response body to resource schema attributes.
+	// 5. Maps response body to resource schema attributes and populate Computed attribute values
 	// After the function creates an order, it maps the hashicups.Order response to []OrderItem so the provider can update the Terraform state.
 	plan.ID = types.StringValue(strconv.Itoa(order.ID))
 	for orderItemIndex, orderItem := range order.Items {
@@ -166,6 +204,7 @@ func (r *orderResource) Create(ctx context.Context, req resource.CreateRequest,
 	}
 }
 
+// Read resource information.
 func (r *orderResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
 	var state orderResourceModel
 
@@ -194,8 +233,8 @@ func (r *orderResource) Read(ctx context.Context, req resource.ReadRequest, resp
 	order, err := r.client.GetOrder(id)
 	if err != nil {
 		resp.Diagnostics.AddError(
-			"Error reading HashiCups order",
-			"Could not read HashiCups order "+state.ID.ValueString()+": "+err.Error(),
+			"Error Reading HashiCups Order",
+			"Could not read HashiCups order ID "+state.ID.ValueString()+": "+err.Error(),
 		)
 		return
 	}
@@ -205,7 +244,7 @@ func (r *orderResource) Read(ctx context.Context, req resource.ReadRequest, resp
 	// After the function retrieves the order, it maps the hashicups.Order response to []OrderItem so the provider can update the Terraform state.
 	state.Items = []orderItemModel{}
 	for _, item := range order.Items {
-		itemState := orderItemModel{
+		state.Items = append(state.Items, orderItemModel{
 			Coffee: orderItemCoffeeModel{
 				ID:          types.Int64Value(int64(item.Coffee.ID)),
 				Name:        types.StringValue(item.Coffee.Name),
@@ -215,8 +254,7 @@ func (r *orderResource) Read(ctx context.Context, req resource.ReadRequest, resp
 				Image:       types.StringValue(item.Coffee.Image),
 			},
 			Quantity: types.Int64Value(int64(item.Quantity)),
-		}
-		state.Items = append(state.Items, itemState)
+		})
 	}
 
 	// 5. Set Terraform's state with the order's model.
@@ -229,33 +267,84 @@ func (r *orderResource) Read(ctx context.Context, req resource.ReadRequest, resp
 	}
 }
 
+// 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) {
-	return
-}
+	// 1. Retrieves values from the plan.
+	// The method will attempt to retrieve values from the plan and convert it to an orderResourceModel.
+	// The model includes the order's id attribute, which specifies which order to update.
+	var plan orderResourceModel
+	diags := req.Plan.Get(ctx, &plan)
+	resp.Diagnostics.Append(diags...)
+	if resp.Diagnostics.HasError() {
+		return
+	}
 
-func (r *orderResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
-	return
-}
+	// 2. Generates an API request body from the plan values.
+	// The method loops through each plan item and maps it to a hashicups.OrderItem.
+	// This is what the API client needs to update an existing order.
+	var hashicupsItems []hashicups.OrderItem
+	for _, item := range plan.Items {
+		hashicupsItems = append(hashicupsItems, hashicups.OrderItem{
+			Coffee: hashicups.Coffee{
+				ID: int(item.Coffee.ID.ValueInt64()),
+			},
+			Quantity: int(item.Quantity.ValueInt64()),
+		})
+	}
 
-// The *Model types describe the types known by the API.
-// They are basically DTOs for the TF provider to talk to the API, while
-// its native type is the Plan with its Schema.
-type orderResourceModel struct {
-	ID          types.String     `tfsdk:"id"`
-	Items       []orderItemModel `tfsdk:"items"`
-	LastUpdated types.String     `tfsdk:"last_updated"`
-}
+	// 3. Updates the order.
+	// 3.1 Apply changes.
+	// The method invokes the API client's UpdateOrder method with the order's ID and OrderItems.
+	_, err := r.client.UpdateOrder(plan.ID.ValueString(), hashicupsItems)
+	if err != nil {
+		resp.Diagnostics.AddError(
+			"Error Reading HashiCups Order",
+			"Could not read HashiCups order ID "+plan.ID.ValueString()+": "+err.Error(),
+		)
+	}
 
-type orderItemModel struct {
-	Quantity types.Int64          `tfsdk:"quantity"`
-	Coffee   orderItemCoffeeModel `tfsdk:"coffee"`
+	// 3.2 Fetch updated items from GetOrder as UpdateOrder items are not populated.
+	order, err := r.client.GetOrder(plan.ID.ValueString())
+	if err != nil {
+		resp.Diagnostics.AddError(
+			"Error Reading HashiCups Order",
+			"Could not read HashiCups order ID "+plan.ID.ValueString()+": "+err.Error(),
+		)
+		return
+	}
+
+	// 4. Maps the response body to resource schema attributes.
+	// After the method updates the order, it maps the hashicups.Order response to []OrderItem
+	// so the provider can update the Terraform state.
+	plan.Items = []orderItemModel{}
+	for _, orderItem := range order.Items {
+		plan.Items = append(plan.Items, 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)),
+		})
+	}
+
+	// 5. Sets the LastUpdated attribute.
+	plan.LastUpdated = types.StringValue(time.Now().Format(time.RFC850))
+
+	// 6. Sets Terraform's state with the updated order.
+	diags = resp.State.Set(ctx, plan)
+	resp.Diagnostics.Append(diags...)
+
+	// Useless code in tutorial, why ?
+	if resp.Diagnostics.HasError() {
+		return
+	}
 }
 
-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"`
+// Delete deletes the resource and removes the Terraform state on success.
+func (r *orderResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
+	return
 }

+ 28 - 16
internal/provider/provider.go

@@ -1,6 +1,3 @@
-// Copyright (c) HashiCorp, Inc.
-// SPDX-License-Identifier: MPL-2.0
-
 package provider
 
 import (
@@ -17,8 +14,10 @@ import (
 	"github.com/hashicorp/terraform-plugin-log/tflog"
 )
 
-// Ensure hashicupsProvider satisfies various provider interfaces.
-var _ provider.Provider = &hashicupsProvider{}
+// Ensure the implementation satisfies the expected interfaces.
+var (
+	_ provider.Provider = &hashicupsProvider{}
+)
 
 // New is a helper function to simplify provider server and testing implementation.
 func New(version string) func() provider.Provider {
@@ -29,7 +28,7 @@ func New(version string) func() provider.Provider {
 	}
 }
 
-// hashicupsProvider defines the provider implementation.
+// hashicupsProvider is the provider implementation.
 type hashicupsProvider struct {
 	// version is set to the provider version on release, "dev" when the
 	// provider is built and ran locally, and "test" when running acceptance
@@ -37,11 +36,22 @@ type hashicupsProvider struct {
 	version string
 }
 
+// The Terraform Plugin Framework uses Go struct types with tfsdk struct field tags
+// to map schema definitions into Go types with the actual data.
+// The types within the struct must align with the types in the schema.
+type hashicupsProviderModel struct {
+	Host     types.String `tfsdk:"host"`
+	Username types.String `tfsdk:"username"`
+	Password types.String `tfsdk:"password"`
+}
+
+// Metadata returns the provider type name.
 func (p *hashicupsProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) {
 	resp.TypeName = "hashicups"
 	resp.Version = p.version
 }
 
+// Schema defines the provider-level schema for configuration data.
 func (p *hashicupsProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) {
 	resp.Schema = schema.Schema{
 		Attributes: map[string]schema.Attribute{
@@ -84,6 +94,7 @@ func (p *hashicupsProvider) Configure(ctx context.Context, req provider.Configur
 				"Either target apply the source of the value first, set the value statically in the configuration, or use the HASHICUPS_HOST environment variable.",
 		)
 	}
+
 	// 2.2 username
 	if config.Username.IsUnknown() {
 		resp.Diagnostics.AddAttributeError(
@@ -93,6 +104,7 @@ func (p *hashicupsProvider) Configure(ctx context.Context, req provider.Configur
 				"Either target apply the source of the value first, set the value statically in the configuration, or use the HASHICUPS_USERNAME environment variable.",
 		)
 	}
+
 	// 2.3 password
 	if config.Password.IsUnknown() {
 		resp.Diagnostics.AddAttributeError(
@@ -102,6 +114,7 @@ func (p *hashicupsProvider) Configure(ctx context.Context, req provider.Configur
 				"Either target apply the source of the value first, set the value statically in the configuration, or use the HASHICUPS_PASSWORD environment variable.",
 		)
 	}
+
 	// 2.4 check results
 	if resp.Diagnostics.HasError() {
 		return
@@ -119,6 +132,7 @@ func (p *hashicupsProvider) Configure(ctx context.Context, req provider.Configur
 	if !config.Host.IsNull() {
 		host = config.Host.ValueString()
 	}
+
 	if !config.Username.IsNull() {
 		username = config.Username.ValueString()
 	}
@@ -126,7 +140,11 @@ func (p *hashicupsProvider) Configure(ctx context.Context, req provider.Configur
 	if !config.Password.IsNull() {
 		password = config.Password.ValueString()
 	}
+
 	// 3.3 verify values are set
+	// If any of the expected configurations are missing, return
+	// errors with provider-specific guidance.
+
 	if host == "" {
 		resp.Diagnostics.AddAttributeError(
 			path.Root("host"),
@@ -165,6 +183,8 @@ func (p *hashicupsProvider) Configure(ctx context.Context, req provider.Configur
 	ctx = tflog.SetField(ctx, "hashicups_host", host)
 	ctx = tflog.SetField(ctx, "hashicups_username", username)
 	ctx = tflog.SetField(ctx, "hashicups_password", password)
+	ctx = tflog.MaskFieldValuesWithFieldKeys(ctx, "hashicups_password")
+
 	tflog.Debug(ctx, "Creating HashiCups API Client")
 
 	// 4. Creates API client.
@@ -196,28 +216,20 @@ func (p *hashicupsProvider) Configure(ctx context.Context, req provider.Configur
 	})
 }
 
+// DataSources defines the data sources implemented in the provider.
 func (p *hashicupsProvider) DataSources(_ context.Context) []func() datasource.DataSource {
-	// return nil
 	return []func() datasource.DataSource{
 		NewCoffeesDataSource,
 	}
 }
 
+// Resources defines the resources implemented in the provider.
 func (p *hashicupsProvider) Resources(_ context.Context) []func() resource.Resource {
 	return []func() resource.Resource{
 		NewOrderResource,
 	}
 }
 
-// The Terraform Plugin Framework uses Go struct types with tfsdk struct field tags
-// to map schema definitions into Go types with the actual data.
-// The types within the struct must align with the types in the schema.
-type hashicupsProviderModel struct {
-	Host     types.String `tfsdk:"host"`
-	Username types.String `tfsdk:"username"`
-	Password types.String `tfsdk:"password"`
-}
-
 //// Ensure hashicupsProvider satisfies various provider interfaces.
 //var _ provider.Provider = &hashicupsProvider{}
 //var _ provider.ProviderWithFunctions = &hashicupsProvider{}

+ 0 - 3
internal/provider/provider_test.go

@@ -1,6 +1,3 @@
-// Copyright (c) HashiCorp, Inc.
-// SPDX-License-Identifier: MPL-2.0
-
 package provider
 
 import (

+ 0 - 1
terraformrc.tf

@@ -1 +0,0 @@
-/Users/fredericmarand/.terraformrc