Skip to content

Commit

Permalink
Update snapshot APIs and add validateSnapshot (#409)
Browse files Browse the repository at this point in the history
  • Loading branch information
shilgapira authored Nov 1, 2024
1 parent 0cf407b commit b78075c
Show file tree
Hide file tree
Showing 5 changed files with 349 additions and 62 deletions.
28 changes: 22 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -808,16 +808,32 @@ With using a company management key you can get a list of all the projects in th
const projects = await descopeClient.management.project.listProjects();
```

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.
You can manage your project's settings and configurations by exporting a snapshot:

```typescript
// Exports the current state of the project
const files = await descopeClient.management.project.export();
const exportRes = await descopeClient.management.project.exportSnapshot();
```

You can also import previously exported snapshots into the same project or a different one:

```typescript
const validateReq = {
files: exportRes.files,
};

// Validate that an exported snapshot can be imported into the current project
const validateRes = await descopeClient.management.project.import(files);
if (!validateRes.ok) {
// validation failed, check failures and missingSecrets to fix this
}

// Import the previously exported snapshot into the current project
const importReq = {
files: exportRes.files,
};

// Import the previously exported data into the current project
await descopeClient.management.project.import(files);
await descopeClient.management.project.importSnapshot(files);
```

### Manage Access Keys
Expand Down
5 changes: 3 additions & 2 deletions lib/management/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ export default {
updateName: '/v1/mgmt/project/update/name',
updateTags: '/v1/mgmt/project/update/tags',
clone: '/v1/mgmt/project/clone',
export: '/v1/mgmt/project/export',
import: '/v1/mgmt/project/import',
projectsList: '/v1/mgmt/projects/list',
exportSnapshot: '/v1/mgmt/project/snapshot/export',
importSnapshot: '/v1/mgmt/project/snapshot/import',
validateSnapshot: '/v1/mgmt/project/snapshot/validate',
},
accessKey: {
create: '/v1/mgmt/accesskey/create',
Expand Down
197 changes: 162 additions & 35 deletions lib/management/project.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ import { SdkResponse } from '@descope/core-js-sdk';
import withManagement from '.';
import apiPaths from './paths';
import { mockCoreSdk, mockHttpClient } from './testutils';
import { Project } from './types';
import {
ExportSnapshotResponse,
ImportSnapshotRequest,
Project,
ValidateSnapshotRequest,
ValidateSnapshotResponse,
} from './types';

const management = withManagement(mockCoreSdk, 'key');

Expand Down Expand Up @@ -159,7 +165,128 @@ describe('Management Project', () => {
});
});

describe('export', () => {
describe('exportSnapshot', () => {
it('should send the correct request and receive correct response', async () => {
const mockExportSnapshotResponse: ExportSnapshotResponse = {
files: {
'foo/bar.json': {
foo: 'bar',
},
},
};

const httpResponse = {
ok: true,
json: () => mockExportSnapshotResponse,
clone: () => ({
json: () => Promise.resolve(mockExportSnapshotResponse),
}),
status: 200,
};
mockHttpClient.post.mockResolvedValue(httpResponse);

const resp = await management.project.exportSnapshot();

expect(mockHttpClient.post).toHaveBeenCalledWith(
apiPaths.project.exportSnapshot,
{},
{ token: 'key' },
);

expect(resp).toEqual({
code: 200,
data: mockExportSnapshotResponse,
ok: true,
response: httpResponse,
});
});
});

describe('importSnapshot', () => {
it('should send the correct request and receive correct response', async () => {
const mockImportSnapshotResponse = {};

const httpResponse = {
ok: true,
json: () => mockImportSnapshotResponse,
clone: () => ({
json: () => Promise.resolve(mockImportSnapshotResponse),
}),
status: 200,
};
mockHttpClient.post.mockResolvedValue(httpResponse);

const importSnapshotRequest: ImportSnapshotRequest = {
files: { 'foo/bar.json': { foo: 'bar' } },
inputSecrets: {
connectors: [{ id: 'i', name: 'n', type: 't', value: 'v' }],
oauthProviders: [{ id: 'a', name: 'b', type: 'c', value: 'd' }],
},
};

const resp = await management.project.importSnapshot(importSnapshotRequest);

expect(mockHttpClient.post).toHaveBeenCalledWith(
apiPaths.project.importSnapshot,
importSnapshotRequest,
{ token: 'key' },
);

expect(resp).toEqual({
code: 200,
data: mockImportSnapshotResponse,
ok: true,
response: httpResponse,
});
});
});

describe('validateSnapshot', () => {
it('should send the correct request and receive correct response', async () => {
const mockSecrets = {
connectors: [{ id: 'i', name: 'n', type: 't', value: 'v' }],
oauthProviders: [{ id: 'a', name: 'b', type: 'c', value: 'd' }],
};

const mockValidateSnapshotResponse: ValidateSnapshotResponse = {
ok: true,
failures: ['a'],
missingSecrets: mockSecrets,
};

const httpResponse = {
ok: true,
json: () => mockValidateSnapshotResponse,
clone: () => ({
json: () => Promise.resolve(mockValidateSnapshotResponse),
}),
status: 200,
};
mockHttpClient.post.mockResolvedValue(httpResponse);

const validateSnapshotRequest: ValidateSnapshotRequest = {
files: { 'foo/bar.json': { foo: 'bar' } },
inputSecrets: mockSecrets,
};

const resp = await management.project.validateSnapshot(validateSnapshotRequest);

expect(mockHttpClient.post).toHaveBeenCalledWith(
apiPaths.project.validateSnapshot,
validateSnapshotRequest,
{ token: 'key' },
);

expect(resp).toEqual({
code: 200,
data: mockValidateSnapshotResponse,
ok: true,
response: httpResponse,
});
});
});

describe('export deprecated', () => {
it('should send the correct request and receive correct response', async () => {
const mockExportProjectResponse = {
files: {
Expand All @@ -182,7 +309,7 @@ describe('Management Project', () => {
const resp = await management.project.export();

expect(mockHttpClient.post).toHaveBeenCalledWith(
apiPaths.project.export,
apiPaths.project.exportSnapshot,
{},
{ token: 'key' },
);
Expand All @@ -195,41 +322,41 @@ describe('Management Project', () => {
});
});
});
});

describe('import', () => {
it('should send the correct request and receive correct response', async () => {
const mockImportProjectResponse = {};

const httpResponse = {
ok: true,
json: () => mockImportProjectResponse,
clone: () => ({
json: () => Promise.resolve(mockImportProjectResponse),
}),
status: 200,
};
mockHttpClient.post.mockResolvedValue(httpResponse);

const resp = await management.project.import({ 'foo/bar.json': { foo: 'bar' } });

expect(mockHttpClient.post).toHaveBeenCalledWith(
apiPaths.project.import,
{
files: {
'foo/bar.json': {
foo: 'bar',
describe('import deprecated', () => {
it('should send the correct request and receive correct response', async () => {
const mockImportProjectResponse = {};

const httpResponse = {
ok: true,
json: () => mockImportProjectResponse,
clone: () => ({
json: () => Promise.resolve(mockImportProjectResponse),
}),
status: 200,
};
mockHttpClient.post.mockResolvedValue(httpResponse);

const resp = await management.project.import({ 'foo/bar.json': { foo: 'bar' } });

expect(mockHttpClient.post).toHaveBeenCalledWith(
apiPaths.project.importSnapshot,
{
files: {
'foo/bar.json': {
foo: 'bar',
},
},
},
},
{ token: 'key' },
);

expect(resp).toEqual({
code: 200,
data: mockImportProjectResponse,
ok: true,
response: httpResponse,
{ token: 'key' },
);

expect(resp).toEqual({
code: 200,
data: mockImportProjectResponse,
ok: true,
response: httpResponse,
});
});
});
});
Loading

0 comments on commit b78075c

Please sign in to comment.