Skip to content

Fix opslevel_service dropping preferred_api_document_source when api_document_path is unset (#14748)#647

Open
saditya370 wants to merge 4 commits intomainfrom
fix/14748-preferred-api-document-source-without-path
Open

Fix opslevel_service dropping preferred_api_document_source when api_document_path is unset (#14748)#647
saditya370 wants to merge 4 commits intomainfrom
fix/14748-preferred-api-document-source-without-path

Conversation

@saditya370
Copy link
Copy Markdown
Collaborator

@saditya370 saditya370 commented Apr 29, 2026

Summary

Fixes #14748. Configuring preferred_api_document_source on opslevel_service without also setting api_document_path caused apply to fail with Provider produced inconsistent result after apply: .preferred_api_document_source: was cty.StringVal("PUSH"), but now null. The provider was gating the serviceApiDocSettingsUpdate mutation behind a non-empty api_document_path, so the user's preferred-source value was never sent to the backend — Terraform's plan/state consistency check then rejected the apply.

This is a provider-only fix. The GraphQL backend and opslevel-go client already accepted the two arguments independently; only the provider was incorrectly coupling them.

Changes

  • Add serviceApiDocSettingsUpdateInput(plan, state) helper in opslevel/resource_opslevel_service.go that decides when to call ServiceApiDocSettingsUpdate and with what arguments. Returns shouldUpdate=true whenever either field is configured in plan, or whenever either field was previously managed in state (so unsetting still works).
  • Create now uses the helper, fixing source-only configs.
  • Update now uses the same helper, fixing the prior bug where api_document_path being null silently cleared preferred_api_document_source.
  • Behavior is unchanged for path-only and path-plus-source configs — the helper returns the same arguments the old code sent.

Customer-reported repro

resource "opslevel_service" "example" {
  name                          = "example"
  preferred_api_document_source = "PUSH"
}

Before the fix: terraform apply errored with the inconsistent-result message and left an orphaned service in OpsLevel that Terraform did not record in state.

After the fix: apply succeeds, and a follow-up terraform plan reports No changes.

Test plan

  • New unit tests for serviceApiDocSettingsUpdateInput covering source-only PUSH, source-only PULL, path-only, unmanaged, unset previously-managed source, and unset previously-managed path — see opslevel/resource_opslevel_service_test.go.
  • New tftest regression resource_service_create_with_preferred_api_document_source_without_path reproducing the exact customer config and asserting api_document_path == null and preferred_api_document_source == "PUSH" after apply — see tests/service.tftest.hcl.
  • GOWORK=off go test ./opslevel passes.
  • GOWORK=off go vet ./... clean.
  • GOWORK=off task test passes (104 Terraform tests + Go unit tests).
  • Verified end-to-end against a live OpsLevel tenant: source-only PUSH and PULL configs apply cleanly, idempotent on re-plan, and the resulting services row in the database has the expected api_docs_source value with api_docs_path null.

@saditya370 saditya370 requested a review from wesleyjellis April 29, 2026 13:34
@saditya370 saditya370 self-assigned this Apr 29, 2026
Comment thread opslevel/resource_opslevel_service.go Outdated
Comment thread opslevel/resource_opslevel_service.go Outdated
Comment thread opslevel/resource_opslevel_service.go Outdated
return nil
}

func serviceApiDocSettingsUpdateInput(plan ServiceResourceModel, state *ServiceResourceModel) (bool, string, *opslevel.ApiDocumentSourceEnum) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This helper should live at the bottom of the file since it isn't global.

Comment thread opslevel/resource_opslevel_service.go Outdated
return false, "", nil
}

managesApiDocPath := !plan.ApiDocumentPath.IsNull()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The naming for these variables doesn't make much sense as these attributes are always managed.

Comment thread opslevel/resource_opslevel_service.go Outdated
return nil
}

func serviceApiDocSettingsUpdateInput(plan ServiceResourceModel, state *ServiceResourceModel) (bool, string, *opslevel.ApiDocumentSourceEnum) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can remove the state argument if we put the plan/state equality check into the update lifecycle method.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored as suggested

  1. helper moved to bottom of file,
  2. dropped the state arg,
  3. equality check moved into Update.

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.

2 participants