Skip to content

Commit

Permalink
Add export and import project APIs (#94)
Browse files Browse the repository at this point in the history
  • Loading branch information
shilgapira authored Feb 6, 2024
1 parent ad4a631 commit af528c1
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 6 deletions.
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1181,7 +1181,34 @@ try {
// Clone the current project to a new one
// Note that this action is supported only with a pro license or above.
try {
NewProjectResponse resp = ps.clone("New Project Name", ProjectTag.None);
NewProjectResponse resp = ps.cloneProject("New Project Name", ProjectTag.None);
} catch (DescopeException de) {
// Handle the error
}

```

You can manage your project's settings and configurations by exporting your project's environment.

```java
// Exports the current state of the project
try {
ExportProjectResponse resp = ps.exportProject();
} catch (DescopeException de) {
// Handle the error
}

```

You can also import previously exported data into the same project or a different one.

```java
try {
// Load data from a previous export of this project or some other one
Map<String, Object> files = ...

// Update the current project's settings to mirror those in the exported data
ps.importProject(files);
} catch (DescopeException de) {
// Handle the error
}
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/descope/literals/Routes.java
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ public static class ManagementEndPoints {
// Project
public static final String MANAGEMENT_PROJECT_UPDATE_NAME = "/v1/mgmt/project/update/name";
public static final String MANAGEMENT_PROJECT_CLONE = "/v1/mgmt/project/clone";
public static final String MANAGEMENT_PROJECT_EXPORT = "/v1/mgmt/project/export";
public static final String MANAGEMENT_PROJECT_IMPORT = "/v1/mgmt/project/import";

// Audit
public static final String MANAGEMENT_AUDIT_SEARCH_LINK = "/v1/mgmt/audit/search";
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/com/descope/model/project/ExportProjectResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.descope.model.project;

import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ExportProjectResponse {
private Map<String, Object> files;
}
1 change: 1 addition & 0 deletions src/main/java/com/descope/sdk/mgmt/AuthzService.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public interface AuthzService {
* Schema name can be used for projects to track versioning.
*
* @param schema {@link Schema} to save.
* @param upgrade Should we upgrade existing schema or ignore any namespace not provided.
* @throws DescopeException If there occurs any exception, a subtype of this exception will be thrown.
*/
void saveSchema(Schema schema, boolean upgrade) throws DescopeException;
Expand Down
26 changes: 25 additions & 1 deletion src/main/java/com/descope/sdk/mgmt/ProjectService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import com.descope.enums.ProjectTag;
import com.descope.exception.DescopeException;
import com.descope.model.project.ExportProjectResponse;
import com.descope.model.project.NewProjectResponse;
import java.util.Map;

public interface ProjectService {
/**
Expand All @@ -23,5 +25,27 @@ public interface ProjectService {
* @return {@link NewProjectResponse NewProjectResponse}
* @throws DescopeException - error upon failure
*/
NewProjectResponse clone(String name, ProjectTag tag) throws DescopeException;
NewProjectResponse cloneProject(String name, ProjectTag tag) throws DescopeException;

/**
* Exports all settings and configurations for a project and returns the
* raw JSON files response as an object.
* - 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 An object containing the exported JSON files payload.
* @throws DescopeException - error upon failure
*/
ExportProjectResponse exportProject() throws DescopeException;

/**
* 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.
*
* @param files The raw JSON dictionary of files, in the same format as the one returned by calls to export.
*/
void importProject(Map<String, Object> files) throws DescopeException;
}
2 changes: 2 additions & 0 deletions src/main/java/com/descope/sdk/mgmt/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ OTPTestUserResponse generateOtpForTestUser(String loginId, DeliveryMethod delive
* however, it will be used instead of any global configuration.
*
* @param loginId The loginID is required.
* @param uri An ptional redirect URL which will be used instead of any global configuration.
* @param deliveryMethod Choose the selected delivery method for verification.
* @return It returns the link for the login (exactly as it sent via Email)
* @throws DescopeException If there occurs any exception, a subtype of this exception will be
Expand All @@ -449,6 +450,7 @@ MagicLinkTestUserResponse generateMagicLinkForTestUser(
* provided however, it will be used instead of any global configuration.
*
* @param loginId loginId The loginID is required.
* @param uri An ptional redirect URL which will be used instead of any global configuration.
* @return It returns the link for the login (exactly as it sent via Email) and pendingRef which
* is used to poll for a valid session
* @throws DescopeException If there occurs any exception, a subtype of this exception will be
Expand Down
19 changes: 18 additions & 1 deletion src/main/java/com/descope/sdk/mgmt/impl/ProjectServiceImpl.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package com.descope.sdk.mgmt.impl;

import static com.descope.literals.Routes.ManagementEndPoints.MANAGEMENT_PROJECT_CLONE;
import static com.descope.literals.Routes.ManagementEndPoints.MANAGEMENT_PROJECT_EXPORT;
import static com.descope.literals.Routes.ManagementEndPoints.MANAGEMENT_PROJECT_IMPORT;
import static com.descope.literals.Routes.ManagementEndPoints.MANAGEMENT_PROJECT_UPDATE_NAME;
import static com.descope.utils.CollectionUtils.mapOf;

import com.descope.enums.ProjectTag;
import com.descope.exception.DescopeException;
import com.descope.model.client.Client;
import com.descope.model.project.ExportProjectResponse;
import com.descope.model.project.NewProjectResponse;
import com.descope.proxy.ApiProxy;
import com.descope.sdk.mgmt.ProjectService;
Expand All @@ -26,10 +29,24 @@ public void updateName(String name) throws DescopeException {
}

@Override
public NewProjectResponse clone(String name, ProjectTag tag) throws DescopeException {
public NewProjectResponse cloneProject(String name, ProjectTag tag) throws DescopeException {
ApiProxy apiProxy = getApiProxy();
Map<String, Object> request = mapOf("name", name, "tag", tag);
NewProjectResponse resp = apiProxy.post(getUri(MANAGEMENT_PROJECT_CLONE), request, NewProjectResponse.class);
return resp;
}

@Override
public ExportProjectResponse exportProject() throws DescopeException {
ApiProxy apiProxy = getApiProxy();
ExportProjectResponse resp = apiProxy.post(getUri(MANAGEMENT_PROJECT_EXPORT), null, ExportProjectResponse.class);
return resp;
}

@Override
public void importProject(Map<String, Object> files) throws DescopeException {
ApiProxy apiProxy = getApiProxy();
Map<String, Object> request = mapOf("files", files);
apiProxy.post(getUri(MANAGEMENT_PROJECT_IMPORT), request, Void.class);
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package com.descope.sdk.mgmt.impl;

import static com.descope.utils.CollectionUtils.mapOf;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import com.descope.enums.ProjectTag;
import com.descope.model.client.Client;
import com.descope.model.mgmt.ManagementServices;
import com.descope.model.project.ExportProjectResponse;
import com.descope.model.project.NewProjectResponse;
import com.descope.proxy.ApiProxy;
import com.descope.proxy.impl.ApiProxyBuilder;
Expand All @@ -18,12 +22,12 @@
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;


public class ProjectServiceImplTest {
private ProjectService projectService;
private final NewProjectResponse mockCloneResponse = NewProjectResponse.builder()
.projectId("id1").projectName("name1").build();

private final ExportProjectResponse mockExportResponse = ExportProjectResponse.builder()
.files(mapOf("foo/bar.json", mapOf("a", "b"))).build();

@BeforeEach
void setUp() {
Expand All @@ -50,8 +54,32 @@ void testCloneForSuccess() {
try (MockedStatic<ApiProxyBuilder> mockedApiProxyBuilder = mockStatic(ApiProxyBuilder.class)) {
mockedApiProxyBuilder.when(
() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy);
NewProjectResponse response = projectService.clone("new-name", ProjectTag.Production);
NewProjectResponse response = projectService.cloneProject("new-name", ProjectTag.Production);
Assertions.assertThat(response.getProjectId()).isNotBlank();
}
}

@Test
void testExportForSuccess() {
ApiProxy apiProxy = mock(ApiProxy.class);
doReturn(mockExportResponse).when(apiProxy).post(any(), any(), any());
try (MockedStatic<ApiProxyBuilder> mockedApiProxyBuilder = mockStatic(ApiProxyBuilder.class)) {
mockedApiProxyBuilder.when(
() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy);
ExportProjectResponse response = projectService.exportProject();
Assertions.assertThat(response.getFiles()).isEqualTo(mapOf("foo/bar.json", mapOf("a", "b")));
}
}

@Test
void testImportForSuccess() {
ApiProxy apiProxy = mock(ApiProxy.class);
doReturn(Void.class).when(apiProxy).post(any(), any(), any());
try (MockedStatic<ApiProxyBuilder> mockedApiProxyBuilder = mockStatic(ApiProxyBuilder.class)) {
mockedApiProxyBuilder.when(
() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy);
projectService.importProject(mockExportResponse.getFiles());
verify(apiProxy, times(1)).post(any(), any(), any());
}
}
}

0 comments on commit af528c1

Please sign in to comment.