Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions redisinsight/api/src/constants/agreements-spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"version": "1.0.6",
"agreements": {
"analytics": {
"code": "analytics",
"defaultValue": false,
"displayInSetting": true,
"required": false,
Expand All @@ -15,6 +16,7 @@
"description": "Help improve Redis Insight by sharing anonymous usage data. This helps us understand feature usage and make the app better. By enabling this, you agree to our "
},
"notifications": {
"code": "notifications",
"defaultValue": false,
"displayInSetting": true,
"required": false,
Expand All @@ -34,6 +36,7 @@

"options": {
"true": {
"code": "encryptionEnabled",
"defaultValue": true,
"displayInSetting": false,
"required": false,
Expand All @@ -47,6 +50,7 @@
"description": "Select to encrypt sensitive information using system keychain. Otherwise, this information is stored locally in plain text, which may incur security risk."
},
"false": {
"code": "encryptionDisabled",
"defaultValue": false,
"displayInSetting": false,
"required": false,
Expand All @@ -60,6 +64,7 @@
"description": "Install or enable the system keychain to encrypt and securely store your sensitive information added before using the application. Otherwise, this information will be stored locally in plain text and may lead to security risks."
},
"stack_false": {
"code": "encryptionStackDisabled",
"defaultValue": false,
"displayInSetting": false,
"required": false,
Expand All @@ -75,6 +80,7 @@
}
},
"eula": {
"code": "eula",
"defaultValue": false,
"displayInSetting": false,
"required": true,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export interface IAgreement {
code?: string;
defaultValue?: boolean;
displayInSetting?: boolean;
required?: boolean;
Expand Down
19 changes: 16 additions & 3 deletions redisinsight/api/src/modules/settings/settings.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
InternalServerErrorException,
Logger,
} from '@nestjs/common';
import { difference, isEmpty, map, cloneDeep } from 'lodash';
import { difference, isEmpty, map, cloneDeep, forEach } from 'lodash';
import { readFile } from 'fs-extra';
import { join } from 'path';
import * as AGREEMENTS_SPEC from 'src/constants/agreements-spec.json';
Expand All @@ -20,7 +20,10 @@
import { AgreementsRepository } from 'src/modules/settings/repositories/agreements.repository';
import { FeatureServerEvents } from 'src/modules/feature/constants';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { IAgreementSpecFile } from 'src/modules/settings/models/agreements.interface';
import {
IAgreement,
IAgreementSpecFile,
} from 'src/modules/settings/models/agreements.interface';
import { SessionMetadata } from 'src/common/models';

import { DatabaseDiscoveryService } from 'src/modules/database-discovery/database-discovery.service';
Expand Down Expand Up @@ -227,10 +230,20 @@
*/
private async getAgreementsSpecFromFile(): Promise<IAgreementSpecFile> {
try {
if (SERVER_CONFIG.agreementsPath) {
return JSON.parse(
const spec: IAgreementSpecFile = JSON.parse(
await readFile(join(__dirname, SERVER_CONFIG.agreementsPath), 'utf8'),

Check warning on line 235 in redisinsight/api/src/modules/settings/settings.service.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
);

// A custom spec (RI_AGREEMENTS_PATH) owns its consent/legal copy. Drop
// the i18n `code` fields (even if copied from the bundled spec) so the
// UI renders that text verbatim instead of the built-in translation.
forEach(spec?.agreements, (agreement: IAgreement) => {

Check warning on line 241 in redisinsight/api/src/modules/settings/settings.service.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

Check warning on line 241 in redisinsight/api/src/modules/settings/settings.service.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

Check warning on line 241 in redisinsight/api/src/modules/settings/settings.service.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

Check warning on line 241 in redisinsight/api/src/modules/settings/settings.service.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

Check warning on line 241 in redisinsight/api/src/modules/settings/settings.service.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🕹️ Function is not covered

Warning! Not covered function
delete agreement.code;

Check warning on line 242 in redisinsight/api/src/modules/settings/settings.service.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
forEach(agreement?.options, (option) => delete option.code);

Check warning on line 243 in redisinsight/api/src/modules/settings/settings.service.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

Check warning on line 243 in redisinsight/api/src/modules/settings/settings.service.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

Check warning on line 243 in redisinsight/api/src/modules/settings/settings.service.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

Check warning on line 243 in redisinsight/api/src/modules/settings/settings.service.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

Check warning on line 243 in redisinsight/api/src/modules/settings/settings.service.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

Check warning on line 243 in redisinsight/api/src/modules/settings/settings.service.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

Check warning on line 243 in redisinsight/api/src/modules/settings/settings.service.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🕹️ Function is not covered

Warning! Not covered function
});

Check warning on line 244 in redisinsight/api/src/modules/settings/settings.service.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

return spec;

Check warning on line 246 in redisinsight/api/src/modules/settings/settings.service.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
}

Check warning on line 247 in redisinsight/api/src/modules/settings/settings.service.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
} catch (e) {
// ignore error
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const { server, request } = deps;
const endpoint = () => request(server).get('/settings/agreements/spec');

const agreementItemSchema = Joi.object().keys({
code: Joi.string().optional(),
defaultValue: Joi.bool().required(),
required: Joi.bool().required(),
disabled: Joi.bool().required(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import ConsentOption from './ConsentOption'
import { IConsent } from '../ConsentsSettings'

const mockConsent: IConsent = {
code: 'test-consent',
agreementName: 'analytics',
title: 'Analytics',
label: 'Share usage data',
Expand Down Expand Up @@ -121,6 +122,44 @@ describe('ConsentOption', () => {
expect(screen.queryByText('Privacy Policy')).not.toBeInTheDocument()
})

it('should localize label and description by agreement code', () => {
const analyticsConsent = {
...mockConsent,
code: 'analytics',
label: 'Share usage data',
description: 'Help us improve Redis Insight by sharing usage data.',
}

render(<ConsentOption {...defaultProps} consent={analyticsConsent} />)

// The spec-provided English is overridden by the api.agreement.analytics.* copy.
expect(screen.getByText('Usage Data')).toBeInTheDocument()
expect(
screen.queryByText(
'Help us improve Redis Insight by sharing usage data.',
),
).not.toBeInTheDocument()
})

it('should keep server-provided copy when no code is sent (custom spec)', () => {
const customConsent = {
...mockConsent,
code: undefined,
agreementName: 'analytics',
label: 'Custom usage data label',
description: 'Custom usage data description.',
}

render(<ConsentOption {...defaultProps} consent={customConsent} />)

// No code -> do not fall back to api.agreement.analytics.* built-in copy.
expect(screen.getByText('Custom usage data label')).toBeInTheDocument()
expect(
screen.getByText('Custom usage data description.'),
).toBeInTheDocument()
expect(screen.queryByText('Usage Data')).not.toBeInTheDocument()
})

it('should render disabled switch when consent is disabled', () => {
const disabledConsent = {
...mockConsent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import { Text } from 'uiSrc/components/base/text'
import { SwitchInput } from 'uiSrc/components/base/inputs'
import { useTranslation } from 'uiSrc/i18n'

import { ItemDescription } from './components'
import { IConsent } from '../ConsentsSettings'
Expand All @@ -27,14 +28,29 @@
withoutSpacer = false,
} = props

const { t } = useTranslation()

// Localize the backend-supplied copy by its stable agreement code, falling
// back to the English text shipped in the spec (mirrors the error-code i18n).
const label = consent.code
? t(`api.agreement.${consent.code}.label` as never, {
Comment thread
valkirilov marked this conversation as resolved.
defaultValue: consent.label,
})
: consent.label
const description = consent.code
? t(`api.agreement.${consent.code}.description` as never, {
defaultValue: consent.description ?? '',

Check warning on line 42 in redisinsight/ui/src/components/consents-settings/ConsentOption/ConsentOption.tsx

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
})
: (consent.description ?? '')

return (
<FlexItem key={consent.agreementName} grow>
{isSettingsPage && consent.description && (
{isSettingsPage && description && (
<>
<Spacer size="s" />
<Text size="M" color="primary">
<ItemDescription
description={consent.description}
description={description}
withLink={consent.linkToPrivacyPolicy}
/>
</Text>
Expand All @@ -55,14 +71,14 @@
</FlexItem>
<FlexItem>
<Text size="M" color="primary">
{parse(consent.label)}
{parse(label)}
</Text>
{!isSettingsPage && consent.description && (
{!isSettingsPage && description && (
<>
<Spacer size="xs" />
<Text size="s" color="secondary">
<ItemDescription
description={consent.description}
description={description}
withLink={consent.linkToPrivacyPolicy}
/>
</Text>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from 'react'
import { Link } from 'uiSrc/components/base/link/Link'
import { EXTERNAL_LINKS, UTM_MEDIUMS } from 'uiSrc/constants/links'
import { getUtmExternalLink } from 'uiSrc/utils/links'
import { useTranslation } from 'uiSrc/i18n'

interface ItemDescriptionProps {
description: string
Expand All @@ -12,25 +13,29 @@ interface ItemDescriptionProps {
export const ItemDescription = ({
description,
withLink,
}: ItemDescriptionProps) => (
<>
{description && parse(description)}
{withLink && (
<>
<Link
variant="inline"
target="_blank"
color="secondary"
size="S"
href={getUtmExternalLink(EXTERNAL_LINKS.legalPrivacyPolicy, {
medium: UTM_MEDIUMS.App,
campaign: 'telemetry',
})}
>
Privacy Policy
</Link>
.
</>
)}
</>
)
}: ItemDescriptionProps) => {
const { t } = useTranslation()

return (
<>
{description && parse(description)}
{withLink && (
<>
<Link
variant="inline"
target="_blank"
color="secondary"
size="S"
href={getUtmExternalLink(EXTERNAL_LINKS.legalPrivacyPolicy, {
medium: UTM_MEDIUMS.App,
campaign: 'telemetry',
})}
>
{t('common.privacyPolicy')}
</Link>
.
</>
)}
</>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ interface Values {
}

export interface IConsent {
code?: string
defaultValue: boolean
displayInSetting: boolean
required: boolean
Expand Down
5 changes: 5 additions & 0 deletions redisinsight/ui/src/i18n/locales/bg.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{
"api.agreement.analytics.description": "Помогнете за подобряването на Redis Insight, като споделяте анонимни данни за употреба. Това ни помага да разберем използването на функциите и да направим приложението по-добро. Активирайки това, се съгласявате с нашата ",
"api.agreement.analytics.label": "Данни за употреба",
"api.agreement.notifications.description": "Изберете, за да се показват известия. В противен случай известията се показват в Центъра за известия.",
"api.agreement.notifications.label": "Показвай известия",
"api.error.code.11000.message": "Опитайте да рестартирате Redis Insight.",
"api.error.code.11000.title": "Сървърна грешка",
"api.error.code.11001.message": "Влезте отново, за да продължите работа с Redis Cloud.",
Expand Down Expand Up @@ -44,6 +48,7 @@
"browser.array.delete.range.trigger": "Изтриване на диапазон",
"browser.array.delete.row.message": "Този елемент ще бъде премахнат за постоянно от масива.",
"browser.array.delete.row.title": "Изтриване на елемент",
"common.privacyPolicy": "Политика за поверителност",
"notification.error.arrayBulkDeleteLimit.message": "Можете да изтриете най-много {{max}} елемента наведнъж. Изчистете част от селекцията и опитайте отново.",
"notification.error.arrayBulkDeleteLimit.title": "Избрани са твърде много елементи",
"notification.error.button.copied": "Копирано",
Expand Down
5 changes: 5 additions & 0 deletions redisinsight/ui/src/i18n/locales/en.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{
"api.agreement.analytics.description": "Help improve Redis Insight by sharing anonymous usage data. This helps us understand feature usage and make the app better. By enabling this, you agree to our ",
"api.agreement.analytics.label": "Usage Data",
"api.agreement.notifications.description": "Select to display notification. Otherwise, notifications are shown in the Notification Center.",
"api.agreement.notifications.label": "Show notification",
"api.error.code.11000.message": "Try restarting Redis Insight.",
"api.error.code.11000.title": "Server error",
"api.error.code.11001.message": "Sign in again to continue working with Redis Cloud.",
Expand Down Expand Up @@ -44,6 +48,7 @@
"browser.array.delete.range.trigger": "Delete range",
"browser.array.delete.row.message": "This element will be permanently removed from the array.",
"browser.array.delete.row.title": "Delete element",
"common.privacyPolicy": "Privacy Policy",
"notification.error.arrayBulkDeleteLimit.message": "You can delete up to {{max}} elements at once. Clear some of the selection and try again.",
"notification.error.arrayBulkDeleteLimit.title": "Too many elements selected",
"notification.error.button.copied": "Copied",
Expand Down
Loading