Browse Source

Merge remote-tracking branch 'upstream/main' into feature/label-time-axis-of-performance-chart

# Conflicts:
#	CHANGELOG.md
pull/8267/head
mister-ideas 1 day ago
parent
commit
d5d31cb971
  1. 2
      .github/workflows/build-code.yml
  2. 41
      CHANGELOG.md
  3. 2
      DEVELOPMENT.md
  4. 1227
      apps/api/src/app/__snapshots__/app.module.spec.ts.snap
  5. 4
      apps/api/src/app/activities/activities.module.ts
  6. 212
      apps/api/src/app/app.module.spec.ts
  7. 20
      apps/api/src/app/app.module.ts
  8. 17
      apps/api/src/app/asset/asset.module.ts
  9. 19
      apps/api/src/app/auth-device/auth-device.module.ts
  10. 6
      apps/api/src/app/auth/auth.module.ts
  11. 4
      apps/api/src/app/auth/web-auth.service.ts
  12. 3
      apps/api/src/app/endpoints/asset-profiles/asset-profiles.module.ts
  13. 10
      apps/api/src/app/endpoints/asset-profiles/asset.controller.ts
  14. 10
      apps/api/src/app/endpoints/auth-devices/auth-devices.controller.ts
  15. 11
      apps/api/src/app/endpoints/auth-devices/auth-devices.module.ts
  16. 2
      apps/api/src/app/endpoints/benchmarks/benchmarks.module.ts
  17. 2
      apps/api/src/app/endpoints/cache/cache.controller.ts
  18. 2
      apps/api/src/app/endpoints/cache/cache.module.ts
  19. 2
      apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.module.ts
  20. 18
      apps/api/src/app/endpoints/exchange-rates/exchange-rates.controller.ts
  21. 13
      apps/api/src/app/endpoints/exchange-rates/exchange-rates.module.ts
  22. 2
      apps/api/src/app/endpoints/exchange-rates/exchange-rates.service.ts
  23. 0
      apps/api/src/app/endpoints/info/info.controller.ts
  24. 4
      apps/api/src/app/endpoints/info/info.module.ts
  25. 2
      apps/api/src/app/endpoints/info/info.service.ts
  26. 0
      apps/api/src/app/endpoints/logo/get-logo.dto.ts
  27. 0
      apps/api/src/app/endpoints/logo/logo.controller.ts
  28. 0
      apps/api/src/app/endpoints/logo/logo.module.ts
  29. 0
      apps/api/src/app/endpoints/logo/logo.service.ts
  30. 4
      apps/api/src/app/endpoints/platforms/platform.controller.ts
  31. 113
      apps/api/src/app/endpoints/platforms/platforms.controller.ts
  32. 5
      apps/api/src/app/endpoints/platforms/platforms.module.ts
  33. 2
      apps/api/src/app/endpoints/public/public.module.ts
  34. 14
      apps/api/src/app/exchange-rate/exchange-rate.module.ts
  35. 2
      apps/api/src/app/health/health.module.ts
  36. 2
      apps/api/src/app/health/health.service.ts
  37. 6
      apps/api/src/app/import/import.module.ts
  38. 2
      apps/api/src/app/import/import.service.ts
  39. 2
      apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts
  40. 2
      apps/api/src/app/portfolio/calculator/portfolio-calculator.ts
  41. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-buy.spec.ts
  42. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts
  43. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts
  44. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts
  45. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur-in-base-currency-eur.spec.ts
  46. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur.spec.ts
  47. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts
  48. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-before-first-market-price.spec.ts
  49. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-short.spec.ts
  50. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts
  51. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts
  52. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts
  53. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy-with-dividend.spec.ts
  54. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts
  55. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-fee-and-dividend.spec.ts
  56. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-jnug-buy-and-sell-and-buy-and-sell.spec.ts
  57. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts
  58. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-manual-buy-before-first-market-price.spec.ts
  59. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-manual-buy-with-market-price-between-chart-dates.spec.ts
  60. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-and-sell.spec.ts
  61. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-from-two-data-sources.spec.ts
  62. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts
  63. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-no-activities.spec.ts
  64. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts
  65. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts
  66. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-stock-split.spec.ts
  67. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-valuable.spec.ts
  68. 6
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.spec.ts
  69. 6
      apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-baln-buy-and-buy.spec.ts
  70. 6
      apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-baln-buy-and-sell.spec.ts
  71. 6
      apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-baln-buy.spec.ts
  72. 6
      apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-baln-sell-and-buy-on-same-day.spec.ts
  73. 6
      apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts
  74. 6
      apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-cash.spec.ts
  75. 6
      apps/api/src/app/portfolio/calculator/roi/portfolio-calculator-msft-buy-with-dividend.spec.ts
  76. 2
      apps/api/src/app/portfolio/portfolio.module.ts
  77. 2
      apps/api/src/events/events.module.ts
  78. 2
      apps/api/src/events/portfolio-changed.listener.ts
  79. 12
      apps/api/src/services/auth-device/auth-device.module.ts
  80. 0
      apps/api/src/services/auth-device/auth-device.service.ts
  81. 2
      apps/api/src/services/benchmark/benchmark.module.ts
  82. 2
      apps/api/src/services/benchmark/benchmark.service.ts
  83. 2
      apps/api/src/services/data-provider/data-provider.module.ts
  84. 2
      apps/api/src/services/data-provider/data-provider.service.spec.ts
  85. 2
      apps/api/src/services/data-provider/data-provider.service.ts
  86. 2
      apps/api/src/services/platform/platform.module.ts
  87. 7
      apps/api/src/services/platform/platform.service.ts
  88. 2
      apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.module.ts
  89. 2
      apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.processor.ts
  90. 2
      apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock.ts
  91. 0
      apps/api/src/services/redis-cache/redis-cache.module.ts
  92. 0
      apps/api/src/services/redis-cache/redis-cache.service.mock.ts
  93. 0
      apps/api/src/services/redis-cache/redis-cache.service.ts
  94. 18
      apps/client/src/app/app.component.ts
  95. 1
      apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html
  96. 20
      apps/client/src/app/components/admin-platform/admin-platform.component.ts
  97. 2
      apps/client/src/app/components/admin-users/admin-users.html
  98. 1
      apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html
  99. 12
      apps/client/src/app/components/home-overview/home-overview.component.ts
  100. 14
      apps/client/src/app/components/home-overview/home-overview.html

2
.github/workflows/build-code.yml

@ -13,7 +13,7 @@ jobs:
strategy:
matrix:
node_version:
- 22.23.2
- 22.23.4
steps:
- name: Checkout code
uses: actions/checkout@v4

41
CHANGELOG.md

@ -14,11 +14,52 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Hid the performance chart on the overview page if it has less than three data points
- Added a link to the holdings tab in the holdings table on the overview tab of the home page (experimental)
### Changed
- Improved the market price in the benchmark detail dialog by adding the currency and reducing the precision on mobile
- Improved the style of the close button in the dialog footer component
- Improved the style of the close button in the dialog header component
- Improved the language localization for German (`de`)
- Upgraded `prettier` from version `3.9.9` to `3.9.10`
## 3.90.0 - 2026-10-21
### Changed
- Extended the variations of the interstitials for the subscription by a trial
- Deprecated the `api/v1/auth-device` endpoint in favor of the `api/v1/auth-devices` endpoint
- Deprecated the `api/v1/exchange-rate` endpoint in favor of the `api/v1/exchange-rates` endpoint
- Upgraded the `Node.js` engine from version `>=22.22.3` to `>=22.23.4` (`package.json`)
- 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`
- Upgraded `@rekog/mcp-nest` from version `2.0.7` to `2.0.8`
- Upgraded `undici` from version `8.11.2` to `8.12.2`
## 3.89.0 - 2026-10-20
### Added
- Added a _Storybook_ story for the file drop area component
### 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

2
DEVELOPMENT.md

@ -5,7 +5,7 @@
### Prerequisites
- [Docker](https://www.docker.com/products/docker-desktop)
- [Node.js](https://nodejs.org/en/download) (version `>=22.18.0`)
- [Node.js](https://nodejs.org/en/download) (version `>=22.23.4`)
- Create a local copy of this Git repository (clone)
- Copy the file `.env.dev` to `.env` and populate it with your data (`cp .env.dev .env`)

1227
apps/api/src/app/__snapshots__/app.module.spec.ts.snap

File diff suppressed because it is too large

4
apps/api/src/app/activities/activities.module.ts

@ -1,7 +1,5 @@
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 +12,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';
@ -29,7 +28,6 @@ import { ActivitiesService } from './activities.service';
ApiModule,
AssetProfileSplitModule,
BenchmarkModule,
CacheModule,
DataGatheringQueueModule,
DataProviderModule,
ExchangeRateDataModule,

212
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<DynamicModule> | 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<T>(
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<string, object>;
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<boolean>(
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<string[]>(
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<VersionValue>(
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();
});
});

20
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,
@ -39,16 +40,18 @@ import { AccountModule } from './account/account.module';
import { ActivitiesModule } from './activities/activities.module';
import { AdminModule } from './admin/admin.module';
import { AppController } from './app.controller';
import { AssetModule } from './asset/asset.module';
import { AuthDeviceModule } from './auth-device/auth-device.module';
import { AuthModule } from './auth/auth.module';
import { CacheModule } from './cache/cache.module';
import { AiModule } from './endpoints/ai/ai.module';
import { ApiKeysModule } from './endpoints/api-keys/api-keys.module';
import { AssetProfilesModule } from './endpoints/asset-profiles/asset-profiles.module';
import { AssetsModule } from './endpoints/assets/assets.module';
import { AuthDevicesModule } from './endpoints/auth-devices/auth-devices.module';
import { BenchmarksModule } from './endpoints/benchmarks/benchmarks.module';
import { CacheModule } from './endpoints/cache/cache.module';
import { GhostfolioModule } from './endpoints/data-providers/ghostfolio/ghostfolio.module';
import { ExchangeRatesModule } from './endpoints/exchange-rates/exchange-rates.module';
import { InfoModule } from './endpoints/info/info.module';
import { LogoModule } from './endpoints/logo/logo.module';
import { MarketDataModule } from './endpoints/market-data/market-data.module';
import { McpModule } from './endpoints/mcp/mcp.module';
import { PlatformsModule } from './endpoints/platforms/platforms.module';
@ -56,15 +59,10 @@ import { PublicModule } from './endpoints/public/public.module';
import { SitemapModule } from './endpoints/sitemap/sitemap.module';
import { TagsModule } from './endpoints/tags/tags.module';
import { WatchlistModule } from './endpoints/watchlist/watchlist.module';
import { ExchangeRateModule } from './exchange-rate/exchange-rate.module';
import { ExportModule } from './export/export.module';
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';
@ -79,9 +77,8 @@ import { UserModule } from './user/user.module';
AiModule,
ApiKeysModule,
AssetProfilesModule,
AssetModule,
AssetsModule,
AuthDeviceModule,
AuthDevicesModule,
AuthModule,
BenchmarksModule,
BullBoardModule.forRoot({
@ -120,8 +117,8 @@ import { UserModule } from './user/user.module';
DataProviderModule,
EventEmitterModule.forRoot(),
EventsModule,
ExchangeRateModule,
ExchangeRateDataModule,
ExchangeRatesModule,
ExportModule,
GhostfolioModule,
HealthModule,
@ -130,7 +127,6 @@ import { UserModule } from './user/user.module';
LogoModule,
MarketDataModule,
McpModule,
PlatformModule,
PlatformsModule,
PortfolioModule,
PortfolioSnapshotQueueModule,

17
apps/api/src/app/asset/asset.module.ts

@ -1,17 +0,0 @@
import { AssetProfilesModule } from '@ghostfolio/api/app/endpoints/asset-profiles/asset-profiles.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 { Module } from '@nestjs/common';
import { AssetController } from './asset.controller';
@Module({
controllers: [AssetController],
imports: [
AssetProfilesModule,
TransformDataSourceInRequestModule,
TransformDataSourceInResponseModule
]
})
export class AssetModule {}

19
apps/api/src/app/auth-device/auth-device.module.ts

@ -1,19 +0,0 @@
import { AuthDeviceController } from '@ghostfolio/api/app/auth-device/auth-device.controller';
import { AuthDeviceService } from '@ghostfolio/api/app/auth-device/auth-device.service';
import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module';
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
@Module({
controllers: [AuthDeviceController],
imports: [
JwtModule.register({
secret: process.env.JWT_SECRET_KEY,
signOptions: { expiresIn: '180 days' }
}),
PrismaModule
],
providers: [AuthDeviceService]
})
export class AuthDeviceModule {}

6
apps/api/src/app/auth/auth.module.ts

@ -1,10 +1,9 @@
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';
import { ApiModule } from '@ghostfolio/api/services/api/api.module';
import { AuthDeviceModule } from '@ghostfolio/api/services/auth-device/auth-device.module';
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { FetchModule } from '@ghostfolio/api/services/fetch/fetch.module';
@ -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';
@ -29,6 +29,7 @@ import { OidcStrategy } from './oidc.strategy';
controllers: [AuthController],
imports: [
ApiModule,
AuthDeviceModule,
ConfigurationModule,
FetchModule,
JwtModule.register({
@ -45,7 +46,6 @@ import { OidcStrategy } from './oidc.strategy';
providers: [
ApiKeyService,
ApiKeyStrategy,
AuthDeviceService,
AuthService,
GoogleStrategy,
JwtStrategy,

4
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 { AuthDeviceService } from '@ghostfolio/api/services/auth-device/auth-device.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,

3
apps/api/src/app/endpoints/asset-profiles/asset-profiles.module.ts

@ -15,9 +15,10 @@ import { Module } from '@nestjs/common';
import { AssetProfilesController } from './asset-profiles.controller';
import { AssetProfilesService } from './asset-profiles.service';
import { AssetController } from './asset.controller';
@Module({
controllers: [AssetProfilesController],
controllers: [AssetController, AssetProfilesController],
exports: [AssetProfilesService],
imports: [
ActivitiesModule,

10
apps/api/src/app/asset/asset.controller.ts → apps/api/src/app/endpoints/asset-profiles/asset.controller.ts

@ -1,4 +1,3 @@
import { AssetProfilesService } from '@ghostfolio/api/app/endpoints/asset-profiles/asset-profiles.service';
import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor';
import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor';
import type { AssetResponse } from '@ghostfolio/common/interfaces';
@ -7,6 +6,8 @@ import { Controller, Get, Param, UseInterceptors } from '@nestjs/common';
import { DataSource } from '@prisma/client';
import { pick } from 'lodash-es';
import { AssetProfilesService } from './asset-profiles.service';
@Controller('asset')
export class AssetController {
public constructor(
@ -29,7 +30,12 @@ export class AssetController {
return {
marketData,
splits,
assetProfile: pick(assetProfile, ['dataSource', 'name', 'symbol'])
assetProfile: {
...pick(assetProfile, ['dataSource', 'name', 'symbol']),
// An index has no asset class and its value has no currency,
// thus show no currency for any asset profile without asset class
currency: assetProfile.assetClass ? assetProfile.currency : undefined
}
};
}
}

10
apps/api/src/app/auth-device/auth-device.controller.ts → apps/api/src/app/endpoints/auth-devices/auth-devices.controller.ts

@ -1,7 +1,7 @@
import { AuthDeviceService } from '@ghostfolio/api/app/auth-device/auth-device.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 { AuthDeviceService } from '@ghostfolio/api/services/auth-device/auth-device.service';
import { permissions } from '@ghostfolio/common/permissions';
import type { RequestWithUser } from '@ghostfolio/common/types';
@ -18,8 +18,12 @@ import { AuthGuard } from '@nestjs/passport';
import { getReasonPhrase, StatusCodes } from 'http-status-codes';
@AllowDuringImpersonation()
@Controller('auth-device')
export class AuthDeviceController {
@Controller([
'auth-devices',
/** @deprecated */
'auth-device'
])
export class AuthDevicesController {
public constructor(
private readonly authDeviceService: AuthDeviceService,
@Inject(REQUEST) private readonly request: RequestWithUser

11
apps/api/src/app/endpoints/auth-devices/auth-devices.module.ts

@ -0,0 +1,11 @@
import { AuthDeviceModule } from '@ghostfolio/api/services/auth-device/auth-device.module';
import { Module } from '@nestjs/common';
import { AuthDevicesController } from './auth-devices.controller';
@Module({
controllers: [AuthDevicesController],
imports: [AuthDeviceModule]
})
export class AuthDevicesModule {}

2
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';

2
apps/api/src/app/cache/cache.controller.ts → apps/api/src/app/endpoints/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';

2
apps/api/src/app/cache/cache.module.ts → apps/api/src/app/endpoints/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';

2
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';

18
apps/api/src/app/exchange-rate/exchange-rate.controller.ts → apps/api/src/app/endpoints/exchange-rates/exchange-rates.controller.ts

@ -1,5 +1,5 @@
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
import { DataProviderHistoricalResponse } from '@ghostfolio/common/interfaces';
import { ExchangeRateResponse } from '@ghostfolio/common/interfaces';
import {
Controller,
@ -12,12 +12,16 @@ import { AuthGuard } from '@nestjs/passport';
import { parseISO } from 'date-fns';
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
import { ExchangeRateService } from './exchange-rate.service';
import { ExchangeRatesService } from './exchange-rates.service';
@Controller('exchange-rate')
export class ExchangeRateController {
@Controller([
'exchange-rates',
/** @deprecated */
'exchange-rate'
])
export class ExchangeRatesController {
public constructor(
private readonly exchangeRateService: ExchangeRateService
private readonly exchangeRatesService: ExchangeRatesService
) {}
@Get(':symbol/:dateString')
@ -25,10 +29,10 @@ export class ExchangeRateController {
public async getExchangeRate(
@Param('dateString') dateString: string,
@Param('symbol') symbol: string
): Promise<DataProviderHistoricalResponse> {
): Promise<ExchangeRateResponse> {
const date = parseISO(dateString);
const exchangeRate = await this.exchangeRateService.getExchangeRate({
const exchangeRate = await this.exchangeRatesService.getExchangeRate({
date,
symbol
});

13
apps/api/src/app/endpoints/exchange-rates/exchange-rates.module.ts

@ -0,0 +1,13 @@
import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module';
import { Module } from '@nestjs/common';
import { ExchangeRatesController } from './exchange-rates.controller';
import { ExchangeRatesService } from './exchange-rates.service';
@Module({
controllers: [ExchangeRatesController],
imports: [ExchangeRateDataModule],
providers: [ExchangeRatesService]
})
export class ExchangeRatesModule {}

2
apps/api/src/app/exchange-rate/exchange-rate.service.ts → apps/api/src/app/endpoints/exchange-rates/exchange-rates.service.ts

@ -3,7 +3,7 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-
import { Injectable } from '@nestjs/common';
@Injectable()
export class ExchangeRateService {
export class ExchangeRatesService {
public constructor(
private readonly exchangeRateDataService: ExchangeRateDataService
) {}

0
apps/api/src/app/info/info.controller.ts → apps/api/src/app/endpoints/info/info.controller.ts

4
apps/api/src/app/info/info.module.ts → apps/api/src/app/endpoints/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';

2
apps/api/src/app/info/info.service.ts → apps/api/src/app/endpoints/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,

0
apps/api/src/app/logo/get-logo.dto.ts → apps/api/src/app/endpoints/logo/get-logo.dto.ts

0
apps/api/src/app/logo/logo.controller.ts → apps/api/src/app/endpoints/logo/logo.controller.ts

0
apps/api/src/app/logo/logo.module.ts → apps/api/src/app/endpoints/logo/logo.module.ts

0
apps/api/src/app/logo/logo.service.ts → apps/api/src/app/endpoints/logo/logo.service.ts

4
apps/api/src/app/platform/platform.controller.ts → 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 {

113
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<PlatformResponse> {
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<void> {
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<PlatformsResponse> {
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<PlatformResponse> {
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']);
}
}

5
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 {}

2
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';

14
apps/api/src/app/exchange-rate/exchange-rate.module.ts

@ -1,14 +0,0 @@
import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module';
import { Module } from '@nestjs/common';
import { ExchangeRateController } from './exchange-rate.controller';
import { ExchangeRateService } from './exchange-rate.service';
@Module({
controllers: [ExchangeRateController],
exports: [ExchangeRateService],
imports: [ExchangeRateDataModule],
providers: [ExchangeRateService]
})
export class ExchangeRateModule {}

2
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';

2
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';

6
apps/api/src/app/import/import.module.ts

@ -1,9 +1,6 @@
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 +8,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';
@ -28,7 +27,6 @@ import { ImportService } from './import.service';
AccountModule,
ActivitiesModule,
ApiModule,
CacheModule,
ConfigurationModule,
DataGatheringQueueModule,
DataProviderModule,

2
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';

2
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,

2
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

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

6
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;

2
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';

2
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';

2
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,

12
apps/api/src/services/auth-device/auth-device.module.ts

@ -0,0 +1,12 @@
import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module';
import { Module } from '@nestjs/common';
import { AuthDeviceService } from './auth-device.service';
@Module({
exports: [AuthDeviceService],
imports: [PrismaModule],
providers: [AuthDeviceService]
})
export class AuthDeviceModule {}

0
apps/api/src/app/auth-device/auth-device.service.ts → apps/api/src/services/auth-device/auth-device.service.ts

2
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';

2
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,

2
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';

2
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';

2
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,

2
apps/api/src/app/platform/platform.module.ts → 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]

7
apps/api/src/app/platform/platform.service.ts → 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 }

2
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

2
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,

2
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';

0
apps/api/src/app/redis-cache/redis-cache.module.ts → apps/api/src/services/redis-cache/redis-cache.module.ts

0
apps/api/src/app/redis-cache/redis-cache.service.mock.ts → apps/api/src/services/redis-cache/redis-cache.service.mock.ts

0
apps/api/src/app/redis-cache/redis-cache.service.ts → apps/api/src/services/redis-cache/redis-cache.service.ts

18
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<Date, string>>(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) => {

1
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)"
/>
</div>

20
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<Platform>();
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({

2
apps/client/src/app/components/admin-users/admin-users.html

@ -199,7 +199,7 @@
<div class="align-items-center d-flex">
@if (element.lastRequestClient) {
<ion-icon
class="me-1"
class="me-1 pe-auto"
[name]="requestClientIcons[element.lastRequestClient]"
[title]="translate(element.lastRequestClient)"
/>

1
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)"
/>
</mat-tab>

12
apps/client/src/app/components/home-overview/home-overview.component.ts

@ -38,6 +38,9 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { Router, RouterModule } from '@angular/router';
import { IonIcon } from '@ionic/angular/standalone';
import { addIcons } from 'ionicons';
import { arrowForwardOutline } from 'ionicons/icons';
import { DeviceDetectorService } from 'ngx-device-detector';
import {
@ -53,6 +56,7 @@ const MINIMUM_CHART_ITEMS = 3;
GfHoldingsTableComponent,
GfLineChartComponent,
GfPortfolioPerformanceComponent,
IonIcon,
MatButtonModule,
MatCardModule,
RouterModule
@ -90,9 +94,15 @@ export class GfHomeOverviewComponent implements OnInit {
protected readonly user = signal<User | null>(null);
protected readonly routerLinkAccounts = internalRoutes.accounts.routerLink;
protected readonly routerLinkHomeHoldings =
internalRoutes.home.subRoutes.holdings.routerLink;
protected readonly routerLinkPortfolio = internalRoutes.portfolio.routerLink;
protected readonly routerLinkPortfolioActivities =
internalRoutes.portfolio.subRoutes.activities.routerLink;
protected readonly routerLinkPortfolioActivitiesCreate =
internalRoutes.portfolio.subRoutes.activities.subRoutes.create.routerLink;
@ -153,6 +163,8 @@ export class GfHomeOverviewComponent implements OnInit {
this.update();
}
});
addIcons({ arrowForwardOutline });
}
public ngOnInit() {

14
apps/client/src/app/components/home-overview/home-overview.html

@ -102,8 +102,20 @@
appearance="outlined"
class="h-100 holdings position-absolute w-100"
>
<mat-card-header>
<mat-card-header class="align-items-center">
<mat-card-title i18n>Holdings</mat-card-title>
<a
class="ms-auto"
i18n-title
mat-icon-button
title="View Details"
[routerLink]="routerLinkHomeHoldings"
>
<ion-icon
class="d-block text-body-secondary"
name="arrow-forward-outline"
/>
</a>
</mat-card-header>
<mat-card-content class="overflow-auto">
<gf-holdings-table

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save