Skip to content

Commit

Permalink
Add support for import/export project (#338)
Browse files Browse the repository at this point in the history
  • Loading branch information
shilgapira authored Jan 26, 2024
1 parent e834841 commit 9a402e9
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 6 deletions.
20 changes: 16 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1196,17 +1196,29 @@ res = descope_client.mgmt.authz.get_modified()

### Manage Project

You can change the project name, as well as to clone the current project to a new one.
You can change the project name, as well as clone the current project to
create a new one.

```python

# Change the project name
descope.client.mgmt.project.change_name("new-project-name")
descope_client.mgmt.project.change_name("new-project-name")

# Clone the current project, including its settings and configurations.
# Note that this action is supported only with a pro license or above.
# Users, tenants and access keys are not cloned.
clone_resp = descope.client.mgmt.project.clone("new-project-name")
clone_resp = descope_client.mgmt.project.clone("new-project-name")
```

You can manage your project's settings and configurations by exporting your
project's environment. You can also import previously exported data into
the same project or a different one.

```python
# Exports the current state of the project
export = descope_client.mgmt.project.export_project()

# Import the previously exported data into the current project
descope_client.mgmt.project.import_project(export)
```

### Manage SSO Applications
Expand Down
2 changes: 2 additions & 0 deletions descope/management/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ class MgmtV1:
# Project
project_update_name = "/v1/mgmt/project/update/name"
project_clone = "/v1/mgmt/project/clone"
project_export = "/v1/mgmt/project/export"
project_import = "/v1/mgmt/project/import"


class AssociatedTenant:
Expand Down
53 changes: 51 additions & 2 deletions descope/management/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ def clone(
Args:
name (str): The new name for the project.
tag(str): Optional tag for the project. Currently, only the "production" tag is supported.
tag (str): Optional tag for the project. Currently, only the "production" tag is supported.
Return value (dict):
Return value (dict):
Return dict Containing the new project details (name, id, and tag).
Raise:
Expand All @@ -54,3 +54,52 @@ def clone(
pswd=self._auth.management_key,
)
return response.json()

def export_project(
self,
):
"""
Exports all settings and configurations for a project and returns the
raw JSON files response as a dictionary.
- This action is supported only with a pro license or above.
- Users, tenants and access keys are not cloned.
- Secrets, keys and tokens are not stripped from the exported data.
Return value (dict):
Return dict Containing the exported JSON files payload.
Raise:
AuthException: raised if export operation fails
"""
response = self._auth.do_post(
MgmtV1.project_export,
{},
pswd=self._auth.management_key,
)
return response.json()["files"]

def import_project(
self,
files: dict,
):
"""
Imports all settings and configurations for a project overriding any current
configuration.
- This action is supported only with a pro license or above.
- Secrets, keys and tokens are not overwritten unless overwritten in the input.
Args:
files (dict): The raw JSON dictionary of files, in the same format as the one
returned by calls to export.
Raise:
AuthException: raised if import operation fails
"""
response = self._auth.do_post(
MgmtV1.project_import,
{
"files": files,
},
pswd=self._auth.management_key,
)
return
89 changes: 89 additions & 0 deletions tests/management/test_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,92 @@ def test_clone(self):
verify=True,
timeout=DEFAULT_TIMEOUT_SECONDS,
)

def test_export_project(self):
client = DescopeClient(
self.dummy_project_id,
self.public_key_dict,
False,
self.dummy_management_key,
)

# Test failed flows
with patch("requests.post") as mock_post:
mock_post.return_value.ok = False
self.assertRaises(
AuthException,
client.mgmt.project.export_project,
)

# Test success flow
with patch("requests.post") as mock_post:
network_resp = mock.Mock()
network_resp.ok = True
network_resp.json.return_value = json.loads(
"""
{
"files":{"foo":"bar"}
}
"""
)
mock_post.return_value = network_resp
resp = client.mgmt.project.export_project()
self.assertIsNotNone(resp)
self.assertEqual(resp["foo"], "bar")
mock_post.assert_called_with(
f"{common.DEFAULT_BASE_URL}{MgmtV1.project_export}",
headers={
**common.default_headers,
"Authorization": f"Bearer {self.dummy_project_id}:{self.dummy_management_key}",
},
params=None,
json={},
allow_redirects=False,
verify=True,
timeout=DEFAULT_TIMEOUT_SECONDS,
)

def test_import_project(self):
client = DescopeClient(
self.dummy_project_id,
self.public_key_dict,
False,
self.dummy_management_key,
)

# Test failed flows
with patch("requests.post") as mock_post:
mock_post.return_value.ok = False
self.assertRaises(
AuthException,
client.mgmt.project.import_project,
{
"foo": "bar",
},
)

# Test success flow
with patch("requests.post") as mock_post:
network_resp = mock.Mock()
network_resp.ok = True
mock_post.return_value = network_resp
files = {
"foo": "bar",
}
client.mgmt.project.import_project(files)
mock_post.assert_called_with(
f"{common.DEFAULT_BASE_URL}{MgmtV1.project_import}",
headers={
**common.default_headers,
"Authorization": f"Bearer {self.dummy_project_id}:{self.dummy_management_key}",
},
params=None,
json={
"files": {
"foo": "bar",
},
},
allow_redirects=False,
verify=True,
timeout=DEFAULT_TIMEOUT_SECONDS,
)

0 comments on commit 9a402e9

Please sign in to comment.