From 69f52ab1ef3d2767eadf5e0839332b7916177fee Mon Sep 17 00:00:00 2001 From: Jake Scaltreto Date: Tue, 4 Aug 2026 12:06:19 -0400 Subject: [PATCH 1/3] feat(endpoint): add wss_url output to endpoint resource The url attribute only exposed the RPC endpoint, leaving no way to reach the WebSocket endpoint that QuickNode returns alongside it. Surface the API's wss_url as its own computed attribute, normalized the same way as url so both omit the authentication token path. wss_url is null for chains and networks that do not offer WebSocket support. --- docs/resources/endpoint.md | 3 +- internal/provider/endpoint_resource.go | 36 ++++++++++++-- internal/provider/endpoint_resource_test.go | 55 +++++++++++++++++++++ 3 files changed, 88 insertions(+), 6 deletions(-) diff --git a/docs/resources/endpoint.md b/docs/resources/endpoint.md index abe4ca3..fcdde79 100644 --- a/docs/resources/endpoint.md +++ b/docs/resources/endpoint.md @@ -30,7 +30,8 @@ Endpoint resource - `id` (String) ID of the endpoint - `security` (Attributes) Security Configuration of the endpoint (see [below for nested schema](#nestedatt--security)) -- `url` (String) Endpoint URL that was created. +- `url` (String) HTTP(S) URL of the endpoint, without the authentication token path. +- `wss_url` (String) WebSocket URL of the endpoint, without the authentication token path. Null for chains that do not offer WebSocket support. ### Nested Schema for `security` diff --git a/internal/provider/endpoint_resource.go b/internal/provider/endpoint_resource.go index 9f28a0a..972e76b 100644 --- a/internal/provider/endpoint_resource.go +++ b/internal/provider/endpoint_resource.go @@ -75,6 +75,7 @@ type EndpointResourceModel struct { Chain types.String `tfsdk:"chain"` Network types.String `tfsdk:"network"` Url types.String `tfsdk:"url"` + WssUrl types.String `tfsdk:"wss_url"` Id types.String `tfsdk:"id"` Security types.Object `tfsdk:"security"` Tags types.Set `tfsdk:"tags"` @@ -86,6 +87,26 @@ type EndpointResourceSecurityToken struct { Token types.String } +// baseUrl strips the authentication token path from an endpoint URL, leaving +// only the scheme and host. +func baseUrl(raw string) types.String { + u, err := url.Parse(raw) + if err != nil || u.Scheme == "" || u.Host == "" { + return types.StringNull() + } + + return types.StringValue(fmt.Sprintf("%s://%s", u.Scheme, u.Host)) +} + +// setUrls derives the url and wss_url attributes from an API response. +func (data *EndpointResourceModel) setUrls(httpUrl string, wssUrl *string) { + data.Url = baseUrl(httpUrl) + data.WssUrl = types.StringNull() + if wssUrl != nil && *wssUrl != "" { + data.WssUrl = baseUrl(*wssUrl) + } +} + func (r *EndpointResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { resp.TypeName = req.ProviderTypeName + "_endpoint" } @@ -115,7 +136,14 @@ func (r *EndpointResource) Schema(ctx context.Context, req resource.SchemaReques }, "url": schema.StringAttribute{ Computed: true, - MarkdownDescription: "Endpoint URL that was created.", + MarkdownDescription: "HTTP(S) URL of the endpoint, without the authentication token path.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "wss_url": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "WebSocket URL of the endpoint, without the authentication token path. Null for chains that do not offer WebSocket support.", PlanModifiers: []planmodifier.String{ stringplanmodifier.UseStateForUnknown(), }, @@ -275,8 +303,7 @@ func (r *EndpointResource) Create(ctx context.Context, req resource.CreateReques endpoint := endpointResp.JSON200.Data data.Id = types.StringValue(endpoint.Id) - u, _ := url.Parse(endpoint.HttpUrl) - data.Url = types.StringValue(fmt.Sprintf("%s://%s", u.Scheme, u.Host)) + data.setUrls(endpoint.HttpUrl, endpoint.WssUrl) data.Security = types.ObjectNull(securityAttributes) if endpoint.Security.Tokens != nil { var tokens []basetypes.ObjectValuable @@ -464,8 +491,7 @@ func (r *EndpointResource) Read(ctx context.Context, req resource.ReadRequest, r if endpoint.Label != nil && *endpoint.Label != "" { data.Label = types.StringPointerValue(endpoint.Label) } - u, _ := url.Parse(endpoint.HttpUrl) - data.Url = types.StringValue(fmt.Sprintf("%s://%s", u.Scheme, u.Host)) + data.setUrls(endpoint.HttpUrl, endpoint.WssUrl) data.Security = types.ObjectNull(securityAttributes) if endpoint.Security.Tokens != nil { var tokens []basetypes.ObjectValuable diff --git a/internal/provider/endpoint_resource_test.go b/internal/provider/endpoint_resource_test.go index 276be2c..6bad4d4 100644 --- a/internal/provider/endpoint_resource_test.go +++ b/internal/provider/endpoint_resource_test.go @@ -21,6 +21,7 @@ import ( "fmt" "net/http" "os" + "regexp" "strings" "testing" @@ -44,6 +45,10 @@ func TestAccMinimalQuicknodeEndpointResource(t *testing.T) { Config: testAccQuickNodeResource(rName, "created-by-terraform", "tag1", "tag2"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrSet("quicknode_endpoint.main", "id"), + // eth/mainnet offers WebSocket support, so both URLs must be + // populated and must carry no token path. + resource.TestMatchResourceAttr("quicknode_endpoint.main", "url", regexp.MustCompile(`^https://[^/]+$`)), + resource.TestMatchResourceAttr("quicknode_endpoint.main", "wss_url", regexp.MustCompile(`^wss://[^/]+$`)), ), }, // ImportState testing @@ -297,3 +302,53 @@ func TestMultichainDiff_NullVsFalse(t *testing.T) { t.Fatalf("sanity: Equal() should still distinguish null and false; this test only guards against using Equal() for the diff") } } + +func strPtr(s string) *string { return &s } + +func TestSetUrls(t *testing.T) { + tests := map[string]struct { + httpUrl string + wssUrl *string + wantUrl types.String + wantWss types.String + }{ + "http and wss": { + httpUrl: "https://example-endpoint.quiknode.pro/abc123/", + wssUrl: strPtr("wss://example-endpoint.quiknode.pro/abc123/"), + wantUrl: types.StringValue("https://example-endpoint.quiknode.pro"), + wantWss: types.StringValue("wss://example-endpoint.quiknode.pro"), + }, + "nil wss": { + httpUrl: "https://example-endpoint.matic.quiknode.pro/abc123/", + wssUrl: nil, + wantUrl: types.StringValue("https://example-endpoint.matic.quiknode.pro"), + wantWss: types.StringNull(), + }, + "empty wss": { + httpUrl: "https://example-endpoint.quiknode.pro/abc123/", + wssUrl: strPtr(""), + wantUrl: types.StringValue("https://example-endpoint.quiknode.pro"), + wantWss: types.StringNull(), + }, + "unparseable http url": { + httpUrl: "not-a-url", + wssUrl: nil, + wantUrl: types.StringNull(), + wantWss: types.StringNull(), + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + var data EndpointResourceModel + data.setUrls(tc.httpUrl, tc.wssUrl) + + if !data.Url.Equal(tc.wantUrl) { + t.Errorf("url: got %v, want %v", data.Url, tc.wantUrl) + } + if !data.WssUrl.Equal(tc.wantWss) { + t.Errorf("wss_url: got %v, want %v", data.WssUrl, tc.wantWss) + } + }) + } +} From 77f6d894e8e7221e780b4a781c6f304fd9a32ba9 Mon Sep 17 00:00:00 2001 From: Jake Scaltreto Date: Tue, 4 Aug 2026 12:19:25 -0400 Subject: [PATCH 2/3] fix(deps): bump kin-openapi for GHSA-r277-6w6q-xmqw MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trivy flagged two advisories against kin-openapi v0.135.0: a critical fail-open authentication bypass in ValidationHandler.Load() and a nil-pointer panic when validating requests against a content schema. Neither is reachable from this provider — both live in openapi3filter, and only openapi3 is in our build graph (via the generated client's embedded spec). A fixed version exists, so bump rather than suppress. Regenerating the API clients against v0.144.0 produces no diff. --- go.mod | 17 +++++++---------- go.sum | 40 ++++++++++++++++------------------------ 2 files changed, 23 insertions(+), 34 deletions(-) diff --git a/go.mod b/go.mod index 0e30301..a6c1221 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/circlefin/terraform-provider-quicknode go 1.25.0 require ( - github.com/getkin/kin-openapi v0.135.0 + github.com/getkin/kin-openapi v0.144.0 github.com/google/addlicense v1.1.1 github.com/hashicorp/go-retryablehttp v0.7.7 github.com/hashicorp/terraform-plugin-docs v0.20.1 @@ -37,8 +37,9 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect github.com/fatih/color v1.18.0 // indirect - github.com/go-openapi/jsonpointer v0.22.4 // indirect - github.com/go-openapi/swag/jsonname v0.25.4 // indirect + github.com/go-openapi/jsonpointer v0.22.5 // indirect + github.com/go-openapi/swag/jsonname v0.25.5 // indirect + github.com/go-test/deep v1.0.8 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/uuid v1.6.0 // indirect @@ -62,8 +63,6 @@ require ( github.com/hashicorp/terraform-svchost v0.1.1 // indirect github.com/hashicorp/yamux v0.1.2 // indirect github.com/huandu/xstrings v1.5.0 // indirect - github.com/josharian/intern v1.0.0 // indirect - github.com/mailru/easyjson v0.9.1 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect @@ -72,15 +71,14 @@ require ( github.com/mitchellh/go-wordwrap v1.0.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect - github.com/oasdiff/yaml v0.0.9 // indirect - github.com/oasdiff/yaml3 v0.0.9 // indirect + github.com/oasdiff/yaml v0.1.1 // indirect + github.com/oasdiff/yaml3 v0.0.14 // indirect github.com/oklog/run v1.1.0 // indirect github.com/onsi/gomega v1.27.6 // indirect - github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/posener/complete v1.2.3 // indirect github.com/rivo/uniseg v0.2.0 // indirect + github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/speakeasy-api/jsonpath v0.6.3 // indirect github.com/speakeasy-api/openapi v1.19.2 // indirect @@ -89,7 +87,6 @@ require ( github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/vmware-labs/yaml-jsonpath v0.3.2 // indirect - github.com/woodsbury/decimal128 v1.4.0 // indirect github.com/yuin/goldmark v1.7.13 // indirect github.com/yuin/goldmark-meta v1.1.0 // indirect github.com/zclconf/go-cty v1.15.0 // indirect diff --git a/go.sum b/go.sum index 3e5e50c..287b4fa 100644 --- a/go.sum +++ b/go.sum @@ -45,6 +45,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960/go.mod h1:9HQzr9D/0PGwMEbC3d5AB7oi67+h4TsQqItC1GVYG58= github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 h1:PRxIJD8XjimM5aTknUK9w6DHLDox2r2M3DI4i2pnd3w= github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936/go.mod h1:ttYvX5qlB+mlV1okblJqcSMtR4c52UKxDiX9GRBS8+Q= @@ -58,8 +60,8 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/getkin/kin-openapi v0.135.0 h1:751SjYfbiwqukYuVjwYEIKNfrSwS5YpA7DZnKSwQgtg= -github.com/getkin/kin-openapi v0.135.0/go.mod h1:6dd5FJl6RdX4usBtFBaQhk9q62Yb2J0Mk5IhUO/QqFI= +github.com/getkin/kin-openapi v0.144.0 h1:hIRcTH+KjLfkLpYU6bSSfdFpi0fZi1fp+hSPi4aQu9Y= +github.com/getkin/kin-openapi v0.144.0/go.mod h1:3BH9M9XDe/y9M5DSvEocVYAYq1w0qrhJHjC/vZi0AaY= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= @@ -70,12 +72,12 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4= -github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80= -github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI= -github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag= -github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= -github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= +github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA= +github.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0= +github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo= +github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU= +github.com/go-openapi/testify/v2 v2.4.0 h1:8nsPrHVCWkQ4p8h1EsRVymA2XABB4OT40gcvAu+voFM= +github.com/go-openapi/testify/v2 v2.4.0/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= @@ -166,8 +168,6 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOl github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= @@ -178,8 +178,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8= -github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= @@ -200,8 +198,6 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= @@ -211,10 +207,10 @@ github.com/oapi-codegen/oapi-codegen/v2 v2.7.1 h1:a7Ab7YlpqkVG5HKrTaeFstm32Z5QOn github.com/oapi-codegen/oapi-codegen/v2 v2.7.1/go.mod h1:qzFy6iuobJw/hD1aRILee4G87/ShmhR0xYCwcUtZMCw= github.com/oapi-codegen/runtime v1.6.0 h1:7Xx+GlueD6nRuyKoCPzL434Jfi3BetbiJOrzCHp/VPU= github.com/oapi-codegen/runtime v1.6.0/go.mod h1:GwV7hC2hviaMzj+ITfHVRESK5J2W/GefVwIND/bMGvU= -github.com/oasdiff/yaml v0.0.9 h1:zQOvd2UKoozsSsAknnWoDJlSK4lC0mpmjfDsfqNwX48= -github.com/oasdiff/yaml v0.0.9/go.mod h1:8lvhgJG4xiKPj3HN5lDow4jZHPlx1i7dIwzkdAo6oAM= -github.com/oasdiff/yaml3 v0.0.9 h1:rWPrKccrdUm8J0F3sGuU+fuh9+1K/RdJlWF7O/9yw2g= -github.com/oasdiff/yaml3 v0.0.9/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= +github.com/oasdiff/yaml v0.1.1 h1:6nHx+pn9gBRM6YpBlFZFQGCCd1nuvqOBtTD3KKTgGxY= +github.com/oasdiff/yaml v0.1.1/go.mod h1:EYJNoyktvWMJ0Hmhx+6qTaqMOsalUaRGT8Sj1hNcegU= +github.com/oasdiff/yaml3 v0.0.14 h1:aLJee3hxBK2H5wdXd9iPcIXb93Nty1Ge0pT171eHtkw= +github.com/oasdiff/yaml3 v0.0.14/go.mod h1:csto2xfDjYccdUn/yw/bPjj/cYTdp6HtFA0J4TWG+gg= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -230,8 +226,6 @@ github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= -github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= -github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -243,6 +237,8 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= @@ -264,8 +260,6 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= -github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= @@ -275,8 +269,6 @@ github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAh github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/vmware-labs/yaml-jsonpath v0.3.2 h1:/5QKeCBGdsInyDCyVNLbXyilb61MXGi9NP674f9Hobk= github.com/vmware-labs/yaml-jsonpath v0.3.2/go.mod h1:U6whw1z03QyqgWdgXxvVnQ90zN1BWz5V+51Ewf8k+rQ= -github.com/woodsbury/decimal128 v1.4.0 h1:xJATj7lLu4f2oObouMt2tgGiElE5gO6mSWUjQsBgUlc= -github.com/woodsbury/decimal128 v1.4.0/go.mod h1:BP46FUrVjVhdTbKT+XuQh2xfQaGki9LMIRJSFuh6THU= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= From a716b96a49838808ce250347328d9ff35fa180ad Mon Sep 17 00:00:00 2001 From: Jake Scaltreto Date: Tue, 4 Aug 2026 13:07:55 -0400 Subject: [PATCH 3/3] refactor(endpoint): fold empty wss_url handling into baseUrl Review feedback: the wssUrl != nil && *wssUrl != "" guard duplicated what baseUrl already rejects, since the empty string parses to no scheme or host. Document that contract on baseUrl now that it is load-bearing, and drop the single-use strPtr test helper in favour of local fixtures. The nil and empty-string cases stay separate in the table test: they are distinct wire representations, and they are what proves this collapse is behaviour-preserving. --- internal/provider/endpoint_resource.go | 8 +++----- internal/provider/endpoint_resource_test.go | 10 ++++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/provider/endpoint_resource.go b/internal/provider/endpoint_resource.go index 972e76b..b1df121 100644 --- a/internal/provider/endpoint_resource.go +++ b/internal/provider/endpoint_resource.go @@ -88,7 +88,8 @@ type EndpointResourceSecurityToken struct { } // baseUrl strips the authentication token path from an endpoint URL, leaving -// only the scheme and host. +// only the scheme and host. It returns null for input it cannot parse into +// both a scheme and a host, which includes the empty string. func baseUrl(raw string) types.String { u, err := url.Parse(raw) if err != nil || u.Scheme == "" || u.Host == "" { @@ -101,10 +102,7 @@ func baseUrl(raw string) types.String { // setUrls derives the url and wss_url attributes from an API response. func (data *EndpointResourceModel) setUrls(httpUrl string, wssUrl *string) { data.Url = baseUrl(httpUrl) - data.WssUrl = types.StringNull() - if wssUrl != nil && *wssUrl != "" { - data.WssUrl = baseUrl(*wssUrl) - } + data.WssUrl = baseUrl(types.StringPointerValue(wssUrl).ValueString()) } func (r *EndpointResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { diff --git a/internal/provider/endpoint_resource_test.go b/internal/provider/endpoint_resource_test.go index 6bad4d4..2774fd6 100644 --- a/internal/provider/endpoint_resource_test.go +++ b/internal/provider/endpoint_resource_test.go @@ -303,9 +303,9 @@ func TestMultichainDiff_NullVsFalse(t *testing.T) { } } -func strPtr(s string) *string { return &s } - func TestSetUrls(t *testing.T) { + wss, empty := "wss://example-endpoint.quiknode.pro/abc123/", "" + tests := map[string]struct { httpUrl string wssUrl *string @@ -314,10 +314,12 @@ func TestSetUrls(t *testing.T) { }{ "http and wss": { httpUrl: "https://example-endpoint.quiknode.pro/abc123/", - wssUrl: strPtr("wss://example-endpoint.quiknode.pro/abc123/"), + wssUrl: &wss, wantUrl: types.StringValue("https://example-endpoint.quiknode.pro"), wantWss: types.StringValue("wss://example-endpoint.quiknode.pro"), }, + // The spec types wss_url as nullable, but pin both wire + // representations of "no WebSocket" so neither regresses. "nil wss": { httpUrl: "https://example-endpoint.matic.quiknode.pro/abc123/", wssUrl: nil, @@ -326,7 +328,7 @@ func TestSetUrls(t *testing.T) { }, "empty wss": { httpUrl: "https://example-endpoint.quiknode.pro/abc123/", - wssUrl: strPtr(""), + wssUrl: &empty, wantUrl: types.StringValue("https://example-endpoint.quiknode.pro"), wantWss: types.StringNull(), },