Skip to content

fix(API): make clearable string fields on update endpoints nullable #STRINGS-3652 - #1326

Open
Mikko Karkee (Varpuspaavi) wants to merge 2 commits into
mainfrom
STRINGS-3652-nullable-update-fields
Open

Mikko Karkee (Varpuspaavi) wants to merge 2 commits into
mainfrom
STRINGS-3652-nullable-update-fields

Conversation

@Varpuspaavi

@Varpuspaavi Mikko Karkee (Varpuspaavi) commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Follow-up to #1325 (STRINGS-3652). Optional type: string fields on update endpoints generate Go struct fields with json:",omitempty". Sending "" through the generated Go client or the CLI (--data) is dropped before the request is sent, so clearing a value silently no-ops.

Change

Add nullable: true (so the Go client generates *NullableString) to free-text fields that the API lets callers clear, and document that an empty string or null clears the value, omitting leaves it unchanged, and the CLI drops null (use an empty string there).

Endpoint Fields
key/update name_plural, original_file, localized_format_string, localized_format_key
screenshot/update, glossary_term/update, custom_metadata_property/update, webhook/update description
job/update briefing, ticket_url
job_template/update, organization_job_template/update briefing
release/update description, app_min_version, app_max_version
project/update point_of_contact
locale/update language_ai_profile, fallback_locale_id
styleguide/update audience, business, company_branding, formatting, glossary_terms, grammar_consistency, literal_translation, overall_tone, samples

key/update description and translation content are handled in #1325.

Deliberately not changed

An earlier revision of this PR also marked the fields below. Checking the API code showed nullable is wrong or pointless for them:

  • webhook/update secret: null and "" are both ignored on update (the existing secret is kept), so it can't be cleared this way. The "default project secret will be used" text applies at create only (not changed here).
  • authorization/update note: null and "" both return 422 (presence validation).
  • job_annotation/update and job_locale_annotation/update value: null and "" both return 422 (presence validation, NOT NULL column).
  • variable/update value: the API would store null / "", but the controller reads params.require(:variable). Whether a flat JSON body is auto-wrapped is unconfirmed, so left out until checked.
  • release_triggers/update app_min_version, app_max_version: the update rebuilds the template, so omitting these already clears them; there is no omitempty problem to fix.

API behavior (from reading the API code, not from running requests)

For the fields in the table, null stores NULL and "" stores an empty string, with no validation failure. Exceptions that normalize:

  • release/update app_min_version/app_max_version: "" is stripped to NULL; non-blank values are semver-checked (422 if invalid).
  • locale/update fallback_locale_id and project/update point_of_contact: both null and "" clear the value.

Not confirmed: whether any code treats "" specially for key options values (original_file, localized_format_*) or language_ai_profile.

Verification

make lint passes. Other language generators and the Go client were not regenerated for this revision.

Client impact (source-breaking for typed clients)

Same as #1325, on 27 fields across 12 endpoints: in phrase-go these change from string to *NullableString, so direct users must wrap values with phrase.NewNullableString(&s), and NewNullableString(nil), a zero-value &NullableString{} or Unset() now marshal as JSON null, which clears the field. Other typed clients get nullable field types too (not inspected). phrase-cli built, vetted and passed tests against the client generated for #1325 because it unmarshals --data into the struct; not re-checked for this PR.

The PR title is fix(API), which release-please treats as a patch bump; see the release-note question on #1325 and decide both together.

Known limitation

null is still dropped by the CLI/Go client when unmarshalling --data (encoding/json leaves the pointer nil); same as #1325. Use an empty string there.

Merge note

Possible small conflict with #1325 in paths/keys/update.yaml (adjacent lines).

🤖 Generated with Claude Code

…STRINGS-3652

Plain optional strings generate Go fields with omitempty, so sending an
empty string through the generated clients/CLI is dropped before the
request is sent. Mark free-text update fields nullable so the Go client
generates *NullableString and preserves an empty string on the wire.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

API changelog (oasdiff)

Doc-only edits (descriptions, examples) do not appear here.

35 changes: 0 error, 0 warning, 35 info
info	[response-optional-property-added] at doc/compiled.json
	in API GET /accounts/{account_id}/automations
		added the optional property `items/include_branches` to the response with the `200` status

info	[new-optional-request-property] at doc/compiled.json
	in API POST /accounts/{account_id}/automations
		added the new optional request property `include_branches`

info	[response-optional-property-added] at doc/compiled.json
	in API POST /accounts/{account_id}/automations
		added the optional property `include_branches` to the response with the `201` status

info	[response-optional-property-added] at doc/compiled.json
	in API GET /accounts/{account_id}/automations/{automation_id}
		added the optional property `include_branches` to the response with the `200` status

info	[new-optional-request-property] at doc/compiled.json
	in API PATCH /accounts/{account_id}/automations/{automation_id}
		added the new optional request property `include_branches`

info	[response-optional-property-added] at doc/compiled.json
	in API PATCH /accounts/{account_id}/automations/{automation_id}
		added the optional property `include_branches` to the response with the `200` status

info	[response-optional-property-added] at doc/compiled.json
	in API POST /accounts/{account_id}/automations/{automation_id}/activate
		added the optional property `include_branches` to the response with the `200` status

info	[response-optional-property-added] at doc/compiled.json
	in API POST /accounts/{account_id}/automations/{automation_id}/deactivate
		added the optional property `include_branches` to the response with the `200` status

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /accounts/{account_id}/custom_metadata/properties/{id}
		the request property `description` became nullable

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /accounts/{account_id}/distributions/{distribution_id}/releases/{id}
		the request property `app_max_version` became nullable

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /accounts/{account_id}/distributions/{distribution_id}/releases/{id}
		the request property `app_min_version` became nullable

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /accounts/{account_id}/distributions/{distribution_id}/releases/{id}
		the request property `description` became nullable

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /accounts/{account_id}/glossaries/{glossary_id}/terms/{id}
		the request property `description` became nullable

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /accounts/{account_id}/job_templates/{id}
		the request property `briefing` became nullable

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /projects/{id}
		the request property `point_of_contact` became nullable

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /projects/{project_id}/job_templates/{id}
		the request property `briefing` became nullable

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /projects/{project_id}/jobs/{id}
		the request property `briefing` became nullable

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /projects/{project_id}/jobs/{id}
		the request property `ticket_url` became nullable

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /projects/{project_id}/keys/{id}
		the request property `localized_format_key` became nullable

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /projects/{project_id}/keys/{id}
		the request property `localized_format_string` became nullable

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /projects/{project_id}/keys/{id}
		the request property `name_plural` became nullable

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /projects/{project_id}/keys/{id}
		the request property `original_file` became nullable

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /projects/{project_id}/locales/{id}
		the request property `fallback_locale_id` became nullable

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /projects/{project_id}/locales/{id}
		the request property `language_ai_profile` became nullable

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /projects/{project_id}/screenshots/{id}
		the request property `description` became nullable

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /projects/{project_id}/styleguides/{id}
		the request property `audience` became nullable

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /projects/{project_id}/styleguides/{id}
		the request property `business` became nullable

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /projects/{project_id}/styleguides/{id}
		the request property `company_branding` became nullable

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /projects/{project_id}/styleguides/{id}
		the request property `formatting` became nullable

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /projects/{project_id}/styleguides/{id}
		the request property `glossary_terms` became nullable

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /projects/{project_id}/styleguides/{id}
		the request property `grammar_consistency` became nullable

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /projects/{project_id}/styleguides/{id}
		the request property `literal_translation` became nullable

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /projects/{project_id}/styleguides/{id}
		the request property `overall_tone` became nullable

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /projects/{project_id}/styleguides/{id}
		the request property `samples` became nullable

info	[request-property-became-nullable] at doc/compiled.json
	in API PATCH /projects/{project_id}/webhooks/{id}
		the request property `description` became nullable

… #STRINGS-3652

webhook secret (ignored when blank), authorization note and job/job locale
annotation value (presence validated), variable value (unconfirmed body
wrapping) and release_triggers app versions (omitting already clears).
Document that an empty string or null clears the remaining fields.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>

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