Skip to content

Commit

Permalink
initial SDK commit
Browse files Browse the repository at this point in the history
  • Loading branch information
corbadovych committed Nov 15, 2023
1 parent dd10eb7 commit 405bd47
Show file tree
Hide file tree
Showing 26 changed files with 19,088 additions and 0 deletions.
22 changes: 22 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: tests

on:
push:
branches:
- 'main'
pull_request:
branches:
- 'main'

concurrency:
group: ${{ github.ref }}
cancel-in-progress: true

jobs:
test:
uses: "./.github/workflows/ci_test.yml"
secrets: inherit

lint:
uses: "./.github/workflows/ci_lint.yml"
secrets: inherit
20 changes: 20 additions & 0 deletions .github/workflows/ci_lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: lint

on:
workflow_call:

jobs:
lint:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
with:
go-version: 1.18
cache: true

- uses: golangci/golangci-lint-action@v3.7.0
with:
version: v1.55.2
args: --timeout 5m
24 changes: 24 additions & 0 deletions .github/workflows/ci_test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: test

on:
workflow_call:

jobs:
test:
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
go:
- '1.18'

steps:
- uses: actions/checkout@v3

- uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go }}
cache: true

- run: go test ./...
36 changes: 36 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
linters-settings:
lll:
line-length: 200
funlen:
lines: 100
statements: 50

linters:
disable-all: true
enable:
- lll
- funlen
- gosec
- govet
- dogsled
- dupl
- errcheck
- gochecknoinits
- goconst
- gocritic
- gocyclo
- godox
- gofmt
- goimports
- revive
- gosimple
- ineffassign
- nakedret
- prealloc
- exportloopref
- staticcheck
- stylecheck
- typecheck
- unconvert
- unused
- whitespace
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Corbado Go SDK

Go SDK for Corbado Backend API

[![Go Reference](https://pkg.go.dev/badge/github.com/corbado/corbado-go.svg)](https://pkg.go.dev/github.com/corbado/corbado-go)
[![Test Status](https://github.com/corbado/corbado-go/workflows/tests/badge.svg)](https://github.com/corbado/corbado-go/actions?query=workflow%3Atests)
[![documentation](https://img.shields.io/badge/documentation-Corbado_Backend_API_Reference-blue.svg)](https://api.corbado.com/docs/api/)

## Requirements

The SDK supports Go version 1.18 and above.

## Usage

```
$ go get github.com/corbado/corbado-go@v0.1.0
```

Import SDK in your Go files:

```go
import "github.com/corbado/corbado-go"
```

Now create a new SDK client:

```go
config := corbado.MustNewConfig("pro-12345678", "yoursecret")
sdk, err := corbado.NewSDK(config)
if err != nil {
// handle error
}

// list all users
users, err := sdk.Users().List(context.TODO(), nil)
if err != nil {
if serverErr := corbado.AsServerError(err); serverErr != nil {
// handle server error
}
}
```

See [examples](https://github.com/corbado/corbado-go/tree/main/examples) for some real example code
11 changes: 11 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
version: '3'

tasks:
lint:
desc: Runs Golang linters
cmds:
- GOFLAGS="-buildvcs=false" golangci-lint run --timeout=5m
sources:
- ./**/*.go
- ./.golangci.yml
method: checksum
119 changes: 119 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package corbado

import (
"bytes"
"context"
"fmt"
"io"
"net/http"

"github.com/corbado/corbado-go/pkg/logger"
"github.com/corbado/corbado-go/pkg/sdk/assert"
"github.com/corbado/corbado-go/pkg/sdk/entity/api"
"github.com/deepmap/oapi-codegen/pkg/securityprovider"
"github.com/pkg/errors"
)

func newClient(config *Config) (*api.ClientWithResponses, error) {
if err := assert.NotNil(config); err != nil {
return nil, err
}

basicAuth, err := securityprovider.NewSecurityProviderBasicAuth(config.ProjectID, config.APISecret)
if err != nil {
return nil, errors.WithStack(err)
}

extraOptions := []api.ClientOption{
api.WithRequestEditorFn(newSDKVersionHeaderEditorFn),
api.WithRequestEditorFn(basicAuth.Intercept),
}

if config.ExtraClientOptions != nil {
extraOptions = append(extraOptions, config.ExtraClientOptions...)
}

return api.NewClientWithResponses(config.BackendAPI, extraOptions...)
}

type httpRequestDoer interface {
Do(req *http.Request) (*http.Response, error)
}

type loggingClient struct {
underlying httpRequestDoer
}

// Do implements HttpRequestDoer and executes HTTP request
func (l *loggingClient) Do(req *http.Request) (*http.Response, error) {
if err := assert.NotNil(req); err != nil {
return nil, err
}

logger.Debug("Sending request to Public API: %s %s", req.Method, req.URL.String())
if req.Body != nil {
requestBody, err := l.readBody(&req.Body)
if err != nil {
return nil, err
}

logger.Debug("Request body: %s", requestBody)
}

response, err := l.underlying.Do(req)
if err != nil {
return nil, err
}

responseBody, err := l.readBody(&response.Body)
if err != nil {
return nil, err
}

logger.Debug("Received response from Public API: %s", responseBody)

return response, nil
}

func (l *loggingClient) readBody(rc *io.ReadCloser /* nilable */) (string, error) {
if rc == nil {
return "", nil
}

var buf bytes.Buffer
tee := io.TeeReader(*rc, &buf)

body, err := io.ReadAll(tee)
if err != nil {
return "", errors.WithStack(err)
}

*rc = io.NopCloser(&buf)

return string(body), nil
}

// newLoggingClient returns new logging HTTP client
func newLoggingClient() (*loggingClient, error) {
return &loggingClient{&http.Client{}}, nil
}

// NewLoggingClientOption enhances HTTP client to log requests/responses
func NewLoggingClientOption() api.ClientOption {
return func(c *api.Client) error {
client, err := newLoggingClient()
if err != nil {
return err
}

c.Client = client

return nil
}
}

func newSDKVersionHeaderEditorFn(_ context.Context, req *http.Request) error {
req.Header.Set("X-Corbado-SDK-Version", fmt.Sprintf("Go %s", Version))

return nil
}
60 changes: 60 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package corbado

import (
"fmt"
"net/http"
"time"

"github.com/corbado/corbado-go/pkg/sdk/assert"
"github.com/corbado/corbado-go/pkg/sdk/entity/api"
)

type Config struct {
ProjectID string
APISecret string
FrontendAPI string
BackendAPI string
ShortSessionCookieName string
CacheMaxAge time.Duration
JWTIssuer string

HTTPClient *http.Client
ExtraClientOptions []api.ClientOption
}

const (
configDefaultBackendAPI string = "https://backendapi.corbado.io"
configDefaultFrontendAPI string = "https://%s.frontendapi.corbado.io"
configDefaultShortSessionCookieName string = "cbo_short_session"
configDefaultCacheMaxAge = time.Minute
)

// NewConfig returns new config with sane defaults
func NewConfig(projectID string, apiSecret string) (*Config, error) {
if err := assert.StringNotEmpty(projectID); err != nil {
return nil, err
}

if err := assert.StringNotEmpty(apiSecret); err != nil {
return nil, err
}

return &Config{
ProjectID: projectID,
APISecret: apiSecret,
BackendAPI: configDefaultBackendAPI,
FrontendAPI: fmt.Sprintf(configDefaultFrontendAPI, projectID),
ShortSessionCookieName: configDefaultShortSessionCookieName,
CacheMaxAge: configDefaultCacheMaxAge,
}, nil
}

// MustNewConfig returns new config and panics if projectID or apiSecret are not specified/empty
func MustNewConfig(projectID string, apiSecret string) *Config {
config, err := NewConfig(projectID, apiSecret)
if err != nil {
panic(err)
}

return config
}
33 changes: 33 additions & 0 deletions examples/basic/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package main

import (
"context"
"fmt"

"github.com/corbado/corbado-go"
)

func main() {
config := corbado.MustNewConfig("pro-12345678", "yoursecret")
sdk, err := corbado.NewSDK(config)
if err != nil {
panic(err)
}

// list all users
users, err := sdk.Users().List(context.TODO(), nil)
if err != nil {
// handle server errors and client errors differently
if serverErr := corbado.AsServerError(err); serverErr != nil {
fmt.Printf("Received server error: %s", serverErr)

return
} else {
panic(err)
}
}

for _, usr := range users.Data.Users {
fmt.Printf("%s: %s\n", usr.ID, usr.Name)
}
}
16 changes: 16 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module github.com/corbado/corbado-go

go 1.18

require (
github.com/MicahParks/keyfunc v1.9.0
github.com/deepmap/oapi-codegen v1.16.2
github.com/golang-jwt/jwt/v4 v4.4.2
github.com/oapi-codegen/runtime v1.0.0
github.com/pkg/errors v0.9.1
)

require (
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/google/uuid v1.3.1 // indirect
)
Loading

0 comments on commit 405bd47

Please sign in to comment.