From 436e165679892c41435cd514d06780026ef9b90e Mon Sep 17 00:00:00 2001 From: beetzung Date: Mon, 2 Mar 2026 19:22:03 +0100 Subject: [PATCH] Point SDK GraphQL to api-app and switch map markers query to getMarkers. This aligns the app with the new backend endpoint/schema and keeps map marker mapping compatible with the new response shape. Made-with: Cursor --- app/src/main/graphql/schema.graphqls | 1248 +++++++++++++---- sdk/build.gradle.kts | 6 + sdk/src/main/graphql/MapLocations.graphql | 11 +- .../kotlin/org/db3/airmq/sdk/di/SDKModule.kt | 24 +- .../db3/airmq/sdk/map/ApolloMapItemMapper.kt | 21 +- .../org/db3/airmq/sdk/map/MapServiceImpl.kt | 16 +- 6 files changed, 1031 insertions(+), 295 deletions(-) diff --git a/app/src/main/graphql/schema.graphqls b/app/src/main/graphql/schema.graphqls index a7ac540..80fcaad 100644 --- a/app/src/main/graphql/schema.graphqls +++ b/app/src/main/graphql/schema.graphqls @@ -1,80 +1,31 @@ -""" -A custom scalar that returns date and time as JavaScript Date object -""" -scalar Date +type Widget { + lName: String -""" -A custom scalar that returns data as a generic JavaScript Object. Data will be returned -as a String, when data is published as a simple string. -""" -scalar Object + dpHeight: Int -enum Role { - visitor + widgetId: Int - apiUser + lat: Float - admin -} + lon: Float -""" -Location -""" -type Location { - """ - Location ID - """ - _id: String! + position: Int! - """ - City name in English - """ - city: String + type: WidgetType! - """ - Location name - """ - name: String! + whatToShow: String - """ - Location online state - """ - isOnline: Boolean + zoom: Float - """ - Location latitude - """ - latitude: Float! + deviceId: String - """ - Location longitude - """ - longitude: Float! + locationId: String - """ - Location telemetry info - """ - status: Status + filter: String - """ - Location metric list - """ - metricList: [String]! + sensor: String - """ - Last sensor value set - """ - currentValue(filter: TimeSpan): SensorData - - """ - Sensor values for certain time span - """ - timeSeries(filter: TimeSpan): [SensorData] - - """ - Sensor values to export for certain time span - """ - timeSeries_export(filter: TimeSpan): [SensorData] + active: Boolean } """ @@ -83,21 +34,230 @@ The `String` scalar type represents textual data, represented as UTF-8 character scalar String """ -The `Boolean` scalar type represents `true` or `false`. +The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1. """ -scalar Boolean +scalar Int """ The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point). """ scalar Float -type Status { +""" +The `Boolean` scalar type represents `true` or `false`. +""" +scalar Boolean + +input AddWidgetInput { _id: String! - RSSI: Int + wgt: WidgetInput +} - channel: Int +input MoveWidgetInput { + _id: String + + widgetId: Int + + updown: Upndown +} + +input RemoveWidgetInput { + _id: String + + widgetId: Int +} + +input WidgetInput { + widgetId: Int + + lat: Float + + lon: Float + + position: Int + + type: WidgetType! + + whatToShow: String + + zoom: Float + + deviceId: String + + locationId: String + + filter: TimeFilter + + sensor: Sensor +} + +""" +ENUMs +""" +enum Upndown { + UP + + DOWN +} + +enum WidgetType { + CHART + + MAP + + NEWS +} + +type ActionResult { + success: Boolean + + WiFiSSID: String + + WiFiPassword: String +} + +type APIToken { + token: String + + tokenName: String + + issDate: String + + expDate: String +} + +type revokeResponse { + success: Boolean +} + +input newTokenInput { + tokenName: String! + + validityDays: Int = 180 +} + +input oldTokenInput { + tokenName: String +} + +type Device { + _id: String + + """ + model: Int + """ + model: Model + + Sensorcom: Boolean + + Narodmon: Boolean + + sleepEnabled: Boolean + + laserSave: Boolean + + maintenance: Boolean + + testMode: Int + + isUpdated: Boolean + + isCalibrated: Boolean + + mfgDate: ISODate + + addedBy: String + + inventory: Inventory! + + calibration: Calibration + + locationId: String + + status: Status + + location: Location +} + +type Inventory { + HWVer: String + + HTP: String + + GC: String + + PMS: String + + Gas: String + + Ant: String + + RTC: String + + PWRType: String + + LED: Boolean + + configVersion: String! + + firmwareVersion: String + + hardwareVersion: String + + secondaryWiFiSSID: String +} + +type Calibration { + VCC: CalObject + + Temp: CalObject + + Hum: CalObject + + Press: CalObject + + PMS1: CalObject + + PMS25: CalObject + + PMS10: CalObject + + Count: CalObject + + session: CalSession + + report: CalReport +} + +type CalObject { + A: Float + + B: Float +} + +type CalReport { + startTime: ISODate +} + +type CalSession { + _id: String + + startTime: ISODate + + stopTime: ISODate + + deviceList: [String] + + startedBy: String + + refDevice: String +} + +type Status { + _id: String + + RSSI: Int build: String @@ -109,128 +269,440 @@ type Status { isOnline: Boolean + lastOnline: ISODate + + maintenance: Boolean + start: String + stop: String + uptime: Int + + LEDOn: Boolean } -""" -The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1. -""" -scalar Int - -""" - Sensor values -""" -type SensorData { - """ - Time in ISO 8601 format, UTC time - """ - time: DateTime! - - """ - Primary temperature, °C - """ - Temp: Float - - """ - Secondary temperature, °C - """ - Temp1: Float - - """ - Water temperature, °C - """ - wTemp: Float - - """ - Relative humidity, % - """ - Hum: Float - - """ - Air pressure, Pa - """ - Press: Float - - """ - PM1 particulate matter, μg/m³ - """ - PMS1: Float - - """ - PM2.5 particulate matter, μg/m³ - """ - PMS25: Float - - """ - PM10 particulate matter, μg/m³ - """ - PMS10: Float - - """ - Ambient radioactivity, μSv/h - """ - Count: Float - - """ - NOx index - """ - NOx: Float - - """ - VOC index - """ - VOC: Float - - """ - AQI index - """ - AQI: Int - - """ - IKAV index - """ - IKAV: Float - - """ - Water level - """ - WaterLevel: Float - - """ - Solar irradiance - """ - SolRad: Float - - """ - Illumination level - """ - Light: Float -} - -type Telemetry { - deviceId: String - - time: String! - - IP: String - - VCC: String - - RSSI: String - - Uptime: String -} - -type AuthResponse { - token: String +type Model { + _id: Int name: String - email: String + PWRType: String - id: String + sensors: [String] + + telemetry: [String] +} + +input DeviceFilter { + _id: String + + model: String + + locationId: String + + testMode: Boolean + + isCalibrated: Boolean + + sleepEnabled: Boolean +} + +input NewDevice { + id: String! + + firmwareVersion: String + + hardwareVersion: String + + model: Int + + HTP: String + + PMS: String + + Gas: String + + GC: String + + LED: Boolean + + Ant: String +} + +input NewDevices { + id: [String]! + + firmwareVersion: String + + hardwareVersion: String + + model: Int + + HTP: String + + PMS: String + + Gas: String + + GC: String + + LED: Boolean + + Ant: String +} + +input EditDevice { + id: String! + + firmwareVersion: String + + LED: Boolean + + Ant: String +} + +input CalStartInput { + ids: [String]! + + refDevice: String +} + +input CalStopInput { + sessId: String! +} + +input NewCustomDevice { + id: String! + + firmwareVersion: String + + hardwareVersion: String + + model: Int + + HTP: String + + PMS: String + + GC: String +} + +input DeviceInput { + _id: String + + model: Int + + Sensorcom: Boolean + + Narodmon: Boolean + + sleepEnabled: Boolean + + laserSave: Boolean + + maintenance: Boolean + + mfgDate: String + + testing: Boolean + + inventory: InventoryInput +} + +input InventoryInput { + HWVer: String + + HTP: String + + GC: String + + PMS: String + + Ant: String + + RTC: String + + PWRType: String + + configVersion: Int + + firmwareVersion: String +} + +input AddCal { + locationId: String! + + time: ISODate + + reset: Boolean + + extAPI: Boolean + + metric: Metric + + value: Float +} + +input SendAction { + deviceId: String! + + doReboot: Boolean + + LEDOn: Boolean + + setSecondaryWiFi: Creds + + maintenance: Boolean + + fwUpgrade: Boolean + + rebootFlag: Boolean + + OTAFlag: Boolean + + preferredGW: Int + + interval: Int +} + +input Creds { + WiFiSSID: String + + WiFiPassword: String +} + +enum Sensor { + DUST + + TEMPERATURE + + HUMIDITY + + PRESSURE + + RADIOACTIVITY +} + +enum Metric { + Temp + + Hum +} + +""" +A custom scalar that returns time data as JavaScript Date object +""" +scalar Date + +""" +A custom scalar that returns data as a generic JavaScript Object. Data will be returned +as a String, when data is published as a simple string. +""" +scalar Object + +scalar ISODate + +type Query { + """ + deviceList: [Device] @hasScope(scopes: ["Device:view"]) + """ + deviceList: [Location] + + devices(filter: DeviceFilter): [Device] + + getUncalibratedDevices: [Device] + + listCalibrationSessions: [CalSession] + + locations(filter: LocationFilter): [Location] + + """ + locations(filter: LocationFilter): [Location] @hasRole(roles:[visitor, appUser, admin]) + users(filter: UserInput): [User] @hasScope(scopes: ["User:view"]) + """ + users(filter: UserInput): [User] + + """ + me: Me @hasScope(scopes: ["Me:view"]) + me: Me @hasRole(roles:[visitor, appUser, webUser, admin]) + """ + me: User + + widgetList(input: UserInput): [Widget] + + """ + metricList: [Metric] + """ + cityList(langId: String): [City] + + sensorData(filter: LocationFilter): SensorData + + sensorDataRaw(filter: LocationFilter): SensorData + + cityAverageLast(filter: LastFilter): SensorData + + cityAverage(filter: MeanFilter): [SensorData] + + getPlace(filter: PlaceFilter): City + + getMarkers(filter: MarkerFilter): [Marker] + + checkSetupSuccess(deviceId: String): Device +} + +type Mutation { + sayHello(name: String!): String! + + initDevice(input: NewDevice): Device + + initDevices(input: NewDevices): [Device] + + editDevice(input: EditDevice): Device + + startCalibration(input: CalStartInput): CalSession + + stopCalibration(input: CalStopInput): CalSession + + setRefDevice(input: RefDevice): User + + addCustomDevice(input: NewCustomDevice): Device + + initLocation(input: NewLocation): Location + + addLocation(input: LocationInput): Location + + addFBLocation(input: LocationInput): Location + + addWebLocation(input: WebLocation): Location + + changeWebLocation(input: ChgLocation): Location + + changeLocation(input: ChgLocation): Location + + archiveLocation(input: inactivateLocation): Location + + """ + transferLocation(input: TransferLocation): Location @hasScope(scopes: ["Location:create"]) + """ + addUser(input: UserInput): User + + updateUser(input: UserInput): User + + setLastReadNews(_id: String): User + + addWidget(input: AddWidgetInput): [Widget] + + moveWidget(input: MoveWidgetInput): [Widget] + + removeWidget(input: RemoveWidgetInput): [Widget] + + authFacebook(input: AuthInput!): AuthResponse + + """ + authGoogle(input: AuthInput!): AuthResponse + """ + authGoogle(input: AuthInput!): User + + authGoogleApp(input: AuthInput!): AuthResponse + + authGoogleWeb(input: AuthInput!): AuthResponse + + authGoogleNew(input: AuthInput!): AuthResponse + + authGoogleFB(input: AuthInput!): AuthResponse + + updateNotificationToken(token: String!): User + + generateAPIToken(input: newTokenInput): APIToken + + """ + revokeAPIToken(input: oldTokenInput): revokeResponse @hasScope(scopes: ["Device:create"]) + """ + revokeAPIToken: revokeResponse + + setCalibration(input: AddCal): Device + + setCal(input: AddCal): Device + + deviceAction(input: SendAction): ActionResult +} + +type Subscription { + onLocationAdded: Location + + onLocationChanged(input: LocInput): Location +} + +type Location { + _id: String! + + """ + locationId: String! + """ + city: String! + + name: String! + + deviceId: String! + + initDate: ISODate! + + isPublic: Boolean! + + isPrivate: Boolean + + ownerId: String! + + elevation: Int + + latitude: Float + + longitude: Float + + status: Status + + Sensorcom: Boolean + + Narodmon: Boolean + + device: Device! + + user: User + + inactive: Boolean + + inactivationDate: ISODate + + sensorvalues_raw(input: TimeSpan): [SensorData] + + sensorvalues(input: TimeSpan): [SensorData] + + meanvalues_raw(input: TimeSpan): [SensorData] + + meanvalues(input: TimeSpan): [SensorData] + + mean_last_raw(input: TimeSpan): [SensorData] + + mean_last(input: TimeSpan): [SensorData] + + mean_city_raw(input: TimeSpan): [SensorData] + + mean_city(input: TimeSpan): [SensorData] + + telemetry(input: TimeSpan): [Telemetry] + + sensorList: [String] + + metricList: [String] + + externalWeatherData: SensorData +} + +type LatLng { + lat: Float + + lng: Float } type City { @@ -253,10 +725,6 @@ type CityName { ru: String en: String - - ge: String - - pl: String } type Marker { @@ -277,8 +745,6 @@ type Marker { owned: Boolean - metricList: [String] - value: Float values: [SensorData] @@ -287,19 +753,211 @@ type Marker { input LocationFilter { _id: String - name: String -} + locationId: String -input LocationsFilter { - _ids: [String] + deviceId: String city: String + cityId: String + + name: String + + ownerId: String + + isOnline: Boolean + + isPublic: Boolean + + inactive: Boolean + + device: DeviceInput +} + +input PlaceFilter { + latitude: Float + + longitude: Float + + lang: String = "ru" +} + +input MarkerFilter { + mapLayer: String +} + +input NewLocation { + deviceId: String! + + name: String! + + ownerId: String! +} + +input RegLocation { + deviceId: String! + + name: String + + """ + ownerId: String! + """ + elevation: Int + + latitude: Float + + longitude: Float +} + +input WebLocation { + deviceId: String! + + name: String! + + isPublic: Boolean + + elevation: Int + + latitude: Float + + longitude: Float +} + +input ChgLocation { + _id: String! + + name: String + + elevation: Int + + latitude: Float + + longitude: Float + + Sensorcom: Boolean + + Narodmon: Boolean +} + +input inactivateLocation { + _id: String! +} + +input LocationInput { + _id: String + + deviceId: String + + initDate: ISODate + + isPublic: Boolean + + name: String + + ownerId: String + + latitude: Float + + longitude: Float + + elevation: Int + + device: DeviceInput +} + +input LocInput { + _id: String +} + +enum MapLayers { + AQI + + PM1 + + PM25 + + PM10 + + Temp + + Hum + + Press + + Radio +} + +type SensorData { + deviceId: String + + time: ISODate + + Temp: Float + + Hum: Float + + Press: Float + + PMS1: Float + + PMS25: Float + + PMS10: Float + + PPM: Float + + eCO2: Float + + VOC: Float + + Count: Float + + AQI: Int + + IKAV: Float +} + +type Telemetry { + deviceId: String + + time: ISODate + + """ + IP: String + """ + VCC: Float + + RSSI: Int + + Uptime: Int +} + +input LastFilter { cityName: String cityId: String - isOnline: Boolean + interval_d: Int = 0 + + interval_h: Int = 0 + + interval_m: Int = 15 +} + +input MeanFilter { + cityName: String + + cityId: String + + t_from: String + + t_to: String + + interval_d: Int = 0 + + interval_h: Int = 0 + + interval_m: Int = 0 } input TimeSpan { @@ -314,54 +972,6 @@ input TimeSpan { interval_m: Int = 0 } -input LastFilter { - city: String - - interval_d: Int = 0 - - interval_h: Int = 0 - - interval_m: Int = 15 -} - -input MeanFilter { - city: String - - t_from: String! - - t_to: String! - - interval_d: Int = 0 - - interval_h: Int = 0 - - interval_m: Int = 15 -} - -input AuthInput { - uid: String - - accessToken: String! -} - -input LocInput { - _id: String -} - -enum Sensor { - DUST - - TEMPERATURE - - HUMIDITY - - PRESSURE - - RADIOACTIVITY - - WATERLEVEL -} - enum TimeFilter { HOUR @@ -372,36 +982,127 @@ enum TimeFilter { MONTH } -input MarkerFilter { - city: String +type User { + _id: String - isOnline: Boolean + uid: String @deprecated(reason: "Used no more") - mapLayer: String + email: String! + + lastReadNews: Int + + name: String + + locale: String + + regDate: ISODate + + authSource: String + + googleId: String + + roles: [String] + + picture: String @deprecated(reason: "Use photoURL instead.") + + photoURL: String + + preferredCity: City + + APItokens: [APItoken] + + notificationToken: String + + widgets: [Widget] + + locations: [Location] + + devices: [Device] + + adminProperties: AdminProperties } -type Query { - location(filter: LocationFilter): Location +type AdminProperties { + country: [String] - locations(filter: LocationsFilter): [Location] - - myLocation: Location - - myLocations: [Location] - - cityList(countryCode: String): [City] - - cityAverage(filter: LastFilter): SensorData - - cityAverages(filter: MeanFilter): [SensorData] - - getMarkers(filter: MarkerFilter): [Marker] + refDevice: [String] } -""" -A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the `date-time` format outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar. -""" -scalar DateTime +type Me { + name: String + + email: String + + expiryDate: ISODate + + apiRoles: [String] + + apiScopes: [String] +} + +type AuthResponse { + token: String + + name: String + + email: String + + roles: [String] + + _id: String + + regDate: String +} + +type APItoken { + issDate: ISODate + + expDate: ISODate + + token: String + + tokenName: String + + _id: String +} + +input AuthInput { + accessToken: String! +} + +input UserInput { + _id: String + + uid: String + + email: String + + lastReadNews: Int + + name: String + + token: String + + locale: String +} + +input RefDevice { + _id: String +} + +enum Role { + visitor + + appUser + + apiUser + + webUser + + areaAdmin + + admin +} """ A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations. @@ -680,10 +1381,25 @@ directive @hasRole(roles: [Role]) on OBJECT | FIELD_DEFINITION directive @isAuthenticated on OBJECT | FIELD_DEFINITION +""" directive @isHidden on OBJECT | FIELD_DEFINITION | ENUM - +""" directive @undocumented on OBJECT | FIELD_DEFINITION | ENUM +""" +The directive instructs the API to implement the runtime administration behavior +should the query or mutation support a particular administrative behavior, +otherwise, it's ignored +""" +directive @isAdmin on FIELD + +""" +The directive is applied at design time to fields that +require that the caller has permissions to view data that +is deemed to be PERSONAL in scope, for example email addresses. +""" +directive @requiresPersonalScope on FIELD_DEFINITION + """ Directs the executor to include this field or fragment only when the `if` argument is true. """ @@ -706,4 +1422,6 @@ directive @specifiedBy("The URL that specifies the behaviour of this scalar." ur schema { query: Query + mutation: Mutation + subscription: Subscription } diff --git a/sdk/build.gradle.kts b/sdk/build.gradle.kts index 3fc883e..e85168d 100644 --- a/sdk/build.gradle.kts +++ b/sdk/build.gradle.kts @@ -39,6 +39,10 @@ apollo { service("service") { packageName.set("org.db3.airmq.sdk") schemaFiles.from(file("../app/src/main/graphql/schema.graphqls")) + introspection { + endpointUrl.set("https://api-app.airmq.cc/graphql") + schemaFile.set(file("../app/src/main/graphql/schema.graphqls")) + } } } @@ -56,4 +60,6 @@ dependencies { // Hilt implementation(libs.hilt.android) ksp(libs.hilt.compiler) + + testImplementation(libs.junit) } diff --git a/sdk/src/main/graphql/MapLocations.graphql b/sdk/src/main/graphql/MapLocations.graphql index 7bb00c9..35600e7 100644 --- a/sdk/src/main/graphql/MapLocations.graphql +++ b/sdk/src/main/graphql/MapLocations.graphql @@ -1,13 +1,12 @@ -query MapMarkers($isOnline: Boolean) { - locations(filter: { isOnline: $isOnline }) { +query MapMarkers { + getMarkers { _id name - city - isOnline + text latitude longitude - metricList - currentValue { + value + values { PMS25 Count } diff --git a/sdk/src/main/kotlin/org/db3/airmq/sdk/di/SDKModule.kt b/sdk/src/main/kotlin/org/db3/airmq/sdk/di/SDKModule.kt index 0dae910..23c26de 100644 --- a/sdk/src/main/kotlin/org/db3/airmq/sdk/di/SDKModule.kt +++ b/sdk/src/main/kotlin/org/db3/airmq/sdk/di/SDKModule.kt @@ -8,24 +8,32 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import javax.inject.Singleton +import org.db3.airmq.sdk.auth.ApiTokenStore import org.db3.airmq.sdk.auth.AuthService +import org.db3.airmq.sdk.auth.BackendAuthGateway +import org.db3.airmq.sdk.auth.BackendAuthGatewayImpl import org.db3.airmq.sdk.auth.FirebaseAuthService +import org.db3.airmq.sdk.auth.FirebaseSessionManager +import org.db3.airmq.sdk.auth.FirebaseSessionManagerImpl +import org.db3.airmq.sdk.auth.SharedPreferencesApiTokenStore import org.db3.airmq.sdk.map.MapServiceImpl import org.db3.airmq.sdk.map.MapService import org.db3.airmq.sdk.settings.SettingsService import org.db3.airmq.sdk.settings.SettingsServiceImpl +import org.db3.airmq.sdk.network.ApolloAuthInterceptor import org.db3.airmq.sdk.network.ApolloLoggingInterceptor @Module @InstallIn(SingletonComponent::class) object SDKModule { - private const val API_URL = "https://api.airmq.cc" + private const val API_URL = "https://api-app.airmq.cc/" @Provides @Singleton - fun provideApolloClient(): ApolloClient { + fun provideApolloClient(apiTokenStore: ApiTokenStore): ApolloClient { return ApolloClient.Builder() .serverUrl(API_URL) + .addInterceptor(ApolloAuthInterceptor(apiTokenStore)) .addInterceptor(ApolloLoggingInterceptor()) .build() } @@ -42,6 +50,18 @@ abstract class SDKBindModule { @Singleton abstract fun bindAuthService(impl: FirebaseAuthService): AuthService + @Binds + @Singleton + abstract fun bindApiTokenStore(impl: SharedPreferencesApiTokenStore): ApiTokenStore + + @Binds + @Singleton + abstract fun bindFirebaseSessionManager(impl: FirebaseSessionManagerImpl): FirebaseSessionManager + + @Binds + @Singleton + abstract fun bindBackendAuthGateway(impl: BackendAuthGatewayImpl): BackendAuthGateway + @Binds @Singleton abstract fun bindMapService(impl: MapServiceImpl): MapService diff --git a/sdk/src/main/kotlin/org/db3/airmq/sdk/map/ApolloMapItemMapper.kt b/sdk/src/main/kotlin/org/db3/airmq/sdk/map/ApolloMapItemMapper.kt index 72259a2..a6c3e8c 100644 --- a/sdk/src/main/kotlin/org/db3/airmq/sdk/map/ApolloMapItemMapper.kt +++ b/sdk/src/main/kotlin/org/db3/airmq/sdk/map/ApolloMapItemMapper.kt @@ -5,17 +5,20 @@ import org.db3.airmq.sdk.MapMarkersQuery import org.db3.airmq.sdk.map.domain.MapItem class ApolloMapItemMapper @Inject constructor() { - fun toDomain(location: MapMarkersQuery.Location, index: Int): MapItem? { - val id = location._id.ifBlank { "location-$index" } + fun toDomain(marker: MapMarkersQuery.GetMarker, index: Int): MapItem? { + val latitude = marker.latitude ?: return null + val longitude = marker.longitude ?: return null + val id = marker._id?.ifBlank { "marker-$index" } ?: "marker-$index" + val latestValues = marker.values?.firstOrNull() return MapItem( id = id, - title = location.name.ifBlank { id }, - city = location.city?.ifBlank { null }, - latitude = location.latitude, - longitude = location.longitude, - isOnline = location.isOnline ?: false, - dustValue = location.currentValue?.PMS25?.toDouble(), - radioactivityValue = location.currentValue?.Count?.toDouble() + title = marker.name?.ifBlank { id } ?: id, + city = marker.text?.ifBlank { null }, + latitude = latitude, + longitude = longitude, + isOnline = marker.value != null, + dustValue = latestValues?.PMS25?.toDouble() ?: marker.value?.toDouble(), + radioactivityValue = latestValues?.Count?.toDouble() ) } } diff --git a/sdk/src/main/kotlin/org/db3/airmq/sdk/map/MapServiceImpl.kt b/sdk/src/main/kotlin/org/db3/airmq/sdk/map/MapServiceImpl.kt index 9ace738..18d8859 100644 --- a/sdk/src/main/kotlin/org/db3/airmq/sdk/map/MapServiceImpl.kt +++ b/sdk/src/main/kotlin/org/db3/airmq/sdk/map/MapServiceImpl.kt @@ -2,7 +2,6 @@ package org.db3.airmq.sdk.map import android.util.Log import com.apollographql.apollo.ApolloClient -import com.apollographql.apollo.api.Optional import javax.inject.Inject import org.db3.airmq.sdk.MapMarkersQuery import org.db3.airmq.sdk.map.domain.MapItem @@ -13,17 +12,8 @@ class MapServiceImpl @Inject constructor( ) : MapService { override suspend fun fetchMapItems(showOfflineDevices: Boolean): List { Log.d(TAG, "Executing MapMarkers Apollo query") - val isOnlineFilter: Optional = if (showOfflineDevices) { - Optional.Absent - } else { - Optional.Present(true) - } val response = apolloClient - .query( - MapMarkersQuery( - isOnline = isOnlineFilter - ) - ) + .query(MapMarkersQuery()) .execute() response.errors?.firstOrNull()?.let { gqlError -> @@ -31,10 +21,10 @@ class MapServiceImpl @Inject constructor( throw IllegalStateException(gqlError.message) } - val mappedItems = response.data?.locations + val mappedItems = response.data?.getMarkers .orEmpty() .filterNotNull() - .mapIndexedNotNull { index, location -> mapper.toDomain(location, index) } + .mapIndexedNotNull { index, marker -> mapper.toDomain(marker, index) } Log.d(TAG, "MapMarkers Apollo query returned ${mappedItems.size} items") return mappedItems }