Skip to content

Commit

Permalink
feat: API versioning (#276)
Browse files Browse the repository at this point in the history
All requests set the Clerk-API-Version header to pin the SDK to a
specific API version.
The /v1/ part is no longer considered the API version. As such, it can
be appended to the API base URL.
  • Loading branch information
gkats authored Mar 22, 2024
1 parent 5151b40 commit 39301db
Show file tree
Hide file tree
Showing 4 changed files with 14 additions and 11 deletions.
7 changes: 4 additions & 3 deletions clerk.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ import (

const (
sdkVersion string = "v2.0.0"
clerkAPIVersion string = "v1"
clerkAPIVersion string = "2021-02-05"
)

const (
// APIURL is the base URL for the Clerk API.
APIURL string = "https://api.clerk.com"
APIURL string = "https://api.clerk.com/v1"
)

// The Clerk secret key. Configured on a package level.
Expand Down Expand Up @@ -261,7 +261,7 @@ func (b *defaultBackend) Call(ctx context.Context, apiReq *APIRequest, setter Re
}

func (b *defaultBackend) newRequest(ctx context.Context, apiReq *APIRequest) (*http.Request, error) {
path, err := JoinPath(b.URL, clerkAPIVersion, apiReq.Path)
path, err := JoinPath(b.URL, apiReq.Path)
if err != nil {
return nil, err
}
Expand All @@ -272,6 +272,7 @@ func (b *defaultBackend) newRequest(ctx context.Context, apiReq *APIRequest) (*h
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", b.Key))
req.Header.Add("Content-Type", "application/json")
req.Header.Add("User-Agent", fmt.Sprintf("clerk/clerk-sdk-go@%s", sdkVersion))
req.Header.Add("Clerk-API-Version", clerkAPIVersion)
req.Header.Add("X-Clerk-SDK", fmt.Sprintf("go/%s", sdkVersion))
b.CustomRequestHeaders.apply(req)
req = req.WithContext(ctx)
Expand Down
4 changes: 3 additions & 1 deletion clerk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,13 +180,15 @@ func TestBackendCall_RequestHeaders(t *testing.T) {

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, method, r.Method)
require.Equal(t, "/"+clerkAPIVersion+path, r.URL.Path)
require.Equal(t, path, r.URL.Path)

// The client sets the Authorization header correctly.
assert.Equal(t, fmt.Sprintf("Bearer %s", secretKey), r.Header.Get("Authorization"))
// The client sets the User-Agent header.
assert.Equal(t, fmt.Sprintf("clerk/clerk-sdk-go@%s", sdkVersion), r.Header.Get("User-Agent"))
assert.Equal(t, "application/json", r.Header.Get("Content-Type"))
// The client sets the API version header.
assert.Equal(t, clerkAPIVersion, r.Header.Get("Clerk-API-Version"))
// The client includes a custom header with the SDK version.
assert.Equal(t, fmt.Sprintf("go/%s", sdkVersion), r.Header.Get("X-Clerk-SDK"))
// Custom headers are added correctly.
Expand Down
4 changes: 2 additions & 2 deletions http/middleware_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ func TestWithHeaderAuthorization_Caching(t *testing.T) {
kid := "kid"
clock := clockwork.NewFakeClockAt(time.Now().UTC())

// Mock the Clerk API server. We expect requests to GET /v1/jwks.
// Mock the Clerk API server. We expect requests to GET /jwks.
totalJWKSRequests := 0
clerkAPI := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/v1/jwks" && r.Method == http.MethodGet {
if r.URL.Path == "/jwks" && r.Method == http.MethodGet {
// Count the number of requests to the JWKS endpoint
totalJWKSRequests++
_, err := w.Write([]byte(
Expand Down
10 changes: 5 additions & 5 deletions jwt/jwt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,13 +260,13 @@ func TestVerify_CustomClaims(t *testing.T) {

// TestVerify_UsesTheJWKSClient tests that when verifying a JWT if
// you don't provide the JWK, the Verify method will make a request
// to GET /v1/jwks to fetch the JWK set.
// to GET /jwks to fetch the JWK set.
func TestVerify_UsesTheJWKSClient(t *testing.T) {
t.Parallel()
kid := "kid"
totalJWKSRequests := 0
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/v1/jwks" && r.Method == http.MethodGet {
if r.URL.Path == "/jwks" && r.Method == http.MethodGet {
require.Equal(t, "custom client was used", r.Header.Get("X-Clerk-Application"))
// Count the number of requests to the JWKS endpoint
totalJWKSRequests++
Expand Down Expand Up @@ -312,7 +312,7 @@ func TestVerify_DefaultJWKSClient(t *testing.T) {
kid := "kid"
totalJWKSRequests := 0
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/v1/jwks" && r.Method == http.MethodGet {
if r.URL.Path == "/jwks" && r.Method == http.MethodGet {
// Count the number of requests to the JWKS endpoint
totalJWKSRequests++
_, err := w.Write([]byte(
Expand Down Expand Up @@ -397,7 +397,7 @@ func TestGetJSONWebKey_DefaultJWKSClient(t *testing.T) {
kid := "kid"
totalJWKSRequests := 0
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/v1/jwks" && r.Method == http.MethodGet {
if r.URL.Path == "/jwks" && r.Method == http.MethodGet {
// Count the number of requests to the JWKS endpoint
totalJWKSRequests++
_, err := w.Write([]byte(
Expand Down Expand Up @@ -429,7 +429,7 @@ func TestGetJSONWebKey_UsesTheJWKSClient(t *testing.T) {
kid := "kid"
totalJWKSRequests := 0
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/v1/jwks" && r.Method == http.MethodGet {
if r.URL.Path == "/jwks" && r.Method == http.MethodGet {
require.Equal(t, "custom client was used", r.Header.Get("X-Clerk-Application"))
// Count the number of requests to the JWKS endpoint
totalJWKSRequests++
Expand Down

0 comments on commit 39301db

Please sign in to comment.