diff --git a/.github/workflows/setup-ci.yaml b/.github/workflows/setup-ci.yaml new file mode 100644 index 0000000..02d7312 --- /dev/null +++ b/.github/workflows/setup-ci.yaml @@ -0,0 +1,101 @@ +name: Setup repository +on: + push: + paths: + - cookiecutter.json +jobs: + setup: + name: Reinitialize repository + runs-on: ubuntu-latest + env: + REPO_SETUP_TOKEN: ${{ secrets.REPO_SETUP_TOKEN }} + steps: + - name: Do not run scaffolding on template repository + shell: bash + # This workflow runs when the `cookiecutter.json` file is modified. + # This is the trick to re-init a repository, but we don't want to + # run this action if this file is modified in the origin template repository. + # + # Using the GitHub rest API allows us to identify if the current repository + # is a template repository or not. + run: | + curl --silent -X GET \ + -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ + -H "Accept: application/vnd.github.baptiste-preview+json" \ + https://api.github.com/repos/$GITHUB_REPOSITORY \ + | jq --exit-status '.is_template == false'; + + - id: string + uses: ASzc/change-string-case-action@v2 + with: + string: ${{ github.repository }} + + - uses: actions/checkout@v4 + with: + # Committing workflow files using the regular GITHUB_TOKEN will fail with + # `Git Error: Refusing to allow a GitHub App to create or update workflow without workflows permission`. + # This is by design to prevent third-parties from adding malicious workflow files. + # + # Generate a new personal access token with the workflow `scope` does the trick. + # Checkout my blog post https://stefanbuck.com/blog for alternative options + token: ${{ env.REPO_SETUP_TOKEN || secrets.GITHUB_TOKEN }} + + - uses: actions/setup-python@v2 + with: + python-version: '3.x' + + - name: Install dependencies + run: pip install cookiecutter + + - name: Scaffolding repository + # cookiecutter is command-line utility to create projects from templates + # https://github.com/cookiecutter/cookiecutter + # + # --no-input Do not prompt for parameters and only use + # cookiecutter.json file content + # + # --output-dir Where to output the generated project dir into + run: cookiecutter . --no-input --output-dir ./cookiecutter-temp + + - name: Prepare root directory + shell: bash + run: | + find ./ -maxdepth 1 \ + ! -name '.git' \ + ! -name 'cookiecutter-temp' \ + ! -name '.' \ + ! -exec rm -rf {} + + + - name: Move files to root + shell: bash + # The cookiecutter-temp/ folder contains a single folder which is the + # generated project by cookiecutter. We want to move all the project + # files into the root directory so we can reinitialize git in the next step + run: | + rsync -r ./cookiecutter-temp/*/ . && \ + rm -rf ./cookiecutter-temp/ + + - name: Install Go + uses: actions/setup-go@v4 + with: + go-version: '1.20.x' + + - name: go mod init + shell: bash + run: | + go mod init "github.com/${{ steps.string.outputs.lowercase }}" && \ + go mod tidy && \ + go mod vendor + + - name: Reinitialize git repository + shell: bash + # Reinitialize git after scaffolding this repository. + # We use `git checkout --orphan` to create a branch in a git init-like state. + # By force pushing this as `main` we end up with a new clean git history. + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" && \ + git config --global user.name "github-actions[bot]" && \ + git checkout --orphan temp-branch && \ + git add . && \ + git commit -m 'Initial commit' && \ + git push origin temp-branch:main -f \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5b96365 --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib +build/ +*.c1z + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ +dist/ +cookiecutter-temp/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..ed85d9c --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# baton-template +A simple template for quickly building your own Baton connector. + +## How to use +1. Click on the "use this template" button, and you'll have a new repository for your connector. +2. Update `cookiecutter.json` with the appropriate configuration + ```json + { + "repo_owner": "conductorone", + "repo_name": "baton-example", + "name": "baton-example" + } + ``` +10. Commit this change, and a Github action will process the update and initialize the repo for you. diff --git a/cookiecutter.json b/cookiecutter.json new file mode 100644 index 0000000..15d031e --- /dev/null +++ b/cookiecutter.json @@ -0,0 +1,5 @@ +{ + "repo_owner": "conductorone", + "repo_name": "baton-example", + "name": "baton-example" +} \ No newline at end of file diff --git a/{{cookiecutter.name}}/.github/workflows/capabilities.yaml b/{{cookiecutter.name}}/.github/workflows/capabilities.yaml new file mode 100644 index 0000000..9728939 --- /dev/null +++ b/{{cookiecutter.name}}/.github/workflows/capabilities.yaml @@ -0,0 +1,34 @@ +name: Generate connector capabilities + +on: + push: + branches: + - main + +jobs: + calculate-capabilities: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + token: ${{ '{{ secrets.RELENG_GITHUB_TOKEN }}' }} + + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version-file: 'go.mod' + + - name: Build + run: go build -o connector ./cmd/{{ cookiecutter.name }} + + - name: Run and save output + run: ./connector capabilities > baton_capabilities.json + + - name: Commit changes + uses: EndBug/add-and-commit@v9 + with: + default_author: github_actions + message: 'Updating baton capabilities.' + add: 'baton_capabilities.json' \ No newline at end of file diff --git a/{{cookiecutter.name}}/.github/workflows/ci.yaml b/{{cookiecutter.name}}/.github/workflows/ci.yaml new file mode 100644 index 0000000..f9182e8 --- /dev/null +++ b/{{cookiecutter.name}}/.github/workflows/ci.yaml @@ -0,0 +1,38 @@ +name: ci +on: pull_request +jobs: + go-lint: + runs-on: ubuntu-latest + steps: + - name: Install Go + uses: actions/setup-go@v4 + with: + go-version: 1.20.x + - name: Checkout code + uses: actions/checkout@v3 + - name: Run linters + uses: golangci/golangci-lint-action@v3 + with: + version: latest + args: --timeout=3m + go-test: + strategy: + matrix: + go-version: [1.20.x] + platform: [ubuntu-latest] + runs-on: ${{ '{{ matrix.platform }}' }} + steps: + - name: Install Go + if: success() + uses: actions/setup-go@v4 + with: + go-version: ${{ '{{ matrix.go-version }}' }} + - name: Checkout code + uses: actions/checkout@v3 + - name: go tests + run: go test -v -covermode=count -json ./... > test.json + - name: annotate go tests + if: always() + uses: guyarb/golang-test-annotations@v0.5.1 + with: + test-results: test.json \ No newline at end of file diff --git a/{{cookiecutter.name}}/.github/workflows/main.yaml b/{{cookiecutter.name}}/.github/workflows/main.yaml new file mode 100644 index 0000000..24a1491 --- /dev/null +++ b/{{cookiecutter.name}}/.github/workflows/main.yaml @@ -0,0 +1,41 @@ +name: main ci +on: + push: + branches: + - main +jobs: + go-lint: + runs-on: ubuntu-latest + steps: + - name: Install Go + uses: actions/setup-go@v4 + with: + go-version: 1.20.x + - name: Checkout code + uses: actions/checkout@v3 + - name: Run linters + uses: golangci/golangci-lint-action@v3 + with: + version: latest + args: --timeout=3m + go-test: + strategy: + matrix: + go-version: [ 1.20.x ] + platform: [ ubuntu-latest ] + runs-on: ${{ '{{ matrix.platform }}' }} + steps: + - name: Install Go + if: success() + uses: actions/setup-go@v4 + with: + go-version: ${{ '{{ matrix.go-version }}' }} + - name: Checkout code + uses: actions/checkout@v3 + - name: go tests + run: go test -v -covermode=count -json ./... > test.json + - name: annotate go tests + if: always() + uses: guyarb/golang-test-annotations@v0.5.1 + with: + test-results: test.json \ No newline at end of file diff --git a/{{cookiecutter.name}}/.github/workflows/release.yaml b/{{cookiecutter.name}}/.github/workflows/release.yaml new file mode 100644 index 0000000..b568db6 --- /dev/null +++ b/{{cookiecutter.name}}/.github/workflows/release.yaml @@ -0,0 +1,60 @@ +name: Release + +on: + push: + tags: + - '*' + +jobs: + goreleaser: + runs-on: macos-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: 1.20.x + - name: Set up Gon + run: brew tap mitchellh/gon && brew install mitchellh/gon/gon + - name: Import Keychain Certs + uses: apple-actions/import-codesign-certs@v1 + with: + p12-file-base64: ${{ '{{ secrets.APPLE_SIGNING_KEY_P12 }}' }} + p12-password: ${{ '{{ secrets.APPLE_SIGNING_KEY_P12_PASSWORD }}' }} + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v4 + with: + version: latest + args: release --clean + env: + GITHUB_TOKEN: ${{ '{{ secrets.RELENG_GITHUB_TOKEN }}' }} + AC_PASSWORD: ${{ '{{ secrets.AC_PASSWORD }}' }} + goreleaser-docker: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: 1.20.x + - name: Docker Login + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ '{{ github.repository_owner }}' }} + password: ${{ '{{ secrets.RELENG_GITHUB_TOKEN }}' }} + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v4 + with: + version: latest + args: release --clean -f .goreleaser.docker.yaml + env: + GITHUB_TOKEN: ${{ '{{ secrets.RELENG_GITHUB_TOKEN }}' }} \ No newline at end of file diff --git a/{{cookiecutter.name}}/.gitignore b/{{cookiecutter.name}}/.gitignore new file mode 100644 index 0000000..b211806 --- /dev/null +++ b/{{cookiecutter.name}}/.gitignore @@ -0,0 +1,17 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib +*.c1z + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ +dist/ diff --git a/{{cookiecutter.name}}/.golangci.yml b/{{cookiecutter.name}}/.golangci.yml new file mode 100644 index 0000000..dcce381 --- /dev/null +++ b/{{cookiecutter.name}}/.golangci.yml @@ -0,0 +1,107 @@ +linters-settings: + exhaustive: + default-signifies-exhaustive: true + + gocritic: + # The list of supported checkers can be find in https://go-critic.github.io/overview. + settings: + underef: + # Whether to skip (*x).method() calls where x is a pointer receiver. + skipRecvDeref: false + + govet: + enable-all: true + disable: + - fieldalignment # too strict + - shadow # complains too much about shadowing errors. All research points to this being fine. + + nakedret: + max-func-lines: 0 + + nolintlint: + allow-no-explanation: [ forbidigo, tracecheck, gomnd, gochecknoinits, makezero ] + require-explanation: true + require-specific: true + + revive: + ignore-generated-header: true + severity: error + rules: + - name: atomic + - name: line-length-limit + arguments: [ 200 ] + # These are functions that we use without checking the errors often. Most of these can't return an error even + # though they implement an interface that can. + - name: unhandled-error + arguments: + - fmt.Printf + - fmt.Println + - fmt.Fprintf + - fmt.Fprintln + - os.Stderr.Sync + - sb.WriteString + - buf.WriteString + - hasher.Write + - os.Setenv + - os.RemoveAll + - name: var-naming + arguments: [["ID", "URL", "HTTP", "API"], []] + + tenv: + all: true + + varcheck: + exported-fields: false # this appears to improperly detect exported variables as unused when they are used from a package with the same name + + +linters: + disable-all: true + enable: + - deadcode # Finds unused code + - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases + - gosimple # Linter for Go source code that specializes in simplifying a code + - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string + - ineffassign # Detects when assignments to existing variables are not used + - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks + - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code + - unused # Checks Go code for unused constants, variables, functions and types + - varcheck # Finds unused global variables and constants + - asasalint # Check for pass []any as any in variadic func(...any) + - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers + - bidichk # Checks for dangerous unicode character sequences + - bodyclose # checks whether HTTP response body is closed successfully + - durationcheck # check for two durations multiplied together + - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. + - execinquery # execinquery is a linter about query string checker in Query function which reads your Go src files and warning it finds + - exhaustive # check exhaustiveness of enum switch statements + - exportloopref # checks for pointers to enclosing loop variables + - forbidigo # Forbids identifiers + - gochecknoinits # Checks that no init functions are present in Go code + - goconst # Finds repeated strings that could be replaced by a constant + - gocritic # Provides diagnostics that check for bugs, performance and style issues. + - godot # Check if comments end in a period + - goimports # In addition to fixing imports, goimports also formats your code in the same style as gofmt. + - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. + - goprintffuncname # Checks that printf-like functions are named with f at the end + - gosec # Inspects source code for security problems + - nakedret # Finds naked returns in functions greater than a specified function length + - nilerr # Finds the code that returns nil even if it checks that the error is not nil. + - noctx # noctx finds sending http request without context.Context + - nolintlint # Reports ill-formed or insufficient nolint directives + - nonamedreturns # Reports all named returns + - nosprintfhostport # Checks for misuse of Sprintf to construct a host with port in a URL. + - predeclared # find code that shadows one of Go's predeclared identifiers + - revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint. + - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 + - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes + - unconvert # Remove unnecessary type conversions + - usestdlibvars # detect the possibility to use variables/constants from the Go standard library + - whitespace # Tool for detection of leading and trailing whitespace + +issues: + max-same-issues: 50 + + exclude-rules: + # Don't require TODO comments to end in a period + - source: "(TODO)" + linters: [ godot ] diff --git a/{{cookiecutter.name}}/.gon-amd64.json b/{{cookiecutter.name}}/.gon-amd64.json new file mode 100644 index 0000000..236d0ee --- /dev/null +++ b/{{cookiecutter.name}}/.gon-amd64.json @@ -0,0 +1,14 @@ +{ + "source": ["./dist/macos-amd64_darwin_amd64_v1/{{ cookiecutter.name }}"], + "bundle_id": "com.conductorone.{{ cookiecutter.name }}", + "apple_id": { + "username" : "justin.gallardo@conductorone.com", + "password": "@env:AC_PASSWORD" + }, + "sign": { + "application_identity": "Developer ID Application: Justin Gallardo (858DKH55XL)" + }, + "zip" :{ + "output_path": "./dist/{{ cookiecutter.name }}-darwin-amd64.signed.zip" + } +} \ No newline at end of file diff --git a/{{cookiecutter.name}}/.gon-arm64.json b/{{cookiecutter.name}}/.gon-arm64.json new file mode 100644 index 0000000..c99bb1a --- /dev/null +++ b/{{cookiecutter.name}}/.gon-arm64.json @@ -0,0 +1,14 @@ +{ + "source": ["./dist/macos-arm64_darwin_arm64/{{ cookiecutter.name }}"], + "bundle_id": "com.conductorone.{{ cookiecutter.name }}", + "apple_id": { + "username" : "justin.gallardo@conductorone.com", + "password": "@env:AC_PASSWORD" + }, + "sign": { + "application_identity": "Developer ID Application: Justin Gallardo (858DKH55XL)" + }, + "zip" :{ + "output_path": "./dist/{{ cookiecutter.name }}-darwin-arm64.signed.zip" + } +} \ No newline at end of file diff --git a/{{cookiecutter.name}}/.goreleaser.docker.yaml b/{{cookiecutter.name}}/.goreleaser.docker.yaml new file mode 100644 index 0000000..6691707 --- /dev/null +++ b/{{cookiecutter.name}}/.goreleaser.docker.yaml @@ -0,0 +1,54 @@ +project_name: {{ cookiecutter.name }} +builds: + - binary: {{ cookiecutter.name }} + env: + - CGO_ENABLED=0 + id: linux + main: ./cmd/{{ cookiecutter.name }} + goos: + - linux + goarch: + - amd64 + - arm64 +dockers: + - use: buildx + goos: linux + goarch: amd64 + image_templates: + - "ghcr.io/{{ cookiecutter.repo_owner }}/{{ cookiecutter.name }}:{{ '{{ .Version }}' }}-amd64" + - "ghcr.io/{{ cookiecutter.repo_owner }}/{{ cookiecutter.name }}:latest-amd64" + build_flag_templates: + - "--platform=linux/amd64" + - "--label=org.opencontainers.image.created={{ '{{.Date}}' }}" + - "--label=org.opencontainers.image.title={{ cookiecutter.name }}" + - "--label=org.opencontainers.image.revision={{ '{{.FullCommit}}' }}" + - "--label=org.opencontainers.image.version={{ '{{.Version}}' }}" + - "--label=org.opencontainers.image.source=https://github.com/{{ cookiecutter.repo_owner }}/{{ cookiecutter.repo_name }}" + - use: buildx + goos: linux + goarch: arm64 + image_templates: + - "ghcr.io/{{ cookiecutter.repo_owner }}/{{ cookiecutter.name }}:{{ '{{ .Version }}' }}-arm64" + - "ghcr.io/{{ cookiecutter.repo_owner }}/{{ cookiecutter.name }}:latest-arm64" + build_flag_templates: + - "--platform=linux/arm64/v8" + - "--label=org.opencontainers.image.created={{ '{{.Date}}' }}" + - "--label=org.opencontainers.image.title={{ cookiecutter.name }}" + - "--label=org.opencontainers.image.revision={{ '{{.FullCommit}}' }}" + - "--label=org.opencontainers.image.version={{ '{{.Version}}' }}" + - "--label=org.opencontainers.image.source=https://github.com/{{ cookiecutter.repo_owner }}/{{ cookiecutter.repo_name }}" +docker_manifests: + - name_template: ghcr.io/{{ cookiecutter.repo_owner }}/{{ cookiecutter.name }}:{{ '{{ .Version }}' }} + image_templates: + - ghcr.io/{{ cookiecutter.repo_owner }}/{{ cookiecutter.name }}:{{ '{{ .Version }}' }}-amd64 + - ghcr.io/{{ cookiecutter.repo_owner }}/{{ cookiecutter.name }}:{{ '{{ .Version }}' }}-arm64 + - name_template: ghcr.io/{{ cookiecutter.repo_owner }}/{{ cookiecutter.name }}:latest + image_templates: + - ghcr.io/{{ cookiecutter.repo_owner }}/{{ cookiecutter.name }}:latest-amd64 + - ghcr.io/{{ cookiecutter.repo_owner }}/{{ cookiecutter.name }}:latest-arm64 +checksum: + disable: true +release: + disable: true +changelog: + skip: true \ No newline at end of file diff --git a/{{cookiecutter.name}}/.goreleaser.yaml b/{{cookiecutter.name}}/.goreleaser.yaml new file mode 100644 index 0000000..1c9b784 --- /dev/null +++ b/{{cookiecutter.name}}/.goreleaser.yaml @@ -0,0 +1,83 @@ +project_name: {{ cookiecutter.name }} +builds: + - binary: {{ cookiecutter.name }} + env: + - CGO_ENABLED=0 + id: linux + main: ./cmd/{{ cookiecutter.name }} + goos: + - linux + goarch: + - amd64 + - arm64 + - binary: {{ cookiecutter.name }} + env: + - CGO_ENABLED=0 + id: macos-amd64 + main: ./cmd/{{ cookiecutter.name }} + goos: + - darwin + goarch: + - amd64 + hooks: + post: + - gon .gon-amd64.json + - mv dist/{{ cookiecutter.name }}-darwin-amd64.signed.zip dist/macos-amd64_darwin_amd64_v1/{{ cookiecutter.name }} + - binary: {{ cookiecutter.name }} + env: + - CGO_ENABLED=0 + id: macos-arm64 + main: ./cmd/{{ cookiecutter.name }} + goos: + - darwin + goarch: + - arm64 + hooks: + post: + - gon .gon-arm64.json + - mv dist/{{ cookiecutter.name }}-darwin-arm64.signed.zip dist/macos-arm64_darwin_arm64/{{ cookiecutter.name }} +archives: + - id: linux-archive + builds: + - linux + format: tar.gz + name_template: {{ '"{{ .ProjectName }}-v{{ .Version }}-{{ .Os }}-{{ .Arch }}"' }} + files: + - none* + - id: darwin-archive + builds: + - macos-amd64 + - macos-arm64 + format: binary + name_template: {{ '"{{ .ProjectName }}-v{{ .Version }}-{{ .Os }}-{{ .Arch }}.zip"' }} + files: + - none* +release: + ids: + - linux-archive + - darwin-archive +snapshot: + name_template: {{ '"{{ incpatch .Version }}-dev"' }} +checksum: + ids: + - linux-archive + extra_files: + - glob: ./dist/*-darwin-amd64.zip + - glob: ./dist/*-darwin-arm64.zip +brews: + - tap: + owner: conductorone + name: homebrew-baton + folder: Formula + homepage: https://conductorone.com + test: | + system "#{bin}/{{ cookiecutter.name }} -v" + install: |- + bin.install "{{ cookiecutter.name }}" +changelog: + filters: + exclude: + - '^docs:' + - typo + - lint + - Merge pull request diff --git a/{{cookiecutter.name}}/Dockerfile b/{{cookiecutter.name}}/Dockerfile new file mode 100644 index 0000000..a296a64 --- /dev/null +++ b/{{cookiecutter.name}}/Dockerfile @@ -0,0 +1,3 @@ +FROM gcr.io/distroless/static-debian11:nonroot +ENTRYPOINT ["/{{ cookiecutter.name }}"] +COPY {{ cookiecutter.name }} / \ No newline at end of file diff --git a/{{cookiecutter.name}}/Makefile b/{{cookiecutter.name}}/Makefile new file mode 100644 index 0000000..4728cd1 --- /dev/null +++ b/{{cookiecutter.name}}/Makefile @@ -0,0 +1,28 @@ +GOOS = $(shell go env GOOS) +GOARCH = $(shell go env GOARCH) +BUILD_DIR = dist/${GOOS}_${GOARCH} + +ifeq ($(GOOS),windows) +OUTPUT_PATH = ${BUILD_DIR}/{{ cookiecutter.name }}.exe +else +OUTPUT_PATH = ${BUILD_DIR}/{{ cookiecutter.name }} +endif + +.PHONY: build +build: + go build -o ${OUTPUT_PATH} ./cmd/{{ cookiecutter.name }} + +.PHONY: update-deps +update-deps: + go get -d -u ./... + go mod tidy -v + go mod vendor + +.PHONY: add-dep +add-dep: + go mod tidy -v + go mod vendor + +.PHONY: lint +lint: + golangci-lint run diff --git a/{{cookiecutter.name}}/README.md b/{{cookiecutter.name}}/README.md new file mode 100644 index 0000000..6af6575 --- /dev/null +++ b/{{cookiecutter.name}}/README.md @@ -0,0 +1,3 @@ +# {{ cookiecutter.name }} +Welcome to your new connector! To start out, you will want to update the dependencies. +Do this by running `make update-deps`. \ No newline at end of file diff --git a/{{cookiecutter.name}}/cmd/{{cookiecutter.name}}/config.go b/{{cookiecutter.name}}/cmd/{{cookiecutter.name}}/config.go new file mode 100644 index 0000000..bdc0b65 --- /dev/null +++ b/{{cookiecutter.name}}/cmd/{{cookiecutter.name}}/config.go @@ -0,0 +1,17 @@ +package main + +import ( + "context" + + "github.com/conductorone/baton-sdk/pkg/cli" +) + +// config defines the external configuration required for the connector to run. +type config struct { + cli.BaseConfig `mapstructure:",squash"` // Puts the base config options in the same place as the connector options +} + +// validateConfig is run after the configuration is loaded, and should return an error if it isn't valid. +func validateConfig(ctx context.Context, cfg *config) error { + return nil +} diff --git a/{{cookiecutter.name}}/cmd/{{cookiecutter.name}}/main.go b/{{cookiecutter.name}}/cmd/{{cookiecutter.name}}/main.go new file mode 100644 index 0000000..bafad1f --- /dev/null +++ b/{{cookiecutter.name}}/cmd/{{cookiecutter.name}}/main.go @@ -0,0 +1,54 @@ +package main + +import ( + "context" + "fmt" + "os" + + "github.com/conductorone/baton-sdk/pkg/cli" + "github.com/conductorone/baton-sdk/pkg/connectorbuilder" + "github.com/conductorone/baton-sdk/pkg/types" + "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" + "go.uber.org/zap" + + "github.com/{{ cookiecutter.repo_owner }}/{{ cookiecutter.repo_name }}/pkg/connector" +) + +var version = "dev" + +func main() { + ctx := context.Background() + + cfg := &config{} + cmd, err := cli.NewCmd(ctx, "{{ cookiecutter.name }}", cfg, validateConfig, getConnector) + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(1) + } + + cmd.Version = version + + err = cmd.Execute() + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(1) + } +} + +func getConnector(ctx context.Context, cfg *config) (types.ConnectorServer, error) { + l := ctxzap.Extract(ctx) + + cb, err := connector.New(ctx) + if err != nil { + l.Error("error creating connector", zap.Error(err)) + return nil, err + } + + c, err := connectorbuilder.NewConnector(ctx, cb) + if err != nil { + l.Error("error creating connector", zap.Error(err)) + return nil, err + } + + return c, nil +} diff --git a/{{cookiecutter.name}}/pkg/connector/connector.go b/{{cookiecutter.name}}/pkg/connector/connector.go new file mode 100644 index 0000000..1b6ce33 --- /dev/null +++ b/{{cookiecutter.name}}/pkg/connector/connector.go @@ -0,0 +1,44 @@ +package connector + +import ( + "context" + "io" + + v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" + "github.com/conductorone/baton-sdk/pkg/annotations" + "github.com/conductorone/baton-sdk/pkg/connectorbuilder" +) + +type Connector struct{} + +// ResourceSyncers returns a ResourceSyncer for each resource type that should be synced from the upstream service. +func (d *Connector) ResourceSyncers(ctx context.Context) []connectorbuilder.ResourceSyncer { + return []connectorbuilder.ResourceSyncer{ + newUserBuilder(), + } +} + +// Asset takes an input AssetRef and attempts to fetch it using the connector's authenticated http client +// It streams a response, always starting with a metadata object, following by chunked payloads for the asset. +func (d *Connector) Asset(ctx context.Context, asset *v2.AssetRef) (string, io.ReadCloser, error) { + return "", nil, nil +} + +// Metadata returns metadata about the connector. +func (d *Connector) Metadata(ctx context.Context) (*v2.ConnectorMetadata, error) { + return &v2.ConnectorMetadata{ + DisplayName: "My Baton Connector", + Description: "The template implementation of a baton connector", + }, nil +} + +// Validate is called to ensure that the connector is properly configured. It should exercise any API credentials +// to be sure that they are valid. +func (d *Connector) Validate(ctx context.Context) (annotations.Annotations, error) { + return nil, nil +} + +// New returns a new instance of the connector. +func New(ctx context.Context) (*Connector, error) { + return &Connector{}, nil +} diff --git a/{{cookiecutter.name}}/pkg/connector/resource_types.go b/{{cookiecutter.name}}/pkg/connector/resource_types.go new file mode 100644 index 0000000..d931d9a --- /dev/null +++ b/{{cookiecutter.name}}/pkg/connector/resource_types.go @@ -0,0 +1,12 @@ +package connector + +import ( + v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" +) + +// The user resource type is for all user objects from the database. +var userResourceType = &v2.ResourceType{ + Id: "user", + DisplayName: "User", + Traits: []v2.ResourceType_Trait{v2.ResourceType_TRAIT_USER}, +} diff --git a/{{cookiecutter.name}}/pkg/connector/users.go b/{{cookiecutter.name}}/pkg/connector/users.go new file mode 100644 index 0000000..d1f0588 --- /dev/null +++ b/{{cookiecutter.name}}/pkg/connector/users.go @@ -0,0 +1,35 @@ +package connector + +import ( + "context" + + v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" + "github.com/conductorone/baton-sdk/pkg/annotations" + "github.com/conductorone/baton-sdk/pkg/pagination" +) + +type userBuilder struct{} + +func (o *userBuilder) ResourceType(ctx context.Context) *v2.ResourceType { + return userResourceType +} + +// List returns all the users from the database as resource objects. +// Users include a UserTrait because they are the 'shape' of a standard user. +func (o *userBuilder) List(ctx context.Context, parentResourceID *v2.ResourceId, pToken *pagination.Token) ([]*v2.Resource, string, annotations.Annotations, error) { + return nil, "", nil, nil +} + +// Entitlements always returns an empty slice for users. +func (o *userBuilder) Entitlements(_ context.Context, resource *v2.Resource, _ *pagination.Token) ([]*v2.Entitlement, string, annotations.Annotations, error) { + return nil, "", nil, nil +} + +// Grants always returns an empty slice for users since they don't have any entitlements. +func (o *userBuilder) Grants(ctx context.Context, resource *v2.Resource, pToken *pagination.Token) ([]*v2.Grant, string, annotations.Annotations, error) { + return nil, "", nil, nil +} + +func newUserBuilder() *userBuilder { + return &userBuilder{} +}