coffees_data_source.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. // Copyright (c) HashiCorp, Inc.
  2. // SPDX-License-Identifier: MPL-2.0
  3. package provider
  4. import (
  5. "context"
  6. "fmt"
  7. "github.com/hashicorp-demoapp/hashicups-client-go"
  8. "github.com/hashicorp/terraform-plugin-framework/datasource"
  9. "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
  10. "github.com/hashicorp/terraform-plugin-framework/diag"
  11. "github.com/hashicorp/terraform-plugin-framework/types"
  12. )
  13. // Ensure the implementation satisfies the expected interfaces.
  14. var (
  15. _ datasource.DataSource = &coffeesDataSource{}
  16. _ datasource.DataSourceWithConfigure = &coffeesDataSource{}
  17. )
  18. func NewCoffeesDataSource() datasource.DataSource {
  19. return &coffeesDataSource{}
  20. }
  21. type coffeesDataSource struct {
  22. client *hashicups.Client
  23. }
  24. func (d *coffeesDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
  25. // Add a nil check when handling ProviderData because Terraform
  26. // sets that data after it calls the ConfigureProvider RPC.
  27. if req.ProviderData == nil {
  28. return
  29. }
  30. client, ok := req.ProviderData.(*hashicups.Client)
  31. if !ok {
  32. resp.Diagnostics.AddError("Unexpected Data Source Configure Type",
  33. fmt.Sprintf("Expected *hashicups.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData))
  34. return
  35. }
  36. d.client = client
  37. }
  38. func (d *coffeesDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
  39. resp.TypeName = req.ProviderTypeName + "_coffees"
  40. }
  41. func (d *coffeesDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
  42. resp.Schema = schema.Schema{
  43. Attributes: map[string]schema.Attribute{
  44. "coffees": schema.ListNestedAttribute{
  45. Computed: true,
  46. NestedObject: schema.NestedAttributeObject{
  47. Attributes: map[string]schema.Attribute{
  48. "id": schema.Int64Attribute{
  49. Computed: true,
  50. },
  51. "name": schema.StringAttribute{
  52. Computed: true,
  53. },
  54. "teaser": schema.StringAttribute{
  55. Computed: true,
  56. },
  57. "description": schema.StringAttribute{
  58. Computed: true,
  59. },
  60. "price": schema.Float64Attribute{
  61. Computed: true,
  62. },
  63. "image": schema.StringAttribute{
  64. Computed: true,
  65. },
  66. "ingredients": schema.ListNestedAttribute{
  67. Computed: true,
  68. NestedObject: schema.NestedAttributeObject{
  69. Attributes: map[string]schema.Attribute{
  70. "id": schema.Int64Attribute{
  71. Computed: true,
  72. },
  73. },
  74. },
  75. },
  76. },
  77. },
  78. },
  79. },
  80. }
  81. }
  82. // Read is used by the data source to refresh the Terraform state based on the schema data.
  83. func (d *coffeesDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
  84. var diags diag.Diagnostics
  85. // 0. Checks whether the API Client is configured.
  86. if d.client == nil {
  87. resp.Diagnostics.AddError(
  88. "cannot read coffees",
  89. "client is not configured in coffeesDataSource.Read",
  90. )
  91. return
  92. }
  93. // 1. Reads coffees list. The method invokes the API client's GetCoffees method.
  94. var state coffeesDataSourceModel
  95. coffees, err := d.client.GetCoffees()
  96. if err != nil {
  97. resp.Diagnostics.AddError(
  98. "Unable to Read HashiCups Coffees",
  99. err.Error(),
  100. )
  101. return
  102. }
  103. // 2. Maps response body to schema attributes.
  104. // After the method reads the coffees, it maps the []hashicups.Coffee response
  105. // to coffeesModel so the data source can set the Terraform state.
  106. for _, coffee := range coffees {
  107. coffeeState := coffeesModel{
  108. ID: types.Int64Value(int64(coffee.ID)),
  109. Name: types.StringValue(coffee.Name),
  110. Teaser: types.StringValue(coffee.Teaser),
  111. Description: types.StringValue(coffee.Description),
  112. Price: types.Float64Value(coffee.Price),
  113. Image: types.StringValue(coffee.Image),
  114. Ingredients: make([]coffeeIngredientsModel, 0, len(coffee.Ingredient)), // Not in tutorial, why ?
  115. }
  116. for _, ingredient := range coffee.Ingredient {
  117. coffeeState.Ingredients = append(coffeeState.Ingredients, coffeeIngredientsModel{
  118. ID: types.Int64Value(int64(ingredient.ID)),
  119. })
  120. }
  121. state.Coffees = append(state.Coffees, coffeeState)
  122. }
  123. // 3. Set Terraform's state with the coffees model.
  124. diags = resp.State.Set(ctx, state)
  125. resp.Diagnostics.Append(diags...)
  126. // Useless code in tutorial, why ?
  127. if resp.Diagnostics.HasError() {
  128. return
  129. }
  130. }
  131. type coffeesDataSourceModel struct {
  132. Coffees []coffeesModel `tfsdk:"coffees"`
  133. }
  134. type coffeesModel struct {
  135. ID types.Int64 `tfsdk:"id"`
  136. Name types.String `tfsdk:"name"`
  137. Teaser types.String `tfsdk:"teaser"`
  138. Description types.String `tfsdk:"description"`
  139. Price types.Float64 `tfsdk:"price"`
  140. Image types.String `tfsdk:"image"`
  141. Ingredients []coffeeIngredientsModel `tfsdk:"ingredients"`
  142. }
  143. type coffeeIngredientsModel struct {
  144. ID types.Int64 `tfsdk:"id"`
  145. }