Skip to content

Commit 1ad8fac

Browse files
author
arthosofteq
authored
Merge pull request #1756 from RedisInsight/be/feature/RI-4186-Upload_custom_tutorials
#RI-4186 BE poc for custom tutorials
2 parents a37456e + e514b2e commit 1ad8fac

31 files changed

+1312
-3
lines changed

redisinsight/api/config/default.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export default {
2424
logs: join(homedir, 'logs'),
2525
defaultPlugins: join(staticDir, 'plugins'),
2626
customPlugins: join(homedir, 'plugins'),
27+
customTutorials: join(homedir, 'custom-tutorials'),
2728
pluginsAssets: join(staticDir, 'resources', 'plugins'),
2829
commands: join(homedir, 'commands'),
2930
defaultCommandsDir: join(defaultsDir, 'commands'),
@@ -45,6 +46,7 @@ export default {
4546
staticUri: '/static',
4647
guidesUri: '/static/guides',
4748
tutorialsUri: '/static/tutorials',
49+
customTutorialsUri: '/static/custom-tutorials',
4850
contentUri: '/static/content',
4951
defaultPluginsUri: '/static/plugins',
5052
pluginsAssetsUri: '/static/resources/plugins',

redisinsight/api/config/ormconfig.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { ClientCertificateEntity } from 'src/modules/certificate/entities/client
1212
import { DatabaseEntity } from 'src/modules/database/entities/database.entity';
1313
import { SshOptionsEntity } from 'src/modules/ssh/entities/ssh-options.entity';
1414
import { BrowserHistoryEntity } from 'src/modules/browser/entities/browser-history.entity';
15+
import { CustomTutorialEntity } from 'src/modules/custom-tutorial/entities/custom-tutorial.entity';
1516
import migrations from '../migration';
1617
import * as config from '../src/utils/config';
1718

@@ -35,6 +36,7 @@ const ormConfig = {
3536
DatabaseAnalysisEntity,
3637
BrowserHistoryEntity,
3738
SshOptionsEntity,
39+
CustomTutorialEntity,
3840
],
3941
migrations,
4042
};

redisinsight/api/config/production.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export default {
1212
prevHomedir,
1313
logs: join(homedir, 'logs'),
1414
customPlugins: join(homedir, 'plugins'),
15+
customTutorials: join(homedir, 'custom-tutorials'),
1516
commands: join(homedir, 'commands'),
1617
guides: process.env.GUIDES_DEV_PATH || join(homedir, 'guides'),
1718
tutorials: process.env.TUTORIALS_DEV_PATH || join(homedir, 'tutorials'),

redisinsight/api/config/staging.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export default {
1212
prevHomedir,
1313
logs: join(homedir, 'logs'),
1414
customPlugins: join(homedir, 'plugins'),
15+
customTutorials: join(homedir, 'custom-tutorials'),
1516
commands: join(homedir, 'commands'),
1617
guides: process.env.GUIDES_DEV_PATH || join(homedir, 'guides'),
1718
tutorials: process.env.TUTORIALS_DEV_PATH || join(homedir, 'tutorials'),
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { MigrationInterface, QueryRunner } from "typeorm";
2+
3+
export class customTutorials1677135091633 implements MigrationInterface {
4+
name = 'customTutorials1677135091633'
5+
6+
public async up(queryRunner: QueryRunner): Promise<void> {
7+
await queryRunner.query(`CREATE TABLE "custom_tutorials" ("id" varchar PRIMARY KEY NOT NULL, "name" varchar NOT NULL, "link" varchar, "createdAt" datetime NOT NULL DEFAULT (datetime('now')))`);
8+
}
9+
10+
public async down(queryRunner: QueryRunner): Promise<void> {
11+
await queryRunner.query(`DROP TABLE "custom_tutorials"`);
12+
}
13+
14+
}

redisinsight/api/migration/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { workbenchAndAnalysisDbIndex1673934231410 } from './1673934231410-workbe
2727
import { browserHistory1674539211397 } from './1674539211397-browser-history';
2828
import { databaseAnalysisRecommendations1674660306971 } from './1674660306971-database-analysis-recommendations';
2929
import { databaseTimeout1675398140189 } from './1675398140189-database-timeout';
30+
import { customTutorials1677135091633 } from './1677135091633-custom-tutorials';
3031

3132
export default [
3233
initialMigration1614164490968,
@@ -58,4 +59,5 @@ export default [
5859
databaseAnalysisRecommendations1674660306971,
5960
browserHistory1674539211397,
6061
databaseTimeout1675398140189,
62+
customTutorials1677135091633,
6163
];

redisinsight/api/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
"lodash": "^4.17.20",
6666
"nest-router": "^1.0.9",
6767
"nest-winston": "^1.4.0",
68+
"nestjs-form-data": "^1.8.7",
6869
"node-version-compare": "^1.0.3",
6970
"reflect-metadata": "^0.1.13",
7071
"rxjs": "^7.5.6",
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import { CustomTutorial, CustomTutorialActions } from 'src/modules/custom-tutorial/models/custom-tutorial';
2+
import { CustomTutorialEntity } from 'src/modules/custom-tutorial/entities/custom-tutorial.entity';
3+
import { CustomTutorialManifestType } from 'src/modules/custom-tutorial/models/custom-tutorial.manifest';
4+
import { MemoryStoredFile } from 'nestjs-form-data';
5+
import { UploadCustomTutorialDto } from 'src/modules/custom-tutorial/dto/upload.custom-tutorial.dto';
6+
7+
export const mockCustomTutorialId = 'a77b23c1-7816-4ea4-b61f-d37795a0f805-ct-id';
8+
9+
export const mockCustomTutorialId2 = 'a77b23c1-7816-4ea4-b61f-d37795a0f805-ct-id-2';
10+
11+
export const mockCustomTutorialTmpPath = '/tmp/path';
12+
13+
export const mockCustomTutorial = Object.assign(new CustomTutorial(), {
14+
id: mockCustomTutorialId,
15+
name: 'custom tutorial',
16+
createdAt: new Date(),
17+
});
18+
19+
export const mockCustomTutorialEntity = Object.assign(new CustomTutorialEntity(), {
20+
...mockCustomTutorial,
21+
});
22+
23+
export const mockCustomTutorial2 = Object.assign(new CustomTutorial(), {
24+
id: mockCustomTutorialId2,
25+
name: 'custom tutorial 2',
26+
createdAt: new Date(),
27+
});
28+
29+
export const mockCustomTutorialZipFile = Object.assign(new MemoryStoredFile(), {
30+
size: 100,
31+
buffer: Buffer.from('zip-content', 'utf8'),
32+
});
33+
34+
export const mockUploadCustomTutorialDto = Object.assign(new UploadCustomTutorialDto(), {
35+
name: mockCustomTutorial.name,
36+
file: mockCustomTutorialZipFile,
37+
});
38+
39+
export const mockCustomTutorialManifestManifestJson = {
40+
'ct-folder-1': {
41+
type: 'group',
42+
id: 'ct-folder-1',
43+
label: 'ct-folder-1',
44+
// args: {
45+
// withBorder: true,
46+
// initialIsOpen: true,
47+
// },
48+
children: {
49+
'ct-sub-folder-1': {
50+
type: CustomTutorialManifestType.Group,
51+
id: 'ct-sub-folder-1',
52+
label: 'ct-sub-folder-1',
53+
// args: {
54+
// initialIsOpen: false,
55+
// },
56+
children: {
57+
introduction: {
58+
type: CustomTutorialManifestType.InternalLink,
59+
id: 'introduction',
60+
label: 'introduction',
61+
args: {
62+
path: '/ct-folder-1/ct-sub-folder-1/introduction.md',
63+
},
64+
},
65+
'working-with-hashes': {
66+
type: CustomTutorialManifestType.InternalLink,
67+
id: 'working-with-hashes',
68+
label: 'working-with-hashes',
69+
args: {
70+
path: '/ct-folder-1/ct-sub-folder-1/working-with-hashes.md',
71+
},
72+
},
73+
},
74+
},
75+
'ct-sub-folder-2': {
76+
type: CustomTutorialManifestType.Group,
77+
id: 'ct-sub-folder-2',
78+
label: 'ct-sub-folder-2',
79+
// args: {
80+
// withBorder: true,
81+
// initialIsOpen: false,
82+
// },
83+
children: {
84+
introduction: {
85+
type: CustomTutorialManifestType.InternalLink,
86+
id: 'introduction',
87+
label: 'introduction',
88+
args: {
89+
path: '/ct-folder-1/ct-sub-folder-2/introduction.md',
90+
},
91+
},
92+
'working-with-graphs': {
93+
type: CustomTutorialManifestType.InternalLink,
94+
id: 'working-with-graphs',
95+
label: 'working-with-graphs',
96+
args: {
97+
path: '/ct-folder-1/ct-sub-folder-2/working-with-graphs.md',
98+
},
99+
},
100+
},
101+
},
102+
},
103+
},
104+
};
105+
106+
export const mockCustomTutorialManifestManifest = {
107+
type: CustomTutorialManifestType.Group,
108+
id: mockCustomTutorialId,
109+
label: mockCustomTutorial.name,
110+
_actions: mockCustomTutorial.actions,
111+
_path: mockCustomTutorial.path,
112+
children: mockCustomTutorialManifestManifestJson,
113+
};
114+
115+
export const mockCustomTutorialManifestManifest2 = {
116+
type: CustomTutorialManifestType.Group,
117+
id: mockCustomTutorialId2,
118+
label: mockCustomTutorial2.name,
119+
_actions: mockCustomTutorial2.actions,
120+
_path: mockCustomTutorial2.path,
121+
children: mockCustomTutorialManifestManifestJson,
122+
};
123+
124+
export const globalCustomTutorialManifest = {
125+
'custom-tutorials': {
126+
type: CustomTutorialManifestType.Group,
127+
id: 'custom-tutorials',
128+
label: 'My Tutorials',
129+
_actions: [CustomTutorialActions.CREATE],
130+
args: {
131+
withBorder: true,
132+
initialIsOpen: true,
133+
},
134+
children: {
135+
[mockCustomTutorialManifestManifest.id]: mockCustomTutorialManifestManifest,
136+
[mockCustomTutorialManifestManifest2.id]: mockCustomTutorialManifestManifest2,
137+
},
138+
},
139+
};
140+
141+
export const mockCustomTutorialFsProvider = jest.fn(() => ({
142+
unzipToTmpFolder: jest.fn().mockResolvedValue(mockCustomTutorialTmpPath),
143+
moveFolder: jest.fn(),
144+
removeFolder: jest.fn(),
145+
}));
146+
147+
export const mockCustomTutorialManifestProvider = jest.fn(() => ({
148+
getManifestJson: jest.fn().mockResolvedValue(mockCustomTutorialManifestManifestJson),
149+
generateTutorialManifest: jest.fn().mockResolvedValue(mockCustomTutorialManifestManifest),
150+
}));
151+
152+
export const mockCustomTutorialRepository = jest.fn(() => ({
153+
get: jest.fn().mockResolvedValue(mockCustomTutorial),
154+
create: jest.fn().mockResolvedValue(mockCustomTutorial),
155+
delete: jest.fn(),
156+
list: jest.fn().mockResolvedValue([
157+
mockCustomTutorial,
158+
mockCustomTutorial2,
159+
]),
160+
}));

redisinsight/api/src/__mocks__/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export * from './analytics';
1111
export * from './profiler';
1212
export * from './user';
1313
export * from './databases';
14+
export * from './custom-tutorial';
1415
export * from './autodiscovery';
1516
export * from './redis';
1617
export * from './server';

redisinsight/api/src/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { CoreModule } from 'src/core.module';
2121
import { AutodiscoveryModule } from 'src/modules/autodiscovery/autodiscovery.module';
2222
import { DatabaseImportModule } from 'src/modules/database-import/database-import.module';
2323
import { DummyAuthMiddleware } from 'src/common/middlewares/dummy-auth.middleware';
24+
import { CustomTutorialModule } from 'src/modules/custom-tutorial/custom-tutorial.module';
2425
import { BrowserModule } from './modules/browser/browser.module';
2526
import { RedisEnterpriseModule } from './modules/redis-enterprise/redis-enterprise.module';
2627
import { RedisSentinelModule } from './modules/redis-sentinel/redis-sentinel.module';
@@ -53,6 +54,7 @@ const PATH_CONFIG = config.get('dir_path');
5354
NotificationModule,
5455
BulkActionsModule,
5556
ClusterMonitorModule,
57+
CustomTutorialModule.register(),
5658
DatabaseAnalysisModule,
5759
DatabaseImportModule,
5860
...(SERVER_CONFIG.staticContent

0 commit comments

Comments
 (0)