go-sdk
go-sdk copied to clipboard
[BUG]: PATCH request for repos/OWNER/REPO/properties/values broken
What happened?
I'm playing around with this sdk as a means of contributing to https://github.com/integrations/terraform-provider-github/issues/1956, and as such my focus is on the endpoints related to repository custom properties.
The issue I'm facing occurs when calling octokitClient.Repos().ByOwnerId(owner).ByRepoId(repoName).Properties().Values().Patch(), which is mapped to the repos/OWNER/REPO/properties/values PATCH endpoint. I'll list the code I'm using further down in the message.
When running the code below I receive the following error:
error status code received from the API
I've ran the debugger and dug deeper into the call chain, and found that the error code is 400, but haven't been able to find an error message. Digging down all the way to the net/http layer and inspecting the request body being sent, it looks like this:
"{\"properties\":[{\"property_name\":\"text\",\"value\":\"test\"}]}"
Which isn't too dissimilar to what the API expects (example from the docs):
'{"properties":[{"property_name":"environment","value":"production"},{"property_name":"service","value":"web"},{"property_name":"team","value":"octocat"}]}'
After playing around with curl a bit, I've managed to receive a 400 error by using malformed JSON in the body (see example below). My guess is that this SDK somewhere along the line parses the custom property incorrectly, or that I'm passing the property_name and value incorrectly to it.
curl -L \
-X PATCH \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $(gh auth token)" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/YesWeKanske/test-custom-properties/properties/values -d '"{"properties":[{"property_name":"text","value":"test"}]}"'
{
"message": "Problems parsing JSON",
"documentation_url": "https://docs.github.com/rest/repos/custom-properties#create-or-update-custom-property-values-for-a-repository",
"status": "400"
}
package main
import (
"context"
"log"
"os"
"time"
abs "github.com/microsoft/kiota-abstractions-go"
"github.com/octokit/go-sdk/pkg"
"github.com/octokit/go-sdk/pkg/github/models"
"github.com/octokit/go-sdk/pkg/github/repos"
)
func main() {
octokitClient, err := pkg.NewApiClient(
pkg.WithUserAgent("my-user-agent"),
pkg.WithRequestTimeout(60*time.Second),
pkg.WithBaseUrl("https://api.github.com"),
pkg.WithTokenAuthentication(os.Getenv("GITHUB_TOKEN")),
)
ctx := context.Background()
if err != nil {
log.Fatalf("error creating client: %v", err)
}
owner := "YesWeKanske"
repoName := "test-custom-properties"
repoURL := octokitClient.Repos().ByOwnerId(owner).ByRepoId(repoName)
propURL := repoURL.Properties().Values()
headers := abs.NewRequestHeaders()
_ = headers.TryAdd("Accept", "application/vnd.github.v3+json")
_ = headers.TryAdd("X-GitHub-Api-Version", "2022-11-28")
defaultRequestConfig := &abs.RequestConfiguration[abs.DefaultQueryParameters]{
QueryParameters: &abs.DefaultQueryParameters{},
Headers: headers,
}
propertyName := "text"
propertyValue := "test"
testProps := make(map[string]interface{})
testProps[propertyName] = propertyValue
testProperty := models.NewCustomPropertyValue()
testProperty.SetPropertyName(&propertyName)
propertyValueModel := models.NewCustomPropertyValue_CustomPropertyValue_value()
propertyValueModel.SetString(&propertyValue)
testProperty.SetValue(propertyValueModel)
// testProperty.SetAdditionalData(testProps)
patchBody := repos.NewItemItemPropertiesValuesPatchRequestBody()
patchBody.SetProperties([]models.CustomPropertyValueable{
testProperty,
})
// patchBody.SetAdditionalData(testProps)
err = propURL.Patch(ctx, patchBody, defaultRequestConfig)
if err != nil {
panic(err)
}
}
I've also explored the following:
- The
valueattribute of anCustomPropertyValueshould implement theCustomPropertyValue_CustomPropertyValue_valueableinterface, which only have the two methodsGetString()andSetString, which implies that the value of a custom property can only be a string. But it can be either a string or a list of string (multi_selectcustom property type). Due to this this SDK can only work with the other property types currently (except for the stuff mentioned above 😄). Thecurlcall below is valid, but there is no way of replicating it with this SDK from what I can tell
curl -L \
-X PATCH \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $(gh auth token)" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/YesWeKanske/test-custom-properties/properties/values -d '{"properties": [{"property_name": "multi-select","value": ["option1","option2"]}]}'
- Both
models.CustomPropertyValueandrepos.ItemItemPropertiesValuesPatchRequestBodysupports a function calledSetAdditionalData(). I've tried playing around with those two, but they seem to mess up the json body that is being sent to the GitHub API by adding key value pairs to it (the two"test": "text"below):
{
"properties": [
{
"property_name": "text",
"value": "test",
"text": "test"
}
],
"text": "test"
}
Versions
I've used the latest version of the provider at the time of writing this bug report (go get github.com/octokit/go-sdk@a74a3a2a13654bd84794fd91a6a0fbc96f883722, where a74a3a2a13654bd84794fd91a6a0fbc96f883722 is a commit to this repo). My go.mod file looks like so:
module example/hello
go 1.22.3
require (
github.com/microsoft/kiota-abstractions-go v1.6.0
github.com/octokit/go-sdk v0.0.21-0.20240709170617-a74a3a2a1365
)
require (
github.com/cjlapao/common-go v0.0.39 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/kfcampbell/ghinstallation v0.0.6 // indirect
github.com/microsoft/kiota-http-go v1.3.3 // indirect
github.com/microsoft/kiota-serialization-form-go v1.0.0 // indirect
github.com/microsoft/kiota-serialization-json-go v1.0.7 // indirect
github.com/microsoft/kiota-serialization-multipart-go v1.0.0 // indirect
github.com/microsoft/kiota-serialization-text-go v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/std-uritemplate/std-uritemplate/go v0.0.55 // indirect
github.com/stretchr/testify v1.9.0 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Code of Conduct
- [X] I agree to follow this project's Code of Conduct
👋 Hi! Thank you for this contribution! Just to let you know, our GitHub SDK team does a round of issue and PR reviews twice a week, every Monday and Friday! We have a process in place for prioritizing and responding to your input. Because you are a part of this community please feel free to comment, add to, or pick up any issues/PRs that are labeled with Status: Up for grabs. You & others like you are the reason all of this works! So thank you & happy coding! 🚀
I appreciate this super thoughtful bug report, and thank you for working to bring the new SDK into the Terraform provider. That's great!
I've reproduced what you've done here on a dummy repo (kfcampbell-terraform-provider/tf-acc-test-destroy-trfiz), and I agree there's something wonky going on in the generated code. I'm able to add custom properties successfully via the UI, and via a CURL request like the following:
curl -L -X PATCH -H "Accept: application/vnd.github+json" -H "Authorization: Bearer $(gh auth token)" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/kfcampbell-terraform-provider/tf-acc-test-destroy-trfiz/properties/values -d '{"properties":[{"property_name":"environment","value":"test-from-curl"}]}'
When using the SDK, I'm using the following code (after creating the client as we do in the example token CLI):
customPropertyValue := models.NewCustomPropertyValue()
propertyName := "environment"
propertyValue := "test-from-go-sdk"
customPropertyValue.SetPropertyName(&propertyName)
value := models.NewCustomPropertyValue_CustomPropertyValue_value()
value.SetString(&propertyValue)
customPropertyValue.SetValue(value)
patchRequestBody := repos.NewItemItemPropertiesValuesPatchRequestBody()
patchRequestBody.SetProperties([]models.CustomPropertyValueable{customPropertyValue})
err = client.Repos().ByOwnerId("kfcampbell-terraform-provider").ByRepoId("tf-acc-test-destroy-trfiz").
Properties().Values().Patch(context.Background(), patchRequestBody, nil)
if err != nil {
log.Fatalf("error setting custom properties: %v", err)
}
This fails with a 400 Bad Request error, and I see the payload going out as:
"{\"properties\":[{\"property_name\":\"environment\",\"value\":\"test-from-go-sdk\"}]}"
When I attempt a curl request with that as the payload (as I'm somewhat suspicious of the backslashes), I get a 422, not a 400.
I think this is an error with Kiota, our generation engine, so I've filed https://github.com/microsoft/kiota/issues/4995 and will do some investigation with them.
@felixlut this should be fixed as of v0.0.23!
Thanks! I'll take a stab after work today or tomorrow
@kfcampbell setting non-multi_select property values works now!
multi_select values are still broken though from what I can tell, for the reason I stated in the original issue:
- The
valueattribute of anCustomPropertyValueshould implement theCustomPropertyValue_CustomPropertyValue_valueableinterface, which only have the two methodsGetString()andSetString, which implies that the value of a custom property can only be a string. But it can be either a string or a list of string (multi_selectcustom property type). Due to this this SDK can only work with the other property types currently (except for the stuff mentioned above 😄). Thecurlcall below is valid, but there is no way of replicating it with this SDK from what I can tellcurl -L \ -X PATCH \ -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer $(gh auth token)" \ -H "X-GitHub-Api-Version: 2022-11-28" \ https://api.github.com/repos/YesWeKanske/test-custom-properties/properties/values -d '{"properties": [{"property_name": "multi-select","value": ["option1","option2"]}]}'
This might warrant its own issue though 😄
I'll also note that the Organization Ruleset API seems to handle the fact that a property value can be either a string or a list of strings in the way I proposed here, i.e., by just treating it as a list of strings at all times. It's pretty deeply nested, but Org Rulesets can target repos by custom properties, and this is where this occurs. .conditions.repository_property_and_ref_name.repository_property.include.property_values always takes a list of strings
@felixlut it looks like the code on main has been fixed (I'm following along from https://github.com/integrations/terraform-provider-github/issues/1956)?
@stevehipwell wrote a short update on my PR here: https://github.com/integrations/terraform-provider-github/pull/2316#issuecomment-2402699430
TLDR: it's kind of a pain to juggle using this sdk and other people making changes to the terraform provider, which causes conflicts in the vendoring 😅