Skip to content

Add category to opslevel_component_type - #682

Open
jamescarr wants to merge 3 commits into
OpsLevel:mainfrom
jamescarr:jamescarr/support-infrastructure-component
Open

jamescarr wants to merge 3 commits into
OpsLevel:mainfrom
jamescarr:jamescarr/support-infrastructure-component

Conversation

@jamescarr

Copy link
Copy Markdown
Contributor

Summary

Adds a category attribute to the opslevel_component_type resource and to both the singular and plural component type data sources, so infrastructure component types can be managed in Terraform. Setting category = "infrastructure" files the type under the Infrastructure catalog.

The attribute is a plain string with no validator. The backend treats categories as account-scoped aliases rather than a fixed enum — the same way allowed_categories already works on relationship definitions — so hardcoding a closed set would be wrong.

Optional + Computed, and why

category is Optional + Computed with UseStateForUnknown. Leaving it out of config preserves whatever category the type already has rather than forcing it back to default, which matters for adopting existing infrastructure types into Terraform: you can terraform import one without the first apply silently moving it out of the Infrastructure catalog.

The corollary is that this attribute can't be "unset" by deleting it from config. Moving a type back to the Components catalog means writing category = "default" explicitly. That seemed clearly safer than the alternative, but flag it if you'd rather it behave differently.

Drive-by bug fix

Create and Update unconditionally dereferenced planModel.Icon, so the provider panicked on any opslevel_component_type config that omitted the optional icon block. That's pre-existing on main and easy to hit — it's exactly what you write when adopting an existing type — so it's fixed here along with the matching read path. Separate changie entry.

Dependency

Depends on OpsLevel/opslevel-go#647, which adds Category to ComponentTypeInput and ComponentType. The go.mod replace directive and the submodules/opslevel-go pointer are temporary and should be swapped for a released version bump before merge.

Please read the feature-flag section on #647 before merging this. The short version: if category is still gated behind infra_component_types_ui at the schema level, then adding it to the client's ComponentType selection set breaks reads for accounts without the flag, and this PR shouldn't land either. I've asked there for someone with backend visibility to confirm. That question sank the earlier attempt (OpsLevel/opslevel-go#613) and I'd rather resolve it up front than repeat it.

Test plan

  • task lint — gofumpt, golangci-lint (0 issues), terraform validate, terraform fmt -check
  • task test — terraform mock tests + Go unit tests, 109 passed / 0 failed, including new category assertions on the resource and both data sources
  • Verified end to end against a live sandbox account in an earlier pass: create with infrastructure, in-place update between categories, no diff when the attribute is dropped from config, destroy, and import-for-adoption
  • Docs regenerated with tfplugindocs

Made with Cursor

jamescarr and others added 2 commits September 15, 2026 14:39
Exposes the component type catalog category, so a custom type can be
filed under the Infrastructure catalog with `category = "infrastructure"`
instead of being patched out of band after creation.

The attribute is optional and computed. The API leaves an existing
category untouched when the field is omitted, so an unset attribute
keeps whatever the type already has rather than resetting it, and the
input is omitted entirely rather than sent as an explicit null.

No validator: category is a free-form String on the API and the schema
documents it as any component category alias on the account, so pinning
a closed set here would break importing a type with a category the
provider does not know about.

Also fixes a pre-existing nil dereference: `icon` is optional but was
dereferenced unconditionally on create and update, so any component
type configured without an icon block crashed the provider. This is on
the path for adopting existing types, which often have no icon in
config.

Depends on the category support in opslevel-go. The go.mod replace
directive and submodules/opslevel-go pointer are temporary and should
be swapped for a released version before merge.

Co-authored-by: Cursor <cursoragent@cursor.com>
…nfrastructure-component

# Conflicts:
#	go.mod
#	opslevel/datasource_opslevel_component_type.go
#	opslevel/resource_opslevel_component_type.go
#	submodules/opslevel-go
@codecov

codecov Bot commented Sep 16, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 29.62963% with 19 lines in your changes missing coverage. Please review.
✅ Project coverage is 1.90%. Comparing base (decd87c) to head (db07a76).
⚠️ Report is 130 commits behind head on main.

Files with missing lines Patch % Lines
opslevel/resource_opslevel_component_type.go 32.00% 17 Missing ⚠️
opslevel/datasource_opslevel_component_type.go 0.00% 2 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##            main    #682      +/-   ##
========================================
+ Coverage   0.22%   1.90%   +1.67%     
========================================
  Files         85     107      +22     
  Lines       9651   12977    +3326     
========================================
+ Hits          22     247     +225     
- Misses      9626   12707    +3081     
- Partials       3      23      +20     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Every component type belongs to a catalog, and "default" is the one that is
not Infrastructure, so the API is expected to return it rather than nothing.
Reads went through ComputedStringValue, which maps an empty string to null.
If the API ever returns empty for an uncategorized type, a config setting
category = "default" explicitly would plan "default" and apply to null, and
Terraform fails that as an inconsistent result rather than surfacing drift.

Map an empty read to "default" instead so that path cannot hard fail, and
cover both the read mapping and the write omission with unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant