@tryterra/react-native 0.0.0-stage → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +142 -2
- package/TerraReactNative.podspec +48 -0
- package/android/build.gradle +87 -0
- package/android/src/bridgeCore/kotlin/README.md +1 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/BridgeBootstrap.kt +133 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/BridgeHost.kt +32 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/Conversions.kt +557 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/LauncherSlots.kt +41 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/ReadConversions.kt +250 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/Rejections.kt +150 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/StreamRegistry.kt +77 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/TerraBridge.kt +86 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/TerraBridgeOpsImpl.kt +300 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/TerraBridgeStreamsImpl.kt +50 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/TokenBroker.kt +198 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/generated/BridgeErrors.kt +217 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/generated/BridgeOps.kt +582 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/generated/BridgeTypes.kt +1569 -0
- package/android/src/healthConnectOnly/kotlin/co/tryterra/sdk/reactnative/TerraStores.kt +11 -0
- package/android/src/main/AndroidManifest.xml +24 -0
- package/android/src/main/java/co/tryterra/sdk/reactnative/TerraBridgeModule.kt +96 -0
- package/android/src/main/java/co/tryterra/sdk/reactnative/TerraReactNativePackage.kt +26 -0
- package/android/src/main/java/co/tryterra/sdk/reactnative/TerraStartup.kt +46 -0
- package/android/src/samsung/kotlin/co/tryterra/sdk/reactnative/TerraStores.kt +13 -0
- package/docs/availability.md +396 -0
- package/frameworks/TerraSDK.xcframework/Info.plist +48 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Info.plist +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.abi.json +82672 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.package.swiftinterface +2186 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +2186 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.swiftinterface +2163 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/PrivacyInfo.xcprivacy +96 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/TerraSDK +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/dSYMs/TerraSDK.framework.dSYM/Contents/Info.plist +20 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/DWARF/TerraSDK +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/Relocations/aarch64/TerraSDK.yml +6594 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Info.plist +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +82672 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.package.swiftinterface +2186 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +2186 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +2163 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json +82672 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.package.swiftinterface +2186 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +2186 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +2163 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/PrivacyInfo.xcprivacy +96 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/TerraSDK +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/_CodeSignature/CodeResources +223 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/TerraSDK.framework.dSYM/Contents/Info.plist +20 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/DWARF/TerraSDK +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/Relocations/aarch64/TerraSDK.yml +6595 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/Relocations/x86_64/TerraSDK.yml +6513 -0
- package/ios/TerraBridgeCore/BridgeBootstrap.swift +89 -0
- package/ios/TerraBridgeCore/BridgeHost.swift +13 -0
- package/ios/TerraBridgeCore/Conversions+Reads.swift +259 -0
- package/ios/TerraBridgeCore/Conversions.swift +594 -0
- package/ios/TerraBridgeCore/Generated/BridgeErrors.swift +179 -0
- package/ios/TerraBridgeCore/Generated/BridgeOps.swift +967 -0
- package/ios/TerraBridgeCore/Generated/BridgeTypes.swift +2050 -0
- package/ios/TerraBridgeCore/Locked.swift +15 -0
- package/ios/TerraBridgeCore/README.md +1 -0
- package/ios/TerraBridgeCore/Rejections.swift +130 -0
- package/ios/TerraBridgeCore/RuntimeAttachment.swift +60 -0
- package/ios/TerraBridgeCore/StreamRegistry.swift +59 -0
- package/ios/TerraBridgeCore/TerraBridge.swift +122 -0
- package/ios/TerraBridgeCore/TerraBridgeOpsImpl.swift +318 -0
- package/ios/TerraBridgeCore/TerraBridgeStreamsImpl.swift +60 -0
- package/ios/TerraBridgeCore/TokenBroker.swift +147 -0
- package/ios/TerraBridgeModule.mm +79 -0
- package/ios/TerraBridgeModuleImpl.swift +49 -0
- package/ios/TerraReactNative.swift +19 -0
- package/lib/EndUser.d.ts +12 -0
- package/lib/EndUser.js +13 -0
- package/lib/NativeTerraBridge.d.ts +23 -0
- package/lib/NativeTerraBridge.js +13 -0
- package/lib/Terra.d.ts +223 -0
- package/lib/Terra.js +336 -0
- package/lib/TerraError.d.ts +42 -0
- package/lib/TerraError.js +95 -0
- package/lib/createTerra.d.ts +8 -0
- package/lib/createTerra.js +19 -0
- package/lib/generated/client.d.ts +128 -0
- package/lib/generated/client.js +256 -0
- package/lib/generated/codecs.d.ts +219 -0
- package/lib/generated/codecs.js +2066 -0
- package/lib/generated/errors.d.ts +13 -0
- package/lib/generated/errors.js +68 -0
- package/lib/generated/ops.d.ts +13 -0
- package/lib/generated/ops.js +161 -0
- package/lib/generated/streams.d.ts +23 -0
- package/lib/generated/streams.js +58 -0
- package/lib/generated/types.d.ts +794 -0
- package/lib/generated/types.js +224 -0
- package/lib/index.d.ts +29 -0
- package/lib/index.js +55 -0
- package/package.json +71 -4
- package/src/EndUser.ts +15 -0
- package/src/NativeTerraBridge.ts +27 -0
- package/src/Terra.ts +592 -0
- package/src/TerraError.ts +116 -0
- package/src/createTerra.ts +18 -0
- package/src/generated/client.ts +315 -0
- package/src/generated/codecs.ts +1989 -0
- package/src/generated/errors.ts +113 -0
- package/src/generated/ops.ts +233 -0
- package/src/generated/streams.ts +74 -0
- package/src/generated/types.ts +1222 -0
- package/src/index.ts +54 -0
|
@@ -0,0 +1,582 @@
|
|
|
1
|
+
// Generated from surface/terra-surface.json by surface/gen. Do not edit by hand.
|
|
2
|
+
// Regenerate with: node surface/gen/src/cli.ts generate
|
|
3
|
+
|
|
4
|
+
package co.tryterra.sdk.bridge.generated
|
|
5
|
+
|
|
6
|
+
import kotlinx.coroutines.flow.Flow
|
|
7
|
+
import kotlinx.coroutines.flow.catch
|
|
8
|
+
import kotlinx.coroutines.flow.emitAll
|
|
9
|
+
import kotlinx.coroutines.flow.flow
|
|
10
|
+
import kotlinx.coroutines.flow.map
|
|
11
|
+
import kotlinx.coroutines.flow.onCompletion
|
|
12
|
+
import kotlinx.serialization.KSerializer
|
|
13
|
+
import kotlinx.serialization.json.Json
|
|
14
|
+
import kotlinx.serialization.json.JsonObject
|
|
15
|
+
import kotlinx.serialization.json.jsonObject
|
|
16
|
+
import kotlinx.serialization.serializer
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Every op the schema declares, including the ones this platform does not implement.
|
|
20
|
+
*
|
|
21
|
+
* The unsupported ones are entries here rather than absent so that dispatch can answer
|
|
22
|
+
* bridge.unsupported_on_platform deliberately, instead of failing to parse the op.
|
|
23
|
+
*/
|
|
24
|
+
public enum class BridgeOp(public val wire: String, public val isSupported: Boolean) {
|
|
25
|
+
CLIENT_ATTACH("client.attach", true),
|
|
26
|
+
CLIENT_CONNECT("client.connect", true),
|
|
27
|
+
CLIENT_CONNECTIONS("client.connections", true),
|
|
28
|
+
CLIENT_AVAILABILITY("client.availability", true),
|
|
29
|
+
CLIENT_DISCONNECT("client.disconnect", true),
|
|
30
|
+
TOKEN_SET_PROVIDER_PRESENT("token.setProviderPresent", true),
|
|
31
|
+
TOKEN_RESOLVE("token.resolve", true),
|
|
32
|
+
TOKEN_REJECT("token.reject", true),
|
|
33
|
+
PERMISSIONS_REQUEST("permissions.request", true),
|
|
34
|
+
PERMISSIONS_STATUS("permissions.status", true),
|
|
35
|
+
PERMISSIONS_WRITE_STATUS("permissions.writeStatus", true),
|
|
36
|
+
PERMISSIONS_REQUESTED("permissions.requested", true),
|
|
37
|
+
PERMISSIONS_STATUSES("permissions.statuses", true),
|
|
38
|
+
PERMISSIONS_BACKGROUND_READ_STATUS("permissions.backgroundReadStatus", true),
|
|
39
|
+
PERMISSIONS_HISTORY_READ_STATUS("permissions.historyReadStatus", true),
|
|
40
|
+
DIAGNOSTICS_STATE("diagnostics.state", true),
|
|
41
|
+
DIAGNOSTICS_LAST_DELIVERY("diagnostics.lastDelivery", true),
|
|
42
|
+
DIAGNOSTICS_REPORT("diagnostics.report", true),
|
|
43
|
+
DIAGNOSTICS_SEND("diagnostics.send", true),
|
|
44
|
+
BACKGROUND_IS_REGISTERED("background.isRegistered", true),
|
|
45
|
+
BACKGROUND_CANCEL("background.cancel", true),
|
|
46
|
+
BODY_HEART_RATE("body.heartRate", true),
|
|
47
|
+
BODY_RESTING_HEART_RATE("body.restingHeartRate", true),
|
|
48
|
+
BODY_READINGS("body.readings", true),
|
|
49
|
+
BODY_LOG_HEART_RATE("body.logHeartRate", true),
|
|
50
|
+
BODY_LOG("body.log", true),
|
|
51
|
+
BODY_DELETE("body.delete", true),
|
|
52
|
+
DAILY_STEPS("daily.steps", true),
|
|
53
|
+
DAILY_TOTALS("daily.totals", true),
|
|
54
|
+
DAILY_INTRADAY("daily.intraday", true),
|
|
55
|
+
DAILY_LOG("daily.log", true),
|
|
56
|
+
DAILY_DELETE("daily.delete", true),
|
|
57
|
+
SLEEP_SEGMENTS("sleep.segments", true),
|
|
58
|
+
SLEEP_LOG("sleep.log", true),
|
|
59
|
+
SLEEP_DELETE("sleep.delete", true),
|
|
60
|
+
ACTIVITY_WORKOUTS("activity.workouts", true),
|
|
61
|
+
ACTIVITY_ROUTES("activity.routes", true),
|
|
62
|
+
ACTIVITY_INTENSITY("activity.intensity", true),
|
|
63
|
+
ACTIVITY_LOG("activity.log", true),
|
|
64
|
+
ACTIVITY_DELETE("activity.delete", true),
|
|
65
|
+
NUTRITION_QUANTITIES("nutrition.quantities", true),
|
|
66
|
+
NUTRITION_LOG("nutrition.log", true),
|
|
67
|
+
NUTRITION_DELETE("nutrition.delete", true),
|
|
68
|
+
NUTRITION_HYDRATION("nutrition.hydration", true),
|
|
69
|
+
MENSTRUATION_FLOW("menstruation.flow", true),
|
|
70
|
+
MENSTRUATION_EVENTS("menstruation.events", true),
|
|
71
|
+
ATHLETE_PROFILE("athlete.profile", false),
|
|
72
|
+
PLANNED_WORKOUTS_PLANNED("plannedWorkouts.planned", false),
|
|
73
|
+
PLANNED_WORKOUTS_MARK_COMPLETE("plannedWorkouts.markComplete", false),
|
|
74
|
+
PLANNED_WORKOUTS_DELETE("plannedWorkouts.delete", false),
|
|
75
|
+
PLANNED_WORKOUTS_SCHEDULING_STATUS("plannedWorkouts.schedulingStatus", false),
|
|
76
|
+
PLANNED_WORKOUTS_REQUEST_SCHEDULING("plannedWorkouts.requestScheduling", false),
|
|
77
|
+
;
|
|
78
|
+
|
|
79
|
+
public companion object {
|
|
80
|
+
private val byWire: Map<String, BridgeOp> = entries.associateBy { it.wire }
|
|
81
|
+
|
|
82
|
+
public fun of(wire: String): BridgeOp? = byWire[wire]
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* What P2 implements. One suspend function per op this platform supports.
|
|
88
|
+
*
|
|
89
|
+
* An op added to the schema stops this module compiling until the implementation grows a
|
|
90
|
+
* member for it, which is the whole reason the dispatch is generated from the same file.
|
|
91
|
+
*/
|
|
92
|
+
public interface TerraBridgeOps {
|
|
93
|
+
/**
|
|
94
|
+
* Reports the native client the JS side is attaching to. Idempotent, and safe to call after a
|
|
95
|
+
* JS reload.
|
|
96
|
+
*/
|
|
97
|
+
public suspend fun clientAttach(request: Empty): BootstrapState
|
|
98
|
+
public suspend fun clientConnect(request: ConnectRequest): Connection
|
|
99
|
+
public suspend fun clientConnections(request: Empty): ConnectionList
|
|
100
|
+
/** Whether the device can serve the source at all. Needs no connection and prompts for nothing. */
|
|
101
|
+
public suspend fun clientAvailability(request: SourceRequest): AvailabilityResult
|
|
102
|
+
public suspend fun clientDisconnect(request: SourceRequest): Empty
|
|
103
|
+
/**
|
|
104
|
+
* Tells the broker whether JS can answer at all. Without it a link asks a provider that is not
|
|
105
|
+
* there and waits out the timeout.
|
|
106
|
+
*/
|
|
107
|
+
public suspend fun tokenSetProviderPresent(request: TokenProviderRequest): Empty
|
|
108
|
+
/**
|
|
109
|
+
* An unknown or already-settled requestId is ignored rather than rejected, so a reload cannot
|
|
110
|
+
* fail a link that has already moved on.
|
|
111
|
+
*/
|
|
112
|
+
public suspend fun tokenResolve(request: TokenResolveRequest): Empty
|
|
113
|
+
public suspend fun tokenReject(request: TokenRejectRequest): Empty
|
|
114
|
+
public suspend fun permissionsRequest(request: PermissionRequest): PermissionGrant
|
|
115
|
+
public suspend fun permissionsStatus(request: CategoryRequest): StatusResult
|
|
116
|
+
public suspend fun permissionsWriteStatus(request: CategoryRequest): StatusResult
|
|
117
|
+
public suspend fun permissionsRequested(request: SourceRequest): HealthAccess
|
|
118
|
+
/** Available on android only. */
|
|
119
|
+
public suspend fun permissionsStatuses(request: SourceRequest): StatusesResult
|
|
120
|
+
/** Available on android only. */
|
|
121
|
+
public suspend fun permissionsBackgroundReadStatus(request: SourceRequest): StatusResult
|
|
122
|
+
/** Available on android only. */
|
|
123
|
+
public suspend fun permissionsHistoryReadStatus(request: SourceRequest): StatusResult
|
|
124
|
+
public suspend fun diagnosticsState(request: SourceRequest): DiagnosticsState
|
|
125
|
+
public suspend fun diagnosticsLastDelivery(request: SourceRequest): LastDeliveryResult
|
|
126
|
+
public suspend fun diagnosticsReport(request: SourceRequest): SupportReport
|
|
127
|
+
public suspend fun diagnosticsSend(request: SourceRequest): Empty
|
|
128
|
+
/**
|
|
129
|
+
* False means bootstrap did not run. The SDK reports the same thing independently as
|
|
130
|
+
* backgroundRefreshUnscheduled.
|
|
131
|
+
*/
|
|
132
|
+
public suspend fun backgroundIsRegistered(request: Empty): BoolResult
|
|
133
|
+
/** Available on android only. */
|
|
134
|
+
public suspend fun backgroundCancel(request: Empty): BoolResult
|
|
135
|
+
public suspend fun bodyHeartRate(request: RangeRequest): HeartRateList
|
|
136
|
+
/**
|
|
137
|
+
* The store's own daily figures. Health Connect grants them under daily, so Android answers
|
|
138
|
+
* authorization_not_determined where only body was asked for.
|
|
139
|
+
*/
|
|
140
|
+
public suspend fun bodyRestingHeartRate(request: RangeRequest): RestingHeartRateList
|
|
141
|
+
public suspend fun bodyReadings(request: RangeRequest): BodyReadingList
|
|
142
|
+
/** Its own op rather than an overload: Swift overloads log by element type and JS cannot. */
|
|
143
|
+
public suspend fun bodyLogHeartRate(request: HeartRateEntryList): RecordIdList
|
|
144
|
+
public suspend fun bodyLog(request: BodyEntryList): RecordIdList
|
|
145
|
+
public suspend fun bodyDelete(request: RecordIdRequest): Empty
|
|
146
|
+
public suspend fun dailySteps(request: RangeRequest): StepCountList
|
|
147
|
+
/**
|
|
148
|
+
* One total per metric per whole local day the range touches. A day with nothing recorded has
|
|
149
|
+
* no total, not a zero.
|
|
150
|
+
*/
|
|
151
|
+
public suspend fun dailyTotals(request: RangeRequest): DayTotalList
|
|
152
|
+
/** The active energy, basal energy, distance and floors records the totals are made of. */
|
|
153
|
+
public suspend fun dailyIntraday(request: RangeRequest): IntradaySampleList
|
|
154
|
+
public suspend fun dailyLog(request: StepEntryList): RecordIdList
|
|
155
|
+
public suspend fun dailyDelete(request: RecordIdRequest): Empty
|
|
156
|
+
public suspend fun sleepSegments(request: RangeRequest): SleepSegmentList
|
|
157
|
+
public suspend fun sleepLog(request: SleepEntryList): RecordIdList
|
|
158
|
+
public suspend fun sleepDelete(request: RecordIdRequest): Empty
|
|
159
|
+
public suspend fun activityWorkouts(request: RangeRequest): WorkoutList
|
|
160
|
+
/** The GPS tracks of the workouts in the range, each naming its workout. */
|
|
161
|
+
public suspend fun activityRoutes(request: RangeRequest): WorkoutRouteList
|
|
162
|
+
/** Speed, power and cycling cadence readings in the range. */
|
|
163
|
+
public suspend fun activityIntensity(request: RangeRequest): WorkoutIntensityList
|
|
164
|
+
public suspend fun activityLog(request: WorkoutEntryList): RecordIdList
|
|
165
|
+
public suspend fun activityDelete(request: RecordIdRequest): Empty
|
|
166
|
+
/** One quantity per nutrient a meal recorded, not grouped into meals. Water is not among them. */
|
|
167
|
+
public suspend fun nutritionQuantities(request: RangeRequest): DietaryQuantityList
|
|
168
|
+
public suspend fun nutritionLog(request: NutritionEntryList): RecordIdList
|
|
169
|
+
public suspend fun nutritionDelete(request: RecordIdRequest): Empty
|
|
170
|
+
/** Water drunk in the range, one sample per drink. */
|
|
171
|
+
public suspend fun nutritionHydration(request: RangeRequest): HydrationList
|
|
172
|
+
/**
|
|
173
|
+
* Logged flow days overlapping the range, one per day. A day logged with no flow is noFlow,
|
|
174
|
+
* not left out.
|
|
175
|
+
*/
|
|
176
|
+
public suspend fun menstruationFlow(request: RangeRequest): MenstruationFlowDayList
|
|
177
|
+
/** Ovulation tests, cervical mucus and intermenstrual bleeding logged in the range. */
|
|
178
|
+
public suspend fun menstruationEvents(request: RangeRequest): MenstrualEventList
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Decodes one side of an exchange and re-encodes it, for the bridge-vector runner.
|
|
183
|
+
*
|
|
184
|
+
* The runner cannot name the request and response types itself without keeping a second copy
|
|
185
|
+
* of the op table, and a second copy of the op table is the thing this schema exists to
|
|
186
|
+
* remove. A round trip is also exactly what a vector asserts: the bytes in are the bytes out,
|
|
187
|
+
* which pins the DTOs and their serializer against the same file the other two runners read.
|
|
188
|
+
*/
|
|
189
|
+
public object BridgeCodecs {
|
|
190
|
+
public fun roundTripRequest(op: BridgeOp, payload: String, json: Json = BridgeDispatch.defaultJson): String =
|
|
191
|
+
when (op) {
|
|
192
|
+
BridgeOp.CLIENT_ATTACH -> json.encodeToString(json.decodeFromString<Empty>(payload))
|
|
193
|
+
BridgeOp.CLIENT_CONNECT -> json.encodeToString(json.decodeFromString<ConnectRequest>(payload))
|
|
194
|
+
BridgeOp.CLIENT_CONNECTIONS -> json.encodeToString(json.decodeFromString<Empty>(payload))
|
|
195
|
+
BridgeOp.CLIENT_AVAILABILITY -> json.encodeToString(json.decodeFromString<SourceRequest>(payload))
|
|
196
|
+
BridgeOp.CLIENT_DISCONNECT -> json.encodeToString(json.decodeFromString<SourceRequest>(payload))
|
|
197
|
+
BridgeOp.TOKEN_SET_PROVIDER_PRESENT -> json.encodeToString(json.decodeFromString<TokenProviderRequest>(payload))
|
|
198
|
+
BridgeOp.TOKEN_RESOLVE -> json.encodeToString(json.decodeFromString<TokenResolveRequest>(payload))
|
|
199
|
+
BridgeOp.TOKEN_REJECT -> json.encodeToString(json.decodeFromString<TokenRejectRequest>(payload))
|
|
200
|
+
BridgeOp.PERMISSIONS_REQUEST -> json.encodeToString(json.decodeFromString<PermissionRequest>(payload))
|
|
201
|
+
BridgeOp.PERMISSIONS_STATUS -> json.encodeToString(json.decodeFromString<CategoryRequest>(payload))
|
|
202
|
+
BridgeOp.PERMISSIONS_WRITE_STATUS -> json.encodeToString(json.decodeFromString<CategoryRequest>(payload))
|
|
203
|
+
BridgeOp.PERMISSIONS_REQUESTED -> json.encodeToString(json.decodeFromString<SourceRequest>(payload))
|
|
204
|
+
BridgeOp.PERMISSIONS_STATUSES -> json.encodeToString(json.decodeFromString<SourceRequest>(payload))
|
|
205
|
+
BridgeOp.PERMISSIONS_BACKGROUND_READ_STATUS -> json.encodeToString(json.decodeFromString<SourceRequest>(payload))
|
|
206
|
+
BridgeOp.PERMISSIONS_HISTORY_READ_STATUS -> json.encodeToString(json.decodeFromString<SourceRequest>(payload))
|
|
207
|
+
BridgeOp.DIAGNOSTICS_STATE -> json.encodeToString(json.decodeFromString<SourceRequest>(payload))
|
|
208
|
+
BridgeOp.DIAGNOSTICS_LAST_DELIVERY -> json.encodeToString(json.decodeFromString<SourceRequest>(payload))
|
|
209
|
+
BridgeOp.DIAGNOSTICS_REPORT -> json.encodeToString(json.decodeFromString<SourceRequest>(payload))
|
|
210
|
+
BridgeOp.DIAGNOSTICS_SEND -> json.encodeToString(json.decodeFromString<SourceRequest>(payload))
|
|
211
|
+
BridgeOp.BACKGROUND_IS_REGISTERED -> json.encodeToString(json.decodeFromString<Empty>(payload))
|
|
212
|
+
BridgeOp.BACKGROUND_CANCEL -> json.encodeToString(json.decodeFromString<Empty>(payload))
|
|
213
|
+
BridgeOp.BODY_HEART_RATE -> json.encodeToString(json.decodeFromString<RangeRequest>(payload))
|
|
214
|
+
BridgeOp.BODY_RESTING_HEART_RATE -> json.encodeToString(json.decodeFromString<RangeRequest>(payload))
|
|
215
|
+
BridgeOp.BODY_READINGS -> json.encodeToString(json.decodeFromString<RangeRequest>(payload))
|
|
216
|
+
BridgeOp.BODY_LOG_HEART_RATE -> json.encodeToString(json.decodeFromString<HeartRateEntryList>(payload))
|
|
217
|
+
BridgeOp.BODY_LOG -> json.encodeToString(json.decodeFromString<BodyEntryList>(payload))
|
|
218
|
+
BridgeOp.BODY_DELETE -> json.encodeToString(json.decodeFromString<RecordIdRequest>(payload))
|
|
219
|
+
BridgeOp.DAILY_STEPS -> json.encodeToString(json.decodeFromString<RangeRequest>(payload))
|
|
220
|
+
BridgeOp.DAILY_TOTALS -> json.encodeToString(json.decodeFromString<RangeRequest>(payload))
|
|
221
|
+
BridgeOp.DAILY_INTRADAY -> json.encodeToString(json.decodeFromString<RangeRequest>(payload))
|
|
222
|
+
BridgeOp.DAILY_LOG -> json.encodeToString(json.decodeFromString<StepEntryList>(payload))
|
|
223
|
+
BridgeOp.DAILY_DELETE -> json.encodeToString(json.decodeFromString<RecordIdRequest>(payload))
|
|
224
|
+
BridgeOp.SLEEP_SEGMENTS -> json.encodeToString(json.decodeFromString<RangeRequest>(payload))
|
|
225
|
+
BridgeOp.SLEEP_LOG -> json.encodeToString(json.decodeFromString<SleepEntryList>(payload))
|
|
226
|
+
BridgeOp.SLEEP_DELETE -> json.encodeToString(json.decodeFromString<RecordIdRequest>(payload))
|
|
227
|
+
BridgeOp.ACTIVITY_WORKOUTS -> json.encodeToString(json.decodeFromString<RangeRequest>(payload))
|
|
228
|
+
BridgeOp.ACTIVITY_ROUTES -> json.encodeToString(json.decodeFromString<RangeRequest>(payload))
|
|
229
|
+
BridgeOp.ACTIVITY_INTENSITY -> json.encodeToString(json.decodeFromString<RangeRequest>(payload))
|
|
230
|
+
BridgeOp.ACTIVITY_LOG -> json.encodeToString(json.decodeFromString<WorkoutEntryList>(payload))
|
|
231
|
+
BridgeOp.ACTIVITY_DELETE -> json.encodeToString(json.decodeFromString<RecordIdRequest>(payload))
|
|
232
|
+
BridgeOp.NUTRITION_QUANTITIES -> json.encodeToString(json.decodeFromString<RangeRequest>(payload))
|
|
233
|
+
BridgeOp.NUTRITION_LOG -> json.encodeToString(json.decodeFromString<NutritionEntryList>(payload))
|
|
234
|
+
BridgeOp.NUTRITION_DELETE -> json.encodeToString(json.decodeFromString<RecordIdRequest>(payload))
|
|
235
|
+
BridgeOp.NUTRITION_HYDRATION -> json.encodeToString(json.decodeFromString<RangeRequest>(payload))
|
|
236
|
+
BridgeOp.MENSTRUATION_FLOW -> json.encodeToString(json.decodeFromString<RangeRequest>(payload))
|
|
237
|
+
BridgeOp.MENSTRUATION_EVENTS -> json.encodeToString(json.decodeFromString<RangeRequest>(payload))
|
|
238
|
+
BridgeOp.ATHLETE_PROFILE -> json.encodeToString(json.decodeFromString<Empty>(payload))
|
|
239
|
+
BridgeOp.PLANNED_WORKOUTS_PLANNED -> json.encodeToString(json.decodeFromString<Empty>(payload))
|
|
240
|
+
BridgeOp.PLANNED_WORKOUTS_MARK_COMPLETE -> json.encodeToString(json.decodeFromString<MarkCompleteRequest>(payload))
|
|
241
|
+
BridgeOp.PLANNED_WORKOUTS_DELETE -> json.encodeToString(json.decodeFromString<RecordIdRequest>(payload))
|
|
242
|
+
BridgeOp.PLANNED_WORKOUTS_SCHEDULING_STATUS -> json.encodeToString(json.decodeFromString<Empty>(payload))
|
|
243
|
+
BridgeOp.PLANNED_WORKOUTS_REQUEST_SCHEDULING -> json.encodeToString(json.decodeFromString<Empty>(payload))
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
public fun roundTripResponse(op: BridgeOp, payload: String, json: Json = BridgeDispatch.defaultJson): String =
|
|
247
|
+
when (op) {
|
|
248
|
+
BridgeOp.CLIENT_ATTACH -> json.encodeToString(json.decodeFromString<BootstrapState>(payload))
|
|
249
|
+
BridgeOp.CLIENT_CONNECT -> json.encodeToString(json.decodeFromString<Connection>(payload))
|
|
250
|
+
BridgeOp.CLIENT_CONNECTIONS -> json.encodeToString(json.decodeFromString<ConnectionList>(payload))
|
|
251
|
+
BridgeOp.CLIENT_AVAILABILITY -> json.encodeToString(json.decodeFromString<AvailabilityResult>(payload))
|
|
252
|
+
BridgeOp.CLIENT_DISCONNECT -> json.encodeToString(json.decodeFromString<Empty>(payload))
|
|
253
|
+
BridgeOp.TOKEN_SET_PROVIDER_PRESENT -> json.encodeToString(json.decodeFromString<Empty>(payload))
|
|
254
|
+
BridgeOp.TOKEN_RESOLVE -> json.encodeToString(json.decodeFromString<Empty>(payload))
|
|
255
|
+
BridgeOp.TOKEN_REJECT -> json.encodeToString(json.decodeFromString<Empty>(payload))
|
|
256
|
+
BridgeOp.PERMISSIONS_REQUEST -> json.encodeToString(json.decodeFromString<PermissionGrant>(payload))
|
|
257
|
+
BridgeOp.PERMISSIONS_STATUS -> json.encodeToString(json.decodeFromString<StatusResult>(payload))
|
|
258
|
+
BridgeOp.PERMISSIONS_WRITE_STATUS -> json.encodeToString(json.decodeFromString<StatusResult>(payload))
|
|
259
|
+
BridgeOp.PERMISSIONS_REQUESTED -> json.encodeToString(json.decodeFromString<HealthAccess>(payload))
|
|
260
|
+
BridgeOp.PERMISSIONS_STATUSES -> json.encodeToString(json.decodeFromString<StatusesResult>(payload))
|
|
261
|
+
BridgeOp.PERMISSIONS_BACKGROUND_READ_STATUS -> json.encodeToString(json.decodeFromString<StatusResult>(payload))
|
|
262
|
+
BridgeOp.PERMISSIONS_HISTORY_READ_STATUS -> json.encodeToString(json.decodeFromString<StatusResult>(payload))
|
|
263
|
+
BridgeOp.DIAGNOSTICS_STATE -> json.encodeToString(json.decodeFromString<DiagnosticsState>(payload))
|
|
264
|
+
BridgeOp.DIAGNOSTICS_LAST_DELIVERY -> json.encodeToString(json.decodeFromString<LastDeliveryResult>(payload))
|
|
265
|
+
BridgeOp.DIAGNOSTICS_REPORT -> {
|
|
266
|
+
val value = json.decodeFromString<SupportReport>(payload)
|
|
267
|
+
SupportReportEnvelope.validate(value)
|
|
268
|
+
json.encodeToString(value)
|
|
269
|
+
}
|
|
270
|
+
BridgeOp.DIAGNOSTICS_SEND -> json.encodeToString(json.decodeFromString<Empty>(payload))
|
|
271
|
+
BridgeOp.BACKGROUND_IS_REGISTERED -> json.encodeToString(json.decodeFromString<BoolResult>(payload))
|
|
272
|
+
BridgeOp.BACKGROUND_CANCEL -> json.encodeToString(json.decodeFromString<BoolResult>(payload))
|
|
273
|
+
BridgeOp.BODY_HEART_RATE -> json.encodeToString(json.decodeFromString<HeartRateList>(payload))
|
|
274
|
+
BridgeOp.BODY_RESTING_HEART_RATE -> json.encodeToString(json.decodeFromString<RestingHeartRateList>(payload))
|
|
275
|
+
BridgeOp.BODY_READINGS -> json.encodeToString(json.decodeFromString<BodyReadingList>(payload))
|
|
276
|
+
BridgeOp.BODY_LOG_HEART_RATE -> json.encodeToString(json.decodeFromString<RecordIdList>(payload))
|
|
277
|
+
BridgeOp.BODY_LOG -> json.encodeToString(json.decodeFromString<RecordIdList>(payload))
|
|
278
|
+
BridgeOp.BODY_DELETE -> json.encodeToString(json.decodeFromString<Empty>(payload))
|
|
279
|
+
BridgeOp.DAILY_STEPS -> json.encodeToString(json.decodeFromString<StepCountList>(payload))
|
|
280
|
+
BridgeOp.DAILY_TOTALS -> json.encodeToString(json.decodeFromString<DayTotalList>(payload))
|
|
281
|
+
BridgeOp.DAILY_INTRADAY -> json.encodeToString(json.decodeFromString<IntradaySampleList>(payload))
|
|
282
|
+
BridgeOp.DAILY_LOG -> json.encodeToString(json.decodeFromString<RecordIdList>(payload))
|
|
283
|
+
BridgeOp.DAILY_DELETE -> json.encodeToString(json.decodeFromString<Empty>(payload))
|
|
284
|
+
BridgeOp.SLEEP_SEGMENTS -> json.encodeToString(json.decodeFromString<SleepSegmentList>(payload))
|
|
285
|
+
BridgeOp.SLEEP_LOG -> json.encodeToString(json.decodeFromString<RecordIdList>(payload))
|
|
286
|
+
BridgeOp.SLEEP_DELETE -> json.encodeToString(json.decodeFromString<Empty>(payload))
|
|
287
|
+
BridgeOp.ACTIVITY_WORKOUTS -> json.encodeToString(json.decodeFromString<WorkoutList>(payload))
|
|
288
|
+
BridgeOp.ACTIVITY_ROUTES -> json.encodeToString(json.decodeFromString<WorkoutRouteList>(payload))
|
|
289
|
+
BridgeOp.ACTIVITY_INTENSITY -> json.encodeToString(json.decodeFromString<WorkoutIntensityList>(payload))
|
|
290
|
+
BridgeOp.ACTIVITY_LOG -> json.encodeToString(json.decodeFromString<RecordIdList>(payload))
|
|
291
|
+
BridgeOp.ACTIVITY_DELETE -> json.encodeToString(json.decodeFromString<Empty>(payload))
|
|
292
|
+
BridgeOp.NUTRITION_QUANTITIES -> json.encodeToString(json.decodeFromString<DietaryQuantityList>(payload))
|
|
293
|
+
BridgeOp.NUTRITION_LOG -> json.encodeToString(json.decodeFromString<RecordIdList>(payload))
|
|
294
|
+
BridgeOp.NUTRITION_DELETE -> json.encodeToString(json.decodeFromString<Empty>(payload))
|
|
295
|
+
BridgeOp.NUTRITION_HYDRATION -> json.encodeToString(json.decodeFromString<HydrationList>(payload))
|
|
296
|
+
BridgeOp.MENSTRUATION_FLOW -> json.encodeToString(json.decodeFromString<MenstruationFlowDayList>(payload))
|
|
297
|
+
BridgeOp.MENSTRUATION_EVENTS -> json.encodeToString(json.decodeFromString<MenstrualEventList>(payload))
|
|
298
|
+
BridgeOp.ATHLETE_PROFILE -> json.encodeToString(json.decodeFromString<AthleteProfileResult>(payload))
|
|
299
|
+
BridgeOp.PLANNED_WORKOUTS_PLANNED -> json.encodeToString(json.decodeFromString<PlannedWorkoutList>(payload))
|
|
300
|
+
BridgeOp.PLANNED_WORKOUTS_MARK_COMPLETE -> json.encodeToString(json.decodeFromString<Empty>(payload))
|
|
301
|
+
BridgeOp.PLANNED_WORKOUTS_DELETE -> json.encodeToString(json.decodeFromString<Empty>(payload))
|
|
302
|
+
BridgeOp.PLANNED_WORKOUTS_SCHEDULING_STATUS -> json.encodeToString(json.decodeFromString<WorkoutScheduling>(payload))
|
|
303
|
+
BridgeOp.PLANNED_WORKOUTS_REQUEST_SCHEDULING -> json.encodeToString(json.decodeFromString<WorkoutScheduling>(payload))
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/** One element of a stream, by that stream's declared type. */
|
|
307
|
+
public fun roundTripStreamElement(stream: String, payload: String, json: Json = BridgeDispatch.defaultJson): String =
|
|
308
|
+
when (stream) {
|
|
309
|
+
"diagnostics.events" -> {
|
|
310
|
+
val value = json.decodeFromString<TerraEvent>(payload)
|
|
311
|
+
TerraEventEnvelope.validate(value)
|
|
312
|
+
json.encodeToString(value)
|
|
313
|
+
}
|
|
314
|
+
"body.changes" -> json.encodeToString(json.decodeFromString<HealthChange>(payload))
|
|
315
|
+
"daily.changes" -> json.encodeToString(json.decodeFromString<HealthChange>(payload))
|
|
316
|
+
"sleep.changes" -> json.encodeToString(json.decodeFromString<HealthChange>(payload))
|
|
317
|
+
"activity.changes" -> json.encodeToString(json.decodeFromString<HealthChange>(payload))
|
|
318
|
+
"bridge.tokenRequest" -> json.encodeToString(json.decodeFromString<TokenRequest>(payload))
|
|
319
|
+
else -> throw BridgeError.UnsupportedOnPlatform(stream)
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* One message as a subscription actually delivers it: the envelope, and the element inside.
|
|
324
|
+
*
|
|
325
|
+
* Both halves, because checking only the envelope would accept a `data` carrying anything at
|
|
326
|
+
* all, and checking only the element would accept a stream that never says it failed.
|
|
327
|
+
*/
|
|
328
|
+
public fun roundTripStreamMessage(stream: String, payload: String, json: Json = BridgeDispatch.defaultJson): String {
|
|
329
|
+
val message = json.decodeFromString<StreamMessage>(payload)
|
|
330
|
+
return when (message) {
|
|
331
|
+
is StreamMessage.Data ->
|
|
332
|
+
json.encodeToString<StreamMessage>(
|
|
333
|
+
StreamMessage.Data(
|
|
334
|
+
element = json.decodeFromString<JsonObject>(
|
|
335
|
+
roundTripStreamElement(stream, json.encodeToString(message.element), json),
|
|
336
|
+
),
|
|
337
|
+
),
|
|
338
|
+
)
|
|
339
|
+
else -> json.encodeToString<StreamMessage>(message)
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* kotlinx's own message carries the input, which may be a health sample, so only the path is
|
|
346
|
+
* lifted out of it.
|
|
347
|
+
*/
|
|
348
|
+
internal inline fun <reified T> Json.decodeRequest(payload: String, validate: (T) -> Unit = {}): T =
|
|
349
|
+
try {
|
|
350
|
+
decodeFromString<T>(payload).also(validate)
|
|
351
|
+
} catch (malformed: IllegalArgumentException) {
|
|
352
|
+
throw BridgeError.InvalidPayload(BridgeDispatch.pathOf(malformed))
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Turns an op string and a JSON payload into a typed call and a JSON answer.
|
|
357
|
+
*
|
|
358
|
+
* `when` as an expression over BridgeOp, with no else: the compiler, not a test, is what
|
|
359
|
+
* notices an op the schema added and this file has not handled.
|
|
360
|
+
*/
|
|
361
|
+
public class BridgeDispatch(
|
|
362
|
+
private val ops: TerraBridgeOps,
|
|
363
|
+
private val json: Json = defaultJson,
|
|
364
|
+
) {
|
|
365
|
+
public suspend fun dispatch(op: String, payload: String): String {
|
|
366
|
+
val known = BridgeOp.of(op) ?: throw BridgeError.UnsupportedOnPlatform(op)
|
|
367
|
+
return when (known) {
|
|
368
|
+
BridgeOp.CLIENT_ATTACH ->
|
|
369
|
+
json.encodeToString(ops.clientAttach(json.decodeRequest<Empty>(payload)))
|
|
370
|
+
BridgeOp.CLIENT_CONNECT ->
|
|
371
|
+
json.encodeToString(ops.clientConnect(json.decodeRequest<ConnectRequest>(payload)))
|
|
372
|
+
BridgeOp.CLIENT_CONNECTIONS ->
|
|
373
|
+
json.encodeToString(ops.clientConnections(json.decodeRequest<Empty>(payload)))
|
|
374
|
+
BridgeOp.CLIENT_AVAILABILITY ->
|
|
375
|
+
json.encodeToString(ops.clientAvailability(json.decodeRequest<SourceRequest>(payload)))
|
|
376
|
+
BridgeOp.CLIENT_DISCONNECT ->
|
|
377
|
+
json.encodeToString(ops.clientDisconnect(json.decodeRequest<SourceRequest>(payload)))
|
|
378
|
+
BridgeOp.TOKEN_SET_PROVIDER_PRESENT ->
|
|
379
|
+
json.encodeToString(ops.tokenSetProviderPresent(json.decodeRequest<TokenProviderRequest>(payload)))
|
|
380
|
+
BridgeOp.TOKEN_RESOLVE ->
|
|
381
|
+
json.encodeToString(ops.tokenResolve(json.decodeRequest<TokenResolveRequest>(payload)))
|
|
382
|
+
BridgeOp.TOKEN_REJECT ->
|
|
383
|
+
json.encodeToString(ops.tokenReject(json.decodeRequest<TokenRejectRequest>(payload)))
|
|
384
|
+
BridgeOp.PERMISSIONS_REQUEST ->
|
|
385
|
+
json.encodeToString(ops.permissionsRequest(json.decodeRequest<PermissionRequest>(payload)))
|
|
386
|
+
BridgeOp.PERMISSIONS_STATUS ->
|
|
387
|
+
json.encodeToString(ops.permissionsStatus(json.decodeRequest<CategoryRequest>(payload)))
|
|
388
|
+
BridgeOp.PERMISSIONS_WRITE_STATUS ->
|
|
389
|
+
json.encodeToString(ops.permissionsWriteStatus(json.decodeRequest<CategoryRequest>(payload)))
|
|
390
|
+
BridgeOp.PERMISSIONS_REQUESTED ->
|
|
391
|
+
json.encodeToString(ops.permissionsRequested(json.decodeRequest<SourceRequest>(payload)))
|
|
392
|
+
BridgeOp.PERMISSIONS_STATUSES ->
|
|
393
|
+
json.encodeToString(ops.permissionsStatuses(json.decodeRequest<SourceRequest>(payload)))
|
|
394
|
+
BridgeOp.PERMISSIONS_BACKGROUND_READ_STATUS ->
|
|
395
|
+
json.encodeToString(ops.permissionsBackgroundReadStatus(json.decodeRequest<SourceRequest>(payload)))
|
|
396
|
+
BridgeOp.PERMISSIONS_HISTORY_READ_STATUS ->
|
|
397
|
+
json.encodeToString(ops.permissionsHistoryReadStatus(json.decodeRequest<SourceRequest>(payload)))
|
|
398
|
+
BridgeOp.DIAGNOSTICS_STATE ->
|
|
399
|
+
json.encodeToString(ops.diagnosticsState(json.decodeRequest<SourceRequest>(payload)))
|
|
400
|
+
BridgeOp.DIAGNOSTICS_LAST_DELIVERY ->
|
|
401
|
+
json.encodeToString(ops.diagnosticsLastDelivery(json.decodeRequest<SourceRequest>(payload)))
|
|
402
|
+
BridgeOp.DIAGNOSTICS_REPORT -> {
|
|
403
|
+
val request = json.decodeRequest<SourceRequest>(payload)
|
|
404
|
+
val response = ops.diagnosticsReport(request)
|
|
405
|
+
SupportReportEnvelope.validate(response)
|
|
406
|
+
json.encodeToString(response)
|
|
407
|
+
}
|
|
408
|
+
BridgeOp.DIAGNOSTICS_SEND ->
|
|
409
|
+
json.encodeToString(ops.diagnosticsSend(json.decodeRequest<SourceRequest>(payload)))
|
|
410
|
+
BridgeOp.BACKGROUND_IS_REGISTERED ->
|
|
411
|
+
json.encodeToString(ops.backgroundIsRegistered(json.decodeRequest<Empty>(payload)))
|
|
412
|
+
BridgeOp.BACKGROUND_CANCEL ->
|
|
413
|
+
json.encodeToString(ops.backgroundCancel(json.decodeRequest<Empty>(payload)))
|
|
414
|
+
BridgeOp.BODY_HEART_RATE ->
|
|
415
|
+
json.encodeToString(ops.bodyHeartRate(json.decodeRequest<RangeRequest>(payload)))
|
|
416
|
+
BridgeOp.BODY_RESTING_HEART_RATE ->
|
|
417
|
+
json.encodeToString(ops.bodyRestingHeartRate(json.decodeRequest<RangeRequest>(payload)))
|
|
418
|
+
BridgeOp.BODY_READINGS ->
|
|
419
|
+
json.encodeToString(ops.bodyReadings(json.decodeRequest<RangeRequest>(payload)))
|
|
420
|
+
BridgeOp.BODY_LOG_HEART_RATE ->
|
|
421
|
+
json.encodeToString(ops.bodyLogHeartRate(json.decodeRequest<HeartRateEntryList>(payload)))
|
|
422
|
+
BridgeOp.BODY_LOG ->
|
|
423
|
+
json.encodeToString(ops.bodyLog(json.decodeRequest<BodyEntryList>(payload)))
|
|
424
|
+
BridgeOp.BODY_DELETE ->
|
|
425
|
+
json.encodeToString(ops.bodyDelete(json.decodeRequest<RecordIdRequest>(payload)))
|
|
426
|
+
BridgeOp.DAILY_STEPS ->
|
|
427
|
+
json.encodeToString(ops.dailySteps(json.decodeRequest<RangeRequest>(payload)))
|
|
428
|
+
BridgeOp.DAILY_TOTALS ->
|
|
429
|
+
json.encodeToString(ops.dailyTotals(json.decodeRequest<RangeRequest>(payload)))
|
|
430
|
+
BridgeOp.DAILY_INTRADAY ->
|
|
431
|
+
json.encodeToString(ops.dailyIntraday(json.decodeRequest<RangeRequest>(payload)))
|
|
432
|
+
BridgeOp.DAILY_LOG ->
|
|
433
|
+
json.encodeToString(ops.dailyLog(json.decodeRequest<StepEntryList>(payload)))
|
|
434
|
+
BridgeOp.DAILY_DELETE ->
|
|
435
|
+
json.encodeToString(ops.dailyDelete(json.decodeRequest<RecordIdRequest>(payload)))
|
|
436
|
+
BridgeOp.SLEEP_SEGMENTS ->
|
|
437
|
+
json.encodeToString(ops.sleepSegments(json.decodeRequest<RangeRequest>(payload)))
|
|
438
|
+
BridgeOp.SLEEP_LOG ->
|
|
439
|
+
json.encodeToString(ops.sleepLog(json.decodeRequest<SleepEntryList>(payload)))
|
|
440
|
+
BridgeOp.SLEEP_DELETE ->
|
|
441
|
+
json.encodeToString(ops.sleepDelete(json.decodeRequest<RecordIdRequest>(payload)))
|
|
442
|
+
BridgeOp.ACTIVITY_WORKOUTS ->
|
|
443
|
+
json.encodeToString(ops.activityWorkouts(json.decodeRequest<RangeRequest>(payload)))
|
|
444
|
+
BridgeOp.ACTIVITY_ROUTES ->
|
|
445
|
+
json.encodeToString(ops.activityRoutes(json.decodeRequest<RangeRequest>(payload)))
|
|
446
|
+
BridgeOp.ACTIVITY_INTENSITY ->
|
|
447
|
+
json.encodeToString(ops.activityIntensity(json.decodeRequest<RangeRequest>(payload)))
|
|
448
|
+
BridgeOp.ACTIVITY_LOG ->
|
|
449
|
+
json.encodeToString(ops.activityLog(json.decodeRequest<WorkoutEntryList>(payload)))
|
|
450
|
+
BridgeOp.ACTIVITY_DELETE ->
|
|
451
|
+
json.encodeToString(ops.activityDelete(json.decodeRequest<RecordIdRequest>(payload)))
|
|
452
|
+
BridgeOp.NUTRITION_QUANTITIES ->
|
|
453
|
+
json.encodeToString(ops.nutritionQuantities(json.decodeRequest<RangeRequest>(payload)))
|
|
454
|
+
BridgeOp.NUTRITION_LOG ->
|
|
455
|
+
json.encodeToString(ops.nutritionLog(json.decodeRequest<NutritionEntryList>(payload)))
|
|
456
|
+
BridgeOp.NUTRITION_DELETE ->
|
|
457
|
+
json.encodeToString(ops.nutritionDelete(json.decodeRequest<RecordIdRequest>(payload)))
|
|
458
|
+
BridgeOp.NUTRITION_HYDRATION ->
|
|
459
|
+
json.encodeToString(ops.nutritionHydration(json.decodeRequest<RangeRequest>(payload)))
|
|
460
|
+
BridgeOp.MENSTRUATION_FLOW ->
|
|
461
|
+
json.encodeToString(ops.menstruationFlow(json.decodeRequest<RangeRequest>(payload)))
|
|
462
|
+
BridgeOp.MENSTRUATION_EVENTS ->
|
|
463
|
+
json.encodeToString(ops.menstruationEvents(json.decodeRequest<RangeRequest>(payload)))
|
|
464
|
+
BridgeOp.ATHLETE_PROFILE -> throw BridgeError.UnsupportedOnPlatform(known.wire)
|
|
465
|
+
BridgeOp.PLANNED_WORKOUTS_PLANNED -> throw BridgeError.UnsupportedOnPlatform(known.wire)
|
|
466
|
+
BridgeOp.PLANNED_WORKOUTS_MARK_COMPLETE -> throw BridgeError.UnsupportedOnPlatform(known.wire)
|
|
467
|
+
BridgeOp.PLANNED_WORKOUTS_DELETE -> throw BridgeError.UnsupportedOnPlatform(known.wire)
|
|
468
|
+
BridgeOp.PLANNED_WORKOUTS_SCHEDULING_STATUS -> throw BridgeError.UnsupportedOnPlatform(known.wire)
|
|
469
|
+
BridgeOp.PLANNED_WORKOUTS_REQUEST_SCHEDULING -> throw BridgeError.UnsupportedOnPlatform(known.wire)
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
public companion object {
|
|
474
|
+
/**
|
|
475
|
+
* encodeDefaults is on because a DTO field left at its default still has to reach the
|
|
476
|
+
* other side: a response that omitted `userEntered: false` would decode as absent there.
|
|
477
|
+
*/
|
|
478
|
+
private val PATH: Regex = Regex("""at path: (\$\S*)""")
|
|
479
|
+
|
|
480
|
+
/** `$.entries[2].start`, or `$` when the failure names no path. */
|
|
481
|
+
public fun pathOf(error: Throwable): String = error.message?.let { PATH.find(it)?.groupValues?.get(1) } ?: "$"
|
|
482
|
+
|
|
483
|
+
public val defaultJson: Json = Json {
|
|
484
|
+
encodeDefaults = true
|
|
485
|
+
explicitNulls = false
|
|
486
|
+
ignoreUnknownKeys = true
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/** Every stream the schema declares, including the ones this platform does not publish. */
|
|
492
|
+
public enum class BridgeStream(public val wire: String, public val isSupported: Boolean) {
|
|
493
|
+
DIAGNOSTICS_EVENTS("diagnostics.events", true),
|
|
494
|
+
BODY_CHANGES("body.changes", true),
|
|
495
|
+
DAILY_CHANGES("daily.changes", true),
|
|
496
|
+
SLEEP_CHANGES("sleep.changes", true),
|
|
497
|
+
ACTIVITY_CHANGES("activity.changes", true),
|
|
498
|
+
BRIDGE_TOKEN_REQUEST("bridge.tokenRequest", true),
|
|
499
|
+
;
|
|
500
|
+
|
|
501
|
+
public companion object {
|
|
502
|
+
private val byWire: Map<String, BridgeStream> = entries.associateBy { it.wire }
|
|
503
|
+
|
|
504
|
+
public fun of(wire: String): BridgeStream? = byWire[wire]
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* What P2 publishes. One function per stream this platform supports, each a fresh flow per
|
|
510
|
+
* call: a subscriber that goes away cancels its own collection and nobody else's.
|
|
511
|
+
*/
|
|
512
|
+
public interface TerraBridgeStreams {
|
|
513
|
+
/**
|
|
514
|
+
* Every subscriber gets its own native stream, matching the SDK's fresh-stream-per-call
|
|
515
|
+
* contract.
|
|
516
|
+
*/
|
|
517
|
+
public fun diagnosticsEvents(params: Empty): Flow<TerraEvent>
|
|
518
|
+
public fun bodyChanges(params: Empty): Flow<HealthChange>
|
|
519
|
+
public fun dailyChanges(params: Empty): Flow<HealthChange>
|
|
520
|
+
public fun sleepChanges(params: Empty): Flow<HealthChange>
|
|
521
|
+
public fun activityChanges(params: Empty): Flow<HealthChange>
|
|
522
|
+
/** Bridge-owned. The broker's ask, not an SDK stream. */
|
|
523
|
+
public fun bridgeTokenRequest(params: Empty): Flow<TokenRequest>
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
public class BridgeStreamDispatch(
|
|
527
|
+
private val streams: TerraBridgeStreams,
|
|
528
|
+
private val errorPayload: (Throwable) -> TerraErrorPayload,
|
|
529
|
+
private val json: Json = BridgeDispatch.defaultJson,
|
|
530
|
+
) {
|
|
531
|
+
public fun open(stream: String, params: String): Flow<String> {
|
|
532
|
+
val known = BridgeStream.of(stream) ?: throw BridgeError.UnsupportedOnPlatform(stream)
|
|
533
|
+
val messages = when (known) {
|
|
534
|
+
BridgeStream.DIAGNOSTICS_EVENTS -> {
|
|
535
|
+
val request = json.decodeRequest<Empty>(params)
|
|
536
|
+
flow { emitAll(streams.diagnosticsEvents(request)) }.map { element ->
|
|
537
|
+
TerraEventEnvelope.validate(element)
|
|
538
|
+
frame(serializer<TerraEvent>(), element)
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
BridgeStream.BODY_CHANGES -> {
|
|
542
|
+
val request = json.decodeRequest<Empty>(params)
|
|
543
|
+
flow { emitAll(streams.bodyChanges(request)) }.map { element ->
|
|
544
|
+
frame(serializer<HealthChange>(), element)
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
BridgeStream.DAILY_CHANGES -> {
|
|
548
|
+
val request = json.decodeRequest<Empty>(params)
|
|
549
|
+
flow { emitAll(streams.dailyChanges(request)) }.map { element ->
|
|
550
|
+
frame(serializer<HealthChange>(), element)
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
BridgeStream.SLEEP_CHANGES -> {
|
|
554
|
+
val request = json.decodeRequest<Empty>(params)
|
|
555
|
+
flow { emitAll(streams.sleepChanges(request)) }.map { element ->
|
|
556
|
+
frame(serializer<HealthChange>(), element)
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
BridgeStream.ACTIVITY_CHANGES -> {
|
|
560
|
+
val request = json.decodeRequest<Empty>(params)
|
|
561
|
+
flow { emitAll(streams.activityChanges(request)) }.map { element ->
|
|
562
|
+
frame(serializer<HealthChange>(), element)
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
BridgeStream.BRIDGE_TOKEN_REQUEST -> {
|
|
566
|
+
val request = json.decodeRequest<Empty>(params)
|
|
567
|
+
flow { emitAll(streams.bridgeTokenRequest(request)) }.map { element ->
|
|
568
|
+
frame(serializer<TokenRequest>(), element)
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
return messages
|
|
573
|
+
.onCompletion { cause -> if (cause == null) emit(encode(StreamMessage.Complete)) }
|
|
574
|
+
.catch { failure -> emit(encode(StreamMessage.Error(error = errorPayload(failure)))) }
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
private fun <T> frame(serializer: KSerializer<T>, element: T): String =
|
|
578
|
+
encode(StreamMessage.Data(element = json.encodeToJsonElement(serializer, element).jsonObject))
|
|
579
|
+
|
|
580
|
+
private fun encode(message: StreamMessage): String = json.encodeToString(StreamMessage.serializer(), message)
|
|
581
|
+
}
|
|
582
|
+
|