Skip to content

Commit 310dc04

Browse files
Merge pull request #3957 from RedisInsight/be/feature/RI-6154-updates-in-cloud-session
RI-6154 updates in cloud session
2 parents f003628 + 955d4e5 commit 310dc04

24 files changed

+388
-30
lines changed

redisinsight/api/config/ormconfig.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { CloudDatabaseDetailsEntity } from 'src/modules/cloud/database/entities/
2121
import { CloudCapiKeyEntity } from 'src/modules/cloud/capi-key/entity/cloud-capi-key.entity';
2222
import { RdiEntity } from 'src/modules/rdi/entities/rdi.entity';
2323
import { AiQueryMessageEntity } from 'src/modules/ai/query/entities/ai-query.message.entity';
24+
import { CloudSessionEntity } from 'src/modules/cloud/session/entities/cloud.session.entity';
2425
import migrations from '../migration';
2526
import * as config from '../src/utils/config';
2627

@@ -52,6 +53,7 @@ const ormConfig = {
5253
CloudCapiKeyEntity,
5354
RdiEntity,
5455
AiQueryMessageEntity,
56+
CloudSessionEntity,
5557
],
5658
migrations,
5759
};
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 CloudSession1729085495444 implements MigrationInterface {
4+
name = 'CloudSession1729085495444'
5+
6+
public async up(queryRunner: QueryRunner): Promise<void> {
7+
await queryRunner.query(`CREATE TABLE "cloud_session" ("id" varchar PRIMARY KEY NOT NULL, "data" varchar, "encryption" varchar)`);
8+
}
9+
10+
public async down(queryRunner: QueryRunner): Promise<void> {
11+
await queryRunner.query(`DROP TABLE "cloud_session"`);
12+
}
13+
14+
}

redisinsight/api/migration/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import { AiHistory1713515657364 } from './1713515657364-ai-history';
4242
import { AiHistorySteps1714501203616 } from './1714501203616-ai-history-steps';
4343
import { Rdi1716370509836 } from './1716370509836-rdi';
4444
import { AiHistory1718260230164 } from './1718260230164-ai-history';
45+
import { CloudSession1729085495444 } from './1729085495444-cloud-session';
4546
import { CommandExecution1726058563737 } from './1726058563737-command-execution';
4647

4748
export default [
@@ -89,5 +90,6 @@ export default [
8990
AiHistorySteps1714501203616,
9091
Rdi1716370509836,
9192
AiHistory1718260230164,
93+
CloudSession1729085495444,
9294
CommandExecution1726058563737,
9395
];

redisinsight/api/src/__mocks__/cloud-session.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { ICloudApiCredentials } from 'src/modules/cloud/common/models';
22
import { CloudAuthIdpType } from 'src/modules/cloud/auth/models';
33
import { ICloudApiCsrfToken } from 'src/modules/cloud/user/models';
4+
import { CloudSessionData } from 'src/modules/cloud/session/models/cloud-session';
5+
import { CloudSessionEntity } from 'src/modules/cloud/session/entities/cloud.session.entity';
6+
import { EncryptionStrategy } from 'src/modules/encryption/models';
47

58
export const mockCloudApiCsrfToken: ICloudApiCsrfToken = {
69
csrf_token: 'csrf_p6vA6A5tF36Jf6twH2cBOqtt7n',
@@ -14,6 +17,17 @@ export const mockCloudApiAuthDto: ICloudApiCredentials = {
1417
apiSessionId: 'asid_p6v-A6A5tF36J-f6twH2cB!@#$_^&*()Oqtt7n',
1518
};
1619

20+
export const mockCloudSessionData = Object.assign(new CloudSessionData(), {
21+
id: '1',
22+
data: { idpType: CloudAuthIdpType.Google },
23+
});
24+
25+
export const mockCloudSessionEntity = Object.assign(new CloudSessionEntity(), {
26+
...mockCloudSessionData,
27+
data: 'encryptedCloudSessionData',
28+
encryption: EncryptionStrategy.KEYTAR,
29+
});
30+
1731
export const mockCloudSession = {
1832
...mockCloudApiAuthDto,
1933
user: {
@@ -32,6 +46,11 @@ export const mockCloudSession = {
3246
},
3347
};
3448

49+
export const mockCloudSessionRepository = jest.fn(() => ({
50+
get: jest.fn().mockResolvedValue(null),
51+
save: jest.fn(),
52+
}));
53+
3554
export const mockCloudSessionService = jest.fn(() => ({
3655
getSession: jest.fn().mockResolvedValue(mockCloudSession),
3756
updateSessionData: jest.fn().mockResolvedValue(mockCloudSession),

redisinsight/api/src/modules/cloud/auth/auth-strategy/sso-idp.cloud.auth-strategy.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { OktaAuth } from '@okta/okta-auth-js';
77
import { SsoIdpCloudAuthStrategy } from 'src/modules/cloud/auth/auth-strategy/sso-idp.cloud.auth-strategy';
88
import { CloudAuthIdpType } from 'src/modules/cloud/auth/models';
99
import {
10-
CloudOauthSsoUnsupportedEmailException
10+
CloudOauthSsoUnsupportedEmailException,
1111
} from 'src/modules/cloud/auth/exceptions/cloud-oauth.sso-unsupported-email.exception';
1212

1313
jest.mock('@okta/okta-auth-js');

redisinsight/api/src/modules/cloud/auth/cloud-auth.service.spec.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import { CloudSsoFeatureStrategy } from 'src/modules/cloud/cloud-sso.feature.fla
4141
import { CloudApiUnauthorizedException } from 'src/modules/cloud/common/exceptions';
4242
import { SsoIdpCloudAuthStrategy } from 'src/modules/cloud/auth/auth-strategy/sso-idp.cloud.auth-strategy';
4343
import {
44-
CloudOauthSsoUnsupportedEmailException
44+
CloudOauthSsoUnsupportedEmailException,
4545
} from 'src/modules/cloud/auth/exceptions/cloud-oauth.sso-unsupported-email.exception';
4646

4747
const mockedAxios = axios as jest.Mocked<typeof axios>;
@@ -255,7 +255,9 @@ describe('CloudAuthService', () => {
255255
error: 'access_denied',
256256
error_description: 'Some required properties are missing: email and lastName',
257257
},
258-
)).rejects.toThrow(new CloudOauthMissedRequiredDataException('Some required properties are missing: email and lastName'));
258+
)).rejects.toThrow(
259+
new CloudOauthMissedRequiredDataException('Some required properties are missing: email and lastName'),
260+
);
259261
});
260262
it('should throw an error if request not found', async () => {
261263
expect(service['authRequests'].size).toEqual(1);
@@ -343,6 +345,7 @@ describe('CloudAuthService', () => {
343345
{
344346
accessToken: mockCloudAccessTokenNew,
345347
refreshToken: mockCloudRefreshTokenNew,
348+
idpType: mockCloudApiAuthDto.idpType,
346349
csrf: null,
347350
apiSessionId: null,
348351
},

redisinsight/api/src/modules/cloud/auth/cloud-auth.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,6 @@ export class CloudAuthService {
204204
private async revokeRefreshToken(sessionMetadata: SessionMetadata): Promise<void> {
205205
try {
206206
const session = await this.sessionService.getSession(sessionMetadata.sessionId);
207-
208207
if (!session?.refreshToken) {
209208
return;
210209
}
@@ -274,6 +273,7 @@ export class CloudAuthService {
274273
await this.sessionService.updateSessionData(sessionMetadata.sessionId, {
275274
accessToken: data.access_token,
276275
refreshToken: data.refresh_token,
276+
idpType,
277277
csrf: null,
278278
apiSessionId: null,
279279
});

redisinsight/api/src/modules/cloud/cloud.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ import { CloudUserModule } from 'src/modules/cloud/user/cloud-user.module';
55
import { CloudTaskModule } from 'src/modules/cloud/task/cloud-task.module';
66
import { CloudJobModule } from 'src/modules/cloud/job/cloud-job.module';
77
import { CloudCapiKeyModule } from 'src/modules/cloud/capi-key/cloud-capi-key.module';
8+
import { CloudSessionModule } from './session/cloud-session.module';
89

910
@Module({})
1011
export class CloudModule {
1112
static register() {
1213
return {
1314
module: CloudModule,
1415
imports: [
16+
CloudSessionModule.register(),
1517
CloudAuthModule,
1618
CloudUserModule,
1719
CloudAutodiscoveryModule,
Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,23 @@
1-
import { Module } from '@nestjs/common';
1+
import { Global, Type } from '@nestjs/common';
22
import { CloudSessionService } from 'src/modules/cloud/session/cloud-session.service';
3+
import { CloudSessionRepository } from './repositories/cloud.session.repository';
4+
import { LocalCloudSessionRepository } from './repositories/local.cloud.session.repository';
35

4-
@Module({
5-
providers: [CloudSessionService],
6-
exports: [CloudSessionService],
7-
})
8-
export class CloudSessionModule {}
6+
@Global()
7+
export class CloudSessionModule {
8+
static register(
9+
cloudSessionRepository: Type<CloudSessionRepository> = LocalCloudSessionRepository,
10+
) {
11+
return {
12+
module: CloudSessionModule,
13+
providers: [
14+
CloudSessionService,
15+
{
16+
provide: CloudSessionRepository,
17+
useClass: cloudSessionRepository,
18+
},
19+
],
20+
exports: [CloudSessionService],
21+
};
22+
}
23+
}

redisinsight/api/src/modules/cloud/session/cloud-session.service.spec.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import {
44
} from 'src/__mocks__';
55
import { SessionService } from 'src/modules/session/session.service';
66
import { CloudSessionService } from 'src/modules/cloud/session/cloud-session.service';
7-
import { mockCloudSession } from 'src/__mocks__/cloud-session';
7+
import { mockCloudSession, mockCloudSessionRepository } from 'src/__mocks__/cloud-session';
8+
import { CloudSessionRepository } from './repositories/cloud.session.repository';
89

910
describe('CloudSessionService', () => {
1011
let service: CloudSessionService;
1112
let sessionService: MockType<SessionService>;
13+
let cloudSessionRepository: MockType<CloudSessionRepository>;
1214

1315
beforeEach(async () => {
1416
const module: TestingModule = await Test.createTestingModule({
@@ -18,11 +20,16 @@ describe('CloudSessionService', () => {
1820
provide: SessionService,
1921
useFactory: mockSessionService,
2022
},
23+
{
24+
provide: CloudSessionRepository,
25+
useFactory: mockCloudSessionRepository,
26+
},
2127
],
2228
}).compile();
2329

2430
service = module.get(CloudSessionService);
2531
sessionService = module.get(SessionService);
32+
cloudSessionRepository = module.get(CloudSessionRepository);
2633
});
2734

2835
describe('getSession', () => {
@@ -32,6 +39,18 @@ describe('CloudSessionService', () => {
3239
expect(result).toEqual(null);
3340
});
3441

42+
it('should take some additional data in repository if it is not in session', async () => {
43+
cloudSessionRepository.get.mockResolvedValueOnce({ id: 1, data: { idpType: 'test' } });
44+
const result = await service.getSession(mockInitSession.id);
45+
expect(result.idpType).toBe('test');
46+
});
47+
48+
it('should not fail if data in repository is null', async () => {
49+
cloudSessionRepository.get.mockResolvedValueOnce({ id: 1, data: null });
50+
const result = await service.getSession(mockInitSession.id);
51+
expect(result).toEqual(null);
52+
});
53+
3554
it('Should return cloud data only', async () => {
3655
sessionService.getSession.mockResolvedValueOnce({
3756
data: {
@@ -60,6 +79,14 @@ describe('CloudSessionService', () => {
6079
cloud: mockCloudSession,
6180
});
6281
});
82+
83+
it('Should update data in cloud sesssion repository when necessary fields included', async () => {
84+
sessionService.updateSessionData.mockResolvedValue({ data: { cloud: mockCloudSession } });
85+
await service.updateSessionData(mockInitSession.id, mockCloudSession);
86+
87+
expect(cloudSessionRepository.save).toHaveBeenCalled();
88+
});
89+
6390
it('Should merge and update cloud data', async () => {
6491
sessionService.getSession.mockResolvedValueOnce({
6592
data: {
@@ -93,9 +120,10 @@ describe('CloudSessionService', () => {
93120
});
94121

95122
describe('deleteSession', () => {
96-
it('should delete cloud session data by id', async () => {
123+
it('should delete cloud session data by id and clear cloud session repository data', async () => {
97124
await service.deleteSessionData(mockInitSession.id);
98125
expect(sessionService.updateSessionData).toHaveBeenCalledWith(mockInitSession.id, { cloud: null });
126+
expect(cloudSessionRepository.save).toHaveBeenCalledWith({ data: null });
99127
});
100128
});
101129

0 commit comments

Comments
 (0)