diff --git a/.babelrc.json b/.babelrc.json index 2b83d071..376d186a 100644 --- a/.babelrc.json +++ b/.babelrc.json @@ -4,8 +4,7 @@ "@babel/typescript" ], "plugins": [ - "@babel/proposal-class-properties", - "lodash" + "@babel/proposal-class-properties" ], "env": { "test": { diff --git a/package-lock.json b/package-lock.json index 06264a6d..10cb8883 100644 --- a/package-lock.json +++ b/package-lock.json @@ -445,15 +445,6 @@ } } }, - "@babel/helper-module-imports": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.7.0.tgz", - "integrity": "sha512-Dv3hLKIC1jyfTkClvyEkYP2OlkzNvWs5+Q8WgPbxM5LMeorons7iPP91JM+DU7tRbhqA1ZeooPaMFvQrn23RHw==", - "dev": true, - "requires": { - "@babel/types": "^7.7.0" - } - }, "@babel/helper-module-transforms": { "version": "7.15.8", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.8.tgz", @@ -2580,12 +2571,6 @@ "@types/node": "*" } }, - "@types/lodash": { - "version": "4.14.175", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.175.tgz", - "integrity": "sha512-XmdEOrKQ8a1Y/yxQFOMbC47G/V2VDO1GvMRnl4O75M4GW/abC5tnfzadQYkqEveqRM1dEJGFFegfPNA2vvx2iw==", - "dev": true - }, "@types/node": { "version": "16.10.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.3.tgz", @@ -3677,19 +3662,6 @@ "object.assign": "^4.1.0" } }, - "babel-plugin-lodash": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/babel-plugin-lodash/-/babel-plugin-lodash-3.3.4.tgz", - "integrity": "sha512-yDZLjK7TCkWl1gpBeBGmuaDIFhZKmkoL+Cu2MUUjv5VxUZx/z7tBGBCBcQs5RI1Bkz5LLmNdjx7paOyQtMovyg==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.0.0-beta.49", - "@babel/types": "^7.0.0-beta.49", - "glob": "^7.1.1", - "lodash": "^4.17.10", - "require-package-name": "^2.0.1" - } - }, "babel-plugin-polyfill-corejs2": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.2.tgz", @@ -7898,7 +7870,8 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true }, "lodash.debounce": { "version": "4.0.8", @@ -9525,12 +9498,6 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, - "require-package-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/require-package-name/-/require-package-name-2.0.1.tgz", - "integrity": "sha1-wR6XJ2tluOKSP3Xav1+y7ww4Qbk=", - "dev": true - }, "resolve": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", diff --git a/package.json b/package.json index b38b4f32..6cb6dd88 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,6 @@ "dataloader": "^2.0.0", "graphql": "^15.6.1", "graphql-tools": "^8.2.0", - "lodash": "^4.17.21", "mitt": "^3.0.0" }, "devDependencies": { @@ -65,11 +64,9 @@ "@rollup/plugin-node-resolve": "^13.0.5", "@rollup/plugin-typescript": "^8.2.5", "@types/graphql": "^14.5.0", - "@types/lodash": "^4.14.175", "@types/node": "^16.10.3", "@types/whatwg-fetch": "^0.0.33", "audit-ci": "^4.1.0", - "babel-plugin-lodash": "^3.3.4", "chai": "^4.3.4", "copyfiles": "^2.4.1", "cross-var": "^1.1.0", diff --git a/rollup.config.js b/rollup.config.js index 58225505..9e74b768 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -11,13 +11,10 @@ let FORMAT = process.env.FORMAT; // graphql-tools currently has a rollup build failure, so always call it an external until they fix it // otherwise, make all npm production dependencies external, plus their subpath usages // throughout the codebase, which rollup doesn't automatically pick up on -let external = FORMAT==='es' ? - Object.keys(pkg.dependencies) - .concat( - ['castArray', 'get','isError', 'isObject', 'mapValues', 'reduce', 'omitBy', 'uniqBy', 'concat', 'uniqBy', 'differenceBy', 'forEach'].map(v => 'lodash/'+v), - ['graphql', '@graphql-tools/schema']) : - ['@graphql-tools/schema']; - +let external = + FORMAT === 'es' + ? Object.keys(pkg.dependencies).concat(['graphql', '@graphql-tools/schema']) + : ['@graphql-tools/schema']; export default { external, @@ -26,7 +23,7 @@ export default { graphql(), localResolve(), nodeResolve({ - extensions: [ '.js', '.ts', '.json' ] + extensions: ['.js', '.ts', '.json'] }), commonjs(), typescript(), @@ -37,6 +34,6 @@ export default { }) ], output: { - exports: FORMAT==='es' ? null : 'named' - }, + exports: FORMAT === 'es' ? null : 'named' + } }; diff --git a/src/apollo/offline-queue-link/util.ts b/src/apollo/offline-queue-link/util.ts index a3f82a6a..c434eda9 100644 --- a/src/apollo/offline-queue-link/util.ts +++ b/src/apollo/offline-queue-link/util.ts @@ -1,9 +1,8 @@ import { Operation } from '@apollo/client'; -import get from 'lodash/get'; import { OfflineOperationEntry, OperationEntry } from './types'; export function hasSensitiveVariables(operation: Operation) { - return !!get(operation, 'variables.password'); + return !!operation?.variables?.password; } export function isMutationOperation({ query }: Operation) { diff --git a/src/apollo/zimbra-error-link.ts b/src/apollo/zimbra-error-link.ts index 90657bc2..e3a2d578 100644 --- a/src/apollo/zimbra-error-link.ts +++ b/src/apollo/zimbra-error-link.ts @@ -1,5 +1,5 @@ import { ErrorLink } from '@apollo/client/link/error'; -import get from 'lodash/get'; +import { SingleBatchRequestError } from '../request/types'; class ZimbraErrorLink extends ErrorLink { handlers: any[] = []; @@ -8,7 +8,8 @@ class ZimbraErrorLink extends ErrorLink { super(({ graphQLErrors, networkError }) => { graphQLErrors && graphQLErrors.map(({ message, originalError, ...rest }) => { - let errorCode = get(originalError, 'faults.0.Detail.Error.Code', ''); + let error = originalError; + let errorCode = error?.faults?.[0]?.Detail?.Error?.Code; this.executeHandlers({ errorCode, diff --git a/src/apollo/zimbra-in-memory-cache.ts b/src/apollo/zimbra-in-memory-cache.ts index 936f0912..d167ab31 100644 --- a/src/apollo/zimbra-in-memory-cache.ts +++ b/src/apollo/zimbra-in-memory-cache.ts @@ -1,9 +1,8 @@ import { defaultDataIdFromObject, InMemoryCache, InMemoryCacheConfig } from '@apollo/client'; -import get from 'lodash/get'; const dataIdFromPath = (result: any, path: string) => { if (result.__typename) { - const id = get(result, path); + const id = result?.[path]; return id ? `${result.__typename}:${id}` : defaultDataIdFromObject(result); } }; diff --git a/src/batch-client/index.ts b/src/batch-client/index.ts index ab21bdd3..6c4edf83 100644 --- a/src/batch-client/index.ts +++ b/src/batch-client/index.ts @@ -1,8 +1,4 @@ import DataLoader from 'dataloader'; -import castArray from 'lodash/castArray'; -import get from 'lodash/get'; -import isError from 'lodash/isError'; -import mapValues from 'lodash/mapValues'; import { denormalize, normalize } from '../normalize'; import { AccountRights, @@ -107,6 +103,7 @@ import { normalizeMimeParts } from '../utils/normalize-mime-parts'; import { createContactBody, normalizeOtherAttr } from '../utils/normalize-otherAttribute-contact'; +import { castArray, isError, objectMapValues } from '../utils/utils'; import { USER_FOLDER_IDS } from './constants'; import { ActionOptions, @@ -149,8 +146,6 @@ import { import { CASTING_PREFS } from './constants'; import { Notifier } from './notifier'; -const DEBUG = false; - function normalizeMessage( message: { [key: string]: any }, { origin, jwtToken, isDesktop }: { isDesktop?: string; jwtToken?: string; origin?: string } @@ -166,7 +161,7 @@ function normalizeMessage( } const hasUnreadDescendent = (folder: any): any => { - const unreadDescendent = get(folder, 'unreadDescendent'); + const unreadDescendent = folder?.unreadDescendent; if ( folder[ @@ -179,7 +174,7 @@ const hasUnreadDescendent = (folder: any): any => { return true; } - const folderArray = get(folder, 'folders') || []; + const folderArray = folder?.folders || []; for (let i = 0, len = folderArray.length; i < len; i++) { return hasUnreadDescendent(folderArray[i]); } @@ -189,7 +184,7 @@ const hasUnreadDescendent = (folder: any): any => { const updateGroupName = (habGroup: any) => ({ ...habGroup, - name: get(habGroup, 'attributes.displayName') + name: habGroup?.attributes?.displayName }); const updateGroupNameRecur = (habGroups: any) => @@ -200,8 +195,8 @@ const updateGroupNameRecur = (habGroups: any) => }); const setUnreadDescendentFlag = (folder: any) => { - const folderArray = get(folder, 'folders') || []; - const view = get(folder, 'view'); + const folderArray = folder?.folders || []; + const view = folder?.view; // setting this flag only in message view has we dont want to show unread count in // other views @@ -326,9 +321,9 @@ export class ZimbraBatchClient { ...res, attrs: { ...mapValuesDeep(res.attrs._attrs, coerceStringToBoolean), - zimbraMailAlias: [].concat(get(res, 'attrs._attrs.zimbraMailAlias', [])) + zimbraMailAlias: [].concat(res?.attrs?._attrs?.zimbraMailAlias || []) }, - ...(get(res, 'license.attr') && { + ...(res?.license?.attr && { license: { status: res.license.status, attr: mapValuesDeep(res.license.attr, coerceStringToBoolean) @@ -336,8 +331,8 @@ export class ZimbraBatchClient { }), zimlets: { zimlet: - get(res, 'zimlets.zimlet') && - get(res, 'zimlets.zimlet').map(({ zimlet, zimletContext, zimletConfig }: any) => ({ + res?.zimlets?.zimlet && + res?.zimlets?.zimlet.map(({ zimlet, zimletContext, zimletConfig }: any) => ({ zimlet, zimletContext, ...(zimletConfig && { @@ -380,14 +375,16 @@ export class ZimbraBatchClient { [accountType]: mapValuesDeep(accountInfo, coerceBooleanToString) }, singleRequest: true - }).then(res => get(res, `${accountType}.0.id`)); + }).then(res => (accountType ? res?.[accountType]?.[0]?.id : null)); - public addMessage = (options: AddMsgInput) => { - const { folderId, content, meta } = get(options, 'message'); + public addMessage = (message: AddMsgInput) => { + const { folderId, content, meta } = message; let flags, tags, tagNames, date; try { - ({ flags, tags, tagNames, date } = JSON.parse(meta)); + if (meta) { + ({ flags, tags, tagNames, date } = JSON.parse(meta)); + } } catch (err) {} return this.jsonRequest({ @@ -431,7 +428,7 @@ export class ZimbraBatchClient { } } }).then(res => { - const ids = get(res, 'm[0].ids'); + const ids = res?.m?.[0]?.ids; return ids ? ids.split(',') : []; }); @@ -618,23 +615,21 @@ export class ZimbraBatchClient { identity: { ...rest, _attrs: { - ...mapValues(attrs, coerceBooleanToString), + ...objectMapValues(attrs, coerceBooleanToString), zimbraPrefWhenSentToAddresses: convertStringAndArrayValues( - get(attrs, 'zimbraPrefWhenSentToAddresses') + attrs?.zimbraPrefWhenSentToAddresses ), - zimbraPrefWhenInFolderIds: convertStringAndArrayValues( - get(attrs, 'zimbraPrefWhenInFolderIds') - ) + zimbraPrefWhenInFolderIds: convertStringAndArrayValues(attrs?.zimbraPrefWhenInFolderIds) } } }, singleRequest: true }).then(res => { - const mappedResult = mapValuesDeep(res, coerceStringToBoolean); + const mappedResult = mapValuesDeep(res, coerceStringToBoolean); const { _attrs: { zimbraPrefWhenSentToAddresses, zimbraPrefWhenInFolderIds, ...restAttr }, ...restIdentityProps - } = get(mappedResult, 'identity.0'); + } = mappedResult?.identity?.[0]; return { ...mappedResult, @@ -950,7 +945,7 @@ export class ZimbraBatchClient { this.jsonRequest({ name: 'GetConv', body: { - c: mapValues(options, coerceBooleanToInt) + c: objectMapValues(options, coerceBooleanToInt) } }).then(res => { const c = normalize(Conversation)(res.c[0]); @@ -990,7 +985,7 @@ export class ZimbraBatchClient { this.jsonRequest({ name: 'GetDeviceStatus', namespace: Namespace.Sync - }).then(res => get(res, 'device') || []); + }).then(res => res?.device || []); public getDistributionListMembers = (limit: String, offset: String, dl: String) => this.jsonRequest({ @@ -1003,7 +998,7 @@ export class ZimbraBatchClient { offset }, namespace: Namespace.Account - }).then(res => normalize(DlGroupMember)(get(res, 'groupMembers.0.groupMember') || [])); + }).then(res => normalize(DlGroupMember)(res?.groupMembers?.[0]?.groupMember || [])); public getDocumentShareURL = (options: GetDocumentShareURLOptions) => this.jsonRequest({ @@ -1015,7 +1010,7 @@ export class ZimbraBatchClient { public getFilterRules = () => this.jsonRequest({ name: 'GetFilterRules' - }).then(res => normalize(Filter)(get(res, 'filterRules.0.filterRule') || [])); + }).then(res => normalize(Filter)(res?.filterRules?.[0]?.filterRule || [])); public getFolder = (options: GetFolderOptions) => { return this.jsonRequest({ @@ -1023,7 +1018,7 @@ export class ZimbraBatchClient { body: denormalize(GetFolderRequestEntity)(options) }).then(res => { const foldersResponse = normalize(Folder)(res); - const folders = get(foldersResponse, 'folders.0', {}); + const folders = foldersResponse?.folders?.[0] || {}; if (folders.folders) { folders.folders = folders.folders.map(setUnreadDescendentFlag); @@ -1084,7 +1079,7 @@ export class ZimbraBatchClient { }, namespace: Namespace.Account }).then(res => { - const habGroups = get(res, 'ou.0'); + const habGroups = res?.ou?.[0]; return { ...habGroups, habGroups: [...updateGroupNameRecur(habGroups.habGroup)] @@ -1251,7 +1246,10 @@ export class ZimbraBatchClient { this.jsonRequest({ name: 'GetTag', namespace: Namespace.Mail - }).then(({ tag = [] }) => tag.map(normalize(Tag))); + }).then(({ tag = [] }) => { + console.log(tag, tag.map(normalize(Tag))); + return tag.map(normalize(Tag)); + }); public getTasks = (options: SearchOptions) => this.jsonRequest({ @@ -1411,13 +1409,11 @@ export class ZimbraBatchClient { identity: { ...rest, _attrs: { - ...mapValues(attrs, coerceBooleanToString), + ...objectMapValues(attrs, coerceBooleanToString), zimbraPrefWhenSentToAddresses: convertStringAndArrayValues( - get(attrs, 'zimbraPrefWhenSentToAddresses') + attrs?.zimbraPrefWhenSentToAddresses ), - zimbraPrefWhenInFolderIds: convertStringAndArrayValues( - get(attrs, 'zimbraPrefWhenInFolderIds') - ) + zimbraPrefWhenInFolderIds: convertStringAndArrayValues(attrs?.zimbraPrefWhenInFolderIds) } } }, @@ -1771,7 +1767,9 @@ export class ZimbraBatchClient { [accountType]: mapValuesDeep(accountInfo, coerceBooleanToString) }, singleRequest: true - }).then(res => mapValuesDeep(get(res, `${accountType}.0`), coerceStringToBoolean)); + }).then(res => + accountType ? mapValuesDeep(res?.[accountType]?.[0], coerceStringToBoolean) : null + ); public uploadMessage = (message: string): any => { const contentDisposition = 'attachment'; @@ -1820,9 +1818,10 @@ export class ZimbraBatchClient { requests, ...this.getAdditionalRequestOptions() }).then(response => { - const sessionId = get(response, 'header.context.session.id'); - const notifications = get(response, 'header.context.notify.0'); - const refresh = get(response, 'header.context.refresh'); + const respContext = response?.header?.context; + const sessionId = respContext?.session?.id; + const notifications = respContext?.notify?.[0]; + const refresh = respContext?.refresh; this.checkAndUpdateSessionId(sessionId); @@ -1836,12 +1835,7 @@ export class ZimbraBatchClient { } } - return response.requests.map((r, i) => { - if (DEBUG) { - console.log(`[Batch Client Request] ${requests[i].name}`, requests[i].body, r); - } - return isError(r) ? r : r.body; - }); + return response.responses.map(r => (isError(r) ? r : r.body)); }); private checkAndUpdateSessionId = (sessionId: any) => { @@ -1858,9 +1852,10 @@ export class ZimbraBatchClient { // check if login request then don't add csrfToken ...this.getAdditionalRequestOptions(requests[0].name !== 'Auth') }).then(response => { - const sessionId = get(response, 'header.context.session.id'); - const notifications = get(response, 'header.context.notify.0'); - const refresh = get(response, 'header.context.refresh'); + const respContext = response?.header?.context; + const sessionId = respContext?.session?.id; + const notifications = respContext?.notify?.[0]; + const refresh = respContext?.refresh; this.checkAndUpdateSessionId(sessionId); diff --git a/src/normalize/index.ts b/src/normalize/index.ts index af5ed559..5f08bd78 100644 --- a/src/normalize/index.ts +++ b/src/normalize/index.ts @@ -1,5 +1,4 @@ -import reduce from 'lodash/reduce'; - +import { objectReduce } from '../utils/utils'; import { EntityMapping, EntityMappingValue, NormalizedKey } from './types'; function normalizeKey(key: string, schema: Entity, inverse: Boolean = false): NormalizedKey { @@ -20,9 +19,9 @@ function normalizeKey(key: string, schema: Entity, inverse: Boolean = false): No } function _normalize(data: {}, schema: Entity, inverse: Boolean = false) { - return reduce( + return objectReduce( data, - (result: { [key: string]: any }, v, k) => { + (result: { [key: string]: any }, v: any[] | null, k: string) => { const { key, nestedSchema } = normalizeKey(k, schema, inverse); const type = typeof v; if (Array.isArray(v)) { @@ -75,9 +74,9 @@ export class Entity { } initInverseMapping(mapping: EntityMapping, accumulator = {}) { - return reduce( + return objectReduce( mapping, - (result: EntityMapping, v: any, k) => { + (result: EntityMapping, v: any, k: any) => { if (Array.isArray(v)) { result[v[0]] = [k, v[1]]; } else if (typeof v === 'object' && !(v instanceof Entity)) { diff --git a/src/request/index.ts b/src/request/index.ts index 56c15b58..8621933d 100644 --- a/src/request/index.ts +++ b/src/request/index.ts @@ -1,5 +1,3 @@ -import get from 'lodash/get'; -import reduce from 'lodash/reduce'; import { BatchRequestOptions, BatchRequestResponse, @@ -39,7 +37,7 @@ function parseJSON(response: Response): Promise { // for the rest of the cases (as of now only 500 error), parse and return the error response so that it can // be handled properly by the caller return parseErrorJSON(response).then(parsedResponse => { - const fault = get(parsedResponse.parsed, 'Body.Fault'); + const fault = parsedResponse?.parsed?.Body?.Fault; throw faultError(parsedResponse, [fault]); }); } @@ -80,7 +78,7 @@ function faultReasonText(faults: any = []): string { if (!Array.isArray(faults)) faults = [faults]; return faults - .map((f: any) => get(f, 'Reason.Text')) + .map((f: any) => f?.Reason?.Text) .filter(Boolean) .join(', '); } @@ -98,20 +96,18 @@ function faultError(response: ParsedResponse, faults: any) { * containing an array of the requests for that command. */ function batchBody(requests: ReadonlyArray) { - return reduce( - requests, - (body: { [key: string]: any }, request) => { - const key = `${request.name}Request`; - const value = soapCommandBody(request); - if (body[key]) { - body[key].push(value); - } else { - body[key] = [value]; - } - return body; - }, - {} - ); + return requests.reduce((body: { [key: string]: any }, request) => { + const key = `${request.name}Request`; + const value = soapCommandBody(request); + + if (body[key]) { + body[key].push(value); + } else { + body[key] = [value]; + } + + return body; + }, {}); } /** @@ -131,9 +127,11 @@ function batchResponse(requests: any, response: RequestResponse) { return { ...res, - requests: reduce( - requests, - (responses: Array, request) => { + responses: requests.reduce( + ( + responses: Array, + request: { name: string | number } + ) => { const batchResponses = batchBody[`${request.name}Response`]; const index = indexes[request.name]; const response: any = batchResponses && batchResponses[index || 0]; @@ -267,7 +265,7 @@ export function jsonRequest(requestOptions: JsonRequestOptions): Promise { - const globalFault = get(response.parsed, 'Body.Fault'); + const globalFault = response?.parsed?.Body?.Fault; if (globalFault) { throw faultError(response, globalFault); diff --git a/src/request/types.ts b/src/request/types.ts index 91faf718..c77cbf24 100644 --- a/src/request/types.ts +++ b/src/request/types.ts @@ -63,10 +63,11 @@ export interface SingleBatchRequestResponse { } export interface BatchRequestResponse { + [x: string]: any; header: any; namespace: Namespace; originalResponse: Response; - requests: Array; + responses: Array; } export interface ParsedResponse extends Response { diff --git a/src/schema/generated-schema-types.ts b/src/schema/generated-schema-types.ts index 5a02c4ae..e499c191 100644 --- a/src/schema/generated-schema-types.ts +++ b/src/schema/generated-schema-types.ts @@ -1105,7 +1105,7 @@ export type CreateContactInput = { }; export type CreateIdentityInput = { - attrs?: Maybe; + attrs: IdentityAttrsInput; name: Scalars['String']; }; @@ -2324,7 +2324,7 @@ export type ModifyContactInput = { }; export type ModifyIdentityInput = { - attrs?: Maybe; + attrs: IdentityAttrsInput; id: Scalars['ID']; }; diff --git a/src/schema/schema.graphql b/src/schema/schema.graphql index 175fa87a..95a0255d 100644 --- a/src/schema/schema.graphql +++ b/src/schema/schema.graphql @@ -2500,7 +2500,7 @@ input PreferencesInput { input ModifyIdentityInput { id: ID! - attrs: IdentityAttrsInput + attrs: IdentityAttrsInput! } input DeleteIdentityInput { @@ -2510,7 +2510,7 @@ input DeleteIdentityInput { input CreateIdentityInput { name: String! - attrs: IdentityAttrsInput + attrs: IdentityAttrsInput! } input ZimletPreferenceInput { diff --git a/src/schema/schema.ts b/src/schema/schema.ts index 079db5e3..d229617c 100644 --- a/src/schema/schema.ts +++ b/src/schema/schema.ts @@ -1,5 +1,5 @@ import { makeExecutableSchema } from '@graphql-tools/schema'; -import mapValues from 'lodash/mapValues'; +import { objectMapValues } from '../utils/utils'; import { AddMsgInput, @@ -394,7 +394,7 @@ export function createZimbraSchema(options: ZimbraSchemaOptions): { body: { meta: { section: variables.section, - _attrs: mapValues(variables.attrs, coerceBooleanToString) + _attrs: objectMapValues(variables.attrs, coerceBooleanToString) } } }) diff --git a/src/schema/session-handler.ts b/src/schema/session-handler.ts index d1d74510..9c78d184 100644 --- a/src/schema/session-handler.ts +++ b/src/schema/session-handler.ts @@ -1,5 +1,4 @@ import { gql } from '@apollo/client'; -import get from 'lodash/get'; import { ZimbraInMemoryCache } from '../apollo/zimbra-in-memory-cache'; import { ZimbraSessionOptions } from './types'; @@ -22,7 +21,7 @@ export class SessionHandler { id: this.cacheKeyForSession, fragment: SESSION_GQL_FRAGMENT }); - return get(sessionIdFragment, 'id') || '1'; + return sessionIdFragment?.id || '1'; }; public writeSessionId = (sessionId: string) => { diff --git a/src/utils/map-values-deep.ts b/src/utils/map-values-deep.ts index 59bb5c4d..ed602960 100644 --- a/src/utils/map-values-deep.ts +++ b/src/utils/map-values-deep.ts @@ -1,4 +1,4 @@ -import mapValues from 'lodash/mapValues'; +import { objectMapValues } from './utils'; export function mapValuesDeep(obj: {}, callback: (v: any) => any): {} { if (typeof obj !== 'object') { @@ -6,5 +6,6 @@ export function mapValuesDeep(obj: {}, callback: (v: any) => any): {} { } else if (Array.isArray(obj)) { return obj.map(v => mapValuesDeep(v, callback)); } - return mapValues(obj, v => mapValuesDeep(v, callback)); + + return objectMapValues(obj, (v: {}) => mapValuesDeep(v, callback)); } diff --git a/src/utils/normalize-attrs-custommetadata.ts b/src/utils/normalize-attrs-custommetadata.ts index 2acacc38..c77122f1 100644 --- a/src/utils/normalize-attrs-custommetadata.ts +++ b/src/utils/normalize-attrs-custommetadata.ts @@ -1,14 +1,15 @@ -import forEach from 'lodash/forEach'; +import { objectForEach } from './utils'; export function setCustomMetaDataBody(data: any) { const { attrs, id, section } = data; const customMetaAttrs = []; - forEach(attrs, ({ key, value }) => + objectForEach(attrs, (value, key) => customMetaAttrs.push({ [key]: value }) ); + return { id, meta: { @@ -21,9 +22,12 @@ export function setCustomMetaDataBody(data: any) { export function normalizeCustomMetaDataAttrs(data: any) { let attrs: any = []; - Object.keys(data._attrs).forEach( - key => typeof data._attrs[key] === 'string' && attrs.push({ key, value: data._attrs[key] }) - ); + objectForEach(data._attrs, (value, key) => { + if (typeof data._attrs[key] === 'string') { + attrs.push({ key, value }); + } + }); + return { ...data, _attrs: attrs diff --git a/src/utils/normalize-mime-parts.ts b/src/utils/normalize-mime-parts.ts index 25c88e3c..1173cefc 100644 --- a/src/utils/normalize-mime-parts.ts +++ b/src/utils/normalize-mime-parts.ts @@ -144,8 +144,7 @@ export function normalizeMimeParts( if (!isBody && type.split('/')[0] !== 'multipart') { let mode = disposition === 'inline' ? 'inlineAttachments' : 'attachments'; - - part.contentType !== 'application/pkcs7-signature' && + part.contentType !== 'application/pkcs7-signature' && part.contentType !== 'application/x-pkcs7-signature' && (acc[mode] || (acc[mode] = [])).push(processAttachment(part, mode)); diff --git a/src/utils/normalize-otherAttribute-contact.ts b/src/utils/normalize-otherAttribute-contact.ts index b669c7f0..3def8d0f 100644 --- a/src/utils/normalize-otherAttribute-contact.ts +++ b/src/utils/normalize-otherAttribute-contact.ts @@ -1,8 +1,6 @@ -import concat from 'lodash/concat'; -import differenceBy from 'lodash/differenceBy'; -import forEach from 'lodash/forEach'; import { denormalize } from '../normalize'; import { ContactInputRequest } from '../normalize/entities'; +import { arrayDifferenceBy, objectForEach } from './utils'; const supportedContactAttributes = [ 'firstName', @@ -83,19 +81,20 @@ export function createContactBody(data: any) { const { attributes, ...rest } = data; const contactAttrs = []; - forEach(attributes, (val, key) => + objectForEach(attributes, (val, key) => key !== 'other' ? contactAttrs.push({ name: key, [key === 'image' ? 'aid' : 'content']: val }) - : forEach(val, otherValue => + : objectForEach(val, otherValue => contactAttrs.push({ name: otherValue.key, _content: otherValue.value }) ) ); + return { cn: denormalize(ContactInputRequest)({ ...rest, @@ -143,15 +142,17 @@ export function normalizeOtherAttr(data: any) { (a: any, b: any) => Number(a.key.match(/(\d+)/g)[0]) - Number(b.key.match(/(\d+)/g)[0]) ); - const remainingOtherAttribute = differenceBy(other, otherAttributewithCustomKey, 'key').sort( - (a: any, b: any) => a.key.localeCompare(b.key) - ); + const remainingOtherAttribute = arrayDifferenceBy( + other, + otherAttributewithCustomKey, + 'key' + ).sort((a: any, b: any) => a.key.localeCompare(b.key)); return { ...contact, _attrs: { ...contact._attrs, - other: concat(otherAttributewithCustomKey, remainingOtherAttribute) + other: [...otherAttributewithCustomKey, ...remainingOtherAttribute] } }; }); diff --git a/src/utils/utils.ts b/src/utils/utils.ts new file mode 100644 index 00000000..2c765df1 --- /dev/null +++ b/src/utils/utils.ts @@ -0,0 +1,44 @@ +export function castArray(value: any): Array { + return Array.isArray(value) ? value : [value]; +} + +export function isError(value: any): Boolean { + const type = Object.prototype.toString.call(value); + return type === '[object Error]'; +} + +export function objectMapValues( + obj: Record, + iteratee: (value: any, key: string, obj: Record) => any +): Record { + const result: Record = {}; + + Object.keys(obj).forEach(key => { + result[key] = iteratee(obj[key], key, obj); + }); + + return result; +} + +export function objectReduce( + obj: Record, + iteratee: (result: any, value: any, key: string, obj: Record) => any, + initialValue: any +): any { + return Object.keys(obj).reduce((result, key) => { + return iteratee(result, obj[key as keyof object], key, obj); + }, initialValue); +} + +export function objectForEach( + obj: Record, + iteratee: (value: any, key: string, obj: Record) => void +): void { + return Object.keys(obj).forEach(key => { + iteratee(obj[key], key, obj); + }); +} + +export function arrayDifferenceBy(array1: Array, array2: Array, key: string): Array { + return array1.filter(a => !array2.some(b => b[key] === a[key])); +}