Chore: Migrate getPermissions to Typescript (#3720)

* Migrating...

* Fix IPermission

* Playing with types

* Remove `as const`

* Fix lint

* Fix test

* Apply sdk

* Fix lint and autocomplete
This commit is contained in:
Diego Mello 2022-02-17 10:06:31 -03:00 committed by GitHub
parent 86cc8a7d16
commit a58b27e4f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 89 additions and 70 deletions

View File

@ -1,26 +1,26 @@
import { Action } from 'redux'; import { Action } from 'redux';
import { IPermissions } from '../reducers/permissions'; import { IPermissionsState, TSupportedPermissions } from '../reducers/permissions';
import { PERMISSIONS } from './actionsTypes'; import { PERMISSIONS } from './actionsTypes';
interface ISetPermissions extends Action { interface ISetPermissions extends Action {
permissions: IPermissions; permissions: IPermissionsState;
} }
interface IUpdatePermissions extends Action { interface IUpdatePermissions extends Action {
payload: { id: string; roles: string }; payload: { id: TSupportedPermissions; roles: string[] };
} }
export type TActionPermissions = ISetPermissions & IUpdatePermissions; export type TActionPermissions = ISetPermissions & IUpdatePermissions;
export function setPermissions(permissions: IPermissions): ISetPermissions { export function setPermissions(permissions: IPermissionsState): ISetPermissions {
return { return {
type: PERMISSIONS.SET, type: PERMISSIONS.SET,
permissions permissions
}; };
} }
export function updatePermission(id: string, roles: string): IUpdatePermissions { export function updatePermission(id: TSupportedPermissions, roles: string[]): IUpdatePermissions {
return { return {
type: PERMISSIONS.UPDATE, type: PERMISSIONS.UPDATE,
payload: { id, roles } payload: { id, roles }

View File

@ -1,9 +1,9 @@
import Model from '@nozbe/watermelondb/Model'; import Model from '@nozbe/watermelondb/Model';
export interface IPermission { export interface IPermission {
id: string; _id: string;
roles: string[]; roles: string[];
_updatedAt: Date; _updatedAt: Date | string;
} }
export type TPermissionModel = IPermission & Model; export type TPermissionModel = IPermission & Model;

View File

@ -13,6 +13,8 @@ import { IActionSettings } from '../../actions/settings';
import { TActionsShare } from '../../actions/share'; import { TActionsShare } from '../../actions/share';
import { TActionSortPreferences } from '../../actions/sortPreferences'; import { TActionSortPreferences } from '../../actions/sortPreferences';
import { TActionUserTyping } from '../../actions/usersTyping'; import { TActionUserTyping } from '../../actions/usersTyping';
import { TActionPermissions } from '../../actions/permissions';
import { TActionEnterpriseModules } from '../../actions/enterpriseModules';
// REDUCERS // REDUCERS
import { IActiveUsers } from '../../reducers/activeUsers'; import { IActiveUsers } from '../../reducers/activeUsers';
import { IApp } from '../../reducers/app'; import { IApp } from '../../reducers/app';
@ -26,8 +28,8 @@ import { ISelectedUsers } from '../../reducers/selectedUsers';
import { IServer } from '../../reducers/server'; import { IServer } from '../../reducers/server';
import { ISettings } from '../../reducers/settings'; import { ISettings } from '../../reducers/settings';
import { IShare } from '../../reducers/share'; import { IShare } from '../../reducers/share';
import { IPermissionsState } from '../../reducers/permissions';
import { IEnterpriseModules } from '../../reducers/enterpriseModules'; import { IEnterpriseModules } from '../../reducers/enterpriseModules';
import { TActionEnterpriseModules } from '../../actions/enterpriseModules';
export interface IApplicationState { export interface IApplicationState {
settings: ISettings; settings: ISettings;
@ -49,7 +51,7 @@ export interface IApplicationState {
inquiry: any; inquiry: any;
enterpriseModules: IEnterpriseModules; enterpriseModules: IEnterpriseModules;
encryption: IEncryption; encryption: IEncryption;
permissions: any; permissions: IPermissionsState;
roles: IRoles; roles: IRoles;
} }
@ -67,4 +69,5 @@ export type TApplicationActions = TActionActiveUsers &
TActionsShare & TActionsShare &
TActionServer & TActionServer &
TActionApp & TActionApp &
TActionPermissions &
TActionEnterpriseModules; TActionEnterpriseModules;

View File

@ -7,10 +7,12 @@ import database from '../database';
import log from '../../utils/log'; import log from '../../utils/log';
import { store as reduxStore } from '../auxStore'; import { store as reduxStore } from '../auxStore';
import RocketChat from '../rocketchat'; import RocketChat from '../rocketchat';
import sdk from '../rocketchat/services/sdk';
import { setPermissions as setPermissionsAction } from '../../actions/permissions'; import { setPermissions as setPermissionsAction } from '../../actions/permissions';
import protectedFunction from './helpers/protectedFunction'; import protectedFunction from './helpers/protectedFunction';
import { TPermissionModel, IPermission } from '../../definitions';
const PERMISSIONS = [ export const SUPPORTED_PERMISSIONS = [
'add-user-to-any-c-room', 'add-user-to-any-c-room',
'add-user-to-any-p-room', 'add-user-to-any-p-room',
'add-user-to-joined-room', 'add-user-to-joined-room',
@ -57,18 +59,20 @@ const PERMISSIONS = [
'edit-livechat-room-customfields', 'edit-livechat-room-customfields',
'view-canned-responses', 'view-canned-responses',
'mobile-upload-file' 'mobile-upload-file'
]; ] as const;
export async function setPermissions() { export async function setPermissions(): Promise<void> {
const db = database.active; const db = database.active;
const permissionsCollection = db.get('permissions'); const permissionsCollection = db.get('permissions');
const allPermissions = await permissionsCollection.query(Q.where('id', Q.oneOf(PERMISSIONS))).fetch(); const allPermissions = await permissionsCollection
.query(Q.where('id', Q.oneOf(SUPPORTED_PERMISSIONS as unknown as string[])))
.fetch();
const parsed = allPermissions.reduce((acc, item) => ({ ...acc, [item.id]: item.roles }), {}); const parsed = allPermissions.reduce((acc, item) => ({ ...acc, [item.id]: item.roles }), {});
reduxStore.dispatch(setPermissionsAction(parsed)); reduxStore.dispatch(setPermissionsAction(parsed));
} }
const getUpdatedSince = allRecords => { const getUpdatedSince = (allRecords: TPermissionModel[]) => {
try { try {
if (!allRecords.length) { if (!allRecords.length) {
return null; return null;
@ -78,57 +82,63 @@ const getUpdatedSince = allRecords => {
['_updatedAt'], ['_updatedAt'],
['desc'] ['desc']
); );
return ordered && ordered[0]._updatedAt.toISOString(); return new Date(ordered[0]._updatedAt).toISOString();
} catch (e) { } catch (e) {
log(e); log(e);
} }
return null; return null;
}; };
const updatePermissions = async ({ update = [], remove = [], allRecords }) => { const updatePermissions = async ({
update = [],
remove = [],
allRecords
}: {
update?: IPermission[];
remove?: IPermission[];
allRecords: TPermissionModel[];
}) => {
if (!((update && update.length) || (remove && remove.length))) { if (!((update && update.length) || (remove && remove.length))) {
return; return;
} }
const db = database.active; const db = database.active;
const permissionsCollection = db.get('permissions'); const permissionsCollection = db.get('permissions');
// filter permissions const batch: TPermissionModel[] = [];
let permissionsToCreate = [];
let permissionsToUpdate = []; // Delete
let permissionsToDelete = []; if (remove?.length) {
const filteredPermissionsToDelete = allRecords.filter(i1 => remove.find(i2 => i1.id === i2._id));
const permissionsToDelete = filteredPermissionsToDelete.map(permission => permission.prepareDestroyPermanently());
batch.push(...permissionsToDelete);
}
// Create or update // Create or update
if (update && update.length) { if (update?.length) {
permissionsToCreate = update.filter(i1 => !allRecords.find(i2 => i1._id === i2.id)); const filteredPermissionsToCreate = update.filter(i1 => !allRecords.find(i2 => i1._id === i2.id));
permissionsToUpdate = allRecords.filter(i1 => update.find(i2 => i1.id === i2._id)); const filteredPermissionsToUpdate = allRecords.filter(i1 => update.find(i2 => i1.id === i2._id));
permissionsToCreate = permissionsToCreate.map(permission => const permissionsToCreate = filteredPermissionsToCreate.map(permission =>
permissionsCollection.prepareCreate( permissionsCollection.prepareCreate(
protectedFunction(p => { protectedFunction((p: TPermissionModel) => {
p._raw = sanitizedRaw({ id: permission._id }, permissionsCollection.schema); p._raw = sanitizedRaw({ id: permission._id }, permissionsCollection.schema);
Object.assign(p, permission); Object.assign(p, permission);
}) })
) )
); );
permissionsToUpdate = permissionsToUpdate.map(permission => { const permissionsToUpdate = filteredPermissionsToUpdate.map(permission => {
const newPermission = update.find(p => p._id === permission.id); const newPermission = update.find(p => p._id === permission.id);
return permission.prepareUpdate( return permission.prepareUpdate(
protectedFunction(p => { protectedFunction((p: TPermissionModel) => {
Object.assign(p, newPermission); Object.assign(p, newPermission);
}) })
); );
}); });
}
// Delete batch.push(...permissionsToCreate, ...permissionsToUpdate);
if (remove && remove.length) {
permissionsToDelete = allRecords.filter(i1 => remove.find(i2 => i1.id === i2._id));
permissionsToDelete = permissionsToDelete.map(permission => permission.prepareDestroyPermanently());
} }
const batch = [...permissionsToCreate, ...permissionsToUpdate, ...permissionsToDelete];
try { try {
await db.action(async () => { await db.write(async () => {
await db.batch(...batch); await db.batch(...batch);
}); });
return true; return true;
@ -137,18 +147,19 @@ const updatePermissions = async ({ update = [], remove = [], allRecords }) => {
} }
}; };
export function getPermissions() { export function getPermissions(): Promise<void> {
return new Promise(async resolve => { return new Promise(async resolve => {
try { try {
const serverVersion = reduxStore.getState().server.version; const serverVersion: string | null = reduxStore.getState().server.version;
const db = database.active; const db = database.active;
const permissionsCollection = db.get('permissions'); const permissionsCollection = db.get('permissions');
const allRecords = await permissionsCollection.query().fetch(); const allRecords = await permissionsCollection.query().fetch();
RocketChat.subscribe('stream-notify-logged', 'permissions-changed'); RocketChat.subscribe('stream-notify-logged', 'permissions-changed');
// if server version is lower than 0.73.0, fetches from old api // if server version is lower than 0.73.0, fetches from old api
if (compareServerVersion(serverVersion, 'lowerThan', '0.73.0')) { if (serverVersion && compareServerVersion(serverVersion, 'lowerThan', '0.73.0')) {
// RC 0.66.0 // RC 0.66.0
const result = await this.sdk.get('permissions.list'); // @ts-ignore
const result: any = await sdk.get('permissions.list');
if (!result.success) { if (!result.success) {
return resolve(); return resolve();
} }
@ -157,25 +168,25 @@ export function getPermissions() {
setPermissions(); setPermissions();
} }
return resolve(); return resolve();
} else { }
const params = {};
const updatedSince = getUpdatedSince(allRecords);
if (updatedSince) {
params.updatedSince = updatedSince;
}
// RC 0.73.0
const result = await this.sdk.get('permissions.listAll', params);
if (!result.success) { const params: { updatedSince?: string } = {};
return resolve(); const updatedSince = getUpdatedSince(allRecords);
} if (updatedSince) {
params.updatedSince = updatedSince;
}
// RC 0.73.0
const result = await sdk.get('permissions.listAll', params);
const changePermissions = await updatePermissions({ update: result.update, remove: result.delete, allRecords }); if (!result.success) {
if (changePermissions) {
setPermissions();
}
return resolve(); return resolve();
} }
const changePermissions = await updatePermissions({ update: result.update, remove: result.remove, allRecords });
if (changePermissions) {
setPermissions();
}
return resolve();
} catch (e) { } catch (e) {
log(e); log(e);
return resolve(); return resolve();

View File

@ -1,6 +1,6 @@
import { setPermissions, updatePermission } from '../actions/permissions'; import { setPermissions, updatePermission } from '../actions/permissions';
import { mockedStore } from './mockedStore'; import { mockedStore } from './mockedStore';
import { initialState } from './permissions'; import { initialState, IPermissionsState } from './permissions';
describe('test permissions reducer', () => { describe('test permissions reducer', () => {
it('should return initial state', () => { it('should return initial state', () => {
@ -9,15 +9,15 @@ describe('test permissions reducer', () => {
}); });
it('should return modified store after setPermissions', () => { it('should return modified store after setPermissions', () => {
const permissions = { hasEditPermission: 'enabled', hasForceDeletePermission: 'enabled' }; const permissions: IPermissionsState = { 'add-user-to-any-c-room': ['admin'], 'add-team-channel': ['user'] };
mockedStore.dispatch(setPermissions(permissions)); mockedStore.dispatch(setPermissions(permissions));
const state = mockedStore.getState().permissions; const state = mockedStore.getState().permissions;
expect(state).toEqual(permissions); expect(state).toEqual(permissions);
}); });
it('should return empty store after remove user', () => { it('should return empty store after remove user', () => {
mockedStore.dispatch(updatePermission('hasEditPermission', 'disabled')); mockedStore.dispatch(updatePermission('add-team-channel', ['owner']));
const state = mockedStore.getState().permissions; const state = mockedStore.getState().permissions;
expect(state.hasEditPermission).toEqual('disabled'); expect(state['add-team-channel']).toEqual(['owner']);
}); });
}); });

View File

@ -1,11 +1,16 @@
import { PERMISSIONS } from '../actions/actionsTypes'; import { PERMISSIONS } from '../actions/actionsTypes';
import { TActionPermissions } from '../actions/permissions'; import { TActionPermissions } from '../actions/permissions';
import { SUPPORTED_PERMISSIONS } from '../lib/methods/getPermissions';
export type IPermissions = Record<string, string>; export type TSupportedPermissions = typeof SUPPORTED_PERMISSIONS[number];
export const initialState: IPermissions = {}; export type IPermissionsState = {
[K in TSupportedPermissions]?: string[];
};
export default function permissions(state = initialState, action: TActionPermissions): IPermissions { export const initialState: IPermissionsState = {};
export default function permissions(state = initialState, action: TActionPermissions): IPermissionsState {
switch (action.type) { switch (action.type) {
case PERMISSIONS.SET: case PERMISSIONS.SET:
return action.permissions; return action.permissions;

View File

@ -96,8 +96,8 @@ interface ICreateChannelViewProps extends IBaseScreen<ChatsStackParamList, 'Crea
roles: string[]; roles: string[];
}; };
teamId: string; teamId: string;
createPublicChannelPermission: string[]; createPublicChannelPermission: string[] | undefined;
createPrivateChannelPermission: string[]; createPrivateChannelPermission: string[] | undefined;
} }
interface ISwitch extends SwitchProps { interface ISwitch extends SwitchProps {

View File

@ -87,8 +87,8 @@ interface ILivechatEditViewProps {
navigation: StackNavigationProp<ChatsStackParamList, 'LivechatEditView'>; navigation: StackNavigationProp<ChatsStackParamList, 'LivechatEditView'>;
route: RouteProp<ChatsStackParamList, 'LivechatEditView'>; route: RouteProp<ChatsStackParamList, 'LivechatEditView'>;
theme: string; theme: string;
editOmnichannelContact: string[]; editOmnichannelContact: string[] | undefined;
editLivechatRoomCustomfields: string[]; editLivechatRoomCustomfields: string[] | undefined;
} }
const Title = ({ title, theme }: ITitle) => const Title = ({ title, theme }: ITitle) =>

View File

@ -77,11 +77,11 @@ interface INewMessageViewProps extends IBaseScreen<any, 'NewMessageView'> {
maxUsers: number; maxUsers: number;
isMasterDetail: boolean; isMasterDetail: boolean;
serverVersion: string; serverVersion: string;
createTeamPermission: string[]; createTeamPermission: string[] | undefined;
createDirectMessagePermission: string[]; createDirectMessagePermission: string[] | undefined;
createPublicChannelPermission: string[]; createPublicChannelPermission: string[] | undefined;
createPrivateChannelPermission: string[]; createPrivateChannelPermission: string[] | undefined;
createDiscussionPermission: string[]; createDiscussionPermission: string[] | undefined;
} }
class NewMessageView extends React.Component<INewMessageViewProps, INewMessageViewState> { class NewMessageView extends React.Component<INewMessageViewProps, INewMessageViewState> {