Skip to content

Commit f623867

Browse files
committed
react-native: overhaul of database and storage [INT-355]
1 parent d4cd3ff commit f623867

18 files changed

+335
-196
lines changed

packages/react-native/src/BacktraceClient.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@ import { BacktraceClientBuilder } from './builder/BacktraceClientBuilder';
1515
import type { BacktraceClientSetup } from './builder/BacktraceClientSetup';
1616
import { version } from './common/platformHelper';
1717
import { CrashReporter } from './crashReporter/CrashReporter';
18+
import { assertDatabasePath } from './database/utils';
1819
import { generateUnhandledExceptionHandler } from './handlers';
1920
import { type ExceptionHandler } from './handlers/ExceptionHandler';
2021
import { ReactNativeRequestHandler } from './ReactNativeRequestHandler';
2122
import { ReactStackTraceConverter } from './ReactStackTraceConverter';
22-
import { type FileSystem } from './storage/FileSystem';
23+
import { ReactNativePathBacktraceStorageFactory, type BacktraceStorageModule } from './storage';
2324

2425
export class BacktraceClient extends BacktraceCoreClient<BacktraceConfiguration> {
2526
private readonly _crashReporter?: CrashReporter;
@@ -33,7 +34,21 @@ export class BacktraceClient extends BacktraceCoreClient<BacktraceConfiguration>
3334
return NativeModules.BacktraceDirectoryProvider?.applicationDirectory() ?? '';
3435
}
3536

37+
protected get databaseRnStorage() {
38+
return this.databaseStorage as BacktraceStorageModule | undefined;
39+
}
40+
3641
constructor(clientSetup: BacktraceClientSetup) {
42+
const storageFactory = clientSetup.storageFactory ?? new ReactNativePathBacktraceStorageFactory();
43+
const storage =
44+
clientSetup.database?.storage ??
45+
(clientSetup.options.database?.enable
46+
? storageFactory.create({
47+
path: assertDatabasePath(clientSetup.options.database?.path),
48+
createDirectory: clientSetup.options.database.createDatabaseDirectory,
49+
})
50+
: undefined);
51+
3752
super({
3853
sdkOptions: {
3954
agent: '@backtrace/react-native',
@@ -48,14 +63,9 @@ export class BacktraceClient extends BacktraceCoreClient<BacktraceConfiguration>
4863
...clientSetup,
4964
});
5065

51-
const fileSystem = clientSetup.fileSystem as FileSystem;
52-
if (!fileSystem) {
53-
return;
54-
}
55-
5666
const breadcrumbsManager = this.modules.get(BreadcrumbsManager);
57-
if (breadcrumbsManager && this.sessionFiles) {
58-
breadcrumbsManager.setStorage(FileBreadcrumbsStorage.factory(this.sessionFiles, fileSystem));
67+
if (breadcrumbsManager && this.sessionFiles && storage) {
68+
breadcrumbsManager.setStorage(FileBreadcrumbsStorage.factory(this.sessionFiles, storage));
5969
}
6070

6171
this.attributeManager.attributeEvents.on(
@@ -137,21 +147,20 @@ export class BacktraceClient extends BacktraceCoreClient<BacktraceConfiguration>
137147
return;
138148
}
139149

140-
const fileSystem = this.fileSystem;
141-
if (!fileSystem) {
150+
const storage = this.databaseRnStorage;
151+
if (!storage) {
142152
return;
143153
}
144154

145155
const submissionUrl = SubmissionUrlInformation.toJsonReportSubmissionUrl(this.options.url);
146156

147-
const crashReporter = new CrashReporter(fileSystem);
157+
const crashReporter = new CrashReporter(storage);
148158
crashReporter.initialize(
149159
Platform.select({
150160
ios: SubmissionUrlInformation.toPlCrashReporterSubmissionUrl(submissionUrl),
151161
android: SubmissionUrlInformation.toMinidumpSubmissionUrl(submissionUrl),
152162
default: submissionUrl,
153163
}),
154-
this.options.database.path,
155164
this.attributeManager.get('scoped').attributes,
156165
this.attachments,
157166
);
Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,29 @@
1-
import { type BacktraceConfiguration as SdkConfiguration } from '@backtrace/sdk-core';
2-
export interface BacktraceConfiguration extends SdkConfiguration {}
1+
import {
2+
type DisabledBacktraceDatabaseConfiguration as CoreDisabledBacktraceDatabaseConfiguration,
3+
type EnabledBacktraceDatabaseConfiguration as CoreEnabledBacktraceDatabaseConfiguration,
4+
type BacktraceConfiguration as SdkConfiguration,
5+
} from '@backtrace/sdk-core';
6+
7+
export interface EnabledBacktraceDatabaseConfiguration extends CoreEnabledBacktraceDatabaseConfiguration {
8+
/**
9+
* Path where the SDK can store data.
10+
*/
11+
path: string;
12+
/**
13+
* Determine if the directory should be auto created by the SDK.
14+
* @default true
15+
*/
16+
createDatabaseDirectory?: boolean;
17+
}
18+
19+
export interface DisabledBacktraceDatabaseConfiguration
20+
extends CoreDisabledBacktraceDatabaseConfiguration,
21+
Omit<Partial<EnabledBacktraceDatabaseConfiguration>, 'enable'> {}
22+
23+
export type BacktraceDatabaseConfiguration =
24+
| EnabledBacktraceDatabaseConfiguration
25+
| DisabledBacktraceDatabaseConfiguration;
26+
27+
export interface BacktraceConfiguration extends Omit<SdkConfiguration, 'database'> {
28+
database: BacktraceDatabaseConfiguration;
29+
}

packages/react-native/src/attachment/BacktraceFileAttachment.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1-
import { type BacktraceFileAttachment as CoreBacktraceFileAttachment } from '@backtrace/sdk-core';
1+
import {
2+
type BacktraceSyncStorage,
3+
type BacktraceFileAttachment as CoreBacktraceFileAttachment,
4+
} from '@backtrace/sdk-core';
25
import { Platform } from 'react-native';
3-
import { type FileSystem } from '../storage/';
46
import { type FileLocation } from '../types/FileLocation';
57
export class BacktraceFileAttachment implements CoreBacktraceFileAttachment<FileLocation> {
68
public readonly name: string;
79
public readonly mimeType: string;
810

911
private readonly _uploadUri: string;
1012
constructor(
11-
private readonly _fileSystemProvider: FileSystem,
13+
private readonly _storage: BacktraceSyncStorage,
1214
public readonly filePath: string,
1315
name?: string,
1416
mimeType?: string,
@@ -19,7 +21,7 @@ export class BacktraceFileAttachment implements CoreBacktraceFileAttachment<File
1921
}
2022

2123
public get(): FileLocation | undefined {
22-
const exists = this._fileSystemProvider.existsSync(this.filePath);
24+
const exists = this._storage.hasSync(this.filePath);
2325

2426
if (!exists) {
2527
return undefined;

packages/react-native/src/breadcrumbs/FileBreadcrumbsStorage.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import {
55
SessionFiles,
66
TimeHelper,
77
type BacktraceAttachmentProvider,
8+
type BacktraceStorage,
9+
type BacktraceSyncStorage,
810
type Breadcrumb,
911
type BreadcrumbsStorage,
1012
type BreadcrumbsStorageFactory,
@@ -13,7 +15,7 @@ import {
1315
} from '@backtrace/sdk-core';
1416
import { WritableStream } from 'web-streams-polyfill';
1517
import { BacktraceFileAttachment } from '..';
16-
import { type FileSystem } from '../storage';
18+
import { type BacktraceStreamStorage } from '../storage';
1719
import { ChunkifierSink, type ChunkSplitterFactory } from '../storage/Chunkifier';
1820
import { combinedChunkSplitter } from '../storage/combinedChunkSplitter';
1921
import { FileChunkSink } from '../storage/FileChunkSink';
@@ -34,12 +36,12 @@ export class FileBreadcrumbsStorage implements BreadcrumbsStorage {
3436

3537
constructor(
3638
session: SessionFiles,
37-
private readonly _fileSystem: FileSystem,
39+
private readonly _storage: BacktraceStorage & BacktraceSyncStorage & BacktraceStreamStorage,
3840
private readonly _limits: BreadcrumbsStorageLimits,
3941
) {
4042
this._sink = new FileChunkSink({
4143
maxFiles: 2,
42-
fs: this._fileSystem,
44+
storage: this._storage,
4345
file: (n) => session.getFileName(FileBreadcrumbsStorage.getFileName(n)),
4446
});
4547

@@ -72,14 +74,17 @@ export class FileBreadcrumbsStorage implements BreadcrumbsStorage {
7274
this._destinationWriter = this._destinationStream.getWriter();
7375
}
7476

75-
public static factory(session: SessionFiles, fileSystem: FileSystem): BreadcrumbsStorageFactory {
76-
return ({ limits }) => new FileBreadcrumbsStorage(session, fileSystem, limits);
77+
public static factory(
78+
session: SessionFiles,
79+
storage: BacktraceStorage & BacktraceSyncStorage & BacktraceStreamStorage,
80+
): BreadcrumbsStorageFactory {
81+
return ({ limits }) => new FileBreadcrumbsStorage(session, storage, limits);
7782
}
7883

7984
public getAttachments(): BacktraceFileAttachment[] {
8085
const files = [...this._sink.files].map((f) => f.path);
8186
return files.map(
82-
(f, i) => new BacktraceFileAttachment(this._fileSystem, f, `bt-breadcrumbs-${i}`, 'application/json'),
87+
(f, i) => new BacktraceFileAttachment(this._storage, f, `bt-breadcrumbs-${i}`, 'application/json'),
8388
);
8489
}
8590

packages/react-native/src/builder/BacktraceClientBuilder.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import { AppStateBreadcrumbSubscriber } from '../breadcrumbs/events/AppStateBrea
77
import { DimensionChangeBreadcrumbSubscriber } from '../breadcrumbs/events/DimensionChangeBreadcrumbSubscriber';
88
import { WebRequestEventSubscriber } from '../breadcrumbs/events/WebRequestEventSubscriber';
99
import { DebuggerHelper } from '../common/DebuggerHelper';
10-
import { ReactNativeFileSystem } from '../storage';
10+
import { ReactNativePathBacktraceStorageFactory } from '../storage';
11+
import type { BacktraceStorageModuleFactory } from '../storage/PathBacktraceStorageFactory';
1112
import type { BacktraceClientSetup } from './BacktraceClientSetup';
1213

1314
export class BacktraceClientBuilder extends BacktraceCoreClientBuilder<BacktraceClientSetup> {
@@ -45,14 +46,14 @@ export class BacktraceClientBuilder extends BacktraceCoreClientBuilder<Backtrace
4546
this.addAttributeProvider(provider);
4647
}
4748

48-
this.useFileSystem(new ReactNativeFileSystem());
49+
this.useStorageFactory(new ReactNativePathBacktraceStorageFactory());
4950
this.useBreadcrumbSubscriber(new AppStateBreadcrumbSubscriber());
5051
this.useBreadcrumbSubscriber(new DimensionChangeBreadcrumbSubscriber());
5152
this.useBreadcrumbSubscriber(new WebRequestEventSubscriber());
5253
}
5354

54-
public useFileSystem(fileSystem: ReactNativeFileSystem): this {
55-
super.useFileSystem(fileSystem);
55+
public useStorageFactory(factory: BacktraceStorageModuleFactory) {
56+
this.clientSetup.storageFactory = factory;
5657
return this;
5758
}
5859

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
import type { PartialCoreClientSetup } from '@backtrace/sdk-core';
22
import type { BacktraceConfiguration } from '../BacktraceConfiguration';
3+
import type { BacktraceStorageModule } from '../storage';
4+
import type { BacktraceStorageModuleFactory } from '../storage/PathBacktraceStorageFactory';
35

4-
export interface BacktraceClientSetup
5-
extends PartialCoreClientSetup<'sdkOptions' | 'requestHandler', BacktraceConfiguration> {}
6+
type BaseCoreClientSetup = PartialCoreClientSetup<'sdkOptions' | 'requestHandler' | 'database', BacktraceConfiguration>;
7+
8+
export interface BacktraceClientSetup extends BaseCoreClientSetup {
9+
readonly storageFactory?: BacktraceStorageModuleFactory;
10+
readonly database?: Omit<NonNullable<BaseCoreClientSetup['database']>, 'storage'> & {
11+
readonly storage?: BacktraceStorageModule;
12+
};
13+
}

packages/react-native/src/crashReporter/CrashReporter.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1-
import { type AttributeType, type BacktraceAttachment, type FileSystem } from '@backtrace/sdk-core';
1+
import { type AttributeType, type BacktraceAttachment } from '@backtrace/sdk-core';
22
import { NativeModules } from 'react-native';
33
import { BacktraceFileAttachment } from '../attachment/BacktraceFileAttachment';
44
import { DebuggerHelper } from '../common/DebuggerHelper';
5+
import type { BacktraceDirectorySyncStorage, BacktracePathStorage } from '../storage';
56

67
export class CrashReporter {
78
private static readonly BacktraceReactNative = NativeModules.BacktraceReactNative;
89

910
private _enabled = false;
1011

11-
constructor(private readonly _fileSystem: FileSystem) {}
12+
constructor(private readonly _storage: BacktraceDirectorySyncStorage & BacktracePathStorage) {}
1213

1314
/**
1415
* Determines if the crash reporting solution was already initialized.
@@ -17,7 +18,6 @@ export class CrashReporter {
1718

1819
public initialize(
1920
submissionUrl: string,
20-
databasePath: string,
2121
attributes: Record<string, AttributeType>,
2222
attachments: readonly BacktraceAttachment[],
2323
): boolean {
@@ -33,9 +33,9 @@ export class CrashReporter {
3333
return false;
3434
}
3535

36-
const nativeDatabasePath = `${databasePath}/native`;
37-
this._fileSystem.createDirSync(nativeDatabasePath);
36+
this._storage.createDirSync('native');
3837

38+
const nativeDatabasePath = this._storage.getFullPath('native');
3939
CrashReporter.BacktraceReactNative.initialize(
4040
submissionUrl,
4141
nativeDatabasePath,
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export function assertDatabasePath(path: string) {
2+
if (!path) {
3+
throw new Error(
4+
'Missing mandatory path to the database. Please define the database.path option in the configuration.',
5+
);
6+
}
7+
return path;
8+
}

packages/react-native/src/storage/FileChunkSink.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
import type { BacktraceStorage } from '@backtrace/sdk-core';
12
import type { ChunkSink } from './Chunkifier';
2-
import type { FileSystem } from './FileSystem';
3+
import type { BacktraceStreamStorage } from './storage';
34
import type { FileWritableStream } from './StreamWriter';
45

56
interface FileChunkSinkOptions {
@@ -16,7 +17,7 @@ interface FileChunkSinkOptions {
1617
/**
1718
* File system to use.
1819
*/
19-
readonly fs: FileSystem;
20+
readonly storage: BacktraceStorage & BacktraceStreamStorage;
2021
}
2122

2223
/**
@@ -43,7 +44,7 @@ export class FileChunkSink {
4344
// Fail silently here, there's not much we can do about this
4445
})
4546
.finally(() =>
46-
_options.fs.unlink(stream.path).catch(() => {
47+
_options.storage.remove(stream.path).catch(() => {
4748
// Fail silently here, there's not much we can do about this
4849
}),
4950
);
@@ -63,7 +64,7 @@ export class FileChunkSink {
6364

6465
private createStream(n: number) {
6566
const path = this._options.file(n);
66-
return this._options.fs.createWriteStream(path);
67+
return this._options.storage.createWriteStream(path);
6768
}
6869
}
6970

packages/react-native/src/storage/FileSystem.ts

Lines changed: 0 additions & 8 deletions
This file was deleted.

0 commit comments

Comments
 (0)