Scaffold backend gateway and integration docs
This commit is contained in:
@@ -0,0 +1,290 @@
|
||||
// ignore_for_file: invalid_annotation_target
|
||||
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'backend_models.freezed.dart';
|
||||
part 'backend_models.g.dart';
|
||||
|
||||
/// Supported authentication methods.
|
||||
@JsonEnum(fieldRename: FieldRename.snake)
|
||||
enum SignInMethod {
|
||||
/// Email link based sign-in.
|
||||
emailMagicLink,
|
||||
|
||||
/// Email/password sign-in.
|
||||
password,
|
||||
|
||||
/// OIDC token based sign-in.
|
||||
oidc,
|
||||
}
|
||||
|
||||
/// Allowed change operation types.
|
||||
@JsonEnum(alwaysCreate: true)
|
||||
enum ChangeOperation {
|
||||
/// Create or update entity state.
|
||||
@JsonValue('upsert')
|
||||
upsert,
|
||||
|
||||
/// Remove entity state.
|
||||
@JsonValue('delete')
|
||||
delete,
|
||||
}
|
||||
|
||||
/// Action taken by user against a signal item.
|
||||
@JsonEnum(alwaysCreate: true)
|
||||
enum SignalAction {
|
||||
/// Item was viewed.
|
||||
viewed,
|
||||
|
||||
/// Item was dismissed.
|
||||
dismissed,
|
||||
|
||||
/// Item was saved.
|
||||
saved,
|
||||
|
||||
/// Item resulted in purchase.
|
||||
purchased,
|
||||
}
|
||||
|
||||
/// Upload category.
|
||||
@JsonEnum(alwaysCreate: true)
|
||||
enum UploadPurpose {
|
||||
/// Avatar upload.
|
||||
@JsonValue('avatar')
|
||||
avatar,
|
||||
|
||||
/// Generic attachment upload.
|
||||
@JsonValue('attachment')
|
||||
attachment,
|
||||
}
|
||||
|
||||
/// Request body for sign-in.
|
||||
@freezed
|
||||
abstract class SignInRequest with _$SignInRequest {
|
||||
const factory SignInRequest({
|
||||
required SignInMethod method,
|
||||
String? email,
|
||||
String? password,
|
||||
String? idToken,
|
||||
}) = _SignInRequest;
|
||||
|
||||
factory SignInRequest.fromJson(Map<String, dynamic> json) =>
|
||||
_$SignInRequestFromJson(json);
|
||||
}
|
||||
|
||||
/// User profile returned by backend.
|
||||
@freezed
|
||||
abstract class UserProfile with _$UserProfile {
|
||||
const factory UserProfile({
|
||||
required String id,
|
||||
String? email,
|
||||
String? displayName,
|
||||
String? avatarFileId,
|
||||
}) = _UserProfile;
|
||||
|
||||
factory UserProfile.fromJson(Map<String, dynamic> json) =>
|
||||
_$UserProfileFromJson(json);
|
||||
}
|
||||
|
||||
/// Auth session bundle used by gateway and token storage.
|
||||
@freezed
|
||||
abstract class AuthSession with _$AuthSession {
|
||||
@JsonSerializable(explicitToJson: true)
|
||||
const factory AuthSession({
|
||||
required String accessToken,
|
||||
required String refreshToken,
|
||||
required DateTime expiresAt,
|
||||
required UserProfile user,
|
||||
}) = _AuthSession;
|
||||
|
||||
factory AuthSession.fromJson(Map<String, dynamic> json) =>
|
||||
_$AuthSessionFromJson(json);
|
||||
}
|
||||
|
||||
/// Refresh token request body.
|
||||
@freezed
|
||||
abstract class AuthRefreshRequest with _$AuthRefreshRequest {
|
||||
const factory AuthRefreshRequest({required String refreshToken}) =
|
||||
_AuthRefreshRequest;
|
||||
|
||||
factory AuthRefreshRequest.fromJson(Map<String, dynamic> json) =>
|
||||
_$AuthRefreshRequestFromJson(json);
|
||||
}
|
||||
|
||||
/// Refresh token response body.
|
||||
@freezed
|
||||
abstract class AuthRefreshResponse with _$AuthRefreshResponse {
|
||||
const factory AuthRefreshResponse({
|
||||
required String accessToken,
|
||||
String? refreshToken,
|
||||
required DateTime expiresAt,
|
||||
}) = _AuthRefreshResponse;
|
||||
|
||||
factory AuthRefreshResponse.fromJson(Map<String, dynamic> json) =>
|
||||
_$AuthRefreshResponseFromJson(json);
|
||||
}
|
||||
|
||||
/// Generic envelope for offline-first change operations.
|
||||
@freezed
|
||||
abstract class ChangeEnvelope with _$ChangeEnvelope {
|
||||
const factory ChangeEnvelope({
|
||||
required int schemaVersion,
|
||||
required String entityType,
|
||||
required String entityId,
|
||||
required ChangeOperation op,
|
||||
required DateTime modifiedAt,
|
||||
required String clientMutationId,
|
||||
Map<String, dynamic>? payload,
|
||||
}) = _ChangeEnvelope;
|
||||
|
||||
factory ChangeEnvelope.fromJson(Map<String, dynamic> json) =>
|
||||
_$ChangeEnvelopeFromJson(json);
|
||||
}
|
||||
|
||||
/// Push request for batched local changes.
|
||||
@freezed
|
||||
abstract class SyncPushRequest with _$SyncPushRequest {
|
||||
@JsonSerializable(explicitToJson: true)
|
||||
const factory SyncPushRequest({
|
||||
required String deviceId,
|
||||
String? cursor,
|
||||
required List<ChangeEnvelope> changes,
|
||||
required String idempotencyKey,
|
||||
}) = _SyncPushRequest;
|
||||
|
||||
factory SyncPushRequest.fromJson(Map<String, dynamic> json) =>
|
||||
_$SyncPushRequestFromJson(json);
|
||||
}
|
||||
|
||||
/// Acknowledgement for an accepted mutation.
|
||||
@freezed
|
||||
abstract class MutationAck with _$MutationAck {
|
||||
const factory MutationAck({
|
||||
required String clientMutationId,
|
||||
String? serverMutationId,
|
||||
DateTime? acceptedAt,
|
||||
}) = _MutationAck;
|
||||
|
||||
factory MutationAck.fromJson(Map<String, dynamic> json) =>
|
||||
_$MutationAckFromJson(json);
|
||||
}
|
||||
|
||||
/// Rejection detail for a mutation.
|
||||
@freezed
|
||||
abstract class MutationRejection with _$MutationRejection {
|
||||
const factory MutationRejection({
|
||||
required String clientMutationId,
|
||||
required String code,
|
||||
required String message,
|
||||
}) = _MutationRejection;
|
||||
|
||||
factory MutationRejection.fromJson(Map<String, dynamic> json) =>
|
||||
_$MutationRejectionFromJson(json);
|
||||
}
|
||||
|
||||
/// Result body returned by sync push.
|
||||
@freezed
|
||||
abstract class SyncPushResult with _$SyncPushResult {
|
||||
@JsonSerializable(explicitToJson: true)
|
||||
const factory SyncPushResult({
|
||||
required String cursor,
|
||||
@Default(<MutationAck>[]) List<MutationAck> accepted,
|
||||
@Default(<MutationRejection>[]) List<MutationRejection> rejected,
|
||||
}) = _SyncPushResult;
|
||||
|
||||
factory SyncPushResult.fromJson(Map<String, dynamic> json) =>
|
||||
_$SyncPushResultFromJson(json);
|
||||
}
|
||||
|
||||
/// Result body returned by sync pull.
|
||||
@freezed
|
||||
abstract class SyncPullResult with _$SyncPullResult {
|
||||
@JsonSerializable(explicitToJson: true)
|
||||
const factory SyncPullResult({
|
||||
required String cursor,
|
||||
@Default(<ChangeEnvelope>[]) List<ChangeEnvelope> changes,
|
||||
}) = _SyncPullResult;
|
||||
|
||||
factory SyncPullResult.fromJson(Map<String, dynamic> json) =>
|
||||
_$SyncPullResultFromJson(json);
|
||||
}
|
||||
|
||||
/// Signals feed item.
|
||||
@freezed
|
||||
abstract class SignalItem with _$SignalItem {
|
||||
const factory SignalItem({
|
||||
required String id,
|
||||
required String type,
|
||||
required String title,
|
||||
String? description,
|
||||
String? personId,
|
||||
required DateTime createdAt,
|
||||
Map<String, dynamic>? metadata,
|
||||
}) = _SignalItem;
|
||||
|
||||
factory SignalItem.fromJson(Map<String, dynamic> json) =>
|
||||
_$SignalItemFromJson(json);
|
||||
}
|
||||
|
||||
/// Cursor-based signals response.
|
||||
@freezed
|
||||
abstract class SignalsFeed with _$SignalsFeed {
|
||||
@JsonSerializable(explicitToJson: true)
|
||||
const factory SignalsFeed({
|
||||
String? cursor,
|
||||
@Default(<SignalItem>[]) List<SignalItem> items,
|
||||
}) = _SignalsFeed;
|
||||
|
||||
factory SignalsFeed.fromJson(Map<String, dynamic> json) =>
|
||||
_$SignalsFeedFromJson(json);
|
||||
}
|
||||
|
||||
/// Signal acknowledgement request body.
|
||||
@freezed
|
||||
abstract class SignalAckRequest with _$SignalAckRequest {
|
||||
const factory SignalAckRequest({
|
||||
required SignalAction action,
|
||||
required DateTime at,
|
||||
}) = _SignalAckRequest;
|
||||
|
||||
factory SignalAckRequest.fromJson(Map<String, dynamic> json) =>
|
||||
_$SignalAckRequestFromJson(json);
|
||||
}
|
||||
|
||||
/// Upload init request.
|
||||
@freezed
|
||||
abstract class UploadInitRequest with _$UploadInitRequest {
|
||||
const factory UploadInitRequest({
|
||||
required UploadPurpose purpose,
|
||||
required String contentType,
|
||||
required int sizeBytes,
|
||||
}) = _UploadInitRequest;
|
||||
|
||||
factory UploadInitRequest.fromJson(Map<String, dynamic> json) =>
|
||||
_$UploadInitRequestFromJson(json);
|
||||
}
|
||||
|
||||
/// Upload init response.
|
||||
@freezed
|
||||
abstract class UploadInitResult with _$UploadInitResult {
|
||||
const factory UploadInitResult({
|
||||
required String uploadUrl,
|
||||
required String fileId,
|
||||
required DateTime expiresAt,
|
||||
}) = _UploadInitResult;
|
||||
|
||||
factory UploadInitResult.fromJson(Map<String, dynamic> json) =>
|
||||
_$UploadInitResultFromJson(json);
|
||||
}
|
||||
|
||||
/// Download URL response.
|
||||
@freezed
|
||||
abstract class DownloadUrl with _$DownloadUrl {
|
||||
const factory DownloadUrl({
|
||||
required String downloadUrl,
|
||||
required DateTime expiresAt,
|
||||
}) = _DownloadUrl;
|
||||
|
||||
factory DownloadUrl.fromJson(Map<String, dynamic> json) =>
|
||||
_$DownloadUrlFromJson(json);
|
||||
}
|
||||
Reference in New Issue
Block a user