Skip to main content

API spec annotations

When liblab generates a Terraform provider from your API spec, it needs to know which endpoints are Terraform resources and which are data sources, amongst other things. This is done by annotating your API spec with special attributes.

These attributes need to be set in addition to the Terraform configuration options in the liblab config file.

attributes

attribute                                          TypeApplies todescription
x-liblab-resourcestringEndpoint + MethodMarks an endpoint as a Terraform resource operation
x-liblab-datasourcestringEndpoint + MethodMarks an endpoint as a Terraform data source operation
x-liblab-map-tostringParameterMaps a parameter to a field in a schema
x-liblab-show-diffboolSchema propertyMarks a field as showing a diff in the Terraform plan even if hideComputedDiff is true
x-liblab-includeboolSchema propertyMarks an endpoint to be included in the underlying Go SDK
x-liblab-plan-modifiersobjectSchema propertyMarks the property for plan modification

x-liblab-resource

This annotation marks an endpoint and HTTP method as a Terraform resource operation. The value of the operation is both the resource name, a # as a separator, and an action. Supported actions are Create, Read, Update, and Delete.

"paths": {
"/soda": {
"post": {
"x-liblab-resource": "Soda#Create",
"operationId": "CreateSoda",
...
}
}
}

For a resource to be generated, it is required to have resources annotated as Create, Read and Delete, with Update being optional. If you don't have a resource annotated as Update, the provider will have the RequiresReplace plan modifier on those resources, which disallows updates and forces the user to delete and recreate the resource.

You can chain multiple actions together on the same endpoint and method by separating them with a comma. For example:

"paths": {
"/soda": {
"post": {
"x-liblab-resource": "Soda#Create,Update",
"operationId": "UpsertSoda",
...
}
}
}

When the resources are generated, they need to be in PascalCase so that they are exported by Go. If your resource names are not in PascalCase, liblab will convert them. For example, if you have x-liblab-resource: "soda-fountain#Create,Update", the resource will be generated as SodaFountain. It is good practice to use PascalCase for the names of your resources in your API spec annotations.

x-liblab-datasource

This annotation marks an endpoint and HTTP method as a Terraform data source operation. The value of the operation is both the resource name, a # as a separator, and an action. The only supported action is Read as data sources are read only.

"paths": {
"/soda-type": {
"get": {
"x-liblab-datasource": "SodaType#Read",
...
}
}
}

x-liblab-map-to

This annotation allows mapping of endpoint parameters to values in the Terraform resource schema. The value of the annotation is the name of the field in the schema. For example:

"parameters": [
{
"name": "sodaId", // this is just 'id' in the schema
"in": "path",
"x-liblab-map-to": "id"
}
]

x-liblab-show-diff

This annotation marks a field as showing a diff in the Terraform plan even if hideComputedDiff is true. For example:

"properties": {
"createdAt": {
"type": "string",
"format": "date-time",
"x-liblab-show-diff": true
}
}
note

This field must be set to true on fields that are computed on updates if you have hideComputedDiff set to true. If this is not set, Terraform will raise an error.

x-liblab-include

This annotation marks an operation to be included in the underlying Go SDK that is generated under internal/client. By default, only the endpoints marked with x-liblab-resource and x-liblab-datasource are included in the Go SDK to reduce overhead.

This is useful when using resource plan modifiers to add custom functionality to your provider. By default, you will have the internal Go SDK available on every resource, under the client field.

"paths": {
"/pizza": {
"get": {
"x-liblab-include": true,
"operationId": "GetPizza",
...
}
}
}

Now you can use the internal Go SDK to call the endpoint from a resource plan modifier:

func (r *SodaResource) ModifyPlan(ctx context.Context,
req resource.ModifyPlanRequest,
resp *resource.ModifyPlanResponse) {
// Call the API
someResource, err := r.client.PizzaService.GetPizza()
// Have pizza with your soda!
...
}

x-liblab-plan-modifiers

This annotation is used to indicate which custom plan modifiers should be applied to a property. For example, if you have a custom attribute plan modifier to log boolean values with this signature:

import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
)

func LogBool() planmodifier.Bool {
return logBool{}
}

type logBool struct{}

func (l logBool) Description(_ context.Context) string {
return "Logs boolean values"
}

func (l logBool) MarkdownDescription(_ context.Context) string {
return "Logs boolean values"
}

func (l logBool) PlanModifyBool(ctx context.Context, req planmodifier.BoolRequest, resp *planmodifier.BoolResponse) {
// Do some kind of logging here, such as
// logger.Log(fmt.Sprintf("%s bool value: %t", req.Path, req.PlanValue))
return
}

This can then be applied to a property in the schema like so:

"properties": {
"isCold": {
"type": "boolean",
"x-liblab-plan-modifiers": ["LogBool"]
}
}