Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,58 @@ public void reportAddDataAttachment(
}
}

// Task 8.2b. No registry yet, so this does not call the SDK: the SDK made none.
@Override
public void createReport(final Promise promise) {
promise.resolve(null);
}

// Task 8.2b. No created-report handle exists until the registry lands.
@Override
public void createdReportRead(final String handleId, final Promise promise) {
rejectHandleDead(promise);
}

// Task 8.2b. No created-report handle exists until the registry lands.
@Override
public void createdReportUpdate(
final String handleId,
final String patchJson,
final Promise promise
) {
rejectHandleDead(promise);
}

// Task 8.2b. No created-report handle exists until the registry lands.
@Override
public void createdReportAddDataAttachment(
final String handleId,
final String base64,
final String name,
@Nullable final String mimeType,
final Promise promise
) {
rejectHandleDead(promise);
}

// Task 8.2b. No created-report handle exists until the registry lands. No move argument.
@Override
public void createdReportAddFileAttachment(
final String handleId,
final String path,
final String name,
@Nullable final String mimeType,
final Promise promise
) {
rejectHandleDead(promise);
}

// Task 8.2b. No created-report handle exists until the registry lands.
@Override
public void createdReportUpload(final String handleId, final Promise promise) {
rejectHandleDead(promise);
}

/**
* The SDK returns null both for a declined attachment and for a report
* that is no longer live. The handle tells the two apart: if it died while
Expand Down
66 changes: 66 additions & 0 deletions packages/react-native/ios/BugseeModule.mm
Original file line number Diff line number Diff line change
Expand Up @@ -928,6 +928,72 @@ - (void)reportAddDataAttachment:(NSString *)handleId
}
}

// Task 8.2c. No registry yet, so this does not call the SDK: the SDK made none.
- (void)createReport:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject {
(void)reject;
resolve(nil);
}

// Task 8.2c. No created-report handle exists until the registry lands.
- (void)createdReportRead:(NSString *)handleId
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject {
(void)handleId;
(void)resolve;
BGSRNRejectHandleDead(reject);
}

// Task 8.2c. No created-report handle exists until the registry lands.
- (void)createdReportUpdate:(NSString *)handleId
patchJson:(NSString *)patchJson
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject {
(void)handleId;
(void)patchJson;
(void)resolve;
BGSRNRejectHandleDead(reject);
}

// Task 8.2c. No created-report handle exists until the registry lands.
- (void)createdReportAddDataAttachment:(NSString *)handleId
base64:(NSString *)base64
name:(NSString *)name
mimeType:(NSString * _Nullable)mimeType
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject {
(void)handleId;
(void)base64;
(void)name;
(void)mimeType;
(void)resolve;
BGSRNRejectHandleDead(reject);
}

// Task 8.2c. No created-report handle exists until the registry lands. No move argument.
- (void)createdReportAddFileAttachment:(NSString *)handleId
path:(NSString *)path
name:(NSString *)name
mimeType:(NSString * _Nullable)mimeType
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject {
(void)handleId;
(void)path;
(void)name;
(void)mimeType;
(void)resolve;
BGSRNRejectHandleDead(reject);
}

// Task 8.2c. No created-report handle exists until the registry lands.
- (void)createdReportUpload:(NSString *)handleId
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject {
(void)handleId;
(void)resolve;
BGSRNRejectHandleDead(reject);
}

- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
(const facebook::react::ObjCTurboModule::InitParams &)params {
auto module = std::make_shared<facebook::react::NativeBugseeSpecJSI>(params);
Expand Down
29 changes: 29 additions & 0 deletions packages/react-native/src/NativeBugsee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,35 @@ export interface Spec extends TurboModule {
mimeType: string | null,
): Promise<void>;

/**
* Opens a report the app fills and then uploads. Resolves `'cr-<n>'`, or
* `null` when the SDK made none. Rejects `E_REPORT_CREATE_BUSY` when one
* created report is already outstanding.
*/
createReport(): Promise<string | null>;
/** The live state of the created report behind `handleId`, the `BugseeReportSnapshot` wire shape. */
createdReportRead(handleId: string): Promise<UnsafeObject>;
/**
* Applies a validated patch, as JSON text, the same transport as
* {@link reportUpdate}.
*/
createdReportUpdate(handleId: string, patchJson: string): Promise<void>;
createdReportAddDataAttachment(
handleId: string,
base64: string,
name: string,
mimeType: string | null,
): Promise<void>;
/** No `move`: a created-report file attachment is copied. */
createdReportAddFileAttachment(
handleId: string,
path: string,
name: string,
mimeType: string | null,
): Promise<void>;
/** Uploads the created report. The handle is dead afterwards, whatever the result. */
createdReportUpload(handleId: string): Promise<boolean>;

/** A handled JS exception. `payloadJson` is the Task 7.1a payload; `optionsJson` is `{domain?, labels?, includeVideo?}` or null. */
logException(payloadJson: string, optionsJson: string | null): void;
/** An unhandled JS exception. Resolves once the SDK has the report; never rejects. */
Expand Down
13 changes: 13 additions & 0 deletions packages/react-native/src/__mocks__/native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const DEFAULTS: Record<string, unknown> = {
getStatus: 0,
getLaunchOptions: {},
reportRead: EMPTY_REPORT_SNAPSHOT,
createReport: null,
getAttribute: {},
getAllAttributes: {},
getUserIdentifier: {},
Expand Down Expand Up @@ -191,6 +192,18 @@ export const native = {
reportAddDataAttachment:
jest.fn<Promise<void>, [string, string, string, string | null]>(),

/** Resolves `null` by default: native made none. */
createReport: jest.fn<Promise<string | null>, []>(),
createdReportRead: jest.fn<Promise<unknown>, [string]>(),
/** `patchJson` is the validated patch as JSON text (`encodeBridgeObject`). */
createdReportUpdate: jest.fn<Promise<void>, [string, string]>(),
createdReportAddDataAttachment:
jest.fn<Promise<void>, [string, string, string, string | null]>(),
/** No `move` argument: a created-report file is copied. */
createdReportAddFileAttachment:
jest.fn<Promise<void>, [string, string, string, string | null]>(),
createdReportUpload: jest.fn<Promise<boolean>, [string]>(),

/** `payloadJson` / `optionsJson` are JSON text (`encodeBridgeObject` / `encodeExceptionOptions`). */
logException: jest.fn<void, [string, string | null]>(),
/** Resolves by default; tests that hang native replace the implementation. */
Expand Down
34 changes: 32 additions & 2 deletions packages/react-native/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import { Status } from './status';
import { statusForEvent } from './wrapper/events';
import type { LifecycleEvent } from './wrapper/events';
import { setReportHandler as installReportHandler } from './report/dispatcher';
import type { BugseeReportHandler } from './report/types';
import { createReport as openCreatedReport } from './report/CreatedReport';
import type { BugseeCreatedReport, BugseeReportHandler } from './report/types';
import { forwardLog } from './wrapper/channel';
import { type IssueSeverity, LogLevel } from './options/enums';
import { labelsArgument, severityArgument } from './report/fields';
Expand Down Expand Up @@ -253,7 +254,13 @@ class Bugsee {
* `onBeforeReportCreated` is at-most-once and may be skipped for a given
* report; `onAfterReportCreated` is at-least-once and MUST be idempotent
* (`setLabels`/`setAttribute`, not appending; check `getAttachmentNames()`
* before adding one). A mutation made after the callback settles, or after
* before adding one).
*
* **Attachments.** A handler report takes up to 1000 attachments on both
* platforms. A file is captured when it is added, and `move` is honoured.
* `onAfterReportCreated` must check `getAttachmentNames()` before adding.
*
* A mutation made after the callback settles, or after
* its handle's deadline passes -- the SDK's deadline, not one the app
* negotiates -- does not reach the report.
*
Expand Down Expand Up @@ -338,6 +345,28 @@ class Bugsee {
);
}

/**
* A report to fill and upload. Resolves `null` when the SDK made none
* (not launched).
*
* One outstanding created report at a time. A second `createReport()`
* before that one is uploaded rejects with `E_REPORT_CREATE_BUSY`. iOS
* beta3 keeps created-report attributes in file-scope globals, so a second
* create wipes the first.
*
* Android runs `onBeforeReportCreated` inside `createReport` and
* `onAfterReportCreated` at upload. iOS runs both at upload, after copying
* the created report's fields, so a handler `setLabels` there replaces the
* app's labels.
*
* iOS created-report attachments: at most 3, each at most 3 MiB. The bridge
* rejects a further or larger one with `E_REPORT_ATTACHMENT_REJECTED`. iOS
* ignores `mimeType`. Android's cap is 1000.
*/
async createReport(): Promise<BugseeCreatedReport | null> {
return openCreatedReport();
}

/**
* One line into the Bugsee log, attributed to the wrapper (source Custom, no
* tag) and filtered natively by the app's log filter, exactly once. Dropped
Expand Down Expand Up @@ -646,6 +675,7 @@ export type { LifecycleEvent } from './wrapper/events';

export { ReportErrorCode, BugseeReportError } from './report/errors';
export type {
BugseeCreatedReport,
BugseeReport,
BugseeReportHandler,
BugseeReportSnapshot,
Expand Down
4 changes: 2 additions & 2 deletions packages/react-native/src/report/BugseeReport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const KNOWN_ERROR_CODES: ReadonlySet<string> = new Set(
* Anything else -- a rejection with no recognised code -- passes through
* unchanged rather than being reinterpreted as something it is not.
*/
function toReportError(error: unknown): unknown {
export function toReportError(error: unknown): unknown {
if (error instanceof BugseeReportError) {
return error;
}
Expand All @@ -53,7 +53,7 @@ function emptySnapshot(): BugseeReportSnapshot {
};
}

function normalizeSnapshot(raw: unknown): BugseeReportSnapshot {
export function normalizeSnapshot(raw: unknown): BugseeReportSnapshot {
if (typeof raw !== 'object' || raw === null) {
return emptySnapshot();
}
Expand Down
Loading
Loading