fix: include configured false booleans on organization_settings create - #3570
fix: include configured false booleans on organization_settings create#3570somaz94 wants to merge 9 commits into
Conversation
|
👋 Hi, and thank you for this contribution! This repo is maintained by GitHub and community members on a best-effort basis. We'll get to this as soon as we can. You can help us prioritize by joining the discussion on open issues and PRs, sharing details on the changes you need, and reviewing other contributions. 🤖 This is an automated message. |
deiga
left a comment
There was a problem hiding this comment.
Nice work!
Could you refactor the unit tests to use a single testing function with a table for test cases?
There was a problem hiding this comment.
Please don't create a separate unit test file. Add the unit tests to the existing test file instead
There was a problem hiding this comment.
Done in 8cead3a — this file is deleted. The unit tests now live at the bottom of the existing github/resource_github_organization_settings_test.go.
| // attribute explicitly configured as false is included in the create payload. | ||
| // Regression test for the create path dropping false booleans (only fixed by a | ||
| // second apply through the update/HasChange path). | ||
| func TestBuildOrganizationSettingsCreateIncludesFalseBool(t *testing.T) { |
There was a problem hiding this comment.
Please use an underscore to separate function under test and the testcase in the name
There was a problem hiding this comment.
Done in 8cead3a — the five separate funcs are now a single table-driven Test_buildOrganizationSettings, following the repo's existing Test_getBaseURL / Test_configureProviderMeta convention. Testcase names use underscores (e.g. create_includes_explicitly_false_booleans, enterprise_create_includes_internal_repositories_boolean).
20e8bf1 to
8cead3a
Compare
|
@deiga Thanks for the review — all three points addressed in
I also verified the table still has teeth: temporarily reverting the Rebased on latest |
8cead3a to
0f86150
Compare
|
@deiga @stevehipwell — happy to go either way on this, just let me know which you prefer so I do not build the wrong thing. Option A (this PR as it stands): keep Option B (what @deiga raised): remove I am glad to do B in this PR if that is the agreed direction. The only thing blocking me is knowing which one you want. For what it is worth, the three earlier review threads are already addressed ( |
|
I hit this on a first apply against a new organisation, so a data point in favour of fixing it, plus one thing worth weighing before The partial payload on create is load-bearing. It arrived in #2807 to fix #2305, where the resource returned a 422 on every apply for EMU organisations because unconfigured fields were being sent. Dropping There is also a live conflict with #3360. That PR fixes #2689, a 422 when an enterprise policy locks forking, by removing Option 1 in #3493 sidesteps both. Testing the raw configuration for a null attribute distinguishes a configured |
|
Thanks for digging into this — the #3360 point is a fair catch and I'd missed it. On which way to fix it, I don't think I should pick on my own. @deiga suggested the opposite direction on 28 Jul — dropping the Either answer is small work from here. If partial updates stay, I'll switch the create check to a raw-config null test (the sample patch in #3493), which fixes the explicit @stevehipwell @deiga — could I get a steer? Happy to re-cut either way. |
stevehipwell
left a comment
There was a problem hiding this comment.
For me before any other changes are made the helper needs removing and the code needs to be put back into create/update. Then the API surface needs to be correctly designed to support the limitations of Terraform (SDK v2), as once a field is set there is no way to check if was done so by the user or a read.
FYI for boolean schema values Get should be used as getOk always returns ok as true.
|
Thanks @stevehipwell — that's the steer I was after. One thing I want to pin down before re-cutting: with |
|
@somaz94 if a field is optional and can be overridden at a higher layer we shouldn't set/read it if the user hasn't explicitly set it. |
ecd0631 to
fee6498
Compare
|
Done. The helper is gone and create and update are separate handlers again. Inclusion is now decided from the raw config rather than That also means Boolean values are read with I split it into a refactor commit and the fix so the two read separately. |
There was a problem hiding this comment.
🟡 Changes recommended
Create now omits schema-defaulted settings, risking first-apply drift, and lacks enabled acceptance coverage.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
These provider review instructions are being used. This PR fixes false boolean handling when creating organization settings and modernizes CRUD handlers.
Changes:
- Detects explicitly configured values through raw configuration.
- Separates create/update payload builders.
- Adds unit coverage for payload construction.
File summaries
| File | Description |
|---|---|
github/resource_github_organization_settings.go |
Refactors CRUD and payload construction. |
github/resource_github_organization_settings_test.go |
Adds create/update payload tests. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 2
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| } | ||
| } | ||
|
|
||
| func Test_organizationSettingsForCreate(t *testing.T) { |
|
Copilot's first point is right, fixed in bb3b9c1. Create now also sends an attribute when its schema default is a non-zero value, which is what the GetOk path already did before this PR, so the create payload is unchanged apart from explicitly configured false values now reaching it. Attributes defaulting to false or an empty string are still only sent when configured, so nothing new is sent to EMU orgs. The unit case for an unset default-true attribute now expects it to be sent, and the test helper fills schema defaults into the attributes the way the planned state does. |
There was a problem hiding this comment.
@somaz94 there are still too many abstractions in this logic, please refactor back to the CRUD functions and remove all but the obvious abstractions. For example if the logic for getting the payload for create and update are different then there shouldn't be a common function, it should be inline.
And also RE tests, please add acceptance tests and not unit tests.
| // stringAttr returns the string value of the named attribute. The resource | ||
| // schema guarantees the type, so a failed assertion can only mean the schema | ||
| // and this code disagree, in which case the zero value is the safe result. | ||
| func stringAttr(d *schema.ResourceData, name string) string { | ||
| v, _ := d.Get(name).(string) | ||
|
|
||
| return v | ||
| } | ||
|
|
||
| // boolAttr returns the boolean value of the named attribute, with the same | ||
| // type guarantee as stringAttr. | ||
| func boolAttr(d *schema.ResourceData, name string) bool { | ||
| v, _ := d.Get(name).(bool) | ||
|
|
||
| return v | ||
| } |
There was a problem hiding this comment.
These are unnecessary and make the code harder to read, please remove them and keep the logic inline.
| // isConfigured reports whether the named attribute is explicitly set in the | ||
| // configuration, as opposed to merely carrying its schema default. | ||
| // | ||
| // d.GetOk cannot answer this for booleans: it reports a value configured as | ||
| // false exactly like an unset one, which is why an explicit false used to be | ||
| // dropped from the create payload (#3493). The raw configuration keeps the | ||
| // distinction, so a null there means the user did not write the attribute. | ||
| // | ||
| // The raw configuration is absent on some code paths, such as import. In that | ||
| // case there is nothing to read the user's intent from, so this falls back to | ||
| // the previous d.GetOk behaviour rather than inventing one. | ||
| func isConfigured(d *schema.ResourceData, name string) bool { | ||
| if d.GetRawConfig().IsNull() { | ||
| _, ok := d.GetOk(name) | ||
|
|
||
| return ok | ||
| } | ||
|
|
||
| v, diags := d.GetRawConfigAt(cty.GetAttrPath(name)) | ||
| if diags.HasError() { | ||
| _, ok := d.GetOk(name) | ||
|
|
||
| return ok | ||
| } | ||
|
|
||
| return !v.IsNull() | ||
| } |
There was a problem hiding this comment.
If this is a reusable helper then it should be in one of the utils files.
| // Debug: Log the settings being sent to the API with detailed field information | ||
| // logOrganizationSettings records the payload about to be sent to the API. | ||
| func logOrganizationSettings(org string, isEnterprise bool, settings *github.Organization) { | ||
| log.Printf("[DEBUG] Built settings for org %s (enterprise: %v)", org, isEnterprise) |
There was a problem hiding this comment.
Please use tflog instead of log
| return resourceGithubOrganizationSettingsRead(d, meta) | ||
| d.SetId(strconv.FormatInt(orgSettings.GetID(), 10)) | ||
|
|
||
| return resourceGithubOrganizationSettingsRead(ctx, d, owner) |
|
Done: the logic is back inline in create/update, the helpers are gone apart from One question: the whole |
|
@somaz94 ideally |
Resolves #3493
Before the change?
On create, a boolean explicitly set to
falsewas dropped from the payload. Inclusion was decided withd.GetOk, which reportsfalsethe same as unset, so GitHub kept its own value and only a secondapply(through theHasChangeupdate path) converged.After the change?
Create sends an attribute when it is written in the configuration (read from the raw config, so an explicit
falsecounts) or when its schema default is non-zero, which the create payload already contained before. Unset attributes that default tofalse/""still stay out of the request, so the narrow payload from #2807 (#2305) is preserved. Update is unchanged: only changed attributes are sent.Following review, create and update are separate CRUD functions with the logic inline, neither calls Read, logging uses
tflog, and the raw-config check lives inutil.goasisConfigured.Pull request checklist
sends booleans configured as false on create(explicitfalseon five default-truebooleans, then a plan-only step that must be empty).TestAccGithubOrganizationSettingsis still skipped by the existingt.Skip("TODO: Make this test cleanup correctly"), so it does not run in CI, and I have not run it against a live org.Does this introduce a breaking change?