diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ddae43bea..d09a2989b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Changed + +- Upgraded `@modelcontextprotocol/core` and `@modelcontextprotocol/server` from version `2.0.0` to `2.3.1` +- Upgraded `@modelcontextprotocol/node` from version `2.0.0` to `2.1.1` + +## 3.89.0 - 2026-10-20 + ### Added - Added the `DATA_GATHERING_INTERVAL_MINUTES` environment variable to configure the interval of the market data gathering of the asset profiles with the hourly data gathering frequency @@ -14,12 +21,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Improved the layout of the user account settings on mobile - Improved the usability of the activities page by eliminating the reload on canceling the import activities dialog +- Improved the loading state of the file drop area component - Improved the accessibility of the _Buy me a coffee_ button on the about page - Improved the accessibility of the entity logo component - Extracted the file drop area of the import activities dialog to a reusable component - Improved the performance of the historical market data table of the admin control panel by loading only the latest market price per asset profile - Introduced a cooldown of 12 hours for the asset profile and exchange rate gathering on activity creation by retaining the completed jobs +- Deprecated the `api/v1/platform` endpoints in favor of the `api/v1/platforms` endpoints + +### Fixed + +- Fixed the missing localization of the date picker in the _FIRE_ calculator +- Fixed the missing localization of the date picker in the asset profile details dialog of the admin control panel +- Fixed the missing tooltip of the client of the last request in the users table of the admin control panel ## 3.88.0 - 2026-10-17 diff --git a/apps/api/src/app/__snapshots__/app.module.spec.ts.snap b/apps/api/src/app/__snapshots__/app.module.spec.ts.snap new file mode 100644 index 0000000000..68defd9062 --- /dev/null +++ b/apps/api/src/app/__snapshots__/app.module.spec.ts.snap @@ -0,0 +1,1211 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`AppModule should keep the security of each route 1`] = ` +{ + "DELETE /access/:id": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "deleteAccess", + }, + "DELETE /account-balance/:id": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [ + "RedactValuesInResponseInterceptor", + ], + "permission": "deleteAccountBalance", + "scopes": [ + "account:update", + ], + }, + "DELETE /account/:id": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [ + "RedactValuesInResponseInterceptor", + ], + "permission": "deleteAccount", + "scopes": [ + "account:delete", + ], + }, + "DELETE /activities": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [ + "TransformDataSourceInRequestInterceptor", + ], + "permission": "deleteActivity", + "scopes": [ + "activity:delete", + ], + }, + "DELETE /activities/:id": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [ + "RedactValuesInResponseInterceptor", + ], + "permission": "deleteActivity", + "scopes": [ + "activity:delete", + ], + }, + "DELETE /admin/profile-data/:dataSource/:symbol": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "accessAdminControl", + }, + "DELETE /admin/queue/job": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "accessAdminControl", + }, + "DELETE /admin/queue/job/:id": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "accessAdminControl", + }, + "DELETE /asset-profiles/:dataSource/:symbol/splits/:id": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + ], + "interceptors": [ + "TransformDataSourceInRequestInterceptor", + ], + }, + "DELETE /auth-device/:id": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "deleteAuthDevice", + }, + "DELETE /benchmarks/:dataSource/:symbol": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "accessAdminControl", + }, + "DELETE /platform/:id": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "deletePlatform", + }, + "DELETE /platforms/:id": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "deletePlatform", + }, + "DELETE /tags/:id": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "deleteTag", + }, + "DELETE /user": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + ], + "interceptors": [], + "permission": "deleteOwnUser", + }, + "DELETE /user/:id": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "deleteUser", + }, + "DELETE /watchlist/:dataSource/:symbol": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [ + "TransformDataSourceInRequestInterceptor", + ], + "permission": "deleteWatchlistItem", + "scopes": [ + "watchlist:delete", + ], + }, + "GET /access": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + }, + "GET /account": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [ + "TransformDataSourceInRequestInterceptor", + "RedactValuesInResponseInterceptor", + ], + "scopes": [ + "account:read", + ], + }, + "GET /account/:id": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [ + "RedactValuesInResponseInterceptor", + ], + "scopes": [ + "account:read", + ], + }, + "GET /account/:id/balances": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [ + "RedactValuesInResponseInterceptor", + ], + "scopes": [ + "account:read", + ], + }, + "GET /activities": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [ + "TransformDataSourceInResponseInterceptor", + "TransformDataSourceInRequestInterceptor", + "RedactValuesInResponseInterceptor", + ], + "scopes": [ + "activity:read", + ], + }, + "GET /activities/:id": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [ + "TransformDataSourceInResponseInterceptor", + "RedactValuesInResponseInterceptor", + ], + "scopes": [ + "activity:read", + ], + }, + "GET /admin": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "accessAdminControl", + }, + "GET /admin/demo-user/sync": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "syncDemoUserAccount", + }, + "GET /admin/queue/job": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "accessAdminControl", + }, + "GET /admin/queue/job/:id/execute": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "accessAdminControl", + }, + "GET /admin/user": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "accessAdminControl", + }, + "GET /admin/user/:id": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "accessAdminControl", + }, + "GET /ai/prompt/:mode": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [ + "TransformDataSourceInRequestInterceptor", + ], + "permission": "readAiPrompt", + "scopes": [ + "portfolio:read", + ], + }, + "GET /asset-profiles": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "accessAdminControl", + }, + "GET /asset-profiles/:dataSource/:symbol": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + ], + "interceptors": [ + "TransformDataSourceInResponseInterceptor", + "TransformDataSourceInRequestInterceptor", + ], + }, + "GET /asset/:dataSource/:symbol": { + "guards": [], + "interceptors": [ + "TransformDataSourceInResponseInterceptor", + "TransformDataSourceInRequestInterceptor", + ], + }, + "GET /assets/:languageCode/site.webmanifest (version neutral)": { + "guards": [], + "interceptors": [], + }, + "GET /auth/google": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('google')", + ], + "interceptors": [], + }, + "GET /auth/google/callback (version neutral)": { + "allowDuringImpersonation": true, + "guards": [ + "OAuthCallbackGuardMixin('google')", + ], + "interceptors": [], + }, + "GET /auth/oidc (version neutral)": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('oidc')", + ], + "interceptors": [], + }, + "GET /auth/oidc/callback (version neutral)": { + "allowDuringImpersonation": true, + "guards": [ + "OAuthCallbackGuardMixin('oidc')", + ], + "interceptors": [], + }, + "GET /auth/webauthn/generate-registration-options": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + }, + "GET /benchmarks": { + "allowDuringImpersonation": true, + "guards": [], + "interceptors": [ + "TransformDataSourceInResponseInterceptor", + "TransformDataSourceInRequestInterceptor", + ], + }, + "GET /benchmarks/:dataSource/:symbol/:startDateString": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [ + "TransformDataSourceInRequestInterceptor", + ], + "scopes": [ + "portfolio:read", + ], + }, + "GET /data-providers/ghostfolio/asset-profile/:symbol": { + "guards": [ + "AuthGuard('api-key')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "enableDataProviderGhostfolio", + }, + "GET /data-providers/ghostfolio/dividends/:symbol (version 2)": { + "guards": [ + "AuthGuard('api-key')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "enableDataProviderGhostfolio", + }, + "GET /data-providers/ghostfolio/historical/:symbol (version 2)": { + "guards": [ + "AuthGuard('api-key')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "enableDataProviderGhostfolio", + }, + "GET /data-providers/ghostfolio/lookup (version 2)": { + "guards": [ + "AuthGuard('api-key')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "enableDataProviderGhostfolio", + }, + "GET /data-providers/ghostfolio/markets": { + "guards": [ + "AuthGuard('api-key')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "enableDataProviderGhostfolio", + }, + "GET /data-providers/ghostfolio/quotes (version 2)": { + "guards": [ + "AuthGuard('api-key')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "enableDataProviderGhostfolio", + }, + "GET /data-providers/ghostfolio/status (version 2)": { + "guards": [ + "AuthGuard('api-key')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "enableDataProviderGhostfolio", + }, + "GET /exchange-rate/:symbol/:dateString": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + }, + "GET /export": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [ + "TransformDataSourceInResponseInterceptor", + "TransformDataSourceInRequestInterceptor", + ], + }, + "GET /health": { + "guards": [], + "interceptors": [], + }, + "GET /health/ai": { + "guards": [], + "interceptors": [], + }, + "GET /health/data-enhancer/:name": { + "guards": [], + "interceptors": [], + }, + "GET /health/data-provider/:dataSource": { + "guards": [], + "interceptors": [ + "TransformDataSourceInRequestInterceptor", + ], + }, + "GET /health/liveness": { + "guards": [], + "interceptors": [], + }, + "GET /import/dividends/:dataSource/:symbol": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [ + "TransformDataSourceInResponseInterceptor", + "TransformDataSourceInRequestInterceptor", + ], + }, + "GET /info": { + "guards": [], + "interceptors": [ + "TransformDataSourceInResponseInterceptor", + ], + }, + "GET /logo": { + "guards": [], + "interceptors": [], + }, + "GET /logo/:dataSource/:symbol": { + "guards": [], + "interceptors": [ + "TransformDataSourceInRequestInterceptor", + ], + }, + "GET /market-data/markets": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "readMarketDataOfMarkets", + }, + "GET /platform": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "readPlatformsWithAccountCount", + }, + "GET /platforms": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "readPlatforms", + }, + "GET /portfolio/details": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [ + "TransformDataSourceInResponseInterceptor", + "TransformDataSourceInRequestInterceptor", + "RedactValuesInResponseInterceptor", + ], + "scopes": [ + "portfolio:read", + ], + }, + "GET /portfolio/dividends": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [ + "TransformDataSourceInRequestInterceptor", + ], + "scopes": [ + "portfolio:read", + ], + }, + "GET /portfolio/holding/:dataSource/:symbol": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [ + "TransformDataSourceInResponseInterceptor", + "TransformDataSourceInRequestInterceptor", + "RedactValuesInResponseInterceptor", + ], + "scopes": [ + "portfolio:read", + ], + }, + "GET /portfolio/holdings": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [ + "TransformDataSourceInResponseInterceptor", + "TransformDataSourceInRequestInterceptor", + "RedactValuesInResponseInterceptor", + ], + "scopes": [ + "portfolio:read", + ], + }, + "GET /portfolio/investments": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [ + "TransformDataSourceInRequestInterceptor", + ], + "scopes": [ + "portfolio:read", + ], + }, + "GET /portfolio/performance (version 2)": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [ + "TransformDataSourceInResponseInterceptor", + "TransformDataSourceInRequestInterceptor", + "PerformanceLoggingInterceptor", + ], + "scopes": [ + "portfolio:read", + ], + }, + "GET /portfolio/report": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [], + "scopes": [ + "portfolio:read", + ], + }, + "GET /public/:accessId/portfolio": { + "guards": [], + "interceptors": [ + "TransformDataSourceInResponseInterceptor", + "RedactValuesInResponseInterceptor", + ], + }, + "GET /sitemap.xml (version neutral)": { + "guards": [], + "interceptors": [], + }, + "GET /subscription/stripe/callback": { + "allowDuringImpersonation": true, + "guards": [], + "interceptors": [], + }, + "GET /symbol/:dataSource/:symbol": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [ + "TransformDataSourceInResponseInterceptor", + "TransformDataSourceInRequestInterceptor", + ], + }, + "GET /symbol/:dataSource/:symbol/:dateString": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + }, + "GET /symbol/lookup": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [ + "TransformDataSourceInResponseInterceptor", + ], + }, + "GET /tags": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "readTags", + }, + "GET /user": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + ], + "interceptors": [ + "TransformDataSourceInResponseInterceptor", + "RedactValuesInResponseInterceptor", + ], + }, + "GET /watchlist": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [ + "TransformDataSourceInResponseInterceptor", + ], + "permission": "readWatchlist", + "scopes": [ + "watchlist:read", + ], + }, + "PATCH /admin/profile-data/:dataSource/:symbol": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "accessAdminControl", + }, + "PATCH /asset-profiles/:dataSource/:symbol": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "accessAdminControl", + }, + "POST /access": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "createAccess", + }, + "POST /account": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [ + "RedactValuesInResponseInterceptor", + ], + "permission": "createAccount", + "scopes": [ + "account:create", + ], + }, + "POST /account-balance": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [ + "RedactValuesInResponseInterceptor", + ], + "permission": "createAccountBalance", + "scopes": [ + "account:update", + ], + }, + "POST /account/transfer-balance": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [], + "permission": "updateAccount", + "scopes": [ + "account:update", + ], + }, + "POST /activities": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [ + "TransformDataSourceInResponseInterceptor", + "TransformDataSourceInRequestInterceptor", + "RedactValuesInResponseInterceptor", + ], + "permission": "createActivity", + "scopes": [ + "activity:create", + ], + }, + "POST /admin/gather": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "accessAdminControl", + }, + "POST /admin/gather/:dataSource/:symbol": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "accessAdminControl", + }, + "POST /admin/gather/:dataSource/:symbol/:dateString": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "accessAdminControl", + }, + "POST /admin/gather/max": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "accessAdminControl", + }, + "POST /admin/gather/profile-data": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "accessAdminControl", + }, + "POST /admin/gather/profile-data/:dataSource/:symbol": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "accessAdminControl", + }, + "POST /admin/market-data/:dataSource/:symbol/test": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "accessAdminControl", + }, + "POST /admin/profile-data/:dataSource/:symbol": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [ + "TransformDataSourceInRequestInterceptor", + ], + "permission": "accessAdminControl", + }, + "POST /admin/profile-data/:dataSource/:symbol/merge": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [ + "TransformDataSourceInRequestInterceptor", + ], + "permission": "accessAdminControl", + }, + "POST /ai/activities": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "CustomThrottlerGuard", + ], + "interceptors": [ + "MixinInterceptor", + ], + "permission": "enableGhostfolioAi", + }, + "POST /api-keys": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "createApiKey", + }, + "POST /asset-profiles/:dataSource/:symbol/splits": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + ], + "interceptors": [ + "TransformDataSourceInRequestInterceptor", + ], + }, + "POST /auth/anonymous": { + "allowDuringImpersonation": true, + "guards": [ + "CustomThrottlerGuard", + ], + "interceptors": [], + }, + "POST /auth/webauthn/generate-authentication-options": { + "allowDuringImpersonation": true, + "guards": [ + "CustomThrottlerGuard", + ], + "interceptors": [], + }, + "POST /auth/webauthn/verify-attestation": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + }, + "POST /auth/webauthn/verify-authentication": { + "allowDuringImpersonation": true, + "guards": [ + "CustomThrottlerGuard", + ], + "interceptors": [], + }, + "POST /benchmarks": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "accessAdminControl", + }, + "POST /cache/flush": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "accessAdminControl", + }, + "POST /import": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [ + "TransformDataSourceInResponseInterceptor", + "TransformDataSourceInRequestInterceptor", + ], + "permission": "createActivity", + }, + "POST /market-data/:dataSource/:symbol": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + ], + "interceptors": [], + }, + "POST /platform": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "createPlatform", + }, + "POST /platforms": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "createPlatform", + }, + "POST /subscription/redeem-coupon": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "CustomThrottlerGuard", + "HasPermissionGuard", + ], + "interceptors": [], + }, + "POST /subscription/stripe/checkout-session": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "CustomThrottlerGuard", + "HasPermissionGuard", + ], + "interceptors": [], + }, + "POST /tags": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + ], + "interceptors": [], + }, + "POST /user": { + "allowDuringImpersonation": true, + "guards": [ + "CustomThrottlerGuard", + ], + "interceptors": [], + }, + "POST /user/:id/access-token": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "accessAdminControl", + }, + "POST /user/access-token": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "updateOwnAccessToken", + }, + "POST /watchlist": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [ + "TransformDataSourceInRequestInterceptor", + ], + "permission": "createWatchlistItem", + "scopes": [ + "watchlist:create", + ], + }, + "PUT /access/:id": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "updateAccess", + }, + "PUT /account/:id": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [ + "RedactValuesInResponseInterceptor", + ], + "permission": "updateAccount", + "scopes": [ + "account:update", + ], + }, + "PUT /activities/:id": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [ + "TransformDataSourceInRequestInterceptor", + "RedactValuesInResponseInterceptor", + ], + "permission": "updateActivity", + "scopes": [ + "activity:update", + ], + }, + "PUT /admin/settings/:key": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "accessAdminControl", + }, + "PUT /platform/:id": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "updatePlatform", + }, + "PUT /platforms/:id": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "updatePlatform", + }, + "PUT /portfolio/holding/:dataSource/:symbol/tags": { + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + "ScopeGuard", + ], + "interceptors": [ + "TransformDataSourceInRequestInterceptor", + ], + "permission": "updateActivity", + "scopes": [ + "activity:update", + ], + }, + "PUT /tags/:id": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + ], + "interceptors": [], + "permission": "updateTag", + }, + "PUT /user/setting": { + "allowDuringImpersonation": true, + "guards": [ + "AuthGuard('jwt')", + "HasPermissionGuard", + "ImpersonationGuard", + ], + "interceptors": [ + "TransformDataSourceInResponseInterceptor", + ], + }, +} +`; diff --git a/apps/api/src/app/activities/activities.module.ts b/apps/api/src/app/activities/activities.module.ts index 1508917f04..869a4d3650 100644 --- a/apps/api/src/app/activities/activities.module.ts +++ b/apps/api/src/app/activities/activities.module.ts @@ -1,7 +1,6 @@ import { AccountBalanceService } from '@ghostfolio/api/app/account-balance/account-balance.service'; import { AccountService } from '@ghostfolio/api/app/account/account.service'; import { CacheModule } from '@ghostfolio/api/app/cache/cache.module'; -import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; import { RedactValuesInResponseModule } from '@ghostfolio/api/interceptors/redact-values-in-response/redact-values-in-response.module'; import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module'; import { TransformDataSourceInResponseModule } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.module'; @@ -14,6 +13,7 @@ import { ImpersonationModule } from '@ghostfolio/api/services/impersonation/impe import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module'; import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; import { DataGatheringQueueModule } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.module'; +import { RedisCacheModule } from '@ghostfolio/api/services/redis-cache/redis-cache.module'; import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module'; import { TagModule } from '@ghostfolio/api/services/tag/tag.module'; diff --git a/apps/api/src/app/app.module.spec.ts b/apps/api/src/app/app.module.spec.ts index 976e08393b..f04485ccdd 100644 --- a/apps/api/src/app/app.module.spec.ts +++ b/apps/api/src/app/app.module.spec.ts @@ -1,17 +1,26 @@ +import { ALLOW_DURING_IMPERSONATION_KEY } from '@ghostfolio/api/decorators/allow-during-impersonation.decorator'; +import { HAS_PERMISSION_KEY } from '@ghostfolio/api/decorators/has-permission.decorator'; +import { REQUIRES_SCOPE_KEY } from '@ghostfolio/api/decorators/requires-scope.decorator'; + import { DynamicModule, ForwardReference, RequestMethod, - Type + Type, + VERSION_NEUTRAL } from '@nestjs/common'; import { GUARDS_METADATA, + INTERCEPTORS_METADATA, METHOD_METADATA, MODULE_METADATA, - PATH_METADATA + PATH_METADATA, + VERSION_METADATA } from '@nestjs/common/constants'; +import { VersionValue } from '@nestjs/common/interfaces'; import { MetadataScanner } from '@nestjs/core'; import { AuthGuard } from '@nestjs/passport'; +import { isUndefined, omitBy } from 'lodash-es'; import { AppModule } from './app.module'; @@ -26,6 +35,13 @@ jest.mock('ai', () => { type ModuleDefinition = DynamicModule | ForwardReference<() => Type> | Promise | Type; +/** + * The strategies of the authentication which the routes apply via + * AuthGuard(). The class of such a guard has a random name, hence the + * snapshot of the routes names it by its strategy + */ +const AUTHENTICATION_STRATEGIES = ['api-key', 'google', 'jwt', 'oidc']; + /** * The routes of the controllers which answer a request without the * authentication of a user or of an API key, sorted by the request method and @@ -98,6 +114,57 @@ async function getControllers( return [...new Set(controllers)]; } +/** + * Gives the guards or the interceptors which the decorators of the controller + * and of the route handler set, in the order in which they run + */ +function getEnhancers(key: string, controller: Type, routeHandler: object) { + return [controller, routeHandler].flatMap((target) => { + return (Reflect.getMetadata(key, target) ?? []) as Type[]; + }); +} + +/** + * Gives the metadata of the route handler or, if it has none, of the + * controller, as Reflector.getAllAndOverride() does + */ +function getMetadataOfRoute( + key: string, + controller: Type, + routeHandler: object +) { + return (Reflect.getMetadata(key, routeHandler) ?? + Reflect.getMetadata(key, controller)) as T | undefined; +} + +/** + * Gives the name of a guard or of an interceptor. The function mixin() + * replaces the name of a class by a random one, hence the name is read from + * the declaration of the class. A guard which extends AuthGuard() is named + * with its strategy + */ +function getName(enhancer: Type) { + const authenticationStrategy = AUTHENTICATION_STRATEGIES.find((strategy) => { + return AuthGuard(strategy) === enhancer; + }); + + if (authenticationStrategy) { + return `AuthGuard('${authenticationStrategy}')`; + } + + const name = /^class\s+([\w$]+)/.exec(enhancer.toString())?.[1]; + + if (!name || name === 'MixinAuthGuard') { + throw new Error('The name of a guard or of an interceptor is unknown'); + } + + const parentStrategy = AUTHENTICATION_STRATEGIES.find((strategy) => { + return Object.getPrototypeOf(enhancer) === AuthGuard(strategy); + }); + + return parentStrategy ? `${name}('${parentStrategy}')` : name; +} + /** * Gives the paths which the decorator of a controller or of a route sets */ @@ -108,14 +175,10 @@ function getPaths(target: object) { } /** - * Gives the routes of the controller which apply neither AuthGuard('api-key') - * nor AuthGuard('jwt'), each as the request method and the path + * Gives the routes of the controller, each as the request method and the path + * with its route handler */ -function getRoutesWithoutAuthentication(controller: Type) { - const authenticationGuards: unknown[] = [ - AuthGuard('api-key'), - AuthGuard('jwt') - ]; +function getRoutes(controller: Type) { const routeHandlersByName = controller.prototype as Record; return new MetadataScanner() @@ -131,19 +194,6 @@ function getRoutesWithoutAuthentication(controller: Type) { return []; } - const guards = [controller, routeHandler].flatMap((target) => { - return (Reflect.getMetadata(GUARDS_METADATA, target) ?? - []) as unknown[]; - }); - - const hasAuthentication = guards.some((guard) => { - return authenticationGuards.includes(guard); - }); - - if (hasAuthentication) { - return []; - } - return getPaths(controller).flatMap((controllerPath) => { return getPaths(routeHandler).map((routePath) => { const path = `${controllerPath}/${routePath}` @@ -151,12 +201,110 @@ function getRoutesWithoutAuthentication(controller: Type) { .filter(Boolean) .join('/'); - return `${RequestMethod[requestMethod]} /${path}`; + return { + routeHandler, + route: `${RequestMethod[requestMethod]} /${path}` + }; }); }); }); } +/** + * Gives the routes of the controller which apply neither AuthGuard('api-key') + * nor AuthGuard('jwt'), each as the request method and the path + */ +function getRoutesWithoutAuthentication(controller: Type) { + const authenticationGuards: unknown[] = [ + AuthGuard('api-key'), + AuthGuard('jwt') + ]; + + return getRoutes(controller) + .filter(({ routeHandler }) => { + const hasAuthentication = getEnhancers( + GUARDS_METADATA, + controller, + routeHandler + ).some((guard) => { + return authenticationGuards.includes(guard); + }); + + return !hasAuthentication; + }) + .map(({ route }) => { + return route; + }); +} + +/** + * Gives the routes of the controller, each as the request method, the path + * and the version with the guards and the interceptors in the order in which + * they run, the permission, the scopes and whether the route stays available + * while an impersonation is active + */ +function getRoutesWithSecurity(controller: Type) { + return getRoutes(controller).map(({ route, routeHandler }) => { + const security = omitBy( + { + allowDuringImpersonation: getMetadataOfRoute( + ALLOW_DURING_IMPERSONATION_KEY, + controller, + routeHandler + ), + guards: getEnhancers(GUARDS_METADATA, controller, routeHandler).map( + (guard) => { + return getName(guard); + } + ), + interceptors: getEnhancers( + INTERCEPTORS_METADATA, + controller, + routeHandler + ).map((interceptor) => { + return getName(interceptor); + }), + // HasPermissionGuard reads the permission of the route handler only + permission: Reflect.getMetadata(HAS_PERMISSION_KEY, routeHandler) as + string | undefined, + scopes: getMetadataOfRoute( + REQUIRES_SCOPE_KEY, + controller, + routeHandler + ) + }, + isUndefined + ); + + return [ + `${route}${getVersionSuffix(controller, routeHandler)}`, + security + ] as const; + }); +} + +/** + * Gives the version of the route as a suffix of its key, which is empty for + * the default version + */ +function getVersionSuffix(controller: Type, routeHandler: object) { + const version = getMetadataOfRoute( + VERSION_METADATA, + controller, + routeHandler + ); + + if (version === undefined) { + return ''; + } + + const versions = [version].flat().map((value) => { + return value === VERSION_NEUTRAL ? 'neutral' : value; + }); + + return ` (version ${versions.join(', ')})`; +} + describe('AppModule', () => { it('should require the authentication for each route which is not public', async () => { const controllers = await getControllers(AppModule); @@ -169,4 +317,22 @@ describe('AppModule', () => { expect(routesWithoutAuthentication).toEqual(PUBLIC_ROUTES); }); + + // An intended change of a route is confirmed by an update of the snapshot + // (--updateSnapshot), hence each change shows in the review + it('should keep the security of each route', async () => { + const controllers = await getControllers(AppModule); + + const routesWithSecurity = controllers.flatMap((controller) => { + return getRoutesWithSecurity(controller); + }); + + const routes = routesWithSecurity.map(([route]) => { + return route; + }); + + // The snapshot is an object, which would hide a route declared twice + expect(new Set(routes).size).toBe(routes.length); + expect(Object.fromEntries(routesWithSecurity)).toMatchSnapshot(); + }); }); diff --git a/apps/api/src/app/app.module.ts b/apps/api/src/app/app.module.ts index 33617602d6..4286bf7870 100644 --- a/apps/api/src/app/app.module.ts +++ b/apps/api/src/app/app.module.ts @@ -14,6 +14,7 @@ import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; import { PropertyModule } from '@ghostfolio/api/services/property/property.module'; import { DataGatheringQueueModule } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.module'; import { PortfolioSnapshotQueueModule } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.module'; +import { RedisCacheModule } from '@ghostfolio/api/services/redis-cache/redis-cache.module'; import { BULL_BOARD_ROUTE, THROTTLE_DEFAULT_LIMIT, @@ -62,9 +63,7 @@ import { HealthModule } from './health/health.module'; import { ImportModule } from './import/import.module'; import { InfoModule } from './info/info.module'; import { LogoModule } from './logo/logo.module'; -import { PlatformModule } from './platform/platform.module'; import { PortfolioModule } from './portfolio/portfolio.module'; -import { RedisCacheModule } from './redis-cache/redis-cache.module'; import { SubscriptionModule } from './subscription/subscription.module'; import { SymbolModule } from './symbol/symbol.module'; import { UserModule } from './user/user.module'; @@ -130,7 +129,6 @@ import { UserModule } from './user/user.module'; LogoModule, MarketDataModule, McpModule, - PlatformModule, PlatformsModule, PortfolioModule, PortfolioSnapshotQueueModule, diff --git a/apps/api/src/app/auth/auth.module.ts b/apps/api/src/app/auth/auth.module.ts index 879463cc13..1eafdb6091 100644 --- a/apps/api/src/app/auth/auth.module.ts +++ b/apps/api/src/app/auth/auth.module.ts @@ -1,6 +1,5 @@ import { AuthDeviceService } from '@ghostfolio/api/app/auth-device/auth-device.service'; import { WebAuthService } from '@ghostfolio/api/app/auth/web-auth.service'; -import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; import { SubscriptionModule } from '@ghostfolio/api/app/subscription/subscription.module'; import { UserModule } from '@ghostfolio/api/app/user/user.module'; import { ApiKeyService } from '@ghostfolio/api/services/api-key/api-key.service'; @@ -12,6 +11,7 @@ import { FetchService } from '@ghostfolio/api/services/fetch/fetch.service'; import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; import { PropertyModule } from '@ghostfolio/api/services/property/property.module'; import { PortfolioSnapshotQueueModule } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.module'; +import { RedisCacheModule } from '@ghostfolio/api/services/redis-cache/redis-cache.module'; import { Logger, Module } from '@nestjs/common'; import { JwtModule } from '@nestjs/jwt'; diff --git a/apps/api/src/app/auth/web-auth.service.ts b/apps/api/src/app/auth/web-auth.service.ts index 40a86398a6..fe9a9467b2 100644 --- a/apps/api/src/app/auth/web-auth.service.ts +++ b/apps/api/src/app/auth/web-auth.service.ts @@ -1,10 +1,10 @@ import { AuthDeviceService } from '@ghostfolio/api/app/auth-device/auth-device.service'; import { PortfolioSnapshotValue } from '@ghostfolio/api/app/portfolio/interfaces/snapshot-value.interface'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; import { UserService } from '@ghostfolio/api/app/user/user.service'; import { ApiService } from '@ghostfolio/api/services/api/api.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; import { PORTFOLIO_SNAPSHOT_COMPUTATION_QUEUE_PRIORITY_LOW, PORTFOLIO_SNAPSHOT_PROCESS_JOB_NAME, diff --git a/apps/api/src/app/cache/cache.controller.ts b/apps/api/src/app/cache/cache.controller.ts index 680e7e6825..1072550d64 100644 --- a/apps/api/src/app/cache/cache.controller.ts +++ b/apps/api/src/app/cache/cache.controller.ts @@ -1,7 +1,7 @@ -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; import { AllowDuringImpersonation } from '@ghostfolio/api/decorators/allow-during-impersonation.decorator'; import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator'; import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; import { permissions } from '@ghostfolio/common/permissions'; import { Controller, Post, UseGuards } from '@nestjs/common'; diff --git a/apps/api/src/app/cache/cache.module.ts b/apps/api/src/app/cache/cache.module.ts index d435c72a64..6bc9fb16c3 100644 --- a/apps/api/src/app/cache/cache.module.ts +++ b/apps/api/src/app/cache/cache.module.ts @@ -1,4 +1,4 @@ -import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; +import { RedisCacheModule } from '@ghostfolio/api/services/redis-cache/redis-cache.module'; import { Module } from '@nestjs/common'; diff --git a/apps/api/src/app/endpoints/benchmarks/benchmarks.module.ts b/apps/api/src/app/endpoints/benchmarks/benchmarks.module.ts index 3c540c3370..29dc7190d0 100644 --- a/apps/api/src/app/endpoints/benchmarks/benchmarks.module.ts +++ b/apps/api/src/app/endpoints/benchmarks/benchmarks.module.ts @@ -5,7 +5,6 @@ import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calcul import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service'; import { RulesService } from '@ghostfolio/api/app/portfolio/rules.service'; -import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; import { SymbolModule } from '@ghostfolio/api/app/symbol/symbol.module'; import { UserModule } from '@ghostfolio/api/app/user/user.module'; import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module'; @@ -22,6 +21,7 @@ import { MarketDataService } from '@ghostfolio/api/services/market-data/market-d import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; import { PropertyModule } from '@ghostfolio/api/services/property/property.module'; import { PortfolioSnapshotQueueModule } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.module'; +import { RedisCacheModule } from '@ghostfolio/api/services/redis-cache/redis-cache.module'; import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module'; import { TagModule } from '@ghostfolio/api/services/tag/tag.module'; diff --git a/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.module.ts b/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.module.ts index 1b8788ecb7..e77dea9eeb 100644 --- a/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.module.ts +++ b/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.module.ts @@ -1,4 +1,3 @@ -import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; import { SymbolModule } from '@ghostfolio/api/app/symbol/symbol.module'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { CryptocurrencyModule } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.module'; @@ -17,6 +16,7 @@ import { FetchModule } from '@ghostfolio/api/services/fetch/fetch.module'; import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module'; import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; import { PropertyModule } from '@ghostfolio/api/services/property/property.module'; +import { RedisCacheModule } from '@ghostfolio/api/services/redis-cache/redis-cache.module'; import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module'; import { Module } from '@nestjs/common'; diff --git a/apps/api/src/app/platform/platform.controller.ts b/apps/api/src/app/endpoints/platforms/platform.controller.ts similarity index 95% rename from apps/api/src/app/platform/platform.controller.ts rename to apps/api/src/app/endpoints/platforms/platform.controller.ts index 6c0af45158..bddc1b9aa7 100644 --- a/apps/api/src/app/platform/platform.controller.ts +++ b/apps/api/src/app/endpoints/platforms/platform.controller.ts @@ -1,6 +1,7 @@ import { AllowDuringImpersonation } from '@ghostfolio/api/decorators/allow-during-impersonation.decorator'; import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator'; import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; +import { PlatformService } from '@ghostfolio/api/services/platform/platform.service'; import { CreatePlatformDto, UpdatePlatformDto } from '@ghostfolio/common/dtos'; import { permissions } from '@ghostfolio/common/permissions'; @@ -19,8 +20,7 @@ import { AuthGuard } from '@nestjs/passport'; import { Platform } from '@prisma/client'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; -import { PlatformService } from './platform.service'; - +/** @deprecated */ @AllowDuringImpersonation() @Controller('platform') export class PlatformController { diff --git a/apps/api/src/app/endpoints/platforms/platforms.controller.ts b/apps/api/src/app/endpoints/platforms/platforms.controller.ts index 92ba772971..11d3972a81 100644 --- a/apps/api/src/app/endpoints/platforms/platforms.controller.ts +++ b/apps/api/src/app/endpoints/platforms/platforms.controller.ts @@ -1,24 +1,125 @@ -import { PlatformService } from '@ghostfolio/api/app/platform/platform.service'; +import { AllowDuringImpersonation } from '@ghostfolio/api/decorators/allow-during-impersonation.decorator'; import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator'; import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; -import { PlatformsResponse } from '@ghostfolio/common/interfaces'; -import { permissions } from '@ghostfolio/common/permissions'; +import { PlatformService } from '@ghostfolio/api/services/platform/platform.service'; +import { CreatePlatformDto, UpdatePlatformDto } from '@ghostfolio/common/dtos'; +import { + PlatformResponse, + PlatformsResponse +} from '@ghostfolio/common/interfaces'; +import { hasPermission, permissions } from '@ghostfolio/common/permissions'; +import type { RequestWithUser } from '@ghostfolio/common/types'; -import { Controller, Get, UseGuards } from '@nestjs/common'; +import { + Body, + Controller, + Delete, + Get, + HttpException, + Inject, + Param, + Post, + Put, + UseGuards +} from '@nestjs/common'; +import { REQUEST } from '@nestjs/core'; import { AuthGuard } from '@nestjs/passport'; +import { StatusCodes, getReasonPhrase } from 'http-status-codes'; +import { pick } from 'lodash-es'; +@AllowDuringImpersonation() @Controller('platforms') export class PlatformsController { - public constructor(private readonly platformService: PlatformService) {} + public constructor( + private readonly platformService: PlatformService, + @Inject(REQUEST) private readonly request: RequestWithUser + ) {} + + @HasPermission(permissions.createPlatform) + @Post() + @UseGuards(AuthGuard('jwt'), HasPermissionGuard) + public async createPlatform( + @Body() data: CreatePlatformDto + ): Promise { + const platform = await this.platformService.createPlatform(data); + + return pick(platform, ['id', 'name', 'url']); + } + + @Delete(':id') + @HasPermission(permissions.deletePlatform) + @UseGuards(AuthGuard('jwt'), HasPermissionGuard) + public async deletePlatform(@Param('id') id: string): Promise { + const originalPlatform = await this.platformService.getPlatform({ + id + }); + + if (!originalPlatform) { + throw new HttpException( + getReasonPhrase(StatusCodes.FORBIDDEN), + StatusCodes.FORBIDDEN + ); + } + + await this.platformService.deletePlatform({ id }); + } @Get() @HasPermission(permissions.readPlatforms) @UseGuards(AuthGuard('jwt'), HasPermissionGuard) public async getPlatforms(): Promise { + if ( + hasPermission( + this.request.user.permissions, + permissions.readPlatformsWithAccountCount + ) + ) { + const platformsWithAccountCount = + await this.platformService.getPlatformsWithAccountCount({ + orderBy: { name: 'asc' } + }); + + return { platforms: platformsWithAccountCount }; + } + const platforms = await this.platformService.getPlatforms({ orderBy: { name: 'asc' } }); - return { platforms }; + return { + platforms: platforms.map((platform) => { + return pick(platform, ['id', 'name', 'url']); + }) + }; + } + + @HasPermission(permissions.updatePlatform) + @Put(':id') + @UseGuards(AuthGuard('jwt'), HasPermissionGuard) + public async updatePlatform( + @Param('id') id: string, + @Body() data: UpdatePlatformDto + ): Promise { + const originalPlatform = await this.platformService.getPlatform({ + id + }); + + if (!originalPlatform) { + throw new HttpException( + getReasonPhrase(StatusCodes.FORBIDDEN), + StatusCodes.FORBIDDEN + ); + } + + const platform = await this.platformService.updatePlatform({ + data: { + ...data + }, + where: { + id + } + }); + + return pick(platform, ['id', 'name', 'url']); } } diff --git a/apps/api/src/app/endpoints/platforms/platforms.module.ts b/apps/api/src/app/endpoints/platforms/platforms.module.ts index 21d0edf69d..5af50fd63d 100644 --- a/apps/api/src/app/endpoints/platforms/platforms.module.ts +++ b/apps/api/src/app/endpoints/platforms/platforms.module.ts @@ -1,11 +1,12 @@ -import { PlatformModule } from '@ghostfolio/api/app/platform/platform.module'; +import { PlatformModule } from '@ghostfolio/api/services/platform/platform.module'; import { Module } from '@nestjs/common'; +import { PlatformController } from './platform.controller'; import { PlatformsController } from './platforms.controller'; @Module({ - controllers: [PlatformsController], + controllers: [PlatformController, PlatformsController], imports: [PlatformModule] }) export class PlatformsModule {} diff --git a/apps/api/src/app/endpoints/public/public.module.ts b/apps/api/src/app/endpoints/public/public.module.ts index d94f350c36..4e4c4b7c00 100644 --- a/apps/api/src/app/endpoints/public/public.module.ts +++ b/apps/api/src/app/endpoints/public/public.module.ts @@ -6,7 +6,6 @@ import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calcul import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service'; import { RulesService } from '@ghostfolio/api/app/portfolio/rules.service'; -import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; import { UserModule } from '@ghostfolio/api/app/user/user.module'; import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module'; import { BenchmarkModule } from '@ghostfolio/api/services/benchmark/benchmark.module'; @@ -17,6 +16,7 @@ import { ImpersonationModule } from '@ghostfolio/api/services/impersonation/impe import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module'; import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; import { PortfolioSnapshotQueueModule } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.module'; +import { RedisCacheModule } from '@ghostfolio/api/services/redis-cache/redis-cache.module'; import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module'; import { TagModule } from '@ghostfolio/api/services/tag/tag.module'; diff --git a/apps/api/src/app/health/health.module.ts b/apps/api/src/app/health/health.module.ts index c36924121c..7b517ef013 100644 --- a/apps/api/src/app/health/health.module.ts +++ b/apps/api/src/app/health/health.module.ts @@ -1,9 +1,9 @@ import { AiModule } from '@ghostfolio/api/app/endpoints/ai/ai.module'; -import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module'; import { DataEnhancerModule } from '@ghostfolio/api/services/data-provider/data-enhancer/data-enhancer.module'; import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { PropertyModule } from '@ghostfolio/api/services/property/property.module'; +import { RedisCacheModule } from '@ghostfolio/api/services/redis-cache/redis-cache.module'; import { Module } from '@nestjs/common'; diff --git a/apps/api/src/app/health/health.service.ts b/apps/api/src/app/health/health.service.ts index 42a0be61b8..6cfd5cbce3 100644 --- a/apps/api/src/app/health/health.service.ts +++ b/apps/api/src/app/health/health.service.ts @@ -1,7 +1,7 @@ -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; import { DataEnhancerService } from '@ghostfolio/api/services/data-provider/data-enhancer/data-enhancer.service'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; import { PROPERTY_CURRENCIES } from '@ghostfolio/common/config'; import { Injectable } from '@nestjs/common'; diff --git a/apps/api/src/app/import/import.module.ts b/apps/api/src/app/import/import.module.ts index 1a04e7505b..f972448863 100644 --- a/apps/api/src/app/import/import.module.ts +++ b/apps/api/src/app/import/import.module.ts @@ -1,9 +1,7 @@ import { AccountModule } from '@ghostfolio/api/app/account/account.module'; import { ActivitiesModule } from '@ghostfolio/api/app/activities/activities.module'; import { CacheModule } from '@ghostfolio/api/app/cache/cache.module'; -import { PlatformModule } from '@ghostfolio/api/app/platform/platform.module'; import { PortfolioModule } from '@ghostfolio/api/app/portfolio/portfolio.module'; -import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module'; import { TransformDataSourceInResponseModule } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.module'; import { ApiModule } from '@ghostfolio/api/services/api/api.module'; @@ -11,8 +9,10 @@ import { ConfigurationModule } from '@ghostfolio/api/services/configuration/conf import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module'; import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module'; +import { PlatformModule } from '@ghostfolio/api/services/platform/platform.module'; import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; import { DataGatheringQueueModule } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.module'; +import { RedisCacheModule } from '@ghostfolio/api/services/redis-cache/redis-cache.module'; import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module'; import { TagModule } from '@ghostfolio/api/services/tag/tag.module'; diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts index 6f036479c7..2d09cb78df 100644 --- a/apps/api/src/app/import/import.service.ts +++ b/apps/api/src/app/import/import.service.ts @@ -1,6 +1,5 @@ import { AccountService } from '@ghostfolio/api/app/account/account.service'; import { ActivitiesService } from '@ghostfolio/api/app/activities/activities.service'; -import { PlatformService } from '@ghostfolio/api/app/platform/platform.service'; import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service'; import { getTagsWithDraftTag } from '@ghostfolio/api/helper/activity.helper'; import { getMaskedGhostfolioDataSource } from '@ghostfolio/api/helper/data-source.helper'; @@ -9,6 +8,7 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration/con import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; +import { PlatformService } from '@ghostfolio/api/services/platform/platform.service'; import { DataGatheringService } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { TagService } from '@ghostfolio/api/services/tag/tag.service'; diff --git a/apps/api/src/app/info/info.module.ts b/apps/api/src/app/info/info.module.ts index 06b7249095..7d30a7b53f 100644 --- a/apps/api/src/app/info/info.module.ts +++ b/apps/api/src/app/info/info.module.ts @@ -1,5 +1,3 @@ -import { PlatformModule } from '@ghostfolio/api/app/platform/platform.module'; -import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; import { SubscriptionModule } from '@ghostfolio/api/app/subscription/subscription.module'; import { UserModule } from '@ghostfolio/api/app/user/user.module'; import { TransformDataSourceInResponseModule } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.module'; @@ -8,8 +6,10 @@ import { ConfigurationModule } from '@ghostfolio/api/services/configuration/conf import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module'; import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module'; +import { PlatformModule } from '@ghostfolio/api/services/platform/platform.module'; import { PropertyModule } from '@ghostfolio/api/services/property/property.module'; import { DataGatheringQueueModule } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.module'; +import { RedisCacheModule } from '@ghostfolio/api/services/redis-cache/redis-cache.module'; import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module'; import { Module } from '@nestjs/common'; diff --git a/apps/api/src/app/info/info.service.ts b/apps/api/src/app/info/info.service.ts index 2a7382446c..ab373055bf 100644 --- a/apps/api/src/app/info/info.service.ts +++ b/apps/api/src/app/info/info.service.ts @@ -1,4 +1,3 @@ -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; import { SubscriptionService } from '@ghostfolio/api/app/subscription/subscription.service'; import { UserService } from '@ghostfolio/api/app/user/user.service'; import { BenchmarkService } from '@ghostfolio/api/services/benchmark/benchmark.service'; @@ -7,6 +6,7 @@ import { DataProviderService } from '@ghostfolio/api/services/data-provider/data import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; import { DEFAULT_CURRENCY, ghostfolioFearAndGreedIndexSymbolStocks, diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts index 48ab8f3218..138ee1020f 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts @@ -1,8 +1,8 @@ import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; import { SubscriptionType } from '@ghostfolio/common/enums'; import { Activity, diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts index 4ef6a95841..7d0c2ddda9 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts @@ -14,13 +14,13 @@ import { PortfolioCalculatorActivity } from '@ghostfolio/api/app/portfolio/inter import { PortfolioCalculatorHolding } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-calculator-holding.interface'; import { PortfolioSnapshotValue } from '@ghostfolio/api/app/portfolio/interfaces/snapshot-value.interface'; import { PerformancePercentages } from '@ghostfolio/api/app/portfolio/types/performance-percentages.type'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; import { getFactor } from '@ghostfolio/api/helper/portfolio.helper'; import { LogPerformance } from '@ghostfolio/api/interceptors/performance-logging/performance-logging.interceptor'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { DataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; import { getAnnualizedPerformancePercent, getIntervalFromDateRange diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-buy.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-buy.spec.ts index 5d3cbcf320..15f61d0614 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-buy.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-buy.spec.ts @@ -5,12 +5,12 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -35,7 +35,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts index 54f1dfcb48..9d114d8dce 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts @@ -6,12 +6,12 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { Activity } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -37,7 +37,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts index 9ee4877180..6bd391683b 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts @@ -6,12 +6,12 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -36,7 +36,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts index afae22c1c6..de507791c7 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts @@ -5,12 +5,12 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -35,7 +35,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur-in-base-currency-eur.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur-in-base-currency-eur.spec.ts index 5c78f8a42b..f3f069eeae 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur-in-base-currency-eur.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur-in-base-currency-eur.spec.ts @@ -7,13 +7,13 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataServiceMock } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service.mock'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { Activity, ExportResponse } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -51,7 +51,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur.spec.ts index ed8841b167..219b988f5b 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur.spec.ts @@ -7,12 +7,12 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { Activity, ExportResponse } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -39,7 +39,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts index 507dd731e2..1ff6f55d59 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts @@ -6,13 +6,13 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataServiceMock } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service.mock'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper'; import { parseDate } from '@ghostfolio/common/helper'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -38,7 +38,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-before-first-market-price.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-before-first-market-price.spec.ts index 6b3d9ce6d4..bd8d76948c 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-before-first-market-price.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-before-first-market-price.spec.ts @@ -6,12 +6,12 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { Activity } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -35,7 +35,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-short.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-short.spec.ts index 356fdebd03..30201c34f7 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-short.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-short.spec.ts @@ -5,12 +5,12 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -35,7 +35,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts index 0d0f6cfc3b..05ac3f5072 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts @@ -5,12 +5,12 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -35,7 +35,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts index b98aa9e5f2..67aa90899e 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts @@ -9,8 +9,6 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { AssetProfileSplitService } from '@ghostfolio/api/services/asset-profile-split/asset-profile-split.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; @@ -18,6 +16,8 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate- import { ExchangeRateDataServiceMock } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service.mock'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { PortfolioSnapshotHolding } from '@ghostfolio/common/models'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -57,7 +57,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts index 6e9a42e3e8..06c68f8773 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts @@ -6,12 +6,12 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { Activity } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -37,7 +37,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy-with-dividend.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy-with-dividend.spec.ts index 46dbe493bf..1a5af99daf 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy-with-dividend.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy-with-dividend.spec.ts @@ -6,13 +6,13 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataServiceMock } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service.mock'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { Activity } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -38,7 +38,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts index e048d2df51..f375a32419 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts @@ -6,13 +6,13 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataServiceMock } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service.mock'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { Activity } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -38,7 +38,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-fee-and-dividend.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-fee-and-dividend.spec.ts index bb6385d18d..891d4397e0 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-fee-and-dividend.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-fee-and-dividend.spec.ts @@ -7,12 +7,12 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { Activity } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -38,7 +38,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-jnug-buy-and-sell-and-buy-and-sell.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-jnug-buy-and-sell-and-buy-and-sell.spec.ts index b2e0ffab6b..2c5aec649b 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-jnug-buy-and-sell-and-buy-and-sell.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-jnug-buy-and-sell-and-buy-and-sell.spec.ts @@ -5,12 +5,12 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -35,7 +35,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts index 370205fc98..8e9b87bf31 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts @@ -6,12 +6,12 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { Activity } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -37,7 +37,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-manual-buy-before-first-market-price.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-manual-buy-before-first-market-price.spec.ts index 135e6f6407..47ac8df4c7 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-manual-buy-before-first-market-price.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-manual-buy-before-first-market-price.spec.ts @@ -6,12 +6,12 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { Activity } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -35,7 +35,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-manual-buy-with-market-price-between-chart-dates.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-manual-buy-with-market-price-between-chart-dates.spec.ts index 989409e77b..9658d998d0 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-manual-buy-with-market-price-between-chart-dates.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-manual-buy-with-market-price-between-chart-dates.spec.ts @@ -6,12 +6,12 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { Activity } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -37,7 +37,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-and-sell.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-and-sell.spec.ts index 1db23bc6cb..2ca108430d 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-and-sell.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-and-sell.spec.ts @@ -6,12 +6,12 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { Activity } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -35,7 +35,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-from-two-data-sources.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-from-two-data-sources.spec.ts index 00b3ce4af1..d420ee873b 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-from-two-data-sources.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-from-two-data-sources.spec.ts @@ -6,13 +6,13 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataServiceMock } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service.mock'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { Activity } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -38,7 +38,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts index 2b89ddd31b..b3e67a0793 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts @@ -6,12 +6,12 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -36,7 +36,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-no-activities.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-no-activities.spec.ts index f248114fe4..2326e3486f 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-no-activities.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-no-activities.spec.ts @@ -2,12 +2,12 @@ import { userDummyData } from '@ghostfolio/api/app/portfolio/calculator/portfoli import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -32,7 +32,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts index e85a053e61..1e12a6aa2c 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts @@ -5,12 +5,12 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -35,7 +35,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts index a0174b048e..f5c7923853 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts @@ -5,12 +5,12 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -35,7 +35,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-stock-split.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-stock-split.spec.ts index f45e453734..1292cbe04f 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-stock-split.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-stock-split.spec.ts @@ -6,13 +6,13 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { adjustActivityBySplits } from '@ghostfolio/api/services/asset-profile-split/asset-profile-split.helper'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { Activity } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -39,7 +39,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-valuable.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-valuable.spec.ts index 3d0ab45f85..69df7ac49b 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-valuable.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-valuable.spec.ts @@ -6,12 +6,12 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { Activity } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -37,7 +37,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.spec.ts index c2779c2c14..9400ec20ff 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.spec.ts @@ -7,12 +7,12 @@ import { userDummyData } from '@ghostfolio/api/app/portfolio/calculator/portfoli import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper'; import { HistoricalDataItem } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -36,7 +36,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-baln-buy-and-buy.spec.ts b/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-baln-buy-and-buy.spec.ts index a7167a2de1..e2c7d1e5a8 100644 --- a/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-baln-buy-and-buy.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-baln-buy-and-buy.spec.ts @@ -5,12 +5,12 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -35,7 +35,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-baln-buy-and-sell.spec.ts b/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-baln-buy-and-sell.spec.ts index 02300923dd..3a228349c2 100644 --- a/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-baln-buy-and-sell.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-baln-buy-and-sell.spec.ts @@ -6,12 +6,12 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -36,7 +36,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-baln-buy.spec.ts b/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-baln-buy.spec.ts index c590f4503f..9bfcd12b2a 100644 --- a/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-baln-buy.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-baln-buy.spec.ts @@ -5,12 +5,12 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -35,7 +35,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-baln-sell-and-buy-on-same-day.spec.ts b/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-baln-sell-and-buy-on-same-day.spec.ts index 6a66322fcb..080901f3a5 100644 --- a/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-baln-sell-and-buy-on-same-day.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-baln-sell-and-buy-on-same-day.spec.ts @@ -7,12 +7,12 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { Activity } from '@ghostfolio/common/interfaces'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -38,7 +38,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts b/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts index 3d6a9e35ad..8aa8c6bb07 100644 --- a/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts @@ -6,13 +6,13 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataServiceMock } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service.mock'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper'; import { parseDate } from '@ghostfolio/common/helper'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -38,7 +38,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-cash.spec.ts b/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-cash.spec.ts index 59060483ce..1f44b6ce2b 100644 --- a/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-cash.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-cash.spec.ts @@ -10,8 +10,6 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { AssetProfileSplitService } from '@ghostfolio/api/services/asset-profile-split/asset-profile-split.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; @@ -19,6 +17,8 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate- import { ExchangeRateDataServiceMock } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service.mock'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { PortfolioSnapshotHolding } from '@ghostfolio/common/models'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -58,7 +58,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-msft-buy-with-dividend.spec.ts b/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-msft-buy-with-dividend.spec.ts index 0c0e32d931..31173e4f14 100644 --- a/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-msft-buy-with-dividend.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-msft-buy-with-dividend.spec.ts @@ -6,12 +6,12 @@ import { import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import { parseDate } from '@ghostfolio/common/helper'; import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; @@ -36,7 +36,7 @@ jest.mock( } ); -jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { +jest.mock('@ghostfolio/api/services/redis-cache/redis-cache.service', () => { return { RedisCacheService: jest.fn().mockImplementation(() => { return RedisCacheServiceMock; diff --git a/apps/api/src/app/portfolio/portfolio.module.ts b/apps/api/src/app/portfolio/portfolio.module.ts index 7f7a894df0..82eeb5395a 100644 --- a/apps/api/src/app/portfolio/portfolio.module.ts +++ b/apps/api/src/app/portfolio/portfolio.module.ts @@ -2,7 +2,6 @@ import { AccessModule } from '@ghostfolio/api/app/access/access.module'; import { AccountBalanceService } from '@ghostfolio/api/app/account-balance/account-balance.service'; import { AccountService } from '@ghostfolio/api/app/account/account.service'; import { ActivitiesModule } from '@ghostfolio/api/app/activities/activities.module'; -import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; import { UserModule } from '@ghostfolio/api/app/user/user.module'; import { PerformanceLoggingModule } from '@ghostfolio/api/interceptors/performance-logging/performance-logging.module'; import { RedactValuesInResponseModule } from '@ghostfolio/api/interceptors/redact-values-in-response/redact-values-in-response.module'; @@ -19,6 +18,7 @@ import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-da import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; import { DataGatheringQueueModule } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.module'; import { PortfolioSnapshotQueueModule } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.module'; +import { RedisCacheModule } from '@ghostfolio/api/services/redis-cache/redis-cache.module'; import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module'; import { TagModule } from '@ghostfolio/api/services/tag/tag.module'; diff --git a/apps/api/src/events/events.module.ts b/apps/api/src/events/events.module.ts index dabc3edb77..b04a59a00e 100644 --- a/apps/api/src/events/events.module.ts +++ b/apps/api/src/events/events.module.ts @@ -1,5 +1,4 @@ import { ActivitiesModule } from '@ghostfolio/api/app/activities/activities.module'; -import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; import { UserModule } from '@ghostfolio/api/app/user/user.module'; import { ApiModule } from '@ghostfolio/api/services/api/api.module'; import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; @@ -7,6 +6,7 @@ import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data- import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module'; import { DataGatheringQueueModule } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.module'; import { PortfolioSnapshotQueueModule } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.module'; +import { RedisCacheModule } from '@ghostfolio/api/services/redis-cache/redis-cache.module'; import { Module } from '@nestjs/common'; diff --git a/apps/api/src/events/portfolio-changed.listener.ts b/apps/api/src/events/portfolio-changed.listener.ts index 9397eb0e52..a081e1bbdb 100644 --- a/apps/api/src/events/portfolio-changed.listener.ts +++ b/apps/api/src/events/portfolio-changed.listener.ts @@ -1,7 +1,7 @@ -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; import { UserService } from '@ghostfolio/api/app/user/user.service'; import { ApiService } from '@ghostfolio/api/services/api/api.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; import { PORTFOLIO_SNAPSHOT_COMPUTATION_QUEUE_PRIORITY_LOW, PORTFOLIO_SNAPSHOT_PROCESS_JOB_NAME, diff --git a/apps/api/src/services/benchmark/benchmark.module.ts b/apps/api/src/services/benchmark/benchmark.module.ts index 870ef244fe..561a951971 100644 --- a/apps/api/src/services/benchmark/benchmark.module.ts +++ b/apps/api/src/services/benchmark/benchmark.module.ts @@ -1,8 +1,8 @@ -import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module'; import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; import { PropertyModule } from '@ghostfolio/api/services/property/property.module'; +import { RedisCacheModule } from '@ghostfolio/api/services/redis-cache/redis-cache.module'; import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module'; import { Module } from '@nestjs/common'; diff --git a/apps/api/src/services/benchmark/benchmark.service.ts b/apps/api/src/services/benchmark/benchmark.service.ts index 3c39848401..ea7bbaec12 100644 --- a/apps/api/src/services/benchmark/benchmark.service.ts +++ b/apps/api/src/services/benchmark/benchmark.service.ts @@ -1,8 +1,8 @@ -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { CACHE_TTL_INFINITE, diff --git a/apps/api/src/services/data-provider/data-provider.module.ts b/apps/api/src/services/data-provider/data-provider.module.ts index 2c6e9fce18..e932d387fd 100644 --- a/apps/api/src/services/data-provider/data-provider.module.ts +++ b/apps/api/src/services/data-provider/data-provider.module.ts @@ -1,4 +1,3 @@ -import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; import { CryptocurrencyModule } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.module'; import { AlphaVantageService } from '@ghostfolio/api/services/data-provider/alpha-vantage/alpha-vantage.service'; @@ -14,6 +13,7 @@ import { FetchModule } from '@ghostfolio/api/services/fetch/fetch.module'; import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module'; import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; import { PropertyModule } from '@ghostfolio/api/services/property/property.module'; +import { RedisCacheModule } from '@ghostfolio/api/services/redis-cache/redis-cache.module'; import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module'; import { Module } from '@nestjs/common'; diff --git a/apps/api/src/services/data-provider/data-provider.service.spec.ts b/apps/api/src/services/data-provider/data-provider.service.spec.ts index 20f5acaadb..815ab2743e 100644 --- a/apps/api/src/services/data-provider/data-provider.service.spec.ts +++ b/apps/api/src/services/data-provider/data-provider.service.spec.ts @@ -1,9 +1,9 @@ -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; import { DataSource } from '@prisma/client'; diff --git a/apps/api/src/services/data-provider/data-provider.service.ts b/apps/api/src/services/data-provider/data-provider.service.ts index 3206b2442b..826e14d16a 100644 --- a/apps/api/src/services/data-provider/data-provider.service.ts +++ b/apps/api/src/services/data-provider/data-provider.service.ts @@ -1,12 +1,12 @@ import { ImportValidationError } from '@ghostfolio/api/app/import/errors/import-validation.error'; import { ImportDataDto } from '@ghostfolio/api/app/import/import-data.dto'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; import { getMaskedGhostfolioDataSource } from '@ghostfolio/api/helper/data-source.helper'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; import { DEFAULT_CURRENCY, DERIVED_CURRENCIES, diff --git a/apps/api/src/app/platform/platform.module.ts b/apps/api/src/services/platform/platform.module.ts similarity index 76% rename from apps/api/src/app/platform/platform.module.ts rename to apps/api/src/services/platform/platform.module.ts index 42ae9d193d..849e037eda 100644 --- a/apps/api/src/app/platform/platform.module.ts +++ b/apps/api/src/services/platform/platform.module.ts @@ -2,11 +2,9 @@ import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; import { Module } from '@nestjs/common'; -import { PlatformController } from './platform.controller'; import { PlatformService } from './platform.service'; @Module({ - controllers: [PlatformController], exports: [PlatformService], imports: [PrismaModule], providers: [PlatformService] diff --git a/apps/api/src/app/platform/platform.service.ts b/apps/api/src/services/platform/platform.service.ts similarity index 92% rename from apps/api/src/app/platform/platform.service.ts rename to apps/api/src/services/platform/platform.service.ts index 200b4de007..56da19b2a5 100644 --- a/apps/api/src/app/platform/platform.service.ts +++ b/apps/api/src/services/platform/platform.service.ts @@ -49,9 +49,14 @@ export class PlatformService { }); } - public async getPlatformsWithAccountCount() { + public async getPlatformsWithAccountCount({ + orderBy + }: { + orderBy?: Prisma.PlatformOrderByWithRelationInput; + } = {}) { const platformsWithAccountCount = await this.prismaService.platform.findMany({ + orderBy, include: { _count: { select: { accounts: true } diff --git a/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.module.ts b/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.module.ts index 0da5298211..617bf1aae4 100644 --- a/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.module.ts +++ b/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.module.ts @@ -2,12 +2,12 @@ import { AccountBalanceModule } from '@ghostfolio/api/app/account-balance/accoun import { ActivitiesModule } from '@ghostfolio/api/app/activities/activities.module'; import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; -import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module'; import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; +import { RedisCacheModule } from '@ghostfolio/api/services/redis-cache/redis-cache.module'; import { DEFAULT_PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_TIMEOUT, PORTFOLIO_SNAPSHOT_COMPUTATION_QUEUE diff --git a/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.processor.ts b/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.processor.ts index adc66e6028..b9628f9447 100644 --- a/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.processor.ts +++ b/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.processor.ts @@ -2,8 +2,8 @@ import { AccountBalanceService } from '@ghostfolio/api/app/account-balance/accou import { ActivitiesService } from '@ghostfolio/api/app/activities/activities.service'; import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { PortfolioSnapshotValue } from '@ghostfolio/api/app/portfolio/interfaces/snapshot-value.interface'; -import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; +import { RedisCacheService } from '@ghostfolio/api/services/redis-cache/redis-cache.service'; import { CACHE_TTL_INFINITE, DEFAULT_PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_CONCURRENCY, diff --git a/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock.ts b/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock.ts index fddbd01aba..11b794dfc8 100644 --- a/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock.ts +++ b/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock.ts @@ -1,5 +1,5 @@ import { PortfolioSnapshotValue } from '@ghostfolio/api/app/portfolio/interfaces/snapshot-value.interface'; -import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; +import { RedisCacheServiceMock } from '@ghostfolio/api/services/redis-cache/redis-cache.service.mock'; import type { Job, JobId, JobOptions } from 'bull'; import ms from 'ms'; diff --git a/apps/api/src/app/redis-cache/redis-cache.module.ts b/apps/api/src/services/redis-cache/redis-cache.module.ts similarity index 100% rename from apps/api/src/app/redis-cache/redis-cache.module.ts rename to apps/api/src/services/redis-cache/redis-cache.module.ts diff --git a/apps/api/src/app/redis-cache/redis-cache.service.mock.ts b/apps/api/src/services/redis-cache/redis-cache.service.mock.ts similarity index 100% rename from apps/api/src/app/redis-cache/redis-cache.service.mock.ts rename to apps/api/src/services/redis-cache/redis-cache.service.mock.ts diff --git a/apps/api/src/app/redis-cache/redis-cache.service.ts b/apps/api/src/services/redis-cache/redis-cache.service.ts similarity index 100% rename from apps/api/src/app/redis-cache/redis-cache.service.ts rename to apps/api/src/services/redis-cache/redis-cache.service.ts diff --git a/apps/client/src/app/app.component.ts b/apps/client/src/app/app.component.ts index c9e124723d..53d3505a1c 100644 --- a/apps/client/src/app/app.component.ts +++ b/apps/client/src/app/app.component.ts @@ -25,6 +25,7 @@ import { OnInit } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { DateAdapter } from '@angular/material/core'; import { MatDialog } from '@angular/material/dialog'; import { Title } from '@angular/platform-browser'; import { @@ -39,7 +40,7 @@ import { Chart } from 'chart.js'; import { addIcons } from 'ionicons'; import { openOutline } from 'ionicons/icons'; import { DeviceDetectorService } from 'ngx-device-detector'; -import { filter } from 'rxjs/operators'; +import { distinctUntilChanged, filter, map } from 'rxjs/operators'; import { GfFooterComponent } from './components/footer/footer.component'; import { GfHeaderComponent } from './components/header/header.component'; @@ -75,6 +76,7 @@ export class GfAppComponent implements OnInit { private readonly changeDetectorRef = inject(ChangeDetectorRef); private readonly dataService = inject(DataService); + private readonly dateAdapter = inject>(DateAdapter); private readonly destroyRef = inject(DestroyRef); private readonly deviceDetectorService = inject(DeviceDetectorService); private readonly dialog = inject(MatDialog); @@ -212,6 +214,20 @@ export class GfAppComponent implements OnInit { this.changeDetectorRef.markForCheck(); }); + this.userService.stateChanged + .pipe( + map(({ user }) => { + return user?.settings.locale; + }), + distinctUntilChanged(), + takeUntilDestroyed(this.destroyRef) + ) + .subscribe((locale) => { + if (locale) { + this.dateAdapter.setLocale(locale); + } + }); + this.userService.stateChanged .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((state) => { diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html index 0b9e038a02..8e6341a709 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -505,7 +505,6 @@ [locale]="data.locale" [marketData]="marketDataItems" [symbol]="data.symbol" - [user]="user" (marketDataChanged)="onMarketDataChanged($event)" /> diff --git a/apps/client/src/app/components/admin-platform/admin-platform.component.ts b/apps/client/src/app/components/admin-platform/admin-platform.component.ts index 89175c5d33..10634d5dfb 100644 --- a/apps/client/src/app/components/admin-platform/admin-platform.component.ts +++ b/apps/client/src/app/components/admin-platform/admin-platform.component.ts @@ -3,10 +3,11 @@ import { DEFAULT_PAGE_SIZE } from '@ghostfolio/common/config'; import { CreatePlatformDto, UpdatePlatformDto } from '@ghostfolio/common/dtos'; import { ConfirmationDialogType } from '@ghostfolio/common/enums'; import { getLocale, getLowercase } from '@ghostfolio/common/helper'; +import { PlatformsResponse } from '@ghostfolio/common/interfaces'; import type { Platform } from '@ghostfolio/prisma/browser'; import { GfEntityLogoComponent } from '@ghostfolio/ui/entity-logo'; import { NotificationService } from '@ghostfolio/ui/notifications'; -import { AdminService, DataService } from '@ghostfolio/ui/services'; +import { DataService } from '@ghostfolio/ui/services'; import { GfValueComponent } from '@ghostfolio/ui/value'; import { @@ -62,7 +63,9 @@ import { CreateOrUpdatePlatformDialogParams } from './create-or-update-platform- export class GfAdminPlatformComponent implements OnInit { public readonly locale = input(getLocale()); - protected dataSource = new MatTableDataSource(); + protected dataSource = new MatTableDataSource< + PlatformsResponse['platforms'][0] + >(); protected readonly displayedColumns = [ 'icon', @@ -74,7 +77,7 @@ export class GfAdminPlatformComponent implements OnInit { protected isLoading = false; protected readonly pageSize = DEFAULT_PAGE_SIZE; - protected platforms: Platform[]; + protected platforms: PlatformsResponse['platforms']; private readonly deviceType = computed( () => this.deviceDetectorService.deviceInfo().deviceType @@ -83,7 +86,6 @@ export class GfAdminPlatformComponent implements OnInit { private readonly paginator = viewChild.required(MatPaginator); private readonly sort = viewChild.required(MatSort); - private readonly adminService = inject(AdminService); private readonly changeDetectorRef = inject(ChangeDetectorRef); private readonly dataService = inject(DataService); private readonly destroyRef = inject(DestroyRef); @@ -139,7 +141,7 @@ export class GfAdminPlatformComponent implements OnInit { } private deletePlatform(aId: string) { - this.adminService + this.dataService .deletePlatform(aId) .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe({ @@ -157,10 +159,10 @@ export class GfAdminPlatformComponent implements OnInit { private fetchPlatforms() { this.isLoading = true; - this.adminService + this.dataService .fetchPlatforms() .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe((platforms) => { + .subscribe(({ platforms }) => { this.platforms = platforms; this.dataSource = new MatTableDataSource(platforms); @@ -191,7 +193,7 @@ export class GfAdminPlatformComponent implements OnInit { .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((platform: CreatePlatformDto | null) => { if (platform) { - this.adminService + this.dataService .postPlatform(platform) .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe({ @@ -231,7 +233,7 @@ export class GfAdminPlatformComponent implements OnInit { .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((platform: UpdatePlatformDto | null) => { if (platform) { - this.adminService + this.dataService .putPlatform(platform) .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe({ diff --git a/apps/client/src/app/components/admin-users/admin-users.html b/apps/client/src/app/components/admin-users/admin-users.html index 7b49dc5f60..98056f00f7 100644 --- a/apps/client/src/app/components/admin-users/admin-users.html +++ b/apps/client/src/app/components/admin-users/admin-users.html @@ -199,7 +199,7 @@
@if (element.lastRequestClient) { diff --git a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html index efb693e59a..de56a7b003 100644 --- a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html +++ b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -437,7 +437,6 @@ [locale]="data.locale" [marketData]="marketDataItems" [symbol]="assetProfile?.symbol" - [user]="user" (marketDataChanged)="onMarketDataChanged($event)" /> diff --git a/apps/client/src/app/components/portfolio-performance/portfolio-performance.component.html b/apps/client/src/app/components/portfolio-performance/portfolio-performance.component.html index 9be2be344e..c458080e3c 100644 --- a/apps/client/src/app/components/portfolio-performance/portfolio-performance.component.html +++ b/apps/client/src/app/components/portfolio-performance/portfolio-performance.component.html @@ -3,6 +3,7 @@
@if ((errors()?.length ?? 0) > 0 && !isLoading()) { (MAT_DIALOG_DATA); - private readonly dateAdapter = inject>(DateAdapter); private readonly dataService = inject(DataService); private readonly destroyRef = inject(DestroyRef); @@ -189,8 +187,6 @@ export class GfCreateOrUpdateAccessDialogComponent implements OnInit { this.hasExperimentalFeatures = settings.isExperimentalFeatures ?? false; this.tags = getTagFilters(tags); - this.dateAdapter.setLocale(settings.locale ?? DEFAULT_LOCALE); - this.changeDetectorRef.markForCheck(); }); diff --git a/apps/client/src/app/components/user-account-settings/user-account-settings.html b/apps/client/src/app/components/user-account-settings/user-account-settings.html index ff86f9ba35..577497848a 100644 --- a/apps/client/src/app/components/user-account-settings/user-account-settings.html +++ b/apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -5,14 +5,15 @@
-
+
Base Currency
-
+
@if (user?.settings?.isExperimentalFeatures) { -
-
+
+
Performance Calculation
-
+
} -
-
+
+
Language
@if (isCommunityLanguage()) {
@@ -77,7 +78,7 @@
}
-
+
-
-
+
+
Locale
-
+
Date and number format
-
+
{{ locale }} } - +
-
-
+
+
Appearance
-
+
-
-
+
+
Presenter View
Protection for sensitive information like absolute performances and quantity values
-
+
-
-
+
+
Zen Mode
Distraction-free experience for turbulent times
-
+
-
-
+
+
Biometric Authentication
Sign in with fingerprint
-
+
-
-
+
+
Experimental Features
Sneak peek at upcoming functionality
-
+
@if (deviceType() !== 'mobile' && hasPermissionToEnableGhostfolioAi) { -
-
+
+
Ghostfolio AI
Powered by third-party AI providers
-
+
>(DateAdapter); private readonly destroyRef = inject(DestroyRef); private readonly dialogRef = inject>(MatDialogRef); @@ -127,7 +121,6 @@ export class GfCreateOrUpdateActivityDialogComponent { private readonly impersonationStorageService = inject( ImpersonationStorageService ); - private locale = inject(MAT_DATE_LOCALE); private readonly userService = inject(UserService); public constructor() { @@ -143,15 +136,11 @@ export class GfCreateOrUpdateActivityDialogComponent { !this.impersonationStorageService.getId() && hasPermission(this.data.user?.permissions, permissions.createOwnTag); - this.locale = this.data.user.settings.locale ?? DEFAULT_LOCALE; this.mode = this.data.activity?.id ? 'update' : 'create'; - this.dateAdapter.setLocale(this.locale); - const { currencies } = this.dataService.fetchInfo(); this.currencies = currencies; - this.defaultDateFormat = getDateFormatString(this.locale); this.dataService .fetchPortfolioHoldings({ diff --git a/libs/common/src/lib/interfaces/index.ts b/libs/common/src/lib/interfaces/index.ts index 5f22f4734e..c39fd74c55 100644 --- a/libs/common/src/lib/interfaces/index.ts +++ b/libs/common/src/lib/interfaces/index.ts @@ -75,6 +75,7 @@ import type { LivenessHealthResponse } from './responses/liveness-health-respons import type { LookupResponse } from './responses/lookup-response.interface'; import type { MarketDataOfMarketsResponse } from './responses/market-data-of-markets-response.interface'; import type { OAuthResponse } from './responses/oauth-response.interface'; +import type { PlatformResponse } from './responses/platform-response.interface'; import type { PlatformsResponse } from './responses/platforms-response.interface'; import type { PortfolioDividendsResponse } from './responses/portfolio-dividends-response.interface'; import type { PortfolioHoldingResponse } from './responses/portfolio-holding-response.interface'; @@ -170,6 +171,7 @@ export { MarketDataOfMarketsResponse, NullableLineChartItem, OAuthResponse, + PlatformResponse, PlatformsResponse, PortfolioChart, PortfolioDetails, diff --git a/libs/common/src/lib/interfaces/responses/platform-response.interface.ts b/libs/common/src/lib/interfaces/responses/platform-response.interface.ts new file mode 100644 index 0000000000..7c8bee930f --- /dev/null +++ b/libs/common/src/lib/interfaces/responses/platform-response.interface.ts @@ -0,0 +1,6 @@ +import type { Platform } from '@ghostfolio/prisma/browser'; + +export interface PlatformResponse extends Pick< + Platform, + 'id' | 'name' | 'url' +> {} diff --git a/libs/common/src/lib/interfaces/responses/platforms-response.interface.ts b/libs/common/src/lib/interfaces/responses/platforms-response.interface.ts index a6bfc4326c..4f9e98054f 100644 --- a/libs/common/src/lib/interfaces/responses/platforms-response.interface.ts +++ b/libs/common/src/lib/interfaces/responses/platforms-response.interface.ts @@ -1,5 +1,5 @@ -import type { Platform } from '@ghostfolio/prisma/browser'; +import type { PlatformResponse } from './platform-response.interface'; export interface PlatformsResponse { - platforms: Platform[]; + platforms: (PlatformResponse & { accountCount?: number })[]; } diff --git a/libs/ui/src/lib/account-balances/account-balances.component.ts b/libs/ui/src/lib/account-balances/account-balances.component.ts index 678650b2cb..715b4af0ec 100644 --- a/libs/ui/src/lib/account-balances/account-balances.component.ts +++ b/libs/ui/src/lib/account-balances/account-balances.component.ts @@ -11,7 +11,6 @@ import { Component, EventEmitter, OnChanges, - OnInit, Output, effect, inject, @@ -25,7 +24,6 @@ import { ReactiveFormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; -import { DateAdapter } from '@angular/material/core'; import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; @@ -63,7 +61,7 @@ import { GfValueComponent } from '../value'; styleUrls: ['./account-balances.component.scss'], templateUrl: './account-balances.component.html' }) -export class GfAccountBalancesComponent implements OnChanges, OnInit { +export class GfAccountBalancesComponent implements OnChanges { @Output() accountBalanceCreated = new EventEmitter(); @Output() accountBalanceDeleted = new EventEmitter(); @@ -94,7 +92,6 @@ export class GfAccountBalancesComponent implements OnChanges, OnInit { >(); public maxDate = new Date(); - private dateAdapter = inject>(DateAdapter); private notificationService = inject(NotificationService); public constructor() { @@ -112,10 +109,6 @@ export class GfAccountBalancesComponent implements OnChanges, OnInit { }); } - public ngOnInit() { - this.dateAdapter.setLocale(this.locale()); - } - public ngOnChanges() { if (this.accountBalances()) { this.dataSource = new MatTableDataSource(this.accountBalances()); diff --git a/libs/ui/src/lib/file-drop-area/file-drop-area.component.html b/libs/ui/src/lib/file-drop-area/file-drop-area.component.html index b5c6e19021..a4d1d6c91e 100644 --- a/libs/ui/src/lib/file-drop-area/file-drop-area.component.html +++ b/libs/ui/src/lib/file-drop-area/file-drop-area.component.html @@ -1,13 +1,14 @@ @if (isLoading()) { -
- - {{ loadingText() }} +
+
+ {{ loadingText() }} +
} @else {