| ... | @@ -7,7 +7,10 @@ import * as Effect from "effect/Effect"; | ... | @@ -7,7 +7,10 @@ import * as Effect from "effect/Effect"; |
| 7 | import * as Layer from "effect/Layer"; | 7 | import * as Layer from "effect/Layer"; |
| 8 | import * as Schema from "effect/Schema"; | 8 | import * as Schema from "effect/Schema"; |
| 9 | | 9 | |
| 10 | import type { RelayDeliveryResult } from "@t3tools/contracts/relay"; | 10 | import { |
| | 11 | RelayDeliveryKind as RelayDeliveryKindSchema, |
| | 12 | type RelayDeliveryResult, |
| | 13 | } from "@t3tools/contracts/relay"; |
| 11 | | 14 | |
| 12 | import { | 15 | import { |
| 13 | sanitizeAgentActivityAggregateState, | 16 | sanitizeAgentActivityAggregateState, |
| ... | @@ -24,10 +27,17 @@ import * as RelayConfiguration from "../Config.ts"; | ... | @@ -24,10 +27,17 @@ import * as RelayConfiguration from "../Config.ts"; |
| 24 | | 27 | |
| 25 | export class ApnsDeliveryQueueSendError extends Schema.TaggedErrorClass<ApnsDeliveryQueueSendError>()( | 28 | export class ApnsDeliveryQueueSendError extends Schema.TaggedErrorClass<ApnsDeliveryQueueSendError>()( |
| 26 | "ApnsDeliveryQueueSendError", | 29 | "ApnsDeliveryQueueSendError", |
| 27 | { cause: Schema.Defect() }, | 30 | { |
| | 31 | operation: Schema.Literals(["generate-job-id", "send"]), |
| | 32 | jobId: Schema.NullOr(Schema.String), |
| | 33 | kind: RelayDeliveryKindSchema, |
| | 34 | userId: Schema.String, |
| | 35 | deviceId: Schema.String, |
| | 36 | cause: Schema.Defect(), |
| | 37 | }, |
| 28 | ) { | 38 | ) { |
| 29 | override get message(): string { | 39 | override get message(): string { |
| 30 | return "Failed to enqueue APNs delivery"; | 40 | return `Failed to enqueue APNs ${this.kind.replaceAll("_", " ")} delivery during ${this.operation} for device ${this.deviceId}.`; |
| 31 | } | 41 | } |
| 32 | } | 42 | } |
| 33 | | 43 | |
| ... | @@ -36,7 +46,7 @@ export type ApnsDeliveryQueueError = ApnsDeliveryQueueSendError; | ... | @@ -36,7 +46,7 @@ export type ApnsDeliveryQueueError = ApnsDeliveryQueueSendError; |
| 36 | export class ApnsDeliveryQueueSender extends Context.Service< | 46 | export class ApnsDeliveryQueueSender extends Context.Service< |
| 37 | ApnsDeliveryQueueSender, | 47 | ApnsDeliveryQueueSender, |
| 38 | { | 48 | { |
| 39 | readonly send: (body: SignedApnsDeliveryJob) => Effect.Effect<void, ApnsDeliveryQueueSendError>; | 49 | readonly send: (body: SignedApnsDeliveryJob) => Effect.Effect<void, Cloudflare.QueueSendError>; |
| 40 | } | 50 | } |
| 41 | >()("t3code-relay/agentActivity/ApnsDeliveryQueue/ApnsDeliveryQueueSender") {} | 51 | >()("t3code-relay/agentActivity/ApnsDeliveryQueue/ApnsDeliveryQueueSender") {} |
| 42 | | 52 | |
| ... | @@ -73,7 +83,17 @@ export const make = Effect.gen(function* () { | ... | @@ -73,7 +83,17 @@ export const make = Effect.gen(function* () { |
| 73 | }); | 83 | }); |
| 74 | const now = yield* DateTime.now; | 84 | const now = yield* DateTime.now; |
| 75 | const jobId = yield* crypto.randomUUIDv4.pipe( | 85 | const jobId = yield* crypto.randomUUIDv4.pipe( |
| 76 | Effect.mapError((cause) => new ApnsDeliveryQueueSendError({ cause })), | 86 | Effect.mapError( |
| | 87 | (cause) => |
| | 88 | new ApnsDeliveryQueueSendError({ |
| | 89 | operation: "generate-job-id", |
| | 90 | jobId: null, |
| | 91 | kind: input.kind, |
| | 92 | userId: input.userId, |
| | 93 | deviceId: input.deviceId, |
| | 94 | cause, |
| | 95 | }), |
| | 96 | ), |
| 77 | ); | 97 | ); |
| 78 | yield* Effect.annotateCurrentSpan({ "relay.delivery.job_id": jobId }); | 98 | yield* Effect.annotateCurrentSpan({ "relay.delivery.job_id": jobId }); |
| 79 | const payload = makeApnsDeliveryJobPayload({ | 99 | const payload = makeApnsDeliveryJobPayload({ |
| ... | @@ -88,7 +108,19 @@ export const make = Effect.gen(function* () { | ... | @@ -88,7 +108,19 @@ export const make = Effect.gen(function* () { |
| 88 | secret: config.apnsDeliveryJobSigningSecret, | 108 | secret: config.apnsDeliveryJobSigningSecret, |
| 89 | payload, | 109 | payload, |
| 90 | }); | 110 | }); |
| 91 | yield* sender.send(signed); | 111 | yield* sender.send(signed).pipe( |
| | 112 | Effect.mapError( |
| | 113 | (cause) => |
| | 114 | new ApnsDeliveryQueueSendError({ |
| | 115 | operation: "send", |
| | 116 | jobId, |
| | 117 | kind: input.kind, |
| | 118 | userId: input.userId, |
| | 119 | deviceId: input.deviceId, |
| | 120 | cause, |
| | 121 | }), |
| | 122 | ), |
| | 123 | ); |
| 92 | return { | 124 | return { |
| 93 | deviceId: input.deviceId, | 125 | deviceId: input.deviceId, |
| 94 | kind: input.kind, | 126 | kind: input.kind, |
| ... | @@ -110,7 +142,17 @@ export const make = Effect.gen(function* () { | ... | @@ -110,7 +142,17 @@ export const make = Effect.gen(function* () { |
| 110 | }); | 142 | }); |
| 111 | const now = yield* DateTime.now; | 143 | const now = yield* DateTime.now; |
| 112 | const jobId = yield* crypto.randomUUIDv4.pipe( | 144 | const jobId = yield* crypto.randomUUIDv4.pipe( |
| 113 | Effect.mapError((cause) => new ApnsDeliveryQueueSendError({ cause })), | 145 | Effect.mapError( |
| | 146 | (cause) => |
| | 147 | new ApnsDeliveryQueueSendError({ |
| | 148 | operation: "generate-job-id", |
| | 149 | jobId: null, |
| | 150 | kind: "push_notification", |
| | 151 | userId: input.userId, |
| | 152 | deviceId: input.deviceId, |
| | 153 | cause, |
| | 154 | }), |
| | 155 | ), |
| 114 | ); | 156 | ); |
| 115 | yield* Effect.annotateCurrentSpan({ "relay.delivery.job_id": jobId }); | 157 | yield* Effect.annotateCurrentSpan({ "relay.delivery.job_id": jobId }); |
| 116 | const payload = makeApnsDeliveryJobPayload({ | 158 | const payload = makeApnsDeliveryJobPayload({ |
| ... | @@ -128,7 +170,19 @@ export const make = Effect.gen(function* () { | ... | @@ -128,7 +170,19 @@ export const make = Effect.gen(function* () { |
| 128 | secret: config.apnsDeliveryJobSigningSecret, | 170 | secret: config.apnsDeliveryJobSigningSecret, |
| 129 | payload, | 171 | payload, |
| 130 | }); | 172 | }); |
| 131 | yield* sender.send(signed); | 173 | yield* sender.send(signed).pipe( |
| | 174 | Effect.mapError( |
| | 175 | (cause) => |
| | 176 | new ApnsDeliveryQueueSendError({ |
| | 177 | operation: "send", |
| | 178 | jobId, |
| | 179 | kind: "push_notification", |
| | 180 | userId: input.userId, |
| | 181 | deviceId: input.deviceId, |
| | 182 | cause, |
| | 183 | }), |
| | 184 | ), |
| | 185 | ); |
| 132 | return { | 186 | return { |
| 133 | deviceId: input.deviceId, | 187 | deviceId: input.deviceId, |
| 134 | kind: "push_notification" as const, | 188 | kind: "push_notification" as const, |
| ... | @@ -155,10 +209,9 @@ export const layerCloudflareQueues = ( | ... | @@ -155,10 +209,9 @@ export const layerCloudflareQueues = ( |
| 155 | ApnsDeliveryQueueSender, | 209 | ApnsDeliveryQueueSender, |
| 156 | ApnsDeliveryQueueSender.of({ | 210 | ApnsDeliveryQueueSender.of({ |
| 157 | send: (body) => | 211 | send: (body) => |
| 158 | sender.send(body).pipe( | 212 | sender |
| 159 | Effect.mapError((cause) => new ApnsDeliveryQueueSendError({ cause })), | 213 | .send(body) |
| 160 | Effect.provideService(Alchemy.RuntimeContext, alchemyRuntimeContext), | 214 | .pipe(Effect.provideService(Alchemy.RuntimeContext, alchemyRuntimeContext)), |
| 161 | ), | | |
| 162 | }), | 215 | }), |
| 163 | ), | 216 | ), |
| 164 | ), | 217 | ), |