Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Major release 1.0.0 #52

Merged
merged 49 commits into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
87a384a
Introduce HTTP client and base URL as configuration options (#37)
LauraBeatris Oct 23, 2023
c0f69d8
Add livebook example (#38)
LauraBeatris Oct 24, 2023
28aef63
Introduce new SSO module and tests structure (#39)
LauraBeatris Nov 10, 2023
b9fc6d5
Refactor `Organizations` module (#44)
LauraBeatris Nov 23, 2023
ae8ed1d
Refactor `Portal` module (#45)
LauraBeatris Nov 23, 2023
7aac6e6
Refactor `Webhooks` module (#46)
LauraBeatris Nov 23, 2023
99c6543
Refactor `DirectorySync` module (#47)
LauraBeatris Nov 23, 2023
9abe151
Refactor `Passwordless` module (#48)
LauraBeatris Nov 23, 2023
71d990f
Add `Events` module (#49)
LauraBeatris Nov 23, 2023
dbb4d5b
Refactor `AuditLogs` module (#50)
LauraBeatris Nov 25, 2023
ccf44d4
Remove MFA module (#51)
LauraBeatris Nov 29, 2023
7286fe0
Add User Management API (#53)
LauraBeatris Dec 3, 2023
ef630a4
Add Domain Verification API (#55)
LauraBeatris Dec 4, 2023
c55e058
Update README.md
LauraBeatris Dec 4, 2023
cc51967
Format modules
LauraBeatris Dec 4, 2023
ebe557b
Fix `credo` issues
LauraBeatris Dec 4, 2023
0b63c43
Fix dialyzer issues
LauraBeatris Dec 4, 2023
d8bf9dd
Rename `DomainVerification` to `OrganizationDomains` (#56)
LauraBeatris Dec 4, 2023
9a69155
Remove `domain` option and add tests for error case
LauraBeatris Dec 4, 2023
3834a10
Update workflow
LauraBeatris Dec 4, 2023
2a9113b
Bump to `1.0.0`
LauraBeatris Dec 5, 2023
5a73356
Introduce Dialyzer artifacts
LauraBeatris Dec 5, 2023
3e329a5
Remove `:hackney`
LauraBeatris Dec 5, 2023
665346e
Update `elixirc_paths`
LauraBeatris Dec 5, 2023
71d5aa6
Rollback `elixirc_paths`
LauraBeatris Dec 5, 2023
4783777
Import `ExUnit.Assertions` on mock files
LauraBeatris Dec 5, 2023
955114c
Update `plug_crypto`
LauraBeatris Dec 5, 2023
4646295
Update `jason`
LauraBeatris Dec 5, 2023
4de9d26
Remove support for oldest OTP version
LauraBeatris Dec 5, 2023
f3bf834
Update `dialyxir`
LauraBeatris Dec 5, 2023
9d76be0
Remove enum constants
LauraBeatris Dec 5, 2023
b356821
Update mock API keys
LauraBeatris Dec 7, 2023
4a206eb
Add deprecated `MFA` API module (#58)
LauraBeatris Dec 9, 2023
0832165
Fix generate_link types
LauraBeatris Dec 9, 2023
f282315
Install `hackney`
LauraBeatris Dec 9, 2023
5791d9e
Fix query params
LauraBeatris Dec 10, 2023
ef821a6
Fix `list_groups` params
LauraBeatris Dec 10, 2023
b5fc11e
Fix `send_session` route from Magic Link
LauraBeatris Dec 12, 2023
c6d0342
Pass `code` as keyword argument
LauraBeatris Dec 12, 2023
0bf9094
Fix `challenge_factor`
LauraBeatris Dec 13, 2023
a697d93
Fix `get_organization` snippet from Livebook
LauraBeatris Dec 17, 2023
3397781
Add organization ID as parameter on `update_organization`
LauraBeatris Dec 17, 2023
996b410
Add `semaphore.yml`
LauraBeatris Dec 19, 2023
50549ea
Fix `WorkOS.Empty` struct
LauraBeatris Dec 19, 2023
28dc4f5
Fix `get_profile_and_token` snippet on Livebook
LauraBeatris Dec 19, 2023
69a3a4d
Fix `get_profile` Livebook snippet
LauraBeatris Dec 19, 2023
20de24e
Add experimental disclaimer back to README.md
LauraBeatris Dec 20, 2023
d6c95ee
Rollback semaphore
LauraBeatris Dec 20, 2023
2e5dc32
Rollback `elixir.yml` workflow due to branch policies
LauraBeatris Dec 20, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/elixir.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ jobs:
restore-keys: ${{ runner.os }}-mix-
- name: Install dependencies
run: mix deps.get
- name: Check format
run: mix format --check-formatted
- name: Check linter
run: mix credo --strict -a
- name: Run tests
Expand Down
101 changes: 101 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
name: Continuous Integration

on:
push:
branches:
- master
- release/**

pull_request:

env:
MIX_ENV: test

jobs:
test:
name: Test (Elixir ${{ matrix.elixir }}, OTP ${{ matrix.otp }})

runs-on: ubuntu-20.04
strategy:
matrix:
# https://hexdocs.pm/elixir/compatibility-and-deprecations.html#compatibility-between-elixir-and-erlang-otp
include:
# Newest supported Elixir/Erlang pair.
LauraBeatris marked this conversation as resolved.
Show resolved Hide resolved
- elixir: '1.15'
otp: '26.0'
lint: true
dialyzer: true

# One version before the last supported one.
- elixir: '1.14.5'
otp: '25.3'

steps:
- name: Check out this repository
uses: actions/checkout@v3

- name: Setup Elixir and Erlang
uses: erlef/setup-beam@v1
with:
elixir-version: ${{ matrix.elixir }}
otp-version: ${{ matrix.otp }}

# We need to manually restore and then save, so that we can save the "_build" directory
# *without* the Elixir compiled code in it.
- name: Restore Mix dependencies cache
uses: actions/cache/restore@v3
id: mix-deps-cache
with:
path: |
_build
deps
key: |
${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-mix-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }}
restore-keys: |
${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-mix-

- name: Install and compile Mix dependencies
if: steps.mix-deps-cache.outputs.cache-hit != 'true'
run: mix do deps.get, deps.compile

- name: Save Mix dependencies cache
uses: actions/cache/save@v3
if: steps.mix-deps-cache.outputs.cache-hit != 'true'
with:
path: |
_build
deps
key: |
${{ steps.mix-deps-cache.outputs.cache-primary-key }}

- name: Check formatting
if: matrix.lint
run: mix format --check-formatted

- name: Check compiler warnings
if: matrix.lint
run: mix compile --warnings-as-errors

- name: Run tests
run: mix test

- name: Retrieve PLT Cache
uses: actions/cache@v3
if: matrix.dialyzer
id: plt-cache
with:
path: plts
key: |
${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-plts-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }}
restore-keys: |
${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-plts-

- name: Create PLTs
if: steps.plt-cache.outputs.cache-hit != 'true' && matrix.dialyzer
run: |
mkdir -p plts
mix dialyzer --plt

- name: Run dialyzer
if: matrix.dialyzer
run: mix dialyzer --no-check --halt-exit-status
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ erl_crash.dump
# Ignore package tarball (built via "mix hex.build").
workos-*.tar


# Temporary files for e.g. tests
/tmp

# Dialyzer
/plts/*.plt
/plts/*.plt.hash
46 changes: 14 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,53 +14,35 @@ Add this package to the list of dependencies in your `mix.exs` file:

```ex
def deps do
[{:workos, "~> 0.4.0"}]
[{:workos, "~> 1.0.0"}]
end
```
The hex package can be found here: https://hex.pm/packages/workos

## Configuration

The WorkOS API relies on two configuration parameters, the `client_id` and the `api_key`. There are two ways to configure these values with this package.

### Recommended Method
In your `config/config.exs` file you can set the `:client_id` and `:api_key` scoped to `:workos` to be used globally by default across the SDK:
### Configure WorkOS API key & client ID on your app config

```ex
config :workos,
client_id: "project_12345"
api_key: "sk_12345",
config :workos, WorkOS.Client,
api_key: "sk_example_123456789",
client_id: "client_123456789"
```

Ideally, you should use environment variables to store protected keys like your `:api_key` like so:

```ex
config :workos,
client_id: System.get_env("WORKOS_CLIENT_ID"),
api_key: System.get_env("WORKOS_API_KEY")
```
The only required config option is `:api_key` and `:client_id`.

### Opts Method
Alternatively, you can override or avoid using these globally configured variables by passing a `:api_key` or `:client_id` directly to SDK methods via the optional `opts` parameter available on all methods:
By default, this library uses [Tesla](https://github.com/elixir-tesla/tesla) but it can be replaced via the `:client` option, according to the `WorkOS.Client` module behavior.

```ex
WorkOS.SSO.get_authorization_url(%{
connection: "<Connection ID>",
redirect_uri: "https://workos.com"
}, [
client_id: "project_12345",
api_key: "sk_12345"
])
```
This is great if you need to switch client IDs on the fly.
###

## SDK Versioning

For our SDKs WorkOS follows a Semantic Versioning process where all releases will have a version X.Y.Z (like 1.0.0) pattern wherein Z would be a bug fix (I.e. 1.0.1), Y would be a minor release (1.1.0) and X would be a major release (2.0.0). We permit any breaking changes to only be released in major versions and strongly recommend reading changelogs before making any major version upgrades.

## More Information

* [Single Sign-On Guide](https://workos.com/docs/sso/guide)
* [Directory Sync Guide](https://workos.com/docs/directory-sync/guide)
* [Admin Portal Guide](https://workos.com/docs/admin-portal/guide)
* [Magic Link Guide](https://workos.com/docs/magic-link/guide)
- [User Management Guide](https://workos.com/docs/user-management)
- [Single Sign-On Guide](https://workos.com/docs/sso/guide)
- [Directory Sync Guide](https://workos.com/docs/directory-sync/guide)
- [Admin Portal Guide](https://workos.com/docs/admin-portal/guide)
- [Magic Link Guide](https://workos.com/docs/magic-link/guide)
- [Domain Verification Guide](https://workos.com/docs/domain-verification/guide)
3 changes: 3 additions & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Config

import_config "#{Mix.env()}.exs"
5 changes: 5 additions & 0 deletions config/dev.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Config

config :workos, WorkOS.Client,
client_id: System.get_env("WORKOS_CLIENT_ID"),
api_key: System.get_env("WORKOS_API_KEY")
1 change: 1 addition & 0 deletions config/prod.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# This empty module is for configuration purposes
16 changes: 16 additions & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import Config

workos_api_key = System.get_env("WORKOS_API_KEY")
workos_client_id = System.get_env("WORKOS_CLIENT_ID")

case {workos_api_key, workos_client_id} do
{nil, nil} ->
config :tesla, adapter: Tesla.Mock

config :workos, WorkOS.Client,
api_key: "sk_example_123456789",
client_id: "client_123456789"

{api_key, client_id} ->
config :workos, WorkOS.Client, api_key: api_key, client_id: client_id
end
126 changes: 116 additions & 10 deletions lib/workos.ex
Original file line number Diff line number Diff line change
@@ -1,17 +1,123 @@
defmodule WorkOS do
@moduledoc """
Use the WorkOS module to authenticate your requests to the WorkOS API
Documentation for `WorkOS`.
"""

def host, do: Application.get_env(:workos, :host)
def base_url, do: "https://" <> Application.get_env(:workos, :host)
def adapter, do: Application.get_env(:workos, :adapter) || Tesla.Adapter.Hackney
@config_module WorkOS.Client

def api_key(opts \\ [])
def api_key(api_key: api_key), do: api_key
def api_key(_opts), do: Application.get_env(:workos, :api_key)
@type config() ::
list(
{:api_key, String.t()}
| {:client_id, String.t()}
| {:base_url, String.t()}
| {:client, atom()}
)

def client_id(opts \\ [])
def client_id(client_id: client_id), do: client_id
def client_id(_opts), do: Application.get_env(:workos, :client_id)
@doc """
Returns a WorkOS client.

Accepts a keyword list of config opts, though if omitted then it will attempt to load
them from the application environment.
"""
@spec client() :: WorkOS.Client.t()
@spec client(config()) :: WorkOS.Client.t()
def client(config \\ config()) do
WorkOS.Client.new(config)
end

@doc """
Loads config values from the application environment.

Config options are as follows:

```ex
config :workos, WorkOS.Client
api_key: "sk_123",
client_id: "project_123",
base_url: "https://api.workos.com",
client: WorkOs.Client.TeslaClient
```

The only required config option is `:api_key` and `:client_id`. If you would like to replace the
HTTP client used by WorkOS, configure the `:client` option. By default, this library
uses [Tesla](https://github.com/elixir-tesla/tesla), but changing it is as easy as
defining your own client module. See the `WorkOS.Client` module docs for more info.
"""
@spec config() :: config()
def config do
config =
Application.get_env(:workos, @config_module) ||
raise """
Missing client configuration for WorkOS.

Configure your WorkOS API key in one of your config files, for example:

config :workos, #{inspect(@config_module)}, api_key: "sk_123", client_id: "project_123"
"""

validate_config!(config)
end

@spec validate_config!(WorkOS.config()) :: WorkOS.config() | no_return()
defp validate_config!(config) do
Keyword.get(config, :api_key) ||
raise WorkOS.ApiKeyMissingError

Keyword.get(config, :client_id) ||
raise WorkOS.ClientIdMissingError

config
end

@doc """
Defines the WorkOS base API URL
"""
def default_base_url, do: "https://api.workos.com"

@doc """
Retrieves the WorkOS base URL from application config.
"""
@spec base_url() :: String.t()
def base_url do
case Application.get_env(:workos, @config_module) do
config when is_list(config) ->
Keyword.get(config, :base_url, default_base_url())

_ ->
default_base_url()
end
end

@doc """
Retrieves the WorkOS client ID from application config.
"""
@spec client_id() :: String.t()
def client_id do
case Application.get_env(:workos, @config_module) do
config when is_list(config) ->
Keyword.get(config, :client_id, nil)

_ ->
nil
end
end

@spec client_id(WorkOS.Client.t()) :: String.t()
def client_id(client) do
Map.get(client, :client_id)
end

@doc """
Retrieves the WorkOS API key from application config.
"""
@spec api_key() :: String.t()
def api_key do
WorkOS.config()
|> Keyword.get(:api_key)
end

@spec api_key(WorkOS.Client.t()) :: String.t()
def api_key(client) do
Map.get(client, :api_key)
end
end
Loading
Loading