coffees_data_source.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  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. // NewCoffeesDataSource is a helper function to simplify the provider implementation.
  19. func NewCoffeesDataSource() datasource.DataSource {
  20. return &coffeesDataSource{}
  21. }
  22. // coffeesDataSource is the data source implementation.
  23. type coffeesDataSource struct {
  24. client *hashicups.Client
  25. }
  26. // coffeesDataSourceModel maps the data source schema data.
  27. type coffeesDataSourceModel struct {
  28. Coffees []coffeesModel `tfsdk:"coffees"`
  29. }
  30. // coffeesModel maps coffees schema data.
  31. type coffeesModel struct {
  32. ID types.Int64 `tfsdk:"id"`
  33. Name types.String `tfsdk:"name"`
  34. Teaser types.String `tfsdk:"teaser"`
  35. Description types.String `tfsdk:"description"`
  36. Price types.Float64 `tfsdk:"price"`
  37. Image types.String `tfsdk:"image"`
  38. Ingredients []coffeesIngredientsModel `tfsdk:"ingredients"`
  39. }
  40. // coffeesIngredientsModel maps coffee ingredients data.
  41. type coffeesIngredientsModel struct {
  42. ID types.Int64 `tfsdk:"id"`
  43. }
  44. // Metadata returns the data source type name.
  45. func (d *coffeesDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
  46. resp.TypeName = req.ProviderTypeName + "_coffees"
  47. }
  48. // Schema defines the schema for the data source.
  49. func (d *coffeesDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
  50. resp.Schema = schema.Schema{
  51. Attributes: map[string]schema.Attribute{
  52. "coffees": schema.ListNestedAttribute{
  53. Computed: true,
  54. NestedObject: schema.NestedAttributeObject{
  55. Attributes: map[string]schema.Attribute{
  56. "id": schema.Int64Attribute{
  57. Computed: true,
  58. Description: "Numeric identifier of the coffee.",
  59. },
  60. "name": schema.StringAttribute{
  61. Computed: true,
  62. Description: "Product name of the coffee.",
  63. },
  64. "teaser": schema.StringAttribute{
  65. Computed: true,
  66. Description: "Fun tagline for the coffee.",
  67. },
  68. "description": schema.StringAttribute{
  69. Computed: true,
  70. Description: "Product description of the coffee.",
  71. },
  72. "price": schema.Float64Attribute{
  73. Computed: true,
  74. Description: "Suggested cost of the coffee.",
  75. },
  76. "image": schema.StringAttribute{
  77. Computed: true,
  78. Description: "URI for an image of the coffee.",
  79. },
  80. "ingredients": schema.ListNestedAttribute{
  81. Computed: true,
  82. Description: "List of ingredients in the coffee.",
  83. NestedObject: schema.NestedAttributeObject{
  84. Attributes: map[string]schema.Attribute{
  85. "id": schema.Int64Attribute{
  86. Description: "Numeric identifier of the coffee ingredient.",
  87. Computed: true,
  88. },
  89. },
  90. },
  91. },
  92. },
  93. },
  94. },
  95. },
  96. }
  97. }
  98. // Configure adds the provider configured client to the data source.
  99. func (d *coffeesDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
  100. // Add a nil check when handling ProviderData because Terraform
  101. // sets that data after it calls the ConfigureProvider RPC.
  102. if req.ProviderData == nil {
  103. return
  104. }
  105. client, ok := req.ProviderData.(*hashicups.Client)
  106. if !ok {
  107. resp.Diagnostics.AddError(
  108. "Unexpected Data Source Configure Type",
  109. fmt.Sprintf("Expected *hashicups.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
  110. )
  111. return
  112. }
  113. d.client = client
  114. }
  115. // Read is used by the data source to refresh the Terraform state based on the schema data.
  116. func (d *coffeesDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
  117. var state coffeesDataSourceModel
  118. var diags diag.Diagnostics
  119. // 0. Checks whether the API Client is configured.
  120. if d.client == nil {
  121. resp.Diagnostics.AddError(
  122. "cannot read coffees",
  123. "client is not configured in coffeesDataSource.Read",
  124. )
  125. return
  126. }
  127. // 1. Reads coffees list. The method invokes the API client's GetCoffees method.
  128. coffees, err := d.client.GetCoffees()
  129. if err != nil {
  130. resp.Diagnostics.AddError(
  131. "Unable to Read HashiCups Coffees",
  132. err.Error(),
  133. )
  134. return
  135. }
  136. // 2. Maps response body to schema attributes.
  137. // After the method reads the coffees, it maps the []hashicups.Coffee response
  138. // to coffeesModel so the data source can set the Terraform state.
  139. for _, coffee := range coffees {
  140. coffeeState := coffeesModel{
  141. ID: types.Int64Value(int64(coffee.ID)),
  142. Name: types.StringValue(coffee.Name),
  143. Teaser: types.StringValue(coffee.Teaser),
  144. Description: types.StringValue(coffee.Description),
  145. Price: types.Float64Value(coffee.Price),
  146. Image: types.StringValue(coffee.Image + ""),
  147. Ingredients: make([]coffeesIngredientsModel, 0, len(coffee.Ingredient)), // Not in tutorial, why ?
  148. }
  149. for _, ingredient := range coffee.Ingredient {
  150. coffeeState.Ingredients = append(coffeeState.Ingredients, coffeesIngredientsModel{
  151. ID: types.Int64Value(int64(ingredient.ID)),
  152. })
  153. }
  154. state.Coffees = append(state.Coffees, coffeeState)
  155. }
  156. // 3. Set Terraform's state with the coffees model.
  157. diags = resp.State.Set(ctx, state)
  158. resp.Diagnostics.Append(diags...)
  159. // Useless code in tutorial, why ?
  160. if resp.Diagnostics.HasError() {
  161. return
  162. }
  163. }