Skip to content

react-native: overhaul of database and storage [INT-355] #344

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: feature/INT-355-storage-overhaul--sdk-core
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 21 additions & 12 deletions packages/react-native/src/BacktraceClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ import { BacktraceClientBuilder } from './builder/BacktraceClientBuilder';
import type { BacktraceClientSetup } from './builder/BacktraceClientSetup';
import { version } from './common/platformHelper';
import { CrashReporter } from './crashReporter/CrashReporter';
import { assertDatabasePath } from './database/utils';
import { generateUnhandledExceptionHandler } from './handlers';
import { type ExceptionHandler } from './handlers/ExceptionHandler';
import { ReactNativeRequestHandler } from './ReactNativeRequestHandler';
import { ReactStackTraceConverter } from './ReactStackTraceConverter';
import { type FileSystem } from './storage/FileSystem';
import { ReactNativePathBacktraceStorageFactory, type BacktraceStorageModule } from './storage';

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

protected get databaseRnStorage() {
return this.databaseStorage as BacktraceStorageModule | undefined;
}

constructor(clientSetup: BacktraceClientSetup) {
const storageFactory = clientSetup.storageFactory ?? new ReactNativePathBacktraceStorageFactory();
const storage =
clientSetup.database?.storage ??
(clientSetup.options.database?.enable
? storageFactory.create({
path: assertDatabasePath(clientSetup.options.database?.path),
createDirectory: clientSetup.options.database.createDatabaseDirectory,
})
: undefined);

super({
sdkOptions: {
agent: '@backtrace/react-native',
Expand All @@ -48,14 +63,9 @@ export class BacktraceClient extends BacktraceCoreClient<BacktraceConfiguration>
...clientSetup,
});

const fileSystem = clientSetup.fileSystem as FileSystem;
if (!fileSystem) {
return;
}

const breadcrumbsManager = this.modules.get(BreadcrumbsManager);
if (breadcrumbsManager && this.sessionFiles) {
breadcrumbsManager.setStorage(FileBreadcrumbsStorage.factory(this.sessionFiles, fileSystem));
if (breadcrumbsManager && this.sessionFiles && storage) {
breadcrumbsManager.setStorage(FileBreadcrumbsStorage.factory(this.sessionFiles, storage));
}

this.attributeManager.attributeEvents.on(
Expand Down Expand Up @@ -137,21 +147,20 @@ export class BacktraceClient extends BacktraceCoreClient<BacktraceConfiguration>
return;
}

const fileSystem = this.fileSystem;
if (!fileSystem) {
const storage = this.databaseRnStorage;
if (!storage) {
return;
}

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

const crashReporter = new CrashReporter(fileSystem);
const crashReporter = new CrashReporter(storage);
crashReporter.initialize(
Platform.select({
ios: SubmissionUrlInformation.toPlCrashReporterSubmissionUrl(submissionUrl),
android: SubmissionUrlInformation.toMinidumpSubmissionUrl(submissionUrl),
default: submissionUrl,
}),
this.options.database.path,
this.attributeManager.get('scoped').attributes,
this.attachments,
);
Expand Down
31 changes: 29 additions & 2 deletions packages/react-native/src/BacktraceConfiguration.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,29 @@
import { type BacktraceConfiguration as SdkConfiguration } from '@backtrace/sdk-core';
export interface BacktraceConfiguration extends SdkConfiguration {}
import {
type DisabledBacktraceDatabaseConfiguration as CoreDisabledBacktraceDatabaseConfiguration,
type EnabledBacktraceDatabaseConfiguration as CoreEnabledBacktraceDatabaseConfiguration,
type BacktraceConfiguration as SdkConfiguration,
} from '@backtrace/sdk-core';

export interface EnabledBacktraceDatabaseConfiguration extends CoreEnabledBacktraceDatabaseConfiguration {
/**
* Path where the SDK can store data.
*/
path: string;
/**
* Determine if the directory should be auto created by the SDK.
* @default true
*/
createDatabaseDirectory?: boolean;
}

export interface DisabledBacktraceDatabaseConfiguration
extends CoreDisabledBacktraceDatabaseConfiguration,
Omit<Partial<EnabledBacktraceDatabaseConfiguration>, 'enable'> {}

export type BacktraceDatabaseConfiguration =
| EnabledBacktraceDatabaseConfiguration
| DisabledBacktraceDatabaseConfiguration;

export interface BacktraceConfiguration extends Omit<SdkConfiguration, 'database'> {
database: BacktraceDatabaseConfiguration;
}
12 changes: 6 additions & 6 deletions packages/react-native/src/attachment/BacktraceFileAttachment.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { type BacktraceFileAttachment as CoreBacktraceFileAttachment } from '@backtrace/sdk-core';
import { Platform } from 'react-native';
import { type FileSystem } from '../storage/';
import { type BacktraceAttachment } from '@backtrace/sdk-core';
import { NativeModules, Platform } from 'react-native';
import type { ReactNativeFileProvider } from '../storage';
import { type FileLocation } from '../types/FileLocation';
export class BacktraceFileAttachment implements CoreBacktraceFileAttachment<FileLocation> {
export class BacktraceFileAttachment implements BacktraceAttachment<FileLocation> {
private readonly _fileSystemProvider: ReactNativeFileProvider = NativeModules.BacktraceFileSystemProvider;

public readonly name: string;
public readonly mimeType: string;

private readonly _uploadUri: string;
constructor(
private readonly _fileSystemProvider: FileSystem,
public readonly filePath: string,
name?: string,
mimeType?: string,
Expand All @@ -20,7 +21,6 @@ export class BacktraceFileAttachment implements CoreBacktraceFileAttachment<File

public get(): FileLocation | undefined {
const exists = this._fileSystemProvider.existsSync(this.filePath);

if (!exists) {
return undefined;
}
Expand Down
19 changes: 11 additions & 8 deletions packages/react-native/src/breadcrumbs/FileBreadcrumbsStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
SessionFiles,
TimeHelper,
type BacktraceAttachmentProvider,
type BacktraceStorage,
type BacktraceSyncStorage,
type Breadcrumb,
type BreadcrumbsStorage,
type BreadcrumbsStorageFactory,
Expand All @@ -13,7 +15,7 @@ import {
} from '@backtrace/sdk-core';
import { WritableStream } from 'web-streams-polyfill';
import { BacktraceFileAttachment } from '..';
import { type FileSystem } from '../storage';
import { type BacktraceStreamStorage } from '../storage';
import { ChunkifierSink, type ChunkSplitterFactory } from '../storage/Chunkifier';
import { combinedChunkSplitter } from '../storage/combinedChunkSplitter';
import { FileChunkSink } from '../storage/FileChunkSink';
Expand All @@ -34,12 +36,12 @@ export class FileBreadcrumbsStorage implements BreadcrumbsStorage {

constructor(
session: SessionFiles,
private readonly _fileSystem: FileSystem,
private readonly _storage: BacktraceStorage & BacktraceStreamStorage,
private readonly _limits: BreadcrumbsStorageLimits,
) {
this._sink = new FileChunkSink({
maxFiles: 2,
fs: this._fileSystem,
storage: this._storage,
file: (n) => session.getFileName(FileBreadcrumbsStorage.getFileName(n)),
});

Expand Down Expand Up @@ -72,15 +74,16 @@ export class FileBreadcrumbsStorage implements BreadcrumbsStorage {
this._destinationWriter = this._destinationStream.getWriter();
}

public static factory(session: SessionFiles, fileSystem: FileSystem): BreadcrumbsStorageFactory {
return ({ limits }) => new FileBreadcrumbsStorage(session, fileSystem, limits);
public static factory(
session: SessionFiles,
storage: BacktraceStorage & BacktraceSyncStorage & BacktraceStreamStorage,
): BreadcrumbsStorageFactory {
return ({ limits }) => new FileBreadcrumbsStorage(session, storage, limits);
}

public getAttachments(): BacktraceFileAttachment[] {
const files = [...this._sink.files].map((f) => f.path);
return files.map(
(f, i) => new BacktraceFileAttachment(this._fileSystem, f, `bt-breadcrumbs-${i}`, 'application/json'),
);
return files.map((f, i) => new BacktraceFileAttachment(f, `bt-breadcrumbs-${i}`, 'application/json'));
}

public getAttachmentProviders(): BacktraceAttachmentProvider[] {
Expand Down
9 changes: 5 additions & 4 deletions packages/react-native/src/builder/BacktraceClientBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import { AppStateBreadcrumbSubscriber } from '../breadcrumbs/events/AppStateBrea
import { DimensionChangeBreadcrumbSubscriber } from '../breadcrumbs/events/DimensionChangeBreadcrumbSubscriber';
import { WebRequestEventSubscriber } from '../breadcrumbs/events/WebRequestEventSubscriber';
import { DebuggerHelper } from '../common/DebuggerHelper';
import { ReactNativeFileSystem } from '../storage';
import { ReactNativePathBacktraceStorageFactory } from '../storage';
import type { BacktraceStorageModuleFactory } from '../storage/PathBacktraceStorageFactory';
import type { BacktraceClientSetup } from './BacktraceClientSetup';

export class BacktraceClientBuilder extends BacktraceCoreClientBuilder<BacktraceClientSetup> {
Expand Down Expand Up @@ -45,14 +46,14 @@ export class BacktraceClientBuilder extends BacktraceCoreClientBuilder<Backtrace
this.addAttributeProvider(provider);
}

this.useFileSystem(new ReactNativeFileSystem());
this.useStorageFactory(new ReactNativePathBacktraceStorageFactory());
this.useBreadcrumbSubscriber(new AppStateBreadcrumbSubscriber());
this.useBreadcrumbSubscriber(new DimensionChangeBreadcrumbSubscriber());
this.useBreadcrumbSubscriber(new WebRequestEventSubscriber());
}

public useFileSystem(fileSystem: ReactNativeFileSystem): this {
super.useFileSystem(fileSystem);
public useStorageFactory(factory: BacktraceStorageModuleFactory) {
this.clientSetup.storageFactory = factory;
return this;
}

Expand Down
12 changes: 10 additions & 2 deletions packages/react-native/src/builder/BacktraceClientSetup.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import type { PartialCoreClientSetup } from '@backtrace/sdk-core';
import type { BacktraceConfiguration } from '../BacktraceConfiguration';
import type { BacktraceStorageModule } from '../storage';
import type { BacktraceStorageModuleFactory } from '../storage/PathBacktraceStorageFactory';

export interface BacktraceClientSetup
extends PartialCoreClientSetup<'sdkOptions' | 'requestHandler', BacktraceConfiguration> {}
type BaseCoreClientSetup = PartialCoreClientSetup<'sdkOptions' | 'requestHandler' | 'database', BacktraceConfiguration>;

export interface BacktraceClientSetup extends BaseCoreClientSetup {
readonly storageFactory?: BacktraceStorageModuleFactory;
readonly database?: Omit<NonNullable<BaseCoreClientSetup['database']>, 'storage'> & {
readonly storage?: BacktraceStorageModule;
};
}
10 changes: 5 additions & 5 deletions packages/react-native/src/crashReporter/CrashReporter.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { type AttributeType, type BacktraceAttachment, type FileSystem } from '@backtrace/sdk-core';
import { type AttributeType, type BacktraceAttachment } from '@backtrace/sdk-core';
import { NativeModules } from 'react-native';
import { BacktraceFileAttachment } from '../attachment/BacktraceFileAttachment';
import { DebuggerHelper } from '../common/DebuggerHelper';
import type { BacktraceDirectorySyncStorage, BacktracePathStorage } from '../storage';

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

private _enabled = false;

constructor(private readonly _fileSystem: FileSystem) {}
constructor(private readonly _storage: BacktraceDirectorySyncStorage & BacktracePathStorage) {}

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

public initialize(
submissionUrl: string,
databasePath: string,
attributes: Record<string, AttributeType>,
attachments: readonly BacktraceAttachment[],
): boolean {
Expand All @@ -33,9 +33,9 @@ export class CrashReporter {
return false;
}

const nativeDatabasePath = `${databasePath}/native`;
this._fileSystem.createDirSync(nativeDatabasePath);
this._storage.createDirSync('native');

const nativeDatabasePath = this._storage.getFullPath('native');
CrashReporter.BacktraceReactNative.initialize(
submissionUrl,
nativeDatabasePath,
Expand Down
8 changes: 8 additions & 0 deletions packages/react-native/src/database/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export function assertDatabasePath(path: string) {
if (!path) {
throw new Error(
'Missing mandatory path to the database. Please define the database.path option in the configuration.',
);
}
return path;
}
9 changes: 5 additions & 4 deletions packages/react-native/src/storage/FileChunkSink.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { BacktraceStorage } from '@backtrace/sdk-core';
import type { ChunkSink } from './Chunkifier';
import type { FileSystem } from './FileSystem';
import type { BacktraceStreamStorage } from './storage';
import type { FileWritableStream } from './StreamWriter';

interface FileChunkSinkOptions {
Expand All @@ -16,7 +17,7 @@ interface FileChunkSinkOptions {
/**
* File system to use.
*/
readonly fs: FileSystem;
readonly storage: BacktraceStorage & BacktraceStreamStorage;
}

/**
Expand All @@ -43,7 +44,7 @@ export class FileChunkSink {
// Fail silently here, there's not much we can do about this
})
.finally(() =>
_options.fs.unlink(stream.path).catch(() => {
_options.storage.remove(stream.path).catch(() => {
// Fail silently here, there's not much we can do about this
}),
);
Expand All @@ -63,7 +64,7 @@ export class FileChunkSink {

private createStream(n: number) {
const path = this._options.file(n);
return this._options.fs.createWriteStream(path);
return this._options.storage.createWriteStream(path);
}
}

Expand Down
8 changes: 0 additions & 8 deletions packages/react-native/src/storage/FileSystem.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { type BacktraceStorageModule, type BacktraceStorageModuleOptions } from './storage.js';

export interface BacktraceStorageModuleFactory {
create(options: BacktraceStorageModuleOptions): BacktraceStorageModule;
}
Loading
Loading