Browse Source

Improve symbol validation for assets with manual data source

pull/7467/head
Thomas Kaul 1 month ago
parent
commit
682bef004b
  1. 22
      apps/api/src/app/import/import.service.ts
  2. 10
      apps/client/src/app/services/import-activities.service.ts
  3. 34
      package-lock.json
  4. 1
      package.json

22
apps/api/src/app/import/import.service.ts

@ -386,6 +386,19 @@ export class ImportService {
} }
} }
for (const [index, assetProfileWithMarketData] of (
assetProfilesWithMarketDataDto ?? []
).entries()) {
if (
assetProfileWithMarketData.dataSource === DataSource.MANUAL &&
!isValidCustomAssetProfileSymbol(assetProfileWithMarketData.symbol)
) {
throw new Error(
`assetProfiles.${index}.symbol ("${assetProfileWithMarketData.symbol}") must be a UUID or start with the prefix "${ghostfolioPrefix}_" for the data source ("${DataSource.MANUAL}")`
);
}
}
if (!isDryRun && assetProfilesWithMarketDataDto?.length) { if (!isDryRun && assetProfilesWithMarketDataDto?.length) {
const existingAssetProfiles = const existingAssetProfiles =
await this.symbolProfileService.getSymbolProfiles( await this.symbolProfileService.getSymbolProfiles(
@ -395,15 +408,6 @@ export class ImportService {
); );
for (const assetProfileWithMarketData of assetProfilesWithMarketDataDto) { for (const assetProfileWithMarketData of assetProfilesWithMarketDataDto) {
if (
!isValidCustomAssetProfileSymbol(assetProfileWithMarketData.symbol)
) {
// Skip synthetic asset profiles (e.g. of the csv import), where the
// symbol is used as the name of the asset profile created in
// createActivity()
continue;
}
// Check if there is any existing asset profile // Check if there is any existing asset profile
const existingAssetProfile = existingAssetProfiles.find( const existingAssetProfile = existingAssetProfiles.find(
({ dataSource, symbol }) => { ({ dataSource, symbol }) => {

10
apps/client/src/app/services/import-activities.service.ts

@ -17,6 +17,7 @@ import { Account, DataSource, Type as ActivityType } from '@prisma/client';
import { isFinite, isNumber, isString } from 'lodash'; import { isFinite, isNumber, isString } from 'lodash';
import { parse as csvToJson } from 'papaparse'; import { parse as csvToJson } from 'papaparse';
import { firstValueFrom } from 'rxjs'; import { firstValueFrom } from 'rxjs';
import { v4 as uuidv4 } from 'uuid';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -64,18 +65,23 @@ export class ImportActivitiesService {
for (const [index, item] of content.entries()) { for (const [index, item] of content.entries()) {
const currency = this.parseCurrency({ content, index, item }); const currency = this.parseCurrency({ content, index, item });
const dataSource = this.parseDataSource({ item });
const type = this.parseType({ content, index, item }); const type = this.parseType({ content, index, item });
let dataSource = this.parseDataSource({ item });
let symbol = this.parseSymbol({ content, index, item }); let symbol = this.parseSymbol({ content, index, item });
if (!dataSource && ['FEE', 'INTEREST', 'LIABILITY'].includes(type)) {
// Apply the same data source as the import service
dataSource = DataSource.MANUAL;
}
if (dataSource === DataSource.MANUAL) { if (dataSource === DataSource.MANUAL) {
const name = symbol; const name = symbol;
if (!isValidCustomAssetProfileSymbol(symbol)) { if (!isValidCustomAssetProfileSymbol(symbol)) {
// Generate a symbol and keep the free text as the name // Generate a symbol and keep the free text as the name
assetProfileSymbolMapping[name] = assetProfileSymbolMapping[name] =
assetProfileSymbolMapping[name] ?? crypto.randomUUID(); assetProfileSymbolMapping[name] ?? uuidv4();
symbol = assetProfileSymbolMapping[name]; symbol = assetProfileSymbolMapping[name];
} }

34
package-lock.json

@ -97,6 +97,7 @@
"tablemark": "4.1.0", "tablemark": "4.1.0",
"twitter-api-v2": "1.29.0", "twitter-api-v2": "1.29.0",
"undici": "8.5.0", "undici": "8.5.0",
"uuid": "14.0.1",
"yahoo-finance2": "4.0.0", "yahoo-finance2": "4.0.0",
"zod": "4.4.3", "zod": "4.4.3",
"zone.js": "0.16.1" "zone.js": "0.16.1"
@ -15985,6 +15986,16 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/bull/node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).",
"license": "MIT",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/bundle-name": { "node_modules/bundle-name": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz",
@ -32154,6 +32165,17 @@
"websocket-driver": "^0.7.4" "websocket-driver": "^0.7.4"
} }
}, },
"node_modules/sockjs/node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).",
"dev": true,
"license": "MIT",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/socks": { "node_modules/socks": {
"version": "2.8.7", "version": "2.8.7",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz",
@ -34664,12 +34686,16 @@
} }
}, },
"node_modules/uuid": { "node_modules/uuid": {
"version": "8.3.2", "version": "14.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.1.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "integrity": "sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"license": "MIT", "license": "MIT",
"bin": { "bin": {
"uuid": "dist/bin/uuid" "uuid": "dist-node/bin/uuid"
} }
}, },
"node_modules/v8-compile-cache-lib": { "node_modules/v8-compile-cache-lib": {

1
package.json

@ -141,6 +141,7 @@
"tablemark": "4.1.0", "tablemark": "4.1.0",
"twitter-api-v2": "1.29.0", "twitter-api-v2": "1.29.0",
"undici": "8.5.0", "undici": "8.5.0",
"uuid": "14.0.1",
"yahoo-finance2": "4.0.0", "yahoo-finance2": "4.0.0",
"zod": "4.4.3", "zod": "4.4.3",
"zone.js": "0.16.1" "zone.js": "0.16.1"

Loading…
Cancel
Save