Skip to content

Commit b527dab

Browse files
authored
Merge branch 'main' into feature/dsync-2746-add-email-attribute-to-directoryuser-in-python-sdk
2 parents 33c9827 + df3b2e5 commit b527dab

File tree

13 files changed

+151
-39
lines changed

13 files changed

+151
-39
lines changed

.github/workflows/coana-analysis.yml

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,27 @@ name: Coana Vulnerability Analysis
22

33
on:
44
schedule:
5-
# every day at 12 AM
6-
- cron: '0 0 * * *'
5+
- cron: "0 3 * * *" # every day at 3 AM
76
workflow_dispatch:
87
inputs:
98
tags:
10-
description: 'Manually run vulnerability analysis'
9+
description: "Manually run vulnerability analysis"
10+
# Required by the return-dispatch action
11+
distinct_id:
1112

1213
jobs:
1314
coana-vulnerability-analysis:
1415
runs-on: ubuntu-latest
15-
timeout-minutes: 60
1616

1717
steps:
1818
- name: Checkout code
1919
uses: actions/checkout@v4
20+
2021
- name: Run Coana CLI
2122
id: coana-cli
22-
run: |
23-
npx @coana-tech/cli run . \
24-
--api-key ${{ secrets.COANA_API_KEY }} \
25-
--repo-url https://github.com/${{github.repository}}
23+
uses: docker://coana/coana:latest
24+
with:
25+
args: |
26+
coana run . \
27+
--api-key ${{ secrets.COANA_API_KEY }} \
28+
--repo-url https://github.com/${{github.repository}}

.github/workflows/coana-guardrail.yml

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,27 @@ on: pull_request
55
jobs:
66
guardrail:
77
runs-on: ubuntu-latest
8-
timeout-minutes: 15
8+
99
steps:
10-
- name: Get changed files
11-
id: changed-files
12-
uses: tj-actions/changed-files@v44
13-
with:
14-
separator: ' '
15-
1610
- name: Checkout the ${{github.base_ref}} branch
1711
uses: actions/checkout@v4
1812
with:
1913
ref: ${{github.base_ref}} # checkout the base branch (usually master/main).
20-
14+
15+
- name: Fetch the PR branch
16+
run: |
17+
git fetch ${{ github.event.pull_request.head.repo.clone_url }} ${{ github.head_ref }}:${{ github.head_ref }} --depth=1
18+
19+
- name: Get list of changed files relative to the main/master branch
20+
id: changed-files
21+
run: |
22+
echo "all_changed_files=$(git diff --name-only ${{ github.base_ref }} ${{ github.head_ref }} | tr '\n' ' ')" >> $GITHUB_OUTPUT
23+
2124
- name: Use Node.js 20.x
2225
uses: actions/setup-node@v4
2326
with:
2427
node-version: 20.x
25-
28+
2629
- name: Run Coana on the ${{github.base_ref}} branch
2730
run: |
2831
npx @coana-tech/cli run . \
@@ -31,16 +34,20 @@ jobs:
3134
-o /tmp/main-branch \
3235
--changed-files ${{ steps.changed-files.outputs.all_changed_files }} \
3336
--lightweight-reachability \
34-
35-
# Reset file permissions changed by Coana CLI.
37+
38+
# Reset file permissions.
39+
# This is necessary because the Coana CLI may add
40+
# new files with root ownership since it's using docker.
41+
# These files will not be deleted by the clean step in checkout
42+
# if the permissions are not reset.
3643
- name: Reset file permissions
3744
run: sudo chown -R $USER:$USER .
38-
45+
3946
- name: Checkout the current branch
4047
uses: actions/checkout@v4
4148
with:
4249
clean: true
43-
50+
4451
- name: Run Coana on the current branch
4552
run: |
4653
npx @coana-tech/cli run . \
@@ -49,12 +56,12 @@ jobs:
4956
-o /tmp/current-branch \
5057
--changed-files ${{ steps.changed-files.outputs.all_changed_files }} \
5158
--lightweight-reachability \
52-
59+
5360
- name: Run Report Comparison
5461
run: |
5562
npx @coana-tech/cli compare-reports \
5663
--api-key ${{ secrets.COANA_API_KEY || 'api-key-unavailable' }} \
5764
/tmp/main-branch/coana-report.json \
5865
/tmp/current-branch/coana-report.json
5966
env:
60-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
67+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

tests/test_organizations.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,20 +109,20 @@ def test_get_organization(
109109
assert request_kwargs["method"] == "get"
110110
assert request_kwargs["url"].endswith("/organizations/organization_id")
111111

112-
def test_get_organization_by_lookup_key(
112+
def test_get_organization_by_external_id(
113113
self, mock_organization, capture_and_mock_http_client_request
114114
):
115115
request_kwargs = capture_and_mock_http_client_request(
116116
self.http_client, mock_organization, 200
117117
)
118118

119119
organization = syncify(
120-
self.organizations.get_organization_by_lookup_key(lookup_key="test")
120+
self.organizations.get_organization_by_external_id(external_id="test")
121121
)
122122

123123
assert organization.dict() == mock_organization
124124
assert request_kwargs["method"] == "get"
125-
assert request_kwargs["url"].endswith("/organizations/by_lookup_key/test")
125+
assert request_kwargs["url"].endswith("/organizations/external_id/test")
126126

127127
def test_create_organization_with_domain_data(
128128
self, mock_organization, capture_and_mock_http_client_request

tests/test_user_management.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,27 @@ def test_get_user(self, mock_user, capture_and_mock_http_client_request):
392392
assert user.profile_picture_url == "https://example.com/profile-picture.jpg"
393393
assert user.last_sign_in_at == "2021-06-25T19:07:33.155Z"
394394

395+
def test_get_user_by_external_id(
396+
self, mock_user, capture_and_mock_http_client_request
397+
):
398+
request_kwargs = capture_and_mock_http_client_request(
399+
self.http_client, mock_user, 200
400+
)
401+
402+
external_id = "external-id"
403+
user = syncify(
404+
self.user_management.get_user_by_external_id(external_id=external_id)
405+
)
406+
407+
assert request_kwargs["url"].endswith(
408+
f"user_management/users/external_id/{external_id}"
409+
)
410+
assert request_kwargs["method"] == "get"
411+
assert user.id == "user_01H7ZGXFP5C6BBQY6Z7277ZCT0"
412+
assert user.profile_picture_url == "https://example.com/profile-picture.jpg"
413+
assert user.last_sign_in_at == "2021-06-25T19:07:33.155Z"
414+
assert user.metadata == mock_user["metadata"]
415+
395416
def test_list_users_auto_pagination(
396417
self,
397418
mock_users_multiple_pages,

tests/utils/fixtures/mock_organization.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ def __init__(self, id):
2222
domain="example.io",
2323
)
2424
],
25+
metadata={"key": "value"},
2526
)

tests/utils/fixtures/mock_user.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ def __init__(self, id):
1717
last_sign_in_at="2021-06-25T19:07:33.155Z",
1818
created_at=now,
1919
updated_at=now,
20+
metadata={"key": "value"},
2021
)

workos/__about__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
__package_url__ = "https://github.com/workos-inc/workos-python"
1414

15-
__version__ = "5.15.0"
15+
__version__ = "5.16.0"
1616

1717
__author__ = "WorkOS"
1818

workos/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
from workos.client import SyncClient as WorkOSClient
22
from workos.async_client import AsyncClient as AsyncWorkOSClient
3+
4+
__all__ = ["WorkOSClient", "AsyncWorkOSClient"]

workos/organizations.py

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from typing import Optional, Protocol, Sequence
22

3+
from workos.types.metadata import Metadata
34
from workos.types.organizations.domain_data_input import DomainDataInput
45
from workos.types.organizations.list_filters import OrganizationListFilters
56
from workos.types.roles.role import RoleList
@@ -60,13 +61,13 @@ def get_organization(self, organization_id: str) -> SyncOrAsync[Organization]:
6061
"""
6162
...
6263

63-
def get_organization_by_lookup_key(
64-
self, lookup_key: str
64+
def get_organization_by_external_id(
65+
self, external_id: str
6566
) -> SyncOrAsync[Organization]:
66-
"""Gets details for a single Organization by lookup key
67+
"""Gets details for a single Organization by external id
6768
6869
Args:
69-
lookup_key (str): Organization's lookup key
70+
external_id (str): Organization's external id
7071
7172
Returns:
7273
Organization: Organization response from WorkOS
@@ -79,6 +80,8 @@ def create_organization(
7980
name: str,
8081
domain_data: Optional[Sequence[DomainDataInput]] = None,
8182
idempotency_key: Optional[str] = None,
83+
external_id: Optional[str] = None,
84+
metadata: Optional[Metadata] = None,
8285
) -> SyncOrAsync[Organization]:
8386
"""Create an organization
8487
@@ -98,6 +101,8 @@ def update_organization(
98101
organization_id: str,
99102
name: Optional[str] = None,
100103
domain_data: Optional[Sequence[DomainDataInput]] = None,
104+
external_id: Optional[str] = None,
105+
metadata: Optional[Metadata] = None,
101106
) -> SyncOrAsync[Organization]:
102107
"""Update an organization
103108
@@ -125,7 +130,6 @@ def delete_organization(self, organization_id: str) -> SyncOrAsync[None]:
125130

126131

127132
class Organizations(OrganizationsModule):
128-
129133
_http_client: SyncHTTPClient
130134

131135
def __init__(self, http_client: SyncHTTPClient):
@@ -167,9 +171,9 @@ def get_organization(self, organization_id: str) -> Organization:
167171

168172
return Organization.model_validate(response)
169173

170-
def get_organization_by_lookup_key(self, lookup_key: str) -> Organization:
174+
def get_organization_by_external_id(self, external_id: str) -> Organization:
171175
response = self._http_client.request(
172-
"organizations/by_lookup_key/{lookup_key}".format(lookup_key=lookup_key),
176+
"organizations/external_id/{external_id}".format(external_id=external_id),
173177
method=REQUEST_METHOD_GET,
174178
)
175179

@@ -181,6 +185,8 @@ def create_organization(
181185
name: str,
182186
domain_data: Optional[Sequence[DomainDataInput]] = None,
183187
idempotency_key: Optional[str] = None,
188+
external_id: Optional[str] = None,
189+
metadata: Optional[Metadata] = None,
184190
) -> Organization:
185191
headers = {}
186192
if idempotency_key:
@@ -190,6 +196,8 @@ def create_organization(
190196
"name": name,
191197
"domain_data": domain_data,
192198
"idempotency_key": idempotency_key,
199+
"external_id": external_id,
200+
"metadata": metadata,
193201
}
194202

195203
response = self._http_client.request(
@@ -208,11 +216,15 @@ def update_organization(
208216
name: Optional[str] = None,
209217
domain_data: Optional[Sequence[DomainDataInput]] = None,
210218
stripe_customer_id: Optional[str] = None,
219+
external_id: Optional[str] = None,
220+
metadata: Optional[Metadata] = None,
211221
) -> Organization:
212222
json = {
213223
"name": name,
214224
"domain_data": domain_data,
215225
"stripe_customer_id": stripe_customer_id,
226+
"external_id": external_id,
227+
"metadata": metadata,
216228
}
217229

218230
response = self._http_client.request(
@@ -237,7 +249,6 @@ def list_organization_roles(self, organization_id: str) -> RoleList:
237249

238250

239251
class AsyncOrganizations(OrganizationsModule):
240-
241252
_http_client: AsyncHTTPClient
242253

243254
def __init__(self, http_client: AsyncHTTPClient):
@@ -279,9 +290,9 @@ async def get_organization(self, organization_id: str) -> Organization:
279290

280291
return Organization.model_validate(response)
281292

282-
async def get_organization_by_lookup_key(self, lookup_key: str) -> Organization:
293+
async def get_organization_by_external_id(self, external_id: str) -> Organization:
283294
response = await self._http_client.request(
284-
"organizations/by_lookup_key/{lookup_key}".format(lookup_key=lookup_key),
295+
"organizations/external_id/{external_id}".format(external_id=external_id),
285296
method=REQUEST_METHOD_GET,
286297
)
287298

@@ -293,6 +304,8 @@ async def create_organization(
293304
name: str,
294305
domain_data: Optional[Sequence[DomainDataInput]] = None,
295306
idempotency_key: Optional[str] = None,
307+
external_id: Optional[str] = None,
308+
metadata: Optional[Metadata] = None,
296309
) -> Organization:
297310
headers = {}
298311
if idempotency_key:
@@ -302,6 +315,8 @@ async def create_organization(
302315
"name": name,
303316
"domain_data": domain_data,
304317
"idempotency_key": idempotency_key,
318+
"external_id": external_id,
319+
"metadata": metadata,
305320
}
306321

307322
response = await self._http_client.request(
@@ -320,11 +335,15 @@ async def update_organization(
320335
name: Optional[str] = None,
321336
domain_data: Optional[Sequence[DomainDataInput]] = None,
322337
stripe_customer_id: Optional[str] = None,
338+
external_id: Optional[str] = None,
339+
metadata: Optional[Metadata] = None,
323340
) -> Organization:
324341
json = {
325342
"name": name,
326343
"domain_data": domain_data,
327344
"stripe_customer_id": stripe_customer_id,
345+
"external_id": external_id,
346+
"metadata": metadata,
328347
}
329348

330349
response = await self._http_client.request(

workos/types/metadata.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from typing import Dict
2+
3+
4+
Metadata = Dict[str, str]

0 commit comments

Comments
 (0)