diff --git a/.vscode/settings.json b/.vscode/settings.json index 6c8a184c183b867ca543309a8e2fadf6bf498eca..a6d1b84a1db2d01d7b7462f68705f06e488d3eae 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -22,12 +22,14 @@ "javascript", "javascriptreact", "typescript", - "typescriptreact", + "typescriptreact" ], "gitlab.instanceUrl": "https://forge.grandlyon.com/web-et-numerique/llle_project/ecolyo", "gitlab.ignoreCertificateErrors": true, "editor.codeActionsOnSave": { - "source.fixAll.eslint": true + "source.fixAll.eslint": true, + "source.fixAll": true, + "source.organizeImports": true }, "gitlab.showPipelineUpdateNotifications": true, "sonarlint.connectedMode.project": { diff --git a/app.config.environment.alpha.js b/app.config.environment.alpha.js index e680fd5fe34aecf8b46d111a9f994b4cc5721dbc..346a1c982aca46a5971ea7cfa8d91eceb0b32876 100644 --- a/app.config.environment.alpha.js +++ b/app.config.environment.alpha.js @@ -19,6 +19,9 @@ module.exports = { __STACK_ASSETS__: target !== 'mobile', __PIWIK_TRACKER_URL__: JSON.stringify('https://statweb.grandlyon.com/'), __PIWIK_SITEID__: 117, + __SENTRY_DSN__: JSON.stringify( + 'https://c868f6010f3f431d95be8f70d7f37666@grandlyon.errors.cozycloud.cc/6' + ), __SAU_LINK__: JSON.stringify( 'https://portail-citoyen-sau.guichet-recette.grandlyon.com/ecolyo/' ), diff --git a/app.config.environment.dev.js b/app.config.environment.dev.js index 8895164f726f60d89a213300d4ee17fd0192a3e5..8cb7804264915ccc335702a63d7e3a7f60c1786b 100644 --- a/app.config.environment.dev.js +++ b/app.config.environment.dev.js @@ -29,6 +29,9 @@ const stackProvidedLibsConfig = { __SAU_IDEA_DIRECT_LINK__: JSON.stringify( 'https://demarches-sau.guichet-recette.grandlyon.com/retour-ecolyo/ecolyo-une-idee/' ), + __SENTRY_DSN__: JSON.stringify( + 'https://c868f6010f3f431d95be8f70d7f37666@grandlyon.errors.cozycloud.cc/6' + ), }), ], module: { diff --git a/app.config.environment.prod.js b/app.config.environment.prod.js index b09cab9e863a406de56d3e822c0f1efe26475110..6688983f2ee64a0289d0d71c5b218a6b26440948 100644 --- a/app.config.environment.prod.js +++ b/app.config.environment.prod.js @@ -23,6 +23,9 @@ module.exports = { __SAU_IDEA_DIRECT_LINK__: JSON.stringify( 'https://demarches-support.grandlyon.com/retour-ecolyo/ecolyo-une-idee/' ), + __SENTRY_DSN__: JSON.stringify( + 'https://c868f6010f3f431d95be8f70d7f37666@grandlyon.errors.cozycloud.cc/6' + ), }), ], optimization: { diff --git a/package.json b/package.json index e83952341040d62ffe44461946168d61134f6458..f9c0be2480d8b9fbb7408010691e74598283ed84 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,8 @@ "@cozy/minilog": "^1.0.0", "@material-ui/core": "~4.12.0", "@material-ui/styles": "^4.11.3", + "@sentry/react": "^7.21.1", + "@sentry/tracing": "^7.21.1", "@simbathesailor/use-what-changed": "^2.0.0", "axios": "^0.27.0", "cozy-bar": "8.9.2", @@ -56,8 +58,8 @@ "cozy-intent": ">=1.14.1", "cozy-keys-lib": ">=4.1.9", "cozy-logger": ">1.7.0", - "cozy-scripts": "6.3.10", "cozy-realtime": "4.2.8", + "cozy-scripts": "6.3.10", "cozy-ui": "75.4.1", "d3": "^6.0.0", "detect-browser": "^5.1.1", diff --git a/src/components/Connection/FormLogin.tsx b/src/components/Connection/FormLogin.tsx index e8c01d752536c8064f28b95ed884b03f1ad555a5..ba3f0171d3cbfd32cbe26bec820df83988378695 100644 --- a/src/components/Connection/FormLogin.tsx +++ b/src/components/Connection/FormLogin.tsx @@ -9,6 +9,7 @@ import StyledIcon from 'components/CommonKit/Icon/StyledIcon' import { FluidType } from 'enum/fluid.enum' import { getPartnerPicto } from 'utils/picto' import useKonnectorAuth from 'components/Hooks/useKonnectorAuth' +import * as Sentry from '@sentry/react' interface FormLoginProps { fluidStatus: FluidStatus @@ -77,7 +78,8 @@ const FormLogin: React.FC<FormLoginProps> = ({ } else { await update() } - } catch (err) { + } catch (error) { + Sentry.captureException(JSON.stringify({ error })) setLoading(false) } } diff --git a/src/components/Hooks/useKonnectorAuth.tsx b/src/components/Hooks/useKonnectorAuth.tsx index 96e568bf0435674cfe3f51c993602f1f270a7adb..2feceda088b7979cff007c8b2f198db54eecf90e 100644 --- a/src/components/Hooks/useKonnectorAuth.tsx +++ b/src/components/Hooks/useKonnectorAuth.tsx @@ -1,6 +1,8 @@ -import { useState, useCallback } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import * as Sentry from '@sentry/react' import { Client, useClient } from 'cozy-client' +import { useI18n } from 'cozy-ui/transpiled/react/I18n' +import { FluidSlugType } from 'enum/fluidSlug.enum' +import { UsageEventType } from 'enum/usageEvent.enum' import { AccountAuthData, AccountSgeData, @@ -8,15 +10,15 @@ import { FluidStatus, UsageEvent, } from 'models' -import { AppStore } from 'store' -import { UsageEventType } from 'enum/usageEvent.enum' +import { useCallback, useState } from 'react' +import { useDispatch, useSelector } from 'react-redux' import AccountService from 'services/account.service' -import UsageEventService from 'services/usageEvent.service' -import { updatedFluidConnection } from 'store/global/global.actions' import ConnectionService from 'services/connection.service' +import UsageEventService from 'services/usageEvent.service' +import { AppStore } from 'store' import { setLoading } from 'store/chart/chart.actions' -import { useI18n } from 'cozy-ui/transpiled/react/I18n' -import { FluidSlugType } from 'enum/fluidSlug.enum' +import { updatedFluidConnection } from 'store/global/global.actions' +import logApp from 'utils/logger' const useKonnectorAuth = ( fluidStatus: FluidStatus, @@ -81,10 +83,11 @@ const useKonnectorAuth = ( setLoading(false) dispatch(updatedFluidConnection(fluidStatus.fluidType, updatedConnection)) onSuccess() - } catch (err) { + } catch (error) { setLoading(false) sendUsageEventError(konnectorSlug) - console.log(err) + logApp.error(error) + Sentry.captureException(JSON.stringify({ error })) } } diff --git a/src/components/Options/OptionsView.tsx b/src/components/Options/OptionsView.tsx index b6e54afdcf9ff42cb7f24cc82f1916b7f64ee5cf..d63b05610c5dee486dac6bfd761753fffe45cc4f 100644 --- a/src/components/Options/OptionsView.tsx +++ b/src/components/Options/OptionsView.tsx @@ -17,6 +17,7 @@ const OptionsView: React.FC = () => { const defineHeaderHeight = (height: number) => { setHeaderHeight(height) } + return ( <> <CozyBar titleKey={'common.title_options'} /> diff --git a/src/components/ProfileType/ProfileTypeFinished.tsx b/src/components/ProfileType/ProfileTypeFinished.tsx index 24be38a594bf271e7dd159bf2e851d53e5175dd5..9149ff48d098c844b9281d651015a8e5f0aceddd 100644 --- a/src/components/ProfileType/ProfileTypeFinished.tsx +++ b/src/components/ProfileType/ProfileTypeFinished.tsx @@ -1,24 +1,25 @@ -import React, { useEffect, useState } from 'react' +import Button from '@material-ui/core/Button' +import * as Sentry from '@sentry/react' +import finishIcon from 'assets/icons/visu/profileType/finish.svg' +import StyledIcon from 'components/CommonKit/Icon/StyledIcon' +import useExploration from 'components/Hooks/useExploration' import 'components/ProfileType/profileTypeFinished.scss' +import { useClient } from 'cozy-client' import { useI18n } from 'cozy-ui/transpiled/react/I18n' +import { UsageEventType } from 'enum/usageEvent.enum' +import { UserExplorationID } from 'enum/userExploration.enum' +import { DateTime } from 'luxon' +import { TimePeriod } from 'models' +import { ProfileType } from 'models/profileType.model' +import React, { useEffect, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' import { useHistory, useLocation } from 'react-router-dom' -import { updateProfile } from 'store/profile/profile.actions' -import { newProfileTypeEntry } from 'store/profileType/profileType.actions' -import { ProfileType } from 'models/profileType.model' -import Button from '@material-ui/core/Button' -import StyledIcon from 'components/CommonKit/Icon/StyledIcon' -import finishIcon from 'assets/icons/visu/profileType/finish.svg' import ProfileTypeService from 'services/profileType.service' -import useExploration from 'components/Hooks/useExploration' -import { AppStore } from 'store' -import { TimePeriod } from 'models' -import { UserExplorationID } from 'enum/userExploration.enum' -import { UsageEventType } from 'enum/usageEvent.enum' -import { useClient } from 'cozy-client' -import UsageEventService from 'services/usageEvent.service' import ProfileTypeEntityService from 'services/profileTypeEntity.service' -import { DateTime } from 'luxon' +import UsageEventService from 'services/usageEvent.service' +import { AppStore } from 'store' +import { updateProfile } from 'store/profile/profile.actions' +import { newProfileTypeEntry } from 'store/profileType/profileType.actions' interface ProfileTypeFinishedProps { profileType: ProfileType @@ -77,6 +78,7 @@ const ProfileTypeFinished: React.FC<ProfileTypeFinishedProps> = ({ ) } else { console.log('ERROR') + Sentry.captureException('error in profileTypeFinished') } } else { dispatch(newProfileTypeEntry(consistentProfileType)) diff --git a/src/components/Splash/SplashRoot.tsx b/src/components/Splash/SplashRoot.tsx index db60f3bacedc0217adbe02bd591e180f15aae9d8..2da350a56e98ae6d189c237575dce65db8d87cad 100644 --- a/src/components/Splash/SplashRoot.tsx +++ b/src/components/Splash/SplashRoot.tsx @@ -1,68 +1,69 @@ -import React, { useState, useEffect, ReactNode, Dispatch } from 'react' -import { useClient } from 'cozy-client' import classNames from 'classnames' +import { useClient } from 'cozy-client' +import React, { Dispatch, ReactNode, useEffect, useState } from 'react' import { useDispatch } from 'react-redux' -import { FluidState } from 'enum/fluid.enum' +import useExploration from 'components/Hooks/useExploration' +import { FluidState } from 'enum/fluid.enum' +import { UsageEventType } from 'enum/usageEvent.enum' +import { UserActionState } from 'enum/userAction.enum' +import { UserChallengeState } from 'enum/userChallenge.enum' +import { UserDuelState } from 'enum/userDuel.enum' import { + UserExplorationID, + UserExplorationState, +} from 'enum/userExploration.enum' +import { DateTime } from 'luxon' +import { migrations } from 'migrations/migration.data' +import { MigrationService } from 'migrations/migration.service' +import { FluidStatus, TermsStatus, UserChallenge } from 'models' +import { InitSteps, InitStepsErrors } from 'models/initialisationSteps.model' +import { PartnersInfo } from 'models/partnersInfo.model' +import { ReleaseNotes } from 'models/releaseNotes.model' +import ActionService from 'services/action.service' +import ChallengeService from 'services/challenge.service' +import CustomPupopService from 'services/customPopup.service' +import FluidService from 'services/fluid.service' +import InitializationService from 'services/initialization.service' +import PartnersInfoService from 'services/partnersInfo.service' +import UsageEventService from 'services/usageEvent.service' +import { + ChallengeActionTypes, + setChallengeConsumption, + setUserChallengeList, + updateUserChallengeList, +} from 'store/challenge/challenge.actions' +import { ChartActionTypes, setSelectedDate } from 'store/chart/chart.actions' +import { + GlobalActionTypes, + setCustomPopup, + setFluidStatus, + setPartnersIssue, + showReleaseNotes, toggleAnalysisNotification, - toggleChallengeExplorationNotification, toggleChallengeActionNotification, toggleChallengeDuelNotification, - setFluidStatus, - GlobalActionTypes, + toggleChallengeExplorationNotification, updateTermValidation, - showReleaseNotes, - setPartnersIssue, - setCustomPopup, } from 'store/global/global.actions' import { ProfileActionTypes, updateProfile, } from 'store/profile/profile.actions' +import { + ProfileEcogestureActionTypes, + updateProfileEcogestureSuccess, +} from 'store/profileEcogesture/profileEcogesture.actions' import { ProfileTypeActionTypes, updateProfileType, } from 'store/profileType/profileType.actions' -import { - setUserChallengeList, - setChallengeConsumption, - updateUserChallengeList, - ChallengeActionTypes, -} from 'store/challenge/challenge.actions' -import { ChartActionTypes, setSelectedDate } from 'store/chart/chart.actions' -import InitializationService from 'services/initialization.service' +import logApp from 'utils/logger' +import { getTodayDate } from 'utils/utils' import './splashRoot.scss' -import { UserChallengeState } from 'enum/userChallenge.enum' -import { UserDuelState } from 'enum/userDuel.enum' -import ChallengeService from 'services/challenge.service' -import useExploration from 'components/Hooks/useExploration' -import { - UserExplorationID, - UserExplorationState, -} from 'enum/userExploration.enum' -import { DateTime } from 'luxon' -import { UserActionState } from 'enum/userAction.enum' -import ActionService from 'services/action.service' -import { FluidStatus, TermsStatus, UserChallenge } from 'models' -import UsageEventService from 'services/usageEvent.service' -import { UsageEventType } from 'enum/usageEvent.enum' -import { MigrationService } from 'migrations/migration.service' -import { migrations } from 'migrations/migration.data' -import { ReleaseNotes } from 'models/releaseNotes.model' -import PartnersInfoService from 'services/partnersInfo.service' -import FluidService from 'services/fluid.service' -import { PartnersInfo } from 'models/partnersInfo.model' -import { - ProfileEcogestureActionTypes, - updateProfileEcogestureSuccess, -} from 'store/profileEcogesture/profileEcogesture.actions' -import { InitSteps, InitStepsErrors } from 'models/initialisationSteps.model' -import log from 'utils/logger' import SplashScreen from './SplashScreen' import SplashScreenError from './SplashScreenError' -import CustomPupopService from 'services/customPopup.service' -import { getTodayDate } from 'utils/utils' +import * as Sentry from '@sentry/react' interface SplashRootProps { fadeTimer?: number @@ -312,17 +313,18 @@ const SplashRoot = ({ fadeTimer = 1000, children }: SplashRootProps) => { } if (subscribed) { - log.info('Initialization finished successfully !') + logApp.info('[Initialization] Finished successfully !') setState(prev => ({ ...prev, splashStart: true, })) } - } catch (err: any) { - if (err.message === 'Failed to fetch' && !initStepErrors) { + } catch (error: any) { + if (error.message === 'Failed to fetch' && !initStepErrors) { setinitStepErrors(InitStepsErrors.UNKNOWN_ERROR) } - log.error(`[Initialization] Error : ${err}`) + logApp.error(`[Initialization] Error : ${error}`) + Sentry.captureException(JSON.stringify({ error })) } } if (!initStepErrors) loadData() diff --git a/src/migrations/migration.service.ts b/src/migrations/migration.service.ts index 0ede26effbcc62ae082c685bf34e01da1b581ef5..32a7ca9ae2666b13d0dc364758f287db6c461fcd 100644 --- a/src/migrations/migration.service.ts +++ b/src/migrations/migration.service.ts @@ -1,15 +1,16 @@ -import { Client, QueryDefinition, QueryResult, Q } from 'cozy-client' -import { Migration, MigrationResult } from './migration.type' -import { migrationLog, migrate } from './migration' +import * as Sentry from '@sentry/react' +import { Client, Q, QueryDefinition, QueryResult } from 'cozy-client' +import { SCHEMAS_DOCTYPE } from 'doctypes/com-grandlyon-ecolyo-schemas' +import { InitStepsErrors } from 'models/initialisationSteps.model' +import { ReleaseNotes } from 'models/releaseNotes.model' +import { Schema } from 'models/schema.models' +import logApp from 'utils/logger' +import { migrate, migrationLog } from './migration' import { MIGRATION_RESULT_COMPLETE, MIGRATION_RESULT_FAILED, } from './migration.data' -import log from 'utils/logger' -import { ReleaseNotes } from 'models/releaseNotes.model' -import { InitStepsErrors } from 'models/initialisationSteps.model' -import { Schema } from 'models/schema.models' -import { SCHEMAS_DOCTYPE } from 'doctypes/com-grandlyon-ecolyo-schemas' +import { Migration, MigrationResult } from './migration.type' export class MigrationService { private readonly _client: Client @@ -37,7 +38,7 @@ export class MigrationService { } public async runMigrations(migrations: Migration[]): Promise<ReleaseNotes> { - log.info('[Migration] Running migrations...') + logApp.info('[Migration] Running migrations...') let releaseStatus = false const releaseNotes: ReleaseNotes = { show: releaseStatus, @@ -63,7 +64,7 @@ export class MigrationService { migration, this._client ) - log.info(migrationLog(migration, migrationResult)) + logApp.info(migrationLog(migration, migrationResult)) if (migrationResult.type === MIGRATION_RESULT_FAILED) { // Retry in case of failure @@ -71,10 +72,11 @@ export class MigrationService { if (result.type === MIGRATION_RESULT_FAILED) { // Error in case of second failure this._setinitStepError(InitStepsErrors.MIGRATION_ERROR) - log.error(migrationLog(migration, result)) + logApp.error(migrationLog(migration, result)) + Sentry.captureException(migrationLog(migration, result)) throw new Error() } else { - log.info(migrationLog(migration, result)) + logApp.info(migrationLog(migration, result)) } } @@ -89,10 +91,10 @@ export class MigrationService { releaseNotes.show = releaseStatus // In case of first instance, don't show release notes if (startMigrationIndex === 0) releaseNotes.show = false - log.info('[Migration] Done') + logApp.info('[Migration] Done') return releaseNotes } else { - log.info('[Migration] Skipped Migration Process, already up-to-date') + logApp.info('[Migration] Skipped Migration Process, already up-to-date') return releaseNotes } } diff --git a/src/migrations/migration.ts b/src/migrations/migration.ts index 402db829053910fbda726350de22454ebe3a090d..5e278a5798991974fce2e7c0f0bfeea226590b9b 100644 --- a/src/migrations/migration.ts +++ b/src/migrations/migration.ts @@ -1,18 +1,19 @@ -import { - Migration, - MigrationQueryOptions, - MigrationResult, -} from './migration.type' +import * as Sentry from '@sentry/react' import { Client, Q, QueryDefinition, QueryResult } from 'cozy-client' import { SCHEMAS_DOCTYPE } from 'doctypes/com-grandlyon-ecolyo-schemas' -import log from 'utils/logger' +import { Schema } from 'models/schema.models' +import logApp from 'utils/logger' import { MIGRATION_RESULT_COMPLETE, MIGRATION_RESULT_FAILED, MIGRATION_RESULT_NOOP, SCHEMA_INITIAL_VERSION, } from './migration.data' -import { Schema } from 'models/schema.models' +import { + Migration, + MigrationQueryOptions, + MigrationResult, +} from './migration.type' function migrationNoop(): MigrationResult { return { type: MIGRATION_RESULT_NOOP, errors: [] } @@ -64,7 +65,7 @@ async function updateSchemaVersion( _client: Client, targetSchemaVersion: number ): Promise<void> { - log.info('[Migration] Update schema version') + logApp.info('[Migration] Update schema version') const query: QueryDefinition = Q(SCHEMAS_DOCTYPE) const data: QueryResult<Schema[]> = await _client.query(query.limitBy(1)) const doc = data.data[0] @@ -79,11 +80,11 @@ async function updateSchemaVersion( * @returns Promise<MigrationResult> */ async function save(_client: Client, docs: any[]): Promise<MigrationResult> { - log.info('[Migration] Saving docs...') + logApp.info('[Migration] Saving docs...') const migrationResult: MigrationResult = migrationNoop() if (docs.length) { - log.info('[Migration] Saving docs...') + logApp.info('[Migration] Saving docs...') docs.forEach(async doc => { if (doc.deleteAction) { await _client.destroy(doc) @@ -93,7 +94,7 @@ async function save(_client: Client, docs: any[]): Promise<MigrationResult> { await _client.save(doc) } }) - log.info('[Migration] Docs saved') + logApp.info('[Migration] Docs saved') migrationResult.type = migrationResult.errors.length ? MIGRATION_RESULT_FAILED : MIGRATION_RESULT_COMPLETE @@ -114,7 +115,7 @@ const schemaExist = async (_client: Client): Promise<boolean> => { } const initSchemaDoctype = async (_client: Client) => { - log.info('[Migration] Init doctype') + logApp.info('[Migration] Init doctype') await _client.create(SCHEMAS_DOCTYPE, { version: SCHEMA_INITIAL_VERSION, }) @@ -175,11 +176,12 @@ export async function migrate( await updateSchemaVersion(_client, migration.targetSchemaVersion) break } - } catch (err: any) { - console.error(err) + } catch (error: any) { + console.error(error) + Sentry.captureException(JSON.stringify({ error })) result = { type: MIGRATION_RESULT_FAILED, - errors: [err.toString()], + errors: [error.toString()], } } diff --git a/src/services/account.service.ts b/src/services/account.service.ts index 4535ba52fa88efc4fb891b4bf29de15c0dee2fba..4c79724e0d43383c521879e0494fc630ade7d367 100644 --- a/src/services/account.service.ts +++ b/src/services/account.service.ts @@ -1,4 +1,15 @@ -import { Client, QueryDefinition, Q, QueryResult } from 'cozy-client' +import * as Sentry from '@sentry/react' +import { Client, Q, QueryDefinition, QueryResult } from 'cozy-client' +import { + createAccount, + deleteAccount, + fetchAccount, + updateAccount, +} from 'cozy-harvest-lib/dist/connections/accounts' +import { build } from 'cozy-harvest-lib/dist/helpers/accounts' +import logger from 'cozy-logger' +import { ACCOUNTS_DOCTYPE } from 'doctypes' +import { DateTime } from 'luxon' import { Account, AccountAttributes, @@ -7,16 +18,10 @@ import { Konnector, Trigger, } from 'models' -import { ACCOUNTS_DOCTYPE } from 'doctypes' -import { build } from 'cozy-harvest-lib/dist/helpers/accounts' -import { - createAccount, - fetchAccount, - deleteAccount, - updateAccount, -} from 'cozy-harvest-lib/dist/connections/accounts' +import logApp from 'utils/logger' import TriggerService from './triggers.service' -import { DateTime } from 'luxon' + +const logStack = logger.namespace('accountService') export default class AccountService { private _client: Client @@ -48,7 +53,10 @@ export default class AccountService { const account: Account = await fetchAccount(this._client, id) return account } catch (error) { - console.log(error) + const errorMessage = `Get account failed :${JSON.stringify(error)}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw new Error('Get account failed') } } @@ -93,8 +101,11 @@ export default class AccountService { } else { return accounts[0] ? accounts[0] : null } - } catch (err) { - console.error(`Error GetAccountByType: ${err}`) + } catch (error) { + const errorMessage = `Error GetAccountByType: ${JSON.stringify(error)}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) return null } } @@ -108,8 +119,11 @@ export default class AccountService { const { data: accounts }: QueryResult<Account[]> = await this._client.query(query) return accounts - } catch (err) { - console.error(`Error: GetAccountsByType: ${err}`) + } catch (error) { + const errorMessage = `Error: GetAccountsByType: ${JSON.stringify(error)}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) return [] } } @@ -119,7 +133,10 @@ export default class AccountService { const updatedAccount: Account = await updateAccount(this._client, account) return updatedAccount } catch (error) { - console.log(error) + const errorMessage = `Update account failed: ${JSON.stringify(error)}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw new Error('Update account failed') } } @@ -129,8 +146,11 @@ export default class AccountService { await deleteAccount(this._client, account) return true } catch (error) { - console.log(error) - throw new Error('Delete account failed') + const errorMessage = `Delete account failed` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) + throw new Error(errorMessage) } } diff --git a/src/services/challenge.service.ts b/src/services/challenge.service.ts index f08afb593d08dbcc00a9aa2489594f27f4932cee..fb7c042a7eb0a63983263c6bfdcb8e35a14359f5 100644 --- a/src/services/challenge.service.ts +++ b/src/services/challenge.service.ts @@ -1,43 +1,48 @@ -import { Client, QueryDefinition, QueryResult, Q } from 'cozy-client' -import { DateTime } from 'luxon' +import * as Sentry from '@sentry/react' +import { Client, Q, QueryDefinition, QueryResult } from 'cozy-client' +import logger from 'cozy-logger' import { CHALLENGE_DOCTYPE, USERCHALLENGE_DOCTYPE } from 'doctypes' +import { FluidState, FluidType } from 'enum/fluid.enum' +import { TimeStep } from 'enum/timeStep.enum' +import { UserActionState } from 'enum/userAction.enum' +import { + UserChallengeState, + UserChallengeSuccess, + UserChallengeUpdateFlag, +} from 'enum/userChallenge.enum' +import { UserDuelState } from 'enum/userDuel.enum' +import { UserExplorationState } from 'enum/userExploration.enum' +import { DateTime } from 'luxon' import { ChallengeEntity, - UserChallenge, - UserChallengeEntity, - DuelEntity, - UserDuel, Datachart, Dataload, + DuelEntity, + Ecogesture, + ExplorationEntity, + FluidStatus, QuizEntity, Relation, - TimePeriod, - UserQuiz, - UserExploration, - ExplorationEntity, RelationEntitiesObject, - FluidStatus, - Ecogesture, + TimePeriod, UserAction, + UserChallenge, + UserChallengeEntity, + UserDuel, + UserExploration, + UserQuiz, } from 'models' -import { - UserChallengeState, - UserChallengeSuccess, - UserChallengeUpdateFlag, -} from 'enum/userChallenge.enum' -import { TimeStep } from 'enum/timeStep.enum' -import { UserDuelState } from 'enum/userDuel.enum' +import ConsumptionDataManager from 'services/consumption.service' import DuelService from 'services/duel.service' import QuizService from 'services/quiz.service' -import ConsumptionDataManager from 'services/consumption.service' -import { getRelationship, getRelationshipHasMany } from 'utils/utils' import { getLagDays } from 'utils/date' -import ExplorationService from './exploration.service' -import { FluidState, FluidType } from 'enum/fluid.enum' -import { UserExplorationState } from 'enum/userExploration.enum' -import { UserActionState } from 'enum/userAction.enum' -import ActionService from './action.service' +import logApp from 'utils/logger' import { getRoundFloat } from 'utils/math' +import { getRelationship, getRelationshipHasMany } from 'utils/utils' +import ActionService from './action.service' +import ExplorationService from './exploration.service' + +const logStack = logger.namespace('challengeService') export default class ChallengeService { private readonly _client: Client @@ -473,9 +478,14 @@ export default class ChallengeService { await this._client.destroy(challengeEntity[index]) } return true - } catch (err) { - console.log(err) - throw err + } catch (error) { + const errorMessage = `deleteAllChallengeEntities :${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) + throw error } } @@ -494,9 +504,9 @@ export default class ChallengeService { return userChallenges } - /* + /** * Retrieve dataloads for ongoing duel - * sucess return: UserChallenge, Dataload[] + * success return: UserChallenge, Dataload[] * failure throw error */ public async initChallengeDuelProgress( @@ -527,10 +537,12 @@ export default class ChallengeService { ) return { updatedUserChallenge, dataloads } } catch (error) { - console.log( - 'Challenge service error on initChallengeDuelProgress : ', + const errorMessage = `Challenge service error on initChallengeDuelProgress: ${JSON.stringify( error - ) + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } @@ -564,7 +576,12 @@ export default class ChallengeService { this.parseUserChallengeEntityToUserChallenge(updatedUserChallengeEntity) return updatedUserChallenge } catch (error) { - console.log('Challenge service error on startUserChallenge : ', error) + const errorMessage = `Challenge service error on startUserChallenge: ${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } @@ -770,7 +787,12 @@ export default class ChallengeService { this.parseUserChallengeEntityToUserChallenge(userChallengeEntity) return result } catch (error) { - console.log('Update user challenge error : ', error) + const errorMessage = `Update user challenge error: ${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } diff --git a/src/services/connection.service.ts b/src/services/connection.service.ts index fb8563366c2a10cc75539b2c8a7b226a5cbd1200..0a1f855ba4c2f6baa514bf5748bd78d8a4f35d15 100644 --- a/src/services/connection.service.ts +++ b/src/services/connection.service.ts @@ -10,6 +10,11 @@ import AccountService from 'services/account.service' import TriggerService from 'services/triggers.service' import KonnectorService from 'services/konnector.service' import { SgeStore } from 'models/sgeStore.model' +import logger from 'cozy-logger' +import * as Sentry from '@sentry/react' +import logApp from 'utils/logger' + +const logStack = logger.namespace('connectionService') export default class ConnectionService { private _client: Client @@ -29,7 +34,11 @@ export default class ConnectionService { konnectorId ) if (!konnector || !konnector.slug) { - throw new Error(`Could not find konnector for ${konnectorId}`) + const errorMessage = `Could not find konnector for ${konnectorId}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) + throw new Error(errorMessage) } // Creation of the account linked to the konnector retrieved let accountAuthData: AccountAuthData | AccountSgeData @@ -55,7 +64,11 @@ export default class ConnectionService { accountAuthData ) if (!account || !account._id) { - throw new Error(`Error during account creation`) + const errorMessage = `Error during account creation` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) + throw new Error(errorMessage) } // creation of the trigger for the konnector retrieve and the created account const triggersServices = new TriggerService(this._client) @@ -64,7 +77,11 @@ export default class ConnectionService { konnector ) if (!trigger) { - throw new Error(`Error during trigger creation`) + const errorMessage = `Error during trigger creation` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) + throw new Error(errorMessage) } return { account: account, diff --git a/src/services/customPopup.service.ts b/src/services/customPopup.service.ts index c838c8778e6aea20409d01a536ea27ceec72c331..1eee45690bf3851ca28cd1f33dff3a13f48dfde0 100644 --- a/src/services/customPopup.service.ts +++ b/src/services/customPopup.service.ts @@ -1,19 +1,23 @@ +import * as Sentry from '@sentry/react' import { Client } from 'cozy-client' -import { CustomPopup } from 'models/customPopup.model' -import EnvironmentService from './environment.service' import logger from 'cozy-logger' import { REMOTE_ORG_ECOLYO_AGENT_CUSTOM_POPUP, REMOTE_ORG_ECOLYO_AGENT_CUSTOM_POPUP_REC, } from 'doctypes/remote/org.ecolyo.agent.custom.popup' -const log = logger.namespace('customPupopService') +import { CustomPopup } from 'models/customPopup.model' +import logApp from 'utils/logger' +import EnvironmentService from './environment.service' + +const logStack = logger.namespace('customPopupService') + export default class CustomPupopService { private readonly _client: Client constructor(_client: Client) { this._client = _client } - /* + /** * Get information from backoffice about the status of custom popup * On success, respond the customPopup * Else, throw an error @@ -24,15 +28,18 @@ export default class CustomPupopService { ? REMOTE_ORG_ECOLYO_AGENT_CUSTOM_POPUP : REMOTE_ORG_ECOLYO_AGENT_CUSTOM_POPUP_REC try { + logApp.info('[Initialization] Getting CustomPopup') const result = await this._client .getStackClient() .fetchJSON('GET', remoteUrl) return result as CustomPopup } catch (error) { - log( - 'error', - `getCustomPopup: Failed to get custom popup:${JSON.stringify(error)}` - ) + const errorMessage = `getCustomPopup: Failed to get custom popup:${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) } } } diff --git a/src/services/dateChart.service.ts b/src/services/dateChart.service.ts index 5e4e5f7393a8d819a5103cd35b49e39c51dbb3bd..7f7ecc1507f9ad5541546ab7eff5a5413dfbfad4 100644 --- a/src/services/dateChart.service.ts +++ b/src/services/dateChart.service.ts @@ -1,7 +1,6 @@ -import { DateTime, Interval } from 'luxon' - import { FluidType } from 'enum/fluid.enum' import { TimeStep } from 'enum/timeStep.enum' +import { DateTime, Interval } from 'luxon' import { FluidConfig, TimePeriod } from 'models' import ConfigService from './fluidConfig.service' diff --git a/src/services/duel.service.ts b/src/services/duel.service.ts index 867b53c8eecee4d72ecbcc8d04701aba002085ae..be3409ffbd2282f035fc7ce321c8efa7a5a1784a 100644 --- a/src/services/duel.service.ts +++ b/src/services/duel.service.ts @@ -1,21 +1,26 @@ -import { Client, QueryDefinition, QueryResult, Q } from 'cozy-client' +import * as Sentry from '@sentry/react' +import { Client, Q, QueryDefinition, QueryResult } from 'cozy-client' +import logger from 'cozy-logger' import { DUEL_DOCTYPE } from 'doctypes/com-grandlyon-ecolyo-duel' -import { UserDuelState } from 'enum/userDuel.enum' -import { DateTime, Duration } from 'luxon' -import ConsumptionService from './consumption.service' -import PerformanceService from './performanceIndicator.service' import { FluidState, FluidType } from 'enum/fluid.enum' import { TimeStep } from 'enum/timeStep.enum' +import { UserDuelState } from 'enum/userDuel.enum' +import { DateTime, Duration } from 'luxon' import { - UserDuel, - DuelEntity, - TimePeriod, - PerformanceIndicator, Datachart, Dataload, + DuelEntity, + FluidStatus, + PerformanceIndicator, + TimePeriod, + UserDuel, } from 'models' -import { FluidStatus } from 'models' +import logApp from 'utils/logger' import { getRoundFloat } from 'utils/math' +import ConsumptionService from './consumption.service' +import PerformanceService from './performanceIndicator.service' + +const logStack = logger.namespace('duelService') export default class DuelService { private readonly _client: Client @@ -149,9 +154,12 @@ export default class DuelService { await this._client.destroy(dueles[index]) } return true - } catch (err) { - console.log(err) - throw err + } catch (error) { + const errorMessage = `deleteAllDuelEntities:${JSON.stringify(error)}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) + throw error } } diff --git a/src/services/ecogesture.service.ts b/src/services/ecogesture.service.ts index 393699d1b05cc5f4fe7172349b980811283f739c..d3bc02cfc06627a571650c8c3944418ac4989b43 100644 --- a/src/services/ecogesture.service.ts +++ b/src/services/ecogesture.service.ts @@ -1,12 +1,16 @@ -import { Client, QueryDefinition, QueryResult, Q } from 'cozy-client' +import * as Sentry from '@sentry/react' +import { Client, Q, QueryDefinition, QueryResult } from 'cozy-client' import { ECOGESTURE_DOCTYPE } from 'doctypes' -import { Usage } from 'enum/ecogesture.enum' +import { Season, Usage } from 'enum/ecogesture.enum' import { FluidType } from 'enum/fluid.enum' import { IndividualOrCollective, WarmingType } from 'enum/profileType.enum' -import { Season } from 'enum/ecogesture.enum' +import { orderBy } from 'lodash' import { Ecogesture } from 'models' import { ProfileEcogesture } from 'models/profileEcogesture.model' -import { orderBy } from 'lodash' +import logApp from 'utils/logger' +import logger from 'cozy-logger' + +const logStack = logger.namespace('ecogestureService') export default class EcogestureService { private readonly _client: Client @@ -65,7 +69,12 @@ export default class EcogestureService { } return true } catch (error) { - console.log('Error deleteAllEcogestures: ', error) + const errorMessage = `Error deleteAllEcogestures: ${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } @@ -83,7 +92,12 @@ export default class EcogestureService { } return true } catch (error) { - console.log('Error reinitAllEcogestures: ', error) + const errorMessage = `Error reinitAllEcogestures: ${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } diff --git a/src/services/enedisMonthlyAnalysisData.service.ts b/src/services/enedisMonthlyAnalysisData.service.ts index ce18173c4f017cd31cdb401e2f50674c0bbc3071..f8a2af62788fcfaaee295a1f3f3a2e3910c7e090 100644 --- a/src/services/enedisMonthlyAnalysisData.service.ts +++ b/src/services/enedisMonthlyAnalysisData.service.ts @@ -1,5 +1,6 @@ -import { Client, QueryDefinition, QueryResult, Q } from 'cozy-client' - +import * as Sentry from '@sentry/react' +import { Client, Q, QueryDefinition, QueryResult } from 'cozy-client' +import logger from 'cozy-logger' import { ENEDIS_MAXPOWER_DOCTYPE, ENEDIS_MONTHLY_ANALYSIS_DATA_DOCTYPE, @@ -12,7 +13,9 @@ import { EnedisMonthlyAnalysisData, } from 'models/enedisMonthlyAnalysis' import { MaxPowerEntity } from 'models/maxPower.model' +import logApp from 'utils/logger' +const logStack = logger.namespace('enedisMonthlyAnalysisDataService') export default class EnedisMonthlyAnalysisDataService { private readonly _client: Client @@ -136,7 +139,12 @@ export default class EnedisMonthlyAnalysisDataService { ) return EnedisMonthlyAnalysis } catch (error) { - console.log('Error creating new EnedisMonthlyAnalysis: ', error) + const errorMessage = `Error creating new EnedisMonthlyAnalysis: ${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } diff --git a/src/services/environement.service.spec.ts b/src/services/environement.service.spec.ts index 1720cee30411f5e5275e66916d0f098f5115d8fc..02799398bd98156abbeeaa72b1c30edffc3345a0 100644 --- a/src/services/environement.service.spec.ts +++ b/src/services/environement.service.spec.ts @@ -1,9 +1,14 @@ import EnvironmentService from './environment.service' +declare const global: { + __IS_ALPHA__: boolean + __DEVELOPMENT__: boolean +} + describe('Environement service', () => { const environmentService = new EnvironmentService() - describe('isProduction method', () => { + describe('isProduction()', () => { it('should return true and prod url', async () => { global.__IS_ALPHA__ = false const result = environmentService.isProduction() @@ -20,4 +25,47 @@ describe('Environement service', () => { expect(url).toEqual('https://ecolyo-agent-rec.grandlyon.com') }) }) + + describe('isAlpha()', () => { + it('should return false', () => { + global.__IS_ALPHA__ = false + expect(environmentService.isAlpha()).toBe(false) + }) + it('should return true', () => { + global.__IS_ALPHA__ = true + expect(environmentService.isAlpha()).toBe(true) + }) + }) + + describe('isLocal()', () => { + it('should return false', () => { + global.__DEVELOPMENT__ = false + expect(environmentService.isLocal()).toBe(false) + }) + it('should return true', () => { + global.__DEVELOPMENT__ = true + expect(environmentService.isLocal()).toBe(true) + }) + }) + + describe('isDev()', () => { + it('should return false', () => { + global.__DEVELOPMENT__ = false + global.__IS_ALPHA__ = false + expect(environmentService.isDev()).toBe(false) + }) + it('should return true', () => { + global.__DEVELOPMENT__ = true + global.__IS_ALPHA__ = false + expect(environmentService.isDev()).toBe(true) + + global.__DEVELOPMENT__ = false + global.__IS_ALPHA__ = true + expect(environmentService.isDev()).toBe(true) + + global.__DEVELOPMENT__ = true + global.__IS_ALPHA__ = true + expect(environmentService.isDev()).toBe(true) + }) + }) }) diff --git a/src/services/environment.service.ts b/src/services/environment.service.ts index 62768a1b92467b66f0643775fc6fad7ce2c42f90..ab969e750bb0058e5db0e7ed7852414d2b84e3bb 100644 --- a/src/services/environment.service.ts +++ b/src/services/environment.service.ts @@ -2,11 +2,19 @@ declare const __IS_ALPHA__: boolean declare const __DEVELOPMENT__: boolean export default class EnvironmentService { - private isAlpha() { - if (__IS_ALPHA__) { - return __IS_ALPHA__ - } - return false + public isAlpha() { + return __IS_ALPHA__ + } + + public isLocal() { + return __DEVELOPMENT__ + } + + /** + * Returns true for local OR alpha + */ + public isDev() { + return __IS_ALPHA__ || __DEVELOPMENT__ } public isProduction() { @@ -15,6 +23,7 @@ export default class EnvironmentService { } return false } + public getPublicURL() { if (!this.isAlpha()) { return 'https://ecolyo-agent.apps.grandlyon.com' @@ -22,8 +31,4 @@ export default class EnvironmentService { return 'https://ecolyo-agent-rec.grandlyon.com' } } - - public isLocal() { - return __DEVELOPMENT__ - } } diff --git a/src/services/fluidsPrices.service.ts b/src/services/fluidsPrices.service.ts index c4c4c3615ea8d5e7f2e4c342ef19a37be1649d7a..91dea069893fa1b05968be1ef058284c99588676 100644 --- a/src/services/fluidsPrices.service.ts +++ b/src/services/fluidsPrices.service.ts @@ -1,10 +1,15 @@ -import { Q, Client, QueryDefinition, QueryResult } from 'cozy-client' +import * as Sentry from '@sentry/react' +import { Client, Q, QueryDefinition, QueryResult } from 'cozy-client' +import logger from 'cozy-logger' import { FLUIDPRICES_DOCTYPE } from 'doctypes' import { FluidType } from 'enum/fluid.enum' import { DateTime } from 'luxon' import { FluidPrice } from 'models' +import logApp from 'utils/logger' import ConfigService from './fluidConfig.service' +const logStack = logger.namespace('fluidPricesService') + export default class FluidPricesService { private readonly _client: Client @@ -105,6 +110,10 @@ export default class FluidPricesService { } return true } catch (error) { + const errorMessage = `deleteAllFluidsPrices: ${JSON.stringify(error)}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) return false } } @@ -139,7 +148,12 @@ export default class FluidPricesService { await this._client.create(FLUIDPRICES_DOCTYPE, newPrice) return createdPrice } catch (error) { - console.log('Error creating new createdPrice: ', error) + const errorMessage = `'Error creating new createdPrice: ${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } diff --git a/src/services/initialization.service.ts b/src/services/initialization.service.ts index d23da462d770a34b347b9de974d67d2ae568b0ce..2ff22d6eda74a41820c0dbf07cf0294e293a6494 100644 --- a/src/services/initialization.service.ts +++ b/src/services/initialization.service.ts @@ -1,4 +1,6 @@ +import * as Sentry from '@sentry/react' import { Client } from 'cozy-client' +import logger from 'cozy-logger' import challengeEntityData from 'db/challengeEntity.json' import duelEntityData from 'db/duelEntity.json' import ecogestureData from 'db/ecogestureData.json' @@ -13,6 +15,10 @@ import { PROFILE_DOCTYPE, QUIZ_DOCTYPE, } from 'doctypes' +import { + REMOTE_ORG_ECOLYO_AGENT_PRICES, + REMOTE_ORG_ECOLYO_AGENT_PRICES_REC, +} from 'doctypes/remote/org.ecolyo.agent.prices' import { FluidType } from 'enum/fluid.enum' import { DateTime } from 'luxon' import { @@ -37,18 +43,15 @@ import ProfileService from 'services/profile.service' import QuizService from 'services/quiz.service' import { getActualAnalysisDate } from 'utils/date' import { hashFile } from 'utils/hash' -import log from 'utils/logger' +import logApp from 'utils/logger' import EnvironmentService from './environment.service' import FluidPricesService from './fluidsPrices.service' import ProfileEcogestureService from './profileEcogesture.service' import ProfileTypeEntityService from './profileTypeEntity.service' import TermsService from './terms.service' -import logger from 'cozy-logger' -import { - REMOTE_ORG_ECOLYO_AGENT_PRICES, - REMOTE_ORG_ECOLYO_AGENT_PRICES_REC, -} from 'doctypes/remote/org.ecolyo.agent.prices' -const cozyLog = logger.namespace('initializationService') + +const logStack = logger.namespace('initializationService') + export default class InitializationService { private readonly _client: Client private readonly _setinitStep: React.Dispatch<React.SetStateAction<InitSteps>> @@ -68,10 +71,10 @@ export default class InitializationService { this._setinitStepError = _setinitStepError } - /* + /** * Check if profil exist * If not, the profil is created - * sucess return: profil + * success return: profil * failure return: null */ public async initProfile(): Promise<Profile | null> { @@ -86,13 +89,13 @@ export default class InitializationService { profileData[0] ) if (newProfile) { - log.info('[Initialization] Profile created') + logApp.info('[Initialization] Profile created') } else { this._setinitStepError(InitStepsErrors.PROFILE_ERROR) throw new Error('initProfile: Profile not created') } } else { - log.info('[Initialization] Profile loaded') + logApp.info('[Initialization] Profile loaded') } const updatedProfile = await profileService.updateProfile({ lastConnectionDate: DateTime.local().setZone('utc', { @@ -102,26 +105,37 @@ export default class InitializationService { return updatedProfile } catch (error) { this._setinitStepError(InitStepsErrors.PROFILE_ERROR) - log.error('Initialization error - initProfile: ', error) + + const errorMessage = `Initialization error - initProfile: :${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } - /* + /** * Check if profileType exist * If not, the profileType is created - * sucess return: profileType + * success return: profileType * failure return: null */ public async initProfileType(): Promise<ProfileType | null> { const profileTypeEntityService = new ProfileTypeEntityService(this._client) try { const loadedProfileType = await profileTypeEntityService.getProfileType() - log.info('[Initialization] ProfileType loaded') + logApp.info('[Initialization] ProfileType loaded') return loadedProfileType } catch (error) { this._setinitStepError(InitStepsErrors.PROFILETYPE_ERROR) - log.error('Initialization error - initProfileType: ', error) + const errorMessage = `Initialization error - initProfileType: ${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } @@ -130,11 +144,16 @@ export default class InitializationService { try { const loadedProfileEcogesture = await profileEcogestureService.getProfileEcogesture() - log.info('[Initialization] ProfileEcogesture loaded') + logApp.info('[Initialization] ProfileEcogesture loaded') return loadedProfileEcogesture } catch (error) { this._setinitStepError(InitStepsErrors.PROFILETYPE_ERROR) - log.error('Initialization error - initProfileEcogesture: ', error) + const errorMessage = `Initialization error - initProfileEcogesture: ${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } @@ -162,11 +181,16 @@ export default class InitializationService { 'initEcogesture: Created ecogesture type entities does not match' ) } - log.info('[Initialization] Ecogesture list created') + logApp.info('[Initialization] Ecogesture list created') return hashEcogestureType } catch (error) { this._setinitStepError(InitStepsErrors.ECOGESTURE_ERROR) - log.error('Initialization error - initEcogesture: ', error) + const errorMessage = `Initialization error - initEcogesture: ${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } @@ -198,16 +222,21 @@ export default class InitializationService { 'initEcogesture: Created ecogesture type entities does not match' ) } - log.info('[Initialization] Ecogesture updated') + logApp.info('[Initialization] Ecogesture updated') return hashEcogestureType } catch (error) { this._setinitStepError(InitStepsErrors.ECOGESTURE_ERROR) - log.error('Initialization error - initEcogesture: ', error) + const errorMessage = `Initialization error - initEcogesture: ${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } else { // Doctype already up to date - log.info('[Initialization] Ecogesture already up-to-date') + logApp.info('[Initialization] Ecogesture already up-to-date') return hashEcogestureType } } @@ -217,7 +246,7 @@ export default class InitializationService { // Populate data if none ecogesture exists const loadedPrices = await fpService.getAllPrices() if (loadedPrices?.length) { - log.info('[Initialization] FluidPrices db already created') + logApp.info('[Initialization] FluidPrices db already created') return true } else { try { @@ -241,12 +270,14 @@ export default class InitializationService { for (const price of allPrices) { await fpService.createPrice(price) } - - log.info('[Initialization] FluidPrices db created successfully') - return true - } catch (err) { - log.error('Initialization error - initFluidPrices: ', err) - cozyLog('error', `Initialization error - initFluidPrices: ${err}`) + throw new Error('test') + } catch (error) { + const errorMessage = `Initialization error - initFluidPrices: ${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) return false } } @@ -273,11 +304,16 @@ export default class InitializationService { 'initChallengeEntity: Created challenge entities does not match' ) } - log.info('[Initialization] Challenge entities created') + logApp.info('[Initialization] Challenge entities created') return challengeHash } catch (error) { this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR) - log.error('Initialization error - initChallengeEntity: ', error) + const errorMessage = `Initialization error - initChallengeEntity: ${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } @@ -299,16 +335,21 @@ export default class InitializationService { 'initChallengeEntity: Created challenge entities does not match' ) } - log.info('[Initialization] Challenge entities updated') + logApp.info('[Initialization] Challenge entities updated') return challengeHash } catch (error) { this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR) - log.error('Initialization error - initChallengeEntity: ', error) + const errorMessage = `Initialization error - initChallengeEntity: ${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } else { // Doctype already up to date - log.info('[Initialization] Challenge Entity loaded') + logApp.info('[Initialization] Challenge Entity loaded') return challengeHash } } @@ -332,11 +373,16 @@ export default class InitializationService { 'initDuelEntity: Created duel entities does not match' ) } - log.info('[Initialization] UserDuel entities created') + logApp.info('[Initialization] UserDuel entities created') return hashDuelEntity } catch (error) { this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR) - log.error('Initialization error - initDuelEntity: ', error) + const errorMessage = `Initialization error - initDuelEntity: ${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } @@ -358,16 +404,21 @@ export default class InitializationService { 'initDuelEntity: Created duel entities does not match' ) } - log.info('[Initialization] UserDuel entities updated') + logApp.info('[Initialization] UserDuel entities updated') return hashDuelEntity } catch (error) { this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR) - log.error('Initialization error - initDuelEntity: ', error) + const errorMessage = `Initialization error - initDuelEntity: ${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } else { // Doctype already up to date - log.info('[Initialization] Duel Entity loaded') + logApp.info('[Initialization] Duel Entity loaded') return hashDuelEntity } } @@ -392,11 +443,16 @@ export default class InitializationService { ) } - log.info('[Initialization] Quiz entities created') + logApp.info('[Initialization] Quiz entities created') return quizHash } catch (error) { this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR) - log.error('Initialization error - initQuizEntity: ', error) + const errorMessage = `Initialization error - initQuizEntity: ${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } @@ -418,16 +474,21 @@ export default class InitializationService { 'initQuizEntity: Created quiz entities does not match' ) } - log.info('[Initialization] Quiz entities updated') + logApp.info('[Initialization] Quiz entities updated') return quizHash } catch (error) { this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR) - log.error('Initialization error - initQuizEntity: ', error) + const errorMessage = `Initialization error - initQuizEntity: ${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } else { // Doctype already up to date - log.info('[Initialization] Quiz Entity loaded') + logApp.info('[Initialization] Quiz Entity loaded') return quizHash } } @@ -455,11 +516,16 @@ export default class InitializationService { 'initExplorationEntity: Created exploration entities does not match' ) } - log.info('[Initialization] Exploration entities created') + logApp.info('[Initialization] Exploration entities created') return explorationHash } catch (error) { this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR) - log.error('Initialization error - initExplorationEntity: ', error) + const errorMessage = `Initialization error - initExplorationEntity: ${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } @@ -484,16 +550,21 @@ export default class InitializationService { 'initExplorationEntity: Created exploration entities does not match' ) } - log.info('[Initialization] Exploration entities updated') + logApp.info('[Initialization] Exploration entities updated') return explorationHash } catch (error) { this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR) - log.error('Initialization error - initExplorationEntity: ', error) + const errorMessage = `Initialization error - initExplorationEntity: ${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } else { // Doctype already up to date - log.info('[Initialization] Exploration Entity loaded') + logApp.info('[Initialization] Exploration Entity loaded') return explorationHash } } @@ -513,7 +584,9 @@ export default class InitializationService { haveSeenLastAnalysis: profile.haveSeenLastAnalysis, } } else { - log.info('[Initialization] Analysis information from profile updated') + logApp.info( + '[Initialization] Analysis information from profile updated' + ) return { monthlyAnalysisDate: actualAnalysisDate, haveSeenLastAnalysis: profile.isFirstConnection ? true : false, @@ -521,14 +594,19 @@ export default class InitializationService { } } catch (error) { this._setinitStepError(InitStepsErrors.ANALYSIS_ERROR) - log.error('Initialization error - initAnalysis: ', error) + const errorMessage = `Initialization error - initAnalysis: ${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } - /* + /** * Check if FluidTypes exist - * sucess return: FluidType[] + * success return: FluidType[] * failure throw error */ public async initFluidTypes(): Promise<FluidType[]> { @@ -536,7 +614,7 @@ export default class InitializationService { try { const fluidtypes = await kss.getKonnectorAccountStatus() if (fluidtypes) { - log.info('[Initialization] Fluid Types loaded') + logApp.info('[Initialization] Fluid Types loaded') return fluidtypes } else { this._setinitStepError(InitStepsErrors.CONSOS_ERROR) @@ -544,14 +622,20 @@ export default class InitializationService { } } catch (error) { this._setinitStepError(InitStepsErrors.CONSOS_ERROR) - log.error('Initialization error - initFluidTypes: ', error) + logApp.error('Initialization error - : ', error) + const errorMessage = `Initialization error - initFluidTypes: ${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } - /* + /** * For each fluid get the trigger status and the last data date - * sucess return: FluidStatus[] + * success return: FluidStatus[] * failure throw error */ public async initFluidStatus(): Promise<FluidStatus[]> { @@ -560,7 +644,7 @@ export default class InitializationService { this._setinitStep(InitSteps.CONSOS) const fluidStatus = await fs.getFluidStatus() if (fluidStatus) { - log.info('[Initialization] Fluid Status loaded') + logApp.info('[Initialization] Fluid Status loaded') return fluidStatus } else { this._setinitStepError(InitStepsErrors.CONSOS_ERROR) @@ -568,14 +652,19 @@ export default class InitializationService { } } catch (error) { this._setinitStepError(InitStepsErrors.CONSOS_ERROR) - log.error('Initialization error - initFluidStatus: ', error) + const errorMessage = `Initialization error - initFluidStatus: ${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } - /* + /** * Build the userChallengeList - * sucess return: UserChallenge[] + * success return: UserChallenge[] * failure throw error */ public async initUserChallenges( @@ -587,7 +676,7 @@ export default class InitializationService { fluidStatus ) if (userChallengeList) { - log.info('[Initialization] Challenges loaded') + logApp.info('[Initialization] Challenges loaded') return userChallengeList } else { this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR) @@ -595,14 +684,19 @@ export default class InitializationService { } } catch (error) { this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR) - log.error('Initialization error - initUserChallenges: ', error) + const errorMessage = `Initialization error - initUserChallenges: ${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } - /* + /** * Retrieve dataloads for ongoing duel - * sucess return: UserChallenge, Dataload[] + * success return: UserChallenge, Dataload[] * failure throw error */ public async initDuelProgress(userChallenge: UserChallenge): Promise<{ @@ -616,7 +710,10 @@ export default class InitializationService { return { updatedUserChallenge, dataloads } } catch (error) { this._setinitStepError(InitStepsErrors.CHALLENGES_ERROR) - log.error('Initialization error: ', error) + const errorMessage = `Initialization error - : ${JSON.stringify(error)}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } @@ -638,36 +735,41 @@ export default class InitializationService { if (isLastConsentValidated) { termsStatus.accepted = true termsStatus.versionType = 'init' - log.info( + logApp.info( '[Initialization] Last Consent successfully loaded and valid' ) } else { termsStatus.versionType = 'init' termsStatus.accepted = false - log.info('[Initialization] Consent not up-to-date') + logApp.info('[Initialization] Consent not up-to-date') } } else { const versionType = await termService.getTermsVersionType() if (versionType === 'minor') { termsStatus.accepted = false termsStatus.versionType = 'minor' - log.info('[Initialization] Minor Terms update detected') + logApp.info('[Initialization] Minor Terms update detected') } else { termsStatus.accepted = false termsStatus.versionType = 'major' - log.info('[Initialization] Major Terms update detected') + logApp.info('[Initialization] Major Terms update detected') } } } else { termsStatus.accepted = false termsStatus.versionType = 'init' - log.info('[Initialization] Init first terms') + logApp.info('[Initialization] Init first terms') } return termsStatus } catch (error) { this._setinitStepError(InitStepsErrors.CONSENT_ERROR) - log.error('Initialization error - initConsent: ', error) + const errorMessage = `Initialization error - initConsent: ${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } diff --git a/src/services/mail.service.ts b/src/services/mail.service.ts index 762b536615a35e1e6fc558163cca735fa5b0fff6..9528efdfd535ffc35a8cdb970e43be48b9ea0346 100644 --- a/src/services/mail.service.ts +++ b/src/services/mail.service.ts @@ -1,5 +1,9 @@ +import * as Sentry from '@sentry/react' import { Client } from 'cozy-client' +import logger from 'cozy-logger' +import logApp from 'utils/logger' +const logStack = logger.namespace('mailService') export default class MailService { public async SendMail( client: Client, @@ -9,9 +13,11 @@ export default class MailService { const jobCollection = client.collection('io.cozy.jobs') jobCollection.create('sendmail', mailInfo) } catch (error) { - // eslint-disable-next-line no-console - console.log(error) - throw new Error('Failed to send mail') + const errorMessage = `Failed to send mail` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) + throw new Error(errorMessage) } } } diff --git a/src/services/partnersInfo.service.ts b/src/services/partnersInfo.service.ts index dee233ed291c18df9a179c8648a14a636ecff13b..867f42f41710c64ccd0cca0d2f536a790da0dd9d 100644 --- a/src/services/partnersInfo.service.ts +++ b/src/services/partnersInfo.service.ts @@ -1,12 +1,15 @@ +import * as Sentry from '@sentry/react' import { Client } from 'cozy-client' -import { PartnersInfo } from 'models/partnersInfo.model' -import EnvironmentService from './environment.service' import logger from 'cozy-logger' import { REMOTE_ORG_ECOLYO_AGENT_PARTERS_INFO, REMOTE_ORG_ECOLYO_AGENT_PARTERS_INFO_REC, } from 'doctypes/remote/org.ecolyo.agent.partners.info' -const log = logger.namespace('partnersInfoService') +import { PartnersInfo } from 'models/partnersInfo.model' +import logApp from 'utils/logger' +import EnvironmentService from './environment.service' + +const logStack = logger.namespace('partnersInfoService') export default class PartnersInfoService { private readonly _client: Client @@ -14,7 +17,7 @@ export default class PartnersInfoService { this._client = _client } - /* + /** * Get information from backoffice about the status of partners' service * On success, respond the partnersInfo * Else, throw an error @@ -25,15 +28,18 @@ export default class PartnersInfoService { ? REMOTE_ORG_ECOLYO_AGENT_PARTERS_INFO : REMOTE_ORG_ECOLYO_AGENT_PARTERS_INFO_REC try { + logApp.info('[Initialization] Getting PartnersInfo') const result = await this._client .getStackClient() .fetchJSON('GET', remoteUrl) return result as PartnersInfo } catch (error) { - log( - 'error', - `getPartnersInfo: Failed to get partners info: ${JSON.stringify(error)}` - ) + const errorMessage = `getPartnersInfo: Failed to get partners info: ${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) } } } diff --git a/src/services/profileType.service.ts b/src/services/profileType.service.ts index 23a52afccfe73abe460ca8975f08eaff18ab0c0d..3aab00466cb866d4b3e60bba663f1bd975fef282 100644 --- a/src/services/profileType.service.ts +++ b/src/services/profileType.service.ts @@ -1,29 +1,33 @@ -import { - DetailsMonthlyForecast, - FluidForecast, - MonthlyForecast, - ProfileType, -} from 'models/profileType.model' -import { DateTime } from 'luxon' -import heatingData from 'constants/consumptionConstants/heating.json' -import cookingData from 'constants/consumptionConstants/cooking.json' -import elecSpeData from 'constants/consumptionConstants/electricSpecific.json' +import * as Sentry from '@sentry/react' import coldWaterData from 'constants/consumptionConstants/coldWater.json' +import cookingData from 'constants/consumptionConstants/cooking.json' import EcsData from 'constants/consumptionConstants/ecs.json' +import elecSpeData from 'constants/consumptionConstants/electricSpecific.json' +import heatingData from 'constants/consumptionConstants/heating.json' +import { Client } from 'cozy-client' +import logger from 'cozy-logger' +import { FluidType } from 'enum/fluid.enum' import { ConstructionYear, Floor, - IndividualOrCollective, HotWaterEquipment, HousingType, IndividualInsulationWork, + IndividualOrCollective, OutsideFacingWalls, ThreeChoicesAnswer, } from 'enum/profileType.enum' -import { FluidType } from 'enum/fluid.enum' +import { DateTime } from 'luxon' +import { + DetailsMonthlyForecast, + FluidForecast, + MonthlyForecast, + ProfileType, +} from 'models/profileType.model' +import logApp from 'utils/logger' import ConverterService from './converter.service' -import { Client } from 'cozy-client' -import log from 'utils/logger' + +const logStack = logger.namespace('profileTypeService') export default class ProfileTypeService { private readonly profileType: ProfileType @@ -31,7 +35,7 @@ export default class ProfileTypeService { private readonly year: number constructor(profileType: ProfileType, _client: Client, year: number) { - log.info( + logApp.info( '[ProfileType] Analysis loaded profileType related to: ', profileType.updateDate ? profileType.updateDate.toString() @@ -514,7 +518,10 @@ export default class ProfileTypeService { return monthDju } } catch (error) { - console.log('errFetch', error) + const errorMessage = `fetchDju error : ${JSON.stringify(error)}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) return heatingData.dju_average_by_month[month - 1] } } diff --git a/src/services/profileTypeEntity.service.ts b/src/services/profileTypeEntity.service.ts index d059662167a1efdb24bfa79a3e707e54b2fd69ac..361d60fa00ca35818b1db34aec9528b6edc7b673 100644 --- a/src/services/profileTypeEntity.service.ts +++ b/src/services/profileTypeEntity.service.ts @@ -1,10 +1,13 @@ +import * as Sentry from '@sentry/react' import { Client, Q, QueryDefinition, QueryResult } from 'cozy-client' -import { ProfileType, TimePeriod } from 'models' +import logger from 'cozy-logger' +import profileTypeData from 'db/profileTypeData.json' import { PROFILETYPE_DOCTYPE } from 'doctypes' import { DateTime } from 'luxon' -import profileTypeData from 'db/profileTypeData.json' -import log from 'utils/logger' +import { ProfileType, TimePeriod } from 'models' +import logApp from 'utils/logger' +const logStack = logger.namespace('profileTypeEntityService') export default class ProfileTypeEntityService { private readonly _client: Client @@ -34,15 +37,15 @@ export default class ProfileTypeEntityService { if (result) { return this.parseProfileTypeEntityToProfileType(profileType) } else { - log.debug('No profileType found for: ', date.toString()) - log.debug( + logApp.debug('No profileType found for: ', date.toString()) + logApp.debug( 'Checking if user has already filled a profileType and uses it as default' ) const query: QueryDefinition = Q(PROFILETYPE_DOCTYPE) const data: QueryResult<ProfileType[]> = await this._client.query(query) if (data.data.length) { const loadedProfileType: ProfileType = data.data[0] - log.debug( + logApp.debug( 'found oldest profileType filled by user : ', loadedProfileType ) @@ -53,7 +56,7 @@ export default class ProfileTypeEntityService { ...profileTypeData[0].profileType, updateDate: date, } - log.debug('No profileType were found, loading default profileType') + logApp.debug('No profileType were found, loading default profileType') return loadedProfileType } } @@ -159,6 +162,10 @@ export default class ProfileTypeEntityService { } return true } catch (error) { + const errorMessage = `deleteProfileTypes: ${JSON.stringify(error)}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) return false } } diff --git a/src/services/queryRunner.service.ts b/src/services/queryRunner.service.ts index 25437e189a76813c81f96ca47f0586671c9c319c..fdc6c671e06e986f9d3adcae576cc09b66b761c0 100644 --- a/src/services/queryRunner.service.ts +++ b/src/services/queryRunner.service.ts @@ -1,24 +1,23 @@ -import { Client, QueryDefinition, Q } from 'cozy-client' -import { DateTime, Interval } from 'luxon' +import { Client, Q, QueryDefinition } from 'cozy-client' +import { QueryResult } from 'cozy-client/types/types' import { + EGL_DAY_DOCTYPE, + EGL_MONTH_DOCTYPE, + EGL_YEAR_DOCTYPE, ENEDIS_DAY_DOCTYPE, + ENEDIS_MINUTE_DOCTYPE, ENEDIS_MONTH_DOCTYPE, ENEDIS_YEAR_DOCTYPE, GRDF_DAY_DOCTYPE, GRDF_MONTH_DOCTYPE, GRDF_YEAR_DOCTYPE, - EGL_DAY_DOCTYPE, - EGL_MONTH_DOCTYPE, - EGL_YEAR_DOCTYPE, - ENEDIS_MINUTE_DOCTYPE, } from 'doctypes' - +import { DataloadState } from 'enum/dataload.enum' import { FluidType } from 'enum/fluid.enum' import { TimeStep } from 'enum/timeStep.enum' +import { DateTime, Interval } from 'luxon' import { Dataload, TimePeriod } from 'models' -import { QueryResult } from 'cozy-client/types/types' -import log from 'utils/logger' -import { DataloadState } from 'enum/dataload.enum' +import logApp from 'utils/logger' export default class QueryRunner { // TODO to be clean up @@ -103,7 +102,7 @@ export default class QueryRunner { } catch (error) { // log stuff // throw new Error('Fetch data failed in query runner') - log.error('QueryRunner error: ', error) + logApp.error('QueryRunner error: ', error) } return result } diff --git a/src/services/quiz.service.ts b/src/services/quiz.service.ts index a44dd35faa96fe8da5afc0a3cc8e4ebc24246e21..8f6aa656e7543b081b4063f0e77d488fb6552c09 100644 --- a/src/services/quiz.service.ts +++ b/src/services/quiz.service.ts @@ -23,7 +23,7 @@ import { } from 'enum/userQuiz.enum' import { shuffle } from 'lodash' import { formatNumberValues } from 'utils/utils' -import log from 'utils/logger' +import logApp from 'utils/logger' export default class QuizService { private readonly _client: Client @@ -509,7 +509,7 @@ export default class QuizService { singleFluid: boolean ): Promise<number> { const consumptionService = new ConsumptionDataManager(this._client) - log.info('GetAverageOnGivenPeriod is about to call getgraphdata') + logApp.info('GetAverageOnGivenPeriod is about to call getgraphdata') const graphData = await consumptionService.getGraphData( interval, timeStep, diff --git a/src/services/terms.service.ts b/src/services/terms.service.ts index d4cfd9539aee7a5d79bdcd8bb38fa0e0d7425f1c..bb063f147f4557ab2e27a7d1b1a1cb083086472f 100644 --- a/src/services/terms.service.ts +++ b/src/services/terms.service.ts @@ -1,9 +1,13 @@ +import * as Sentry from '@sentry/react' import { Client, Q, QueryDefinition, QueryResult } from 'cozy-client' - +import logger from 'cozy-logger' import { TERMS_DOCTYPE } from 'doctypes' import { DateTime } from 'luxon' import { Term, VersionType } from 'models' +import logApp from 'utils/logger' import config from '../constants/config.json' + +const logStack = logger.namespace('termsService') export default class TermsService { private _client: Client @@ -83,7 +87,10 @@ export default class TermsService { await this._client.create(TERMS_DOCTYPE, newTerm) return createdTerm } catch (error) { - console.log('Error creating new term: ', error) + const errorMessage = `Error creating new term: ${JSON.stringify(error)}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw error } } diff --git a/src/services/triggers.service.ts b/src/services/triggers.service.ts index 12327b1cb55326a935055e4e596f1897d9198bf1..830b22746f0c4f0994b349dea8d529fb2a2d4035 100644 --- a/src/services/triggers.service.ts +++ b/src/services/triggers.service.ts @@ -1,4 +1,9 @@ +import * as Sentry from '@sentry/react' import { Client, Q, QueryDefinition, QueryResult } from 'cozy-client' +import triggersMutations from 'cozy-harvest-lib/dist/connections/triggers' +import { buildAttributes } from 'cozy-harvest-lib/dist/helpers/triggers' +import logger from 'cozy-logger' +import { TRIGGERS_DOCTYPE } from 'doctypes' import { Account, Konnector, @@ -6,10 +11,10 @@ import { TriggerAttributes, TriggerState, } from 'models' -import { buildAttributes } from 'cozy-harvest-lib/dist/helpers/triggers' -import triggersMutations from 'cozy-harvest-lib/dist/connections/triggers' import ConfigService from 'services/fluidConfig.service' -import { TRIGGERS_DOCTYPE } from 'doctypes' +import logApp from 'utils/logger' + +const logStack = logger.namespace('triggersService') export default class TriggerService { private _client: Client @@ -95,7 +100,10 @@ export default class TriggerService { await this._client.destroy(trigger) return true } catch (error) { - console.log(error) + const errorMessage = `Delete trigger failed: ${JSON.stringify(error)}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) throw new Error('Delete trigger failed') } } diff --git a/src/services/usageEvent.service.ts b/src/services/usageEvent.service.ts index 4297186503a3d73d9c62ab710aa0b6f5e749c4c4..6ab397dddc94aded55f52890a8a6c70860ede6ce 100644 --- a/src/services/usageEvent.service.ts +++ b/src/services/usageEvent.service.ts @@ -1,10 +1,12 @@ +import * as Sentry from '@sentry/react' import { Client, + MongoSelector, + Q, QueryDefinition, QueryResult, - Q, - MongoSelector, } from 'cozy-client' +import logger from 'cozy-logger' import { USAGEEVENT_DOCTYPE } from 'doctypes' import { UsageEventType } from 'enum/usageEvent.enum' import { DateTime } from 'luxon' @@ -14,6 +16,9 @@ import { UsageEventCreationEntity, UsageEventEntity, } from 'models' +import logApp from 'utils/logger' + +const logStack = logger.namespace('usageEventService') export default class UsageEventService { /** @@ -92,11 +97,13 @@ export default class UsageEventService { const { data: savedEvent } = await client.save(updatedEvent) return savedEvent } - } catch (err) { - console.log( - 'UsageEvent service error on udpateConnectionAttemptEvent : ', - err - ) + } catch (error) { + const errorMessage = `UsageEvent service error on udpateConnectionAttemptEvent: ${JSON.stringify( + error + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) } } @@ -117,10 +124,12 @@ export default class UsageEventService { aggregated: true, }) } catch (error) { - console.log( - 'UsageEvent service error on updateUsageEventsAggregated : ', + const errorMessage = `UsageEvent service error on updateUsageEventsAggregated: ${JSON.stringify( error - ) + )}` + logStack('error', errorMessage) + logApp.error(errorMessage) + Sentry.captureException(errorMessage) } } return true diff --git a/src/store/index.ts b/src/store/index.ts index b8b4b1e9bab40858d111966aa65880d517a4dee8..c7e28758b3055e2cb11a7fe17c3a8a3784788f71 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -24,6 +24,7 @@ import { ChartState } from 'models/chart.model' import { Client } from 'cozy-client' import { ProfileEcogesture } from 'models/profileEcogesture.model' import { profileEcogestureReducer } from './profileEcogesture/profileEcogesture.reducer' +import * as Sentry from '@sentry/react' export interface EcolyoState { global: GlobalState @@ -50,6 +51,8 @@ export interface AppStore { cozy: never } +const sentryReduxEnhancer = Sentry.createReduxEnhancer({}) + const configureStore = ( client: Client, persistedState: any @@ -63,8 +66,11 @@ const configureStore = ( cozy: client.reducer(), persistedState, }), - // eslint-disable-next-line prefer-spread - composeEnhancers(applyMiddleware.apply(null, middlewares)) + composeEnhancers( + // eslint-disable-next-line prefer-spread + applyMiddleware.apply(null, middlewares), + sentryReduxEnhancer + ) ) return store } diff --git a/src/targets/browser/index.tsx b/src/targets/browser/index.tsx index 92e2c8fec643ce85948c9e3320c1228eeff5b74e..f6ae853033dc29899b76261c543dbedfb47c23c9 100644 --- a/src/targets/browser/index.tsx +++ b/src/targets/browser/index.tsx @@ -3,23 +3,27 @@ declare let cozy: any declare let __PIWIK_TRACKER_URL__: string declare let __PIWIK_SITEID__: number +declare let __SENTRY_DSN__: string declare let Piwik: any -import '../../styles/index.scss' - -import React from 'react' -import { render } from 'react-dom' +import * as Sentry from '@sentry/react' +import { BrowserTracing } from '@sentry/tracing' import CozyClient, { Client, CozyProvider } from 'cozy-client' -import { Provider } from 'react-redux' -import configureStore from 'store' -import { I18n, initTranslation } from 'cozy-ui/transpiled/react/I18n' import { handleOAuthResponse } from 'cozy-harvest-lib/dist/helpers/oauth' -import { memoize } from 'lodash' -import manifest from '../../../manifest.webapp' +import { I18n, initTranslation } from 'cozy-ui/transpiled/react/I18n' import schema from 'doctypes' import { createBrowserHistory, History } from 'history' +import { memoize } from 'lodash' +import React from 'react' +import { render } from 'react-dom' +import { Provider } from 'react-redux' import { HashRouter } from 'react-router-dom' +import EnvironmentService from 'services/environment.service' +import configureStore from 'store' +import logApp from 'utils/logger' import MatomoTracker from 'utils/matomoTracker' +import manifest from '../../../manifest.webapp' +import '../../styles/index.scss' const setupApp = memoize(() => { const history: History = createBrowserHistory() @@ -46,6 +50,9 @@ const setupApp = memoize(() => { const persistedState: any = {} const store: any = configureStore(client, persistedState) + const envService = new EnvironmentService() + const isLocal = envService.isLocal() + const development = envService.isDev() cozy.bar.init({ appName: data.app.name, @@ -70,6 +77,21 @@ const setupApp = memoize(() => { }) } + Sentry.init({ + dsn: __SENTRY_DSN__, + integrations: [new BrowserTracing()], + // Set tracesSampleRate to 1.0 to capture 100% + // of transactions for performance monitoring. + // We recommend adjusting this value in production + // Set to 0 for local development + tracesSampleRate: isLocal ? 0 : 1.0, + + // Custom settings below + release: client.appMetadata.version, + environment: development ? 'development' : 'production', + // cast because init is somehow missing dsn property + } as Sentry.BrowserOptions) + return { root, store, client, locale, polyglot, history, tracker } }) @@ -101,7 +123,13 @@ if (!isSafari && 'serviceWorker' in navigator) { navigator.serviceWorker .register('/serviceWorker.js') .then(reg => console.log('service worker registered', reg.scope)) - .catch(err => console.log('service worker not registered', err)) + .catch(error => { + const errorMessage = `service worker not registered: ${JSON.stringify( + error + )}` + logApp.error(errorMessage) + Sentry.captureException(errorMessage) + }) }) } diff --git a/src/targets/services/aggregatorUsageEvents.ts b/src/targets/services/aggregatorUsageEvents.ts index 2599c301592f58005bb59c91cd41d4700a5b83b0..fbdbf208f27aef9a4d46fa81939868c9d25c87a8 100644 --- a/src/targets/services/aggregatorUsageEvents.ts +++ b/src/targets/services/aggregatorUsageEvents.ts @@ -21,8 +21,9 @@ import ProfileTypeEntityService from 'services/profileTypeEntity.service' import TermsService from 'services/terms.service' import { WarmingType } from 'enum/profileType.enum' import { FluidSlugType } from 'enum/fluidSlug.enum' +import * as Sentry from '@sentry/react' -const log = logger.namespace('aggregatorUsageEvents') +const logStack = logger.namespace('aggregatorUsageEvents') interface AggregatorUsageEventsProps { client: Client @@ -50,7 +51,7 @@ const sendIndicator = async ( ): Promise<boolean> => { try { const environmentService = new EnvironmentService() - log( + logStack( 'info', environmentService.isProduction() ? 'Sending data to dacc' @@ -72,10 +73,11 @@ const sendIndicator = async ( ) return true } catch (error) { - log( + logStack( 'error', `Error while sending indicator ${indicator.measureName} to remote doctype: ${error.message}` ) + Sentry.captureException(JSON.stringify({ error })) throw error } } @@ -353,7 +355,7 @@ const calculateConnectedKonnectorPerDay = async (client: Client) => { const connectedKonnectors = fluidStatus.filter( fluid => fluid.status === FluidState.DONE ) - log('info', 'calculateConnectedKonnectorPerDay') + logStack('info', 'calculateConnectedKonnectorPerDay') if (connectedKonnectors.length > 0) { const konnectorfluidTypes: FluidType[] = [] for (const konnector of connectedKonnectors) { @@ -485,7 +487,7 @@ const getConsumptionValue = async ( * @group [{ slug }, { seniority (in month) }, { profile (ECS, chauffage, etc...) }], */ const calculateConsumptionVariation = async (client: Client) => { - log('info', `calculateConsumptionVariation`) + logStack('info', `calculateConsumptionVariation`) const consumptionData = await getConsumptionValue(client, [ FluidType.ELECTRICITY, FluidType.GAS, @@ -561,7 +563,7 @@ const calculateConsumptionVariation = async (client: Client) => { }) if (events.length > 0 && consumptionVariationIndicator.value !== 0) { - log( + logStack( 'info', `Send variation indicator : ${JSON.stringify( consumptionVariationIndicator @@ -575,7 +577,7 @@ const calculateConsumptionVariation = async (client: Client) => { } const sendConnectionCount = async (client: Client) => { - log('info', `sendConnectionCount`) + logStack('info', `sendConnectionCount`) // Get daily connexion const events: UsageEvent[] = await UsageEventService.getEvents(client, { type: UsageEventType.CONNECTION_EVENT, @@ -616,7 +618,7 @@ const sendConnectionCount = async (client: Client) => { .toISODate(), value: uniqueDates, } - log( + logStack( 'info', `Send connectionMonthly indicator : ${JSON.stringify(connectionMonthly)}` ) @@ -624,7 +626,7 @@ const sendConnectionCount = async (client: Client) => { } const sendProfileCount = async (client: Client) => { - log('info', `sendProfileCount`) + logStack('info', `sendProfileCount`) // Get profile setEvents const events: UsageEvent[] = await UsageEventService.getEvents(client, { type: UsageEventType.PROFILE_SET_EVENT, @@ -648,7 +650,7 @@ const sendProfileCount = async (client: Client) => { } const sendEmailSubscription = async (client: Client) => { - log('info', `sendEmailSubscription`) + logStack('info', `sendEmailSubscription`) const profile = await new ProfileService(client).getProfile() if (profile?.sendAnalysisNotification) { const cameBackFromEmail: Indicator = { @@ -673,7 +675,7 @@ const sendEmailSubscription = async (client: Client) => { * @param client CozyClient */ const sendHalfHourConsumption = async (client: Client) => { - log('info', `sendHalfHourConsumption`) + logStack('info', `sendHalfHourConsumption`) const consumptionService = new ConsumptionService(client) const data = await consumptionService.getLastHourData( @@ -704,7 +706,7 @@ const sendHalfHourConsumption = async (client: Client) => { * @param client CozyClient */ const sendKonnectorEvents = async (client: Client) => { - log('info', `sendKonnectorEvents`) + logStack('info', `sendKonnectorEvents`) const slugs = Object.values(FluidSlugType) const today = DateTime.local().setZone('utc', { keepLocalTime: true, @@ -796,7 +798,7 @@ const sendKonnectorEvents = async (client: Client) => { * @param client CozyClient */ const sendKonnectorAttemptsMonthly = async (client: Client) => { - log('info', `sendkonnectorAttemptsMonthly`) + logStack('info', `sendkonnectorAttemptsMonthly`) const slugs = Object.values(FluidSlugType) const today = DateTime.local().setZone('utc', { keepLocalTime: true, @@ -815,7 +817,7 @@ const sendKonnectorAttemptsMonthly = async (client: Client) => { }, true ) - log('info', ` : ${JSON.stringify(konnectorEvents)}`) + logStack('info', ` : ${JSON.stringify(konnectorEvents)}`) // Check if there is a success (will be false or true since the event is triggered only for the first connexion) const success: boolean = @@ -1153,11 +1155,11 @@ const aggregateEvents = async ( const AggregatorUsageEvents = async ({ client, }: AggregatorUsageEventsProps) => { - log('info', 'Launch service') - log('info', 'Dacc consent validation...') + logStack('info', 'Launch service') + logStack('info', 'Dacc consent validation...') const termService = new TermsService(client) if (!(await termService.getLastTerm())) { - log('info', 'Exit: no consent available') + logStack('info', 'Exit: no consent available') return } @@ -1176,10 +1178,10 @@ const AggregatorUsageEvents = async ({ result: 'firstConnection', }) if (events.length > 0) { - log('info', `Fetching Sessions`) + logStack('info', `Fetching Sessions`) calculSessionTime(events, client) for (const eventType of Object.values(UsageEventType)) { - log('info', `Fetching ${eventType}`) + logStack('info', `Fetching ${eventType}`) const filteredEvents: UsageEvent[] = events.filter( event => event.type === eventType ) @@ -1215,7 +1217,7 @@ const AggregatorUsageEvents = async ({ } const uniqueReadUsageEvents: UsageEvent[] = uniq(readUsageEvents) - log( + logStack( 'info', `Tag aggregated usage: total of ${uniqueReadUsageEvents.length} events` ) @@ -1230,11 +1232,12 @@ const AggregatorUsageEvents = async ({ errorEvent.forEach(el => { error += `${el.doctype}, ` }) - log('error', error) + logStack('error', error) + Sentry.captureException(JSON.stringify({ error })) throw error } - log('info', 'End of service') + logStack('info', 'End of service') } runService(AggregatorUsageEvents) diff --git a/src/targets/services/consumptionAlert.ts b/src/targets/services/consumptionAlert.ts index f00a19b923f962b2c6c9743d1c38c4acf8819895..668306c107dc461601ff8750aeaa0a75f6993611 100644 --- a/src/targets/services/consumptionAlert.ts +++ b/src/targets/services/consumptionAlert.ts @@ -12,7 +12,7 @@ import ConsumptionService from 'services/consumption.service' import { getPreviousMonthName } from 'utils/utils' import EnvironmentService from 'services/environment.service' -const log = logger.namespace('alert') +const logStack = logger.namespace('alert') interface ConsumptionAlertProps { client: Client @@ -20,7 +20,7 @@ interface ConsumptionAlertProps { // Only monitoring WATER fluid for now const consumptionAlert = async ({ client }: ConsumptionAlertProps) => { - log('info', 'Fetching user profile...') + logStack('info', 'Fetching user profile...') const upm = new ProfileService(client) const consumptionService = new ConsumptionService(client) const userProfil = await upm.getProfile() @@ -29,7 +29,7 @@ const consumptionAlert = async ({ client }: ConsumptionAlertProps) => { !userProfil.sendConsumptionAlert || userProfil.waterDailyConsumptionLimit === 0 ) { - log( + logStack( 'info', 'End of process - Alert report notification is disabled or lack informations from user profile to run' ) @@ -38,9 +38,9 @@ const consumptionAlert = async ({ client }: ConsumptionAlertProps) => { let username = '' - log('info', 'water limit is :' + userProfil.waterDailyConsumptionLimit) + logStack('info', 'water limit is :' + userProfil.waterDailyConsumptionLimit) - log('info', 'Fetching fluid data...') + logStack('info', 'Fetching fluid data...') // Retrieve public name from the stack const settings = await client .getStackClient() @@ -69,14 +69,14 @@ const consumptionAlert = async ({ client }: ConsumptionAlertProps) => { }) } if (lastDayValue <= userProfil.waterDailyConsumptionLimit) { - log( + logStack( 'info', 'End of process - Limit consumption set by the user has not been passed.' ) return } - log('info', 'Creation of mail...') + logStack('info', 'Creation of mail...') const mailService = new MailService() const environmentService = new EnvironmentService() @@ -102,7 +102,7 @@ const consumptionAlert = async ({ client }: ConsumptionAlertProps) => { ], } - log('info', 'Sending mail...') + logStack('info', 'Sending mail...') mailService.SendMail(client, mailData) } diff --git a/src/targets/services/enedisHalfHourMonthlyAnalysis.ts b/src/targets/services/enedisHalfHourMonthlyAnalysis.ts index e512b63d28d6773174bd8c09b7db5d2b25f5899e..3f9a56f5067212532b5652b798e69f526cb9a81c 100644 --- a/src/targets/services/enedisHalfHourMonthlyAnalysis.ts +++ b/src/targets/services/enedisHalfHourMonthlyAnalysis.ts @@ -1,20 +1,21 @@ -import logger from 'cozy-logger' +import * as Sentry from '@sentry/react' import { Client } from 'cozy-client' -import { DateTime } from 'luxon' -import { Datachart, DataloadEntity, TimePeriod } from 'models' -import { runService } from './service' -import { FluidType } from 'enum/fluid.enum' -import { TimeStep } from 'enum/timeStep.enum' -import ConsumptionService from 'services/consumption.service' -import { EnedisMonthlyAnalysisData } from 'models/enedisMonthlyAnalysis' -import EnedisMonthlyAnalysisDataService from 'services/enedisMonthlyAnalysisData.service' +import logger from 'cozy-logger' import { ENEDIS_MINUTE_DOCTYPE, ENEDIS_MONTHLY_ANALYSIS_DATA_DOCTYPE, } from 'doctypes' +import { FluidType } from 'enum/fluid.enum' +import { TimeStep } from 'enum/timeStep.enum' import { union } from 'lodash' +import { DateTime } from 'luxon' +import { Datachart, DataloadEntity, TimePeriod } from 'models' +import { EnedisMonthlyAnalysisData } from 'models/enedisMonthlyAnalysis' +import ConsumptionService from 'services/consumption.service' +import EnedisMonthlyAnalysisDataService from 'services/enedisMonthlyAnalysisData.service' +import { runService } from './service' -const log = logger.namespace('report') +const logStack = logger.namespace('report') interface EnedisMonthlyProps { client: Client @@ -35,7 +36,7 @@ const getMinMonthlyLoad = ( const filteredTotal = totalArray.filter(val => val !== -1 && val !== 0) const talonCons = Math.min(...filteredTotal) const minCons = talonCons * 48 * numberofDaysInMonth - log('info', `Minimum value is ${minCons} `) + logStack('info', `Minimum value is ${minCons} `) return minCons } @@ -87,7 +88,7 @@ const getMonthMaxPower = async ( client: Client ) => { const emas = new EnedisMonthlyAnalysisDataService(client) - log('info', `Fetching max power for month ${month} of year ${year}`) + logStack('info', `Fetching max power for month ${month} of year ${year}`) const data = await emas.getMaxPowerByDate(year, month) const maxPowerArray: number[] = [] if (data?.length) { @@ -109,7 +110,10 @@ const getEnedisMonthAnalysisData = async ( month: number, year: number ): Promise<EnedisMonthlyAnalysisData | undefined> => { - log('info', `Getting enedis analysis data for month ${month} of year ${year}`) + logStack( + 'info', + `Getting enedis analysis data for month ${month} of year ${year}` + ) const timePeriod = { startDate: DateTime.fromObject({ month: month, year: year }).startOf( @@ -197,7 +201,7 @@ const syncEnedisMonthlyAnalysisDataDoctype = async ({ const lastEnedisMonthlyAnalysis = await emas.getLastEnedisMonthlyAnalysis() if (firstMinuteData && firstMinuteData[0]) { //First creates the analysis of the month - 1 - log('info', 'Fetching last Enedis monthly Analysis...') + logStack('info', 'Fetching last Enedis monthly Analysis...') const firstMinuteDate = DateTime.fromObject({ year: firstMinuteData[0].year, month: firstMinuteData[0].month, @@ -217,12 +221,17 @@ const syncEnedisMonthlyAnalysisDataDoctype = async ({ if (data) { const created = await emas.createEnedisMonthlyAnalysisData(data) if (created) { - log('success', 'Created successfully ! ') + logStack('success', 'Created successfully ! ') } else { - log('error', 'Failed to create last Enedis monthly Analysis ') + logStack('error', 'Failed to create last Enedis monthly Analysis') + Sentry.captureException( + JSON.stringify({ + error: 'Failed to create last Enedis monthly Analysis ', + }) + ) } } - log('info', 'Getting first endis half hour data date') + logStack('info', 'Getting first endis half hour data date') if (lastEnedisMonthlyAnalysis.length > 0) { //If user has more than one entry (already synced), fetch the full history @@ -233,10 +242,10 @@ const syncEnedisMonthlyAnalysisDataDoctype = async ({ firstEnedisMonthlyAnalysis[0]?.month === firstMinuteData[0].month && firstEnedisMonthlyAnalysis[0]?.year === firstMinuteData[0].year ) { - log('info', 'Every Enedis Anlysis already synchronized') + logStack('info', 'Every Enedis Anlysis already synchronized') return } else if (firstEnedisMonthlyAnalysis) { - log( + logStack( 'info', 'Doctype is partially completed, fetiching all available history' ) @@ -263,7 +272,7 @@ const syncEnedisMonthlyAnalysisDataDoctype = async ({ } } else { //If user only have the last analysis available, fetch one year history - log( + logStack( 'info', 'Doctype is empty, fetching history for one year maximum or until first enedis minute date' ) @@ -288,7 +297,7 @@ const syncEnedisMonthlyAnalysisDataDoctype = async ({ } } } else { - log( + logStack( 'info', 'Enedis Minute is not activated or there is no data yet in this doctype' ) diff --git a/src/targets/services/fluidsPrices.ts b/src/targets/services/fluidsPrices.ts index 0a423e1808527a6d4e4379344b67feac16110520..4f4c1137cee6ae0244fef384fa2dee0d953a6e0f 100644 --- a/src/targets/services/fluidsPrices.ts +++ b/src/targets/services/fluidsPrices.ts @@ -1,20 +1,22 @@ -import logger from 'cozy-logger' +import * as Sentry from '@sentry/react' import { Client } from 'cozy-client' -import { runService } from './service' -import { DateTime } from 'luxon' -import FluidPricesService from 'services/fluidsPrices.service' -import { DataloadEntity, FluidPrice, TimePeriod } from 'models' -import ConsumptionDataManager from 'services/consumption.service' -import { TimeStep } from 'enum/timeStep.enum' -import { ENEDIS_DAY_DOCTYPE, GRDF_DAY_DOCTYPE, EGL_DAY_DOCTYPE } from 'doctypes' -import { FluidType } from 'enum/fluid.enum' -import QueryRunner from 'services/queryRunner.service' -import EnvironmentService from 'services/environment.service' +import logger from 'cozy-logger' +import { EGL_DAY_DOCTYPE, ENEDIS_DAY_DOCTYPE, GRDF_DAY_DOCTYPE } from 'doctypes' import { REMOTE_ORG_ECOLYO_AGENT_PRICES, REMOTE_ORG_ECOLYO_AGENT_PRICES_REC, } from 'doctypes/remote/org.ecolyo.agent.prices' -const log = logger.namespace('fluidPrices') +import { FluidType } from 'enum/fluid.enum' +import { TimeStep } from 'enum/timeStep.enum' +import { DateTime } from 'luxon' +import { DataloadEntity, FluidPrice, TimePeriod } from 'models' +import ConsumptionDataManager from 'services/consumption.service' +import EnvironmentService from 'services/environment.service' +import FluidPricesService from 'services/fluidsPrices.service' +import QueryRunner from 'services/queryRunner.service' +import { runService } from './service' + +const logStack = logger.namespace('fluidPrices') interface PricesProps { client: Client @@ -61,7 +63,10 @@ const synchroPricesToUpdate = async ( remotePrice.UpdatedAt && existingPrice.UpdatedAt < remotePrice.UpdatedAt ) { - log('debug', `Price exist in db but not up to date, updating it`) + logStack( + 'debug', + `Price exist in db but not up to date, updating it` + ) //If a price has been updated, set the oldest startDate of the edited price so we can redo aggregation if (firstEditedPrice === null) { firstEditedPrice = remotePrice.startDate @@ -83,10 +88,10 @@ const synchroPricesToUpdate = async ( UpdatedAt: remotePrice.UpdatedAt, }) } else { - log('debug', `Price up to date`) + logStack('debug', `Price up to date`) } } else { - log('debug', `Price doesn't exist in db, creating new price`) + logStack('debug', `Price doesn't exist in db, creating new price`) //If a price has been updated, set the oldest startDate of the edited price so we can redo aggregation if (firstEditedPrice === null) { firstEditedPrice = remotePrice.startDate @@ -97,8 +102,9 @@ const synchroPricesToUpdate = async ( //create price in db await fps.createPrice(remotePrice) } - } catch (err) { - log('error', `Error: ${err}`) + } catch (error) { + logStack('error', `Error: ${error}`) + Sentry.captureException(JSON.stringify({ error })) } finally { resolve() } @@ -138,7 +144,10 @@ const getTimePeriod = async ( endDate: date.endOf('year'), } default: - log('error', 'Unhandled time period') + logStack('error', 'Unhandled time period') + Sentry.captureException( + JSON.stringify({ error: 'Unhandled time period' }) + ) throw Error('Unhandled time period') } } @@ -151,7 +160,7 @@ const aggregatePrices = async ( fluidType: FluidType ) => { const tsa = [TimeStep.MONTH, TimeStep.YEAR] - log( + logStack( 'debug', `Aggregation started for fluid: ${fluidType}, from ${firstDate} ` ) @@ -185,15 +194,16 @@ const aggregatePrices = async ( date = date.plus({ month: 1 }).startOf('month') } } while (date < today) - } catch (err) { - log('info', `Error : ${err}`) + } catch (error) { + logStack('info', `Error : ${error}`) + Sentry.captureException(JSON.stringify({ error })) } finally { resolve() } }) }) ) - log('debug', `Aggregation done`) + logStack('debug', `Aggregation done`) } const getDoctypeTypeByFluid = (fluidType: FluidType): string => { @@ -206,7 +216,8 @@ const getDoctypeTypeByFluid = (fluidType: FluidType): string => { if (fluidType === FluidType.WATER) { return EGL_DAY_DOCTYPE } - log('error', 'Unkown FluidType') + logStack('error', 'Unkown FluidType') + Sentry.captureException({ error: 'Unkown FluidType Doctype' }) throw new Error() } @@ -217,8 +228,8 @@ const getTimeSetByFluid = (fluidType: FluidType): TimeStep[] => { if (fluidType === FluidType.GAS || fluidType === FluidType.WATER) { return [TimeStep.DAY] } - - log('error', 'Unkown FluidType') + logStack('error', 'Unkown FluidType') + Sentry.captureException({ error: 'Unkown FluidType' }) throw new Error() } @@ -234,7 +245,7 @@ const applyPrices = async (client: Client, fluidType: FluidType) => { const prices = await fluidsPricesService.getAllPrices() // Prices data exsit if (prices.length > 0) { - log('debug', 'fluidPrices data found') + logStack('debug', 'fluidPrices data found') const firstMinuteData = await cdm.getFirstDataDateFromDoctypeWithPrice( getDoctypeTypeByFluid(fluidType) ) @@ -330,8 +341,9 @@ const applyPrices = async (client: Client, fluidType: FluidType) => { date = date.plus({ month: 1 }).startOf('month') } } while (date < today) - } catch (err) { - log('error', `ERROR : ${err} `) + } catch (error) { + logStack('error', `ERROR : ${error} `) + Sentry.captureException(JSON.stringify({ error })) } finally { resolve() } @@ -341,22 +353,22 @@ const applyPrices = async (client: Client, fluidType: FluidType) => { // Call aggregation method await aggregatePrices(qr, cdm, firstDate, today, fluidType) - } else log('info', `No data found for fluid ${fluidType}`) - } else log('info', 'No fluidesPrices data') + } else logStack('info', `No data found for fluid ${fluidType}`) + } else logStack('info', 'No fluidesPrices data') } const processPrices = async ({ client }: PricesProps) => { - log('info', `Processing electricity data...`) + logStack('info', `Processing electricity data...`) const elec = applyPrices(client, FluidType.ELECTRICITY) - log('info', `Electricity data done`) - log('info', `Processing gas data...`) + logStack('info', `Electricity data done`) + logStack('info', `Processing gas data...`) const gas = applyPrices(client, FluidType.GAS) - log('info', `Gas data done`) - log('info', `Processing water data...`) + logStack('info', `Gas data done`) + logStack('info', `Processing water data...`) const water = applyPrices(client, FluidType.WATER) - log('info', `Water data done`) + logStack('info', `Water data done`) await Promise.all([elec, gas, water]) - log('info', `processPrices done`) + logStack('info', `processPrices done`) } runService(processPrices) diff --git a/src/targets/services/monthlyReportNotification.ts b/src/targets/services/monthlyReportNotification.ts index cc925a0a6f43df61e057749122bc688e41d793c9..33bca041bb55e546a208afce0b1a586ef6f51629 100644 --- a/src/targets/services/monthlyReportNotification.ts +++ b/src/targets/services/monthlyReportNotification.ts @@ -1,3 +1,4 @@ +import * as Sentry from '@sentry/react' import { Client } from 'cozy-client' import logger from 'cozy-logger' import { @@ -19,7 +20,7 @@ import { getMonthNameWithPrep } from 'utils/utils' import { runService } from './service' const monthlyReportTemplate = require('notifications/monthlyReport.hbs') -const log = logger.namespace('report') +const logStack = logger.namespace('report') interface MonthlyReportNotificationProps { client: Client @@ -60,7 +61,7 @@ const getConsumptionValue = async ( * @returns string */ const buildConsumptionText = async (client: Client) => { - log('info', 'Building consumption text...') + logStack('info', 'Building consumption text...') const consumption = await getConsumptionValue(client, [ FluidType.ELECTRICITY, FluidType.GAS, @@ -136,7 +137,7 @@ const getMonthlyReport = async ( ): Promise<MonthlyReport> => { try { const environmentService = new EnvironmentService() - log( + logStack( 'info', environmentService.isProduction() ? 'Fetching data from BO prod' @@ -153,7 +154,8 @@ const getMonthlyReport = async ( ) return result } catch (error) { - log('error', error) + logStack('error', JSON.stringify(error)) + Sentry.captureException(JSON.stringify({ error })) return { year: parseInt(year), month: parseInt(month), @@ -171,11 +173,14 @@ const getMonthlyReport = async ( const monthlyReportNotification = async ({ client, }: MonthlyReportNotificationProps) => { - log('info', 'Fetching user profile...') + logStack('info', 'Fetching user profile...') const upm = new ProfileService(client) let userProfil = await upm.getProfile() if (!userProfil || !userProfil.sendAnalysisNotification) { - log('info', 'End of process - Report Notification disabled in user profile') + logStack( + 'info', + 'End of process - Report Notification disabled in user profile' + ) return } @@ -188,7 +193,8 @@ const monthlyReportNotification = async ({ mailToken: token, }) } catch (error) { - log('error', 'Update mailToken user profile error : ' + error) + logStack('error', 'Update mailToken user profile error : ' + error) + Sentry.captureException(JSON.stringify({ error })) throw error } } @@ -196,7 +202,7 @@ const monthlyReportNotification = async ({ let username = '' let url = '' - log('info', 'Fetching data for mail...') + logStack('info', 'Fetching data for mail...') // Retrieve public name from the stack const settings = await client .getStackClient() @@ -213,7 +219,7 @@ const monthlyReportNotification = async ({ url = appLink } - log('info', 'Creation of mail...') + logStack('info', 'Creation of mail...') const mailService = new MailService() const today = DateTime.local().setZone('utc', { @@ -302,7 +308,7 @@ const monthlyReportNotification = async ({ }, ], } - log('info', 'Sending mail...') + logStack('info', 'Sending mail...') mailService.SendMail(client, mailData) } diff --git a/src/targets/vendor/assets/serviceWorker.js b/src/targets/vendor/assets/serviceWorker.js index 15a90d79b460d82998693aea1a1fc403f0e478d8..51dc9b97b60f013203eabb07d760dfee02265eca 100644 --- a/src/targets/vendor/assets/serviceWorker.js +++ b/src/targets/vendor/assets/serviceWorker.js @@ -13,7 +13,11 @@ self.addEventListener('install', event => { // Listen for requests self.addEventListener('fetch', event => { - if (event.request.url.indexOf('statweb') !== -1) { + // Exception for Matomo & Sentry + if ( + event.request.url.includes('statweb') || + event.request.url.includes('grandlyon.errors') + ) { return false } diff --git a/src/utils/logger.js b/src/utils/logger.js index ee502e362b00210c8f0f70a50e84451f171227c9..af75208f69a125e25b1cc7184a29139f77ea07d6 100644 --- a/src/utils/logger.js +++ b/src/utils/logger.js @@ -5,6 +5,6 @@ const inBrowser = typeof window !== 'undefined' // eslint-disable-next-line no-undef const minilog = (inBrowser && window.minilog) || minilog_ -const log = minilog('ecolyo') +const logApp = minilog('ecolyo') -export default log +export default logApp diff --git a/yarn.lock b/yarn.lock index c2f66af57bf17da0f451d9e9b3a26a14b2c94e29..063c3def2ce3c8b4a683c16d3bde2d2d9204a53e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2319,6 +2319,16 @@ "@react-spring/core" "9.0.0-rc.3" "@react-spring/shared" "9.0.0-rc.3" +"@sentry/browser@7.21.1": + version "7.21.1" + resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.21.1.tgz#bffa3ea19050c06400107d2297b9802f9719f98b" + integrity sha512-cS2Jz2+fs9+4pJqLJPtYqGyY97ywJDWAWIR1Yla3hs1QQuH6m0Nz3ojZD1gE2eKH9mHwkGbnNAh+hHcrYrfGzw== + dependencies: + "@sentry/core" "7.21.1" + "@sentry/types" "7.21.1" + "@sentry/utils" "7.21.1" + tslib "^1.9.3" + "@sentry/browser@^6.0.1": version "6.19.7" resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-6.19.7.tgz#a40b6b72d911b5f1ed70ed3b4e7d4d4e625c0b5f" @@ -2340,6 +2350,15 @@ "@sentry/utils" "6.19.7" tslib "^1.9.3" +"@sentry/core@7.21.1": + version "7.21.1" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.21.1.tgz#d0423282d90875625802dfe380f9657e9242b72b" + integrity sha512-Og5wEEsy24fNvT/T7IKjcV4EvVK5ryY2kxbJzKY6GU2eX+i+aBl+n/vp7U0Es351C/AlTkS+0NOUsp2TQQFxZA== + dependencies: + "@sentry/types" "7.21.1" + "@sentry/utils" "7.21.1" + tslib "^1.9.3" + "@sentry/hub@6.19.7": version "6.19.7" resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-6.19.7.tgz#58ad7776bbd31e9596a8ec46365b45cd8b9cfd11" @@ -2358,11 +2377,37 @@ "@sentry/types" "6.19.7" tslib "^1.9.3" +"@sentry/react@^7.21.1": + version "7.21.1" + resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.21.1.tgz#275e6fd46212f608f382c7dde46d21e748f93491" + integrity sha512-w91PIUyX07mErKgrBQA+7ID8zFKrYDUYSOrFSHufg5DdPq4EpHiNDe/Yngg3e9ELhtr1AbCnEvx9wlvqLi3nZQ== + dependencies: + "@sentry/browser" "7.21.1" + "@sentry/types" "7.21.1" + "@sentry/utils" "7.21.1" + hoist-non-react-statics "^3.3.2" + tslib "^1.9.3" + +"@sentry/tracing@^7.21.1": + version "7.21.1" + resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.21.1.tgz#db02643e84960f1ea14b35fe75a93fc0bbca1fcb" + integrity sha512-b1BTPsRaNQpohzegoz59KGuBl+To651vEq0vMS4tCzSyIdxkYso3JCrjDdEqW/2MliQYANNVrUai2bmwmU9h1g== + dependencies: + "@sentry/core" "7.21.1" + "@sentry/types" "7.21.1" + "@sentry/utils" "7.21.1" + tslib "^1.9.3" + "@sentry/types@6.19.7": version "6.19.7" resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.19.7.tgz#c6b337912e588083fc2896eb012526cf7cfec7c7" integrity sha512-jH84pDYE+hHIbVnab3Hr+ZXr1v8QABfhx39KknxqKWr2l0oEItzepV0URvbEhB446lk/S/59230dlUUIBGsXbg== +"@sentry/types@7.21.1": + version "7.21.1" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.21.1.tgz#408a7b95a66ddc30c4359979594e03bee8f9fbdc" + integrity sha512-3/IKnd52Ol21amQvI+kz+WB76s8/LR5YvFJzMgIoI2S8d82smIr253zGijRXxHPEif8kMLX4Yt+36VzrLxg6+A== + "@sentry/utils@6.19.7": version "6.19.7" resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.19.7.tgz#6edd739f8185fd71afe49cbe351c1bbf5e7b7c79" @@ -2371,6 +2416,14 @@ "@sentry/types" "6.19.7" tslib "^1.9.3" +"@sentry/utils@7.21.1": + version "7.21.1" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.21.1.tgz#96582345178015fd32fe9159c25c44ccf2f99d2a" + integrity sha512-F0W0AAi8tgtTx6ApZRI2S9HbXEA9ENX1phTZgdNNWcMFm1BNbc21XEwLqwXBNjub5nlA6CE8xnjXRgdZKx4kzQ== + dependencies: + "@sentry/types" "7.21.1" + tslib "^1.9.3" + "@simbathesailor/use-what-changed@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@simbathesailor/use-what-changed/-/use-what-changed-2.0.0.tgz#7f82d78f92c8588b5fadd702065dde93bd781403"