| author | |
| committer | |
| log | 1862686f9e42bf1d77e7f2ad03e24d7b0c7908f1 |
| tree | b738a7a63a9b28c55d87bdfe77c1fa17e2da41fb |
| parent | 3e6f856f2359421958a3aa046f2c393e00f3dc6a |
| signature | Signed by PGP key B5690EEEBB952194 |
2 files changed, 82 insertions(+), 2 deletions(-)
infra/relay/src/agentActivity/ApnsDeliveries.test.ts+76| ... | @@ -486,6 +486,9 @@ describe("ApnsDeliveries", () => { | ... | @@ -486,6 +486,9 @@ describe("ApnsDeliveries", () => { |
| 486 | Effect.provide( | 486 | Effect.provide( |
| 487 | makeLayer({ | 487 | makeLayer({ |
| 488 | attempts, | 488 | attempts, |
| 489 | currentTargets: [ | ||
| 490 | { ...target, bundle_id: "com.t3tools.t3code.preview", aps_environment: "sandbox" }, | ||
| 491 | ], | ||
| 489 | config: signingConfig, | 492 | config: signingConfig, |
| 490 | execute, | 493 | execute, |
| 491 | }), | 494 | }), |
| ... | @@ -1972,3 +1975,76 @@ describe("fast completion delivery", () => { | ... | @@ -1972,3 +1975,76 @@ describe("fast completion delivery", () => { |
| 1972 | }).pipe(Effect.provide(makeLayer({ attempts: [], queuedJobs }))); | 1975 | }).pipe(Effect.provide(makeLayer({ attempts: [], queuedJobs }))); |
| 1973 | }); | 1976 | }); |
| 1974 | }); | 1977 | }); |
| 1978 | |||
| 1979 | describe("signed APNs registration metadata", () => { | ||
| 1980 | for (const kind of ["live_activity_update", "push_notification"] as const) { | ||
| 1981 | for (const changed of ["bundle", "environment", "legacy"] as const) { | ||
| 1982 | it.effect(`routes ${kind} using current registration with ${changed} job metadata`, () => { | ||
| 1983 | const attempts: DeliveryAttempts.DeliveryAttemptInput[] = []; | ||
| 1984 | const requests: HttpClientRequest.HttpClientRequest[] = []; | ||
| 1985 | const payload = makeApnsDeliveryJobPayload({ | ||
| 1986 | kind, | ||
| 1987 | userId: target.user_id, | ||
| 1988 | deviceId: target.device_id, | ||
| 1989 | token: "unchanged-token", | ||
| 1990 | ...(changed === "legacy" | ||
| 1991 | ? {} | ||
| 1992 | : { bundleId: "com.t3tools.t3code.dev", apsEnvironment: "sandbox" as const }), | ||
| 1993 | aggregate: kind === "live_activity_update" ? aggregate : null, | ||
| 1994 | ...(kind === "push_notification" | ||
| 1995 | ? { | ||
| 1996 | notification: { | ||
| 1997 | title: "Thread", | ||
| 1998 | body: "Input: Project", | ||
| 1999 | environmentId: "env", | ||
| 2000 | threadId: "thread", | ||
| 2001 | deepLink: "/", | ||
| 2002 | }, | ||
| 2003 | } | ||
| 2004 | : {}), | ||
| 2005 | createdAt: "1970-01-01T00:00:00.000Z", | ||
| 2006 | expiresAt: "1970-01-01T00:10:00.000Z", | ||
| 2007 | jobId: `metadata-${kind}-${changed}`, | ||
| 2008 | }); | ||
| 2009 | const signed = signApnsDeliveryJob({ | ||
| 2010 | secret: config.apnsDeliveryJobSigningSecret, | ||
| 2011 | payload, | ||
| 2012 | }); | ||
| 2013 | return Effect.gen(function* () { | ||
| 2014 | const deliveries = yield* ApnsDeliveries.ApnsDeliveries; | ||
| 2015 | const result = yield* deliveries.processSignedJob(signed); | ||
| 2016 | expect(result.ok).toBe(true); | ||
| 2017 | expect(requests).toHaveLength(1); | ||
| 2018 | expect(requests[0]?.url).toBe( | ||
| 2019 | `${changed === "environment" ? "https://api.push.apple.com" : "https://api.sandbox.push.apple.com"}/3/device/unchanged-token`, | ||
| 2020 | ); | ||
| 2021 | expect(requests[0]?.headers["apns-topic"]).toBe( | ||
| 2022 | `${changed === "bundle" ? "com.t3tools.t3code.preview" : "com.t3tools.t3code.dev"}${kind === "live_activity_update" ? ".push-type.liveactivity" : ""}`, | ||
| 2023 | ); | ||
| 2024 | }).pipe( | ||
| 2025 | Effect.provide( | ||
| 2026 | makeLayer({ | ||
| 2027 | attempts, | ||
| 2028 | config: signingConfig, | ||
| 2029 | currentTargets: [ | ||
| 2030 | { | ||
| 2031 | ...target, | ||
| 2032 | push_token: "unchanged-token", | ||
| 2033 | activity_push_token: "unchanged-token", | ||
| 2034 | bundle_id: | ||
| 2035 | changed === "bundle" ? "com.t3tools.t3code.preview" : "com.t3tools.t3code.dev", | ||
| 2036 | aps_environment: changed === "environment" ? "production" : "sandbox", | ||
| 2037 | }, | ||
| 2038 | ], | ||
| 2039 | execute: (request) => | ||
| 2040 | Effect.sync(() => { | ||
| 2041 | requests.push(request); | ||
| 2042 | return HttpClientResponse.fromWeb(request, new Response("", { status: 200 })); | ||
| 2043 | }), | ||
| 2044 | }), | ||
| 2045 | ), | ||
| 2046 | ); | ||
| 2047 | }); | ||
| 2048 | } | ||
| 2049 | } | ||
| 2050 | }); |
infra/relay/src/agentActivity/ApnsDeliveries.ts+6-2| ... | @@ -681,6 +681,7 @@ export const make = Effect.gen(function* () { | ... | @@ -681,6 +681,7 @@ export const make = Effect.gen(function* () { |
| 681 | "relay.delivery.kind": input.kind, | 681 | "relay.delivery.kind": input.kind, |
| 682 | ...(input.sourceJobId ? { "relay.delivery.job_id": input.sourceJobId } : {}), | 682 | ...(input.sourceJobId ? { "relay.delivery.job_id": input.sourceJobId } : {}), |
| 683 | }); | 683 | }); |
| 684 | let deliveryTarget = input.target; | ||
| 684 | const now = yield* DateTime.now; | 685 | const now = yield* DateTime.now; |
| 685 | const aggregate = | 686 | const aggregate = |
| 686 | input.aggregate === null ? null : sanitizeAgentActivityAggregateState(input.aggregate); | 687 | input.aggregate === null ? null : sanitizeAgentActivityAggregateState(input.aggregate); |
| ... | @@ -722,6 +723,7 @@ export const make = Effect.gen(function* () { | ... | @@ -722,6 +723,7 @@ export const make = Effect.gen(function* () { |
| 722 | }); | 723 | }); |
| 723 | return staleJobResult({ deviceId: input.target.device_id, kind: input.kind }); | 724 | return staleJobResult({ deviceId: input.target.device_id, kind: input.kind }); |
| 724 | } | 725 | } |
| 726 | deliveryTarget = currentTarget; | ||
| 725 | if (alert) { | 727 | if (alert) { |
| 726 | const preferences = parsePreferences(currentTarget.preferences_json); | 728 | const preferences = parsePreferences(currentTarget.preferences_json); |
| 727 | const previousAggregate = parseAggregate(currentTarget.last_aggregate_json); | 729 | const previousAggregate = parseAggregate(currentTarget.last_aggregate_json); |
| ... | @@ -779,7 +781,7 @@ export const make = Effect.gen(function* () { | ... | @@ -779,7 +781,7 @@ export const make = Effect.gen(function* () { |
| 779 | ); | 781 | ); |
| 780 | const result = yield* apns | 782 | const result = yield* apns |
| 781 | .sendLiveActivityRequest({ | 783 | .sendLiveActivityRequest({ |
| 782 | credentials: credentialsForTarget(config.apns, input.target), | 784 | credentials: credentialsForTarget(config.apns, deliveryTarget), |
| 783 | request, | 785 | request, |
| 784 | issuedAtUnixSeconds: epochSeconds, | 786 | issuedAtUnixSeconds: epochSeconds, |
| 785 | }) | 787 | }) |
| ... | @@ -845,6 +847,7 @@ export const make = Effect.gen(function* () { | ... | @@ -845,6 +847,7 @@ export const make = Effect.gen(function* () { |
| 845 | "relay.delivery.kind": "push_notification", | 847 | "relay.delivery.kind": "push_notification", |
| 846 | ...(input.sourceJobId ? { "relay.delivery.job_id": input.sourceJobId } : {}), | 848 | ...(input.sourceJobId ? { "relay.delivery.job_id": input.sourceJobId } : {}), |
| 847 | }); | 849 | }); |
| 850 | let deliveryTarget = input.target; | ||
| 848 | const now = yield* DateTime.now; | 851 | const now = yield* DateTime.now; |
| 849 | const epochSeconds = Math.floor(now.epochMilliseconds / 1_000); | 852 | const epochSeconds = Math.floor(now.epochMilliseconds / 1_000); |
| 850 | const notification = sanitizeApnsNotificationPayload(input.notification); | 853 | const notification = sanitizeApnsNotificationPayload(input.notification); |
| ... | @@ -914,6 +917,7 @@ export const make = Effect.gen(function* () { | ... | @@ -914,6 +917,7 @@ export const make = Effect.gen(function* () { |
| 914 | kind: "push_notification", | 917 | kind: "push_notification", |
| 915 | }); | 918 | }); |
| 916 | } | 919 | } |
| 920 | deliveryTarget = currentTarget; | ||
| 917 | const preferences = parsePreferences(currentTarget.preferences_json); | 921 | const preferences = parsePreferences(currentTarget.preferences_json); |
| 918 | const alertAllowed = | 922 | const alertAllowed = |
| 919 | notification.phase !== undefined && notification.updatedAt !== undefined | 923 | notification.phase !== undefined && notification.updatedAt !== undefined |
| ... | @@ -935,7 +939,7 @@ export const make = Effect.gen(function* () { | ... | @@ -935,7 +939,7 @@ export const make = Effect.gen(function* () { |
| 935 | } | 939 | } |
| 936 | const result = yield* apns | 940 | const result = yield* apns |
| 937 | .sendPushNotificationRequest({ | 941 | .sendPushNotificationRequest({ |
| 938 | credentials: credentialsForTarget(config.apns, input.target), | 942 | credentials: credentialsForTarget(config.apns, deliveryTarget), |
| 939 | request, | 943 | request, |
| 940 | issuedAtUnixSeconds: epochSeconds, | 944 | issuedAtUnixSeconds: epochSeconds, |
| 941 | }) | 945 | }) |