Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update DataObjects and API Versions #173

Merged
merged 5 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 4 additions & 39 deletions APIClient/APIClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ final class APIClient: ObservableObject {
/// The end of the currently displayed time window. If nil, defaults to date()
@Published var timeWindowEnd: Date?

@Published var user: DTOv1.UserDTO?
@Published var user: UserInfoDTO?
@Published var userNotLoggedIn: Bool = true
@Published var userLoginFailed: Bool = false
var userLoginErrorMessage: String?
Expand Down Expand Up @@ -197,12 +197,12 @@ extension APIClient {
}
}

func getUserInformation(callback: ((Result<DTOv1.UserDTO, TransferError>) -> Void)? = nil) {
func getUserInformation(callback: ((Result<UserInfoDTO, TransferError>) -> Void)? = nil) {
userLoginFailed = false

let url = urlForPath("users", "me")
let url = urlForPath(apiVersion: .v3, "users", "info")

get(url) { (result: Result<DTOv1.UserDTO, TransferError>) in
get(url) { (result: Result<UserInfoDTO, TransferError>) in
switch result {
case let .success(userDTO):
#if canImport(TelemetryClient)
Expand All @@ -228,41 +228,6 @@ extension APIClient {
}
}

func updatePassword(with passwordChangeRequest: PasswordChangeRequestBody, callback: ((Result<DTOv1.UserDTO, TransferError>) -> Void)? = nil) {
let url = urlForPath("users", "updatePassword")

post(passwordChangeRequest, to: url) { [unowned self] (result: Result<DTOv1.UserDTO, TransferError>) in
switch result {
case let .success(userDTO):
DispatchQueue.main.async {
self.user = userDTO
self.logout()
}
case let .failure(error):
self.handleError(error)
}

callback?(result)
}
}

func updateUser(with dto: DTOv1.UserDTO, callback: ((Result<DTOv1.UserDTO, TransferError>) -> Void)? = nil) {
let url = urlForPath("users", "updateUser")

post(dto, to: url) { [unowned self] (result: Result<DTOv1.UserDTO, TransferError>) in
switch result {
case let .success(userDTO):
DispatchQueue.main.async {
self.user = userDTO
}
case let .failure(error):
self.handleError(error)
}

callback?(result)
}
}

func getOrganizationAdminEntries(callback: ((Result<[OrganizationAdminListEntry], TransferError>) -> Void)? = nil) {
let url = urlForPath("organizationadmin")

Expand Down
24 changes: 24 additions & 0 deletions APIClient/DTOs/AppInfo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// AppInfo.swift
// Telemetry Viewer (iOS)
//
// Created by Lukas on 21.05.24.
//

import Foundation
import DataTransferObjects
import SwiftUI

public struct AppInfo: Codable, Hashable, Identifiable {
public var id: UUID
public var name: String
public var organizationID: UUID
public var insightGroups: [InsightGroupInfo]
public var settings: DTOv2.AppSettings
public var insightGroupIDs: [UUID] {
insightGroups.map { group in
group.id
}
}

}
31 changes: 31 additions & 0 deletions APIClient/DTOs/InsightGroupInfo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// InsightGroupInfo.swift
// Telemetry Viewer (iOS)
//
// Created by Lukas on 21.05.24.
//

import Foundation

public struct InsightGroupInfo: Codable, Hashable, Identifiable {
public init(id: UUID, title: String, order: Double? = nil, appID: UUID, insights: [InsightInfo]) {
self.id = id
self.title = title
self.order = order
self.appID = appID
self.insights = insights
}

public var id: UUID
public var title: String
public var order: Double?
public var appID: UUID
public var insights: [InsightInfo]

public var insightIDs: [UUID] {
insights.map { insight in
insight.id
}
}

}
94 changes: 94 additions & 0 deletions APIClient/DTOs/InsightInfo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//
// InsightInfo.swift
// Telemetry Viewer (iOS)
//
// Created by Lukas on 21.05.24.
//

import Foundation
import DataTransferObjects

public struct InsightInfo: Codable, Hashable, Identifiable {
public enum InsightType: String, Codable, Hashable {
case timeseries
case topN
case customQuery
case funnel
case experiment
}

public var id: UUID
public var groupID: UUID

/// order in which insights appear in the apps (if not expanded)
public var order: Double?
public var title: String

/// What kind of insight is this?
public var type: InsightType

/// If set, display the chart with this accent color, otherwise fall back to default color
public var accentColor: String?

/// If set, use the custom query in this property instead of constructing a query out of the options below
public var customQuery: CustomQuery?

/// Which signal types are we interested in? If nil, do not filter by signal type
public var signalType: String?

/// If true, only include at the newest signal from each user
public var uniqueUser: Bool

/// If set, break down the values in this key
public var breakdownKey: String?

/// If set, group and count found signals by this time interval. Incompatible with breakdownKey
public var groupBy: QueryGranularity?

/// How should this insight's data be displayed?
public var displayMode: InsightDisplayMode

/// If true, the insight will be displayed bigger
public var isExpanded: Bool

/// The amount of time (in seconds) this query took to calculate last time
public var lastRunTime: TimeInterval?

/// The date this query was last run
public var lastRunAt: Date?

public init(
id: UUID,
groupID: UUID,
order: Double?,
title: String,
type: InsightType,
accentColor: String? = nil,
widgetable _: Bool? = false,
customQuery: CustomQuery? = nil,
signalType: String?,
uniqueUser: Bool,
breakdownKey: String?,
groupBy: QueryGranularity?,
displayMode: InsightDisplayMode,
isExpanded: Bool,
lastRunTime: TimeInterval?,
lastRunAt: Date?
) {
self.id = id
self.groupID = groupID
self.order = order
self.title = title
self.type = type
self.accentColor = accentColor
self.customQuery = customQuery
self.signalType = signalType
self.uniqueUser = uniqueUser
self.breakdownKey = breakdownKey
self.groupBy = groupBy
self.displayMode = displayMode
self.isExpanded = isExpanded
self.lastRunTime = lastRunTime
self.lastRunAt = lastRunAt
}
}
20 changes: 20 additions & 0 deletions APIClient/DTOs/UserInfo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// UserInfo.swift
// Telemetry Viewer (iOS)
//
// Created by Lukas on 21.05.24.
//

import Foundation
import DataTransferObjects

struct UserInfoDTO: Identifiable, Codable {
public let id: UUID
public var firstName: String
public var lastName: String
public var email: String
public let emailIsVerified: Bool
public var receiveMarketingEmails: Bool?
public var receiveReports: ReportSendingRate

}
28 changes: 14 additions & 14 deletions Services/AppService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ class AppService: ObservableObject {

var loadingCancellable: AnyCancellable?

@Published var appDictionary: [DTOv2.App.ID: DTOv2.App] = [:]
@Published var loadingStateDictionary: [DTOv2.App.ID: LoadingState] = [:]
@Published var appDictionary: [AppInfo.ID: AppInfo] = [:]
@Published var loadingStateDictionary: [AppInfo.ID: LoadingState] = [:]

init(api: APIClient, errors: ErrorService, orgService: OrgService) {
self.api = api
Expand All @@ -46,23 +46,23 @@ class AppService: ObservableObject {
return loadingState
}

func app(withID appID: DTOv2.App.ID) -> DTOv2.App? {
func app(withID appID: AppInfo.ID) -> AppInfo? {
return appDictionary[appID]
}

func retrieveApp(with appID: DTOv2.App.ID, callback: ((Result<DTOv2.App, TransferError>) -> Void)? = nil) {
let url = api.urlForPath(apiVersion: .v2, "apps", appID.uuidString)
let url = api.urlForPath(apiVersion: .v3, "apps", appID.uuidString)
api.get(url) { (result: Result<DTOv2.App, TransferError>) in
callback?(result)
}
}

func retrieveApp(withID appID: DTOv2.App.ID) async throws -> DTOv2.App {
func retrieveApp(withID appID: DTOv2.App.ID) async throws -> AppInfo {
// guard loadingStateDictionary[appID] != .loading else { let error: TransferError = .transferFailed; throw error }
// loadingStateDictionary[appID] = .loading
return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<DTOv2.App, Error>) in
let url = api.urlForPath(apiVersion: .v2, "apps", appID.uuidString)
api.get(url) { (result: Result<DTOv2.App, TransferError>) in
return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<AppInfo, Error>) in
let url = api.urlForPath(apiVersion: .v3, "apps", appID.uuidString)
api.get(url) { (result: Result<AppInfo, TransferError>) in
switch result {
case let .success(app):

Expand All @@ -76,10 +76,10 @@ class AppService: ObservableObject {
}
}

func create(appNamed name: String, callback: ((Result<DTOv2.App, TransferError>) -> Void)? = nil) {
let url = api.urlForPath(apiVersion: .v2, "apps")
func create(appNamed name: String, callback: ((Result<AppInfo, TransferError>) -> Void)? = nil) {
let url = api.urlForPath(apiVersion: .v3, "apps")

api.post(["name": name], to: url) { [unowned self] (result: Result<DTOv2.App, TransferError>) in
api.post(["name": name], to: url) { [unowned self] (result: Result<AppInfo, TransferError>) in

if let app = try? result.get() {
appDictionary[app.id] = app
Expand All @@ -90,10 +90,10 @@ class AppService: ObservableObject {
}
}

func update(appID: UUID, newName: String, callback: ((Result<DTOv2.App, TransferError>) -> Void)? = nil) {
let url = api.urlForPath(apiVersion: .v2, "apps", appID.uuidString)
func update(appID: UUID, newName: String, callback: ((Result<AppInfo, TransferError>) -> Void)? = nil) {
let url = api.urlForPath(apiVersion: .v3, "apps", appID.uuidString)

api.patch(["name": newName], to: url) { [unowned self] (result: Result<DTOv2.App, TransferError>) in
api.patch(["name": newName], to: url) { [unowned self] (result: Result<AppInfo, TransferError>) in

if let app = try? result.get() {
appDictionary[app.id] = app
Expand Down
26 changes: 13 additions & 13 deletions Services/GroupService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ class GroupService: ObservableObject {
private let api: APIClient
private let errorService: ErrorService

private let loadingState = Cache<DTOv2.Group.ID, LoadingState>()
private let loadingState = Cache<InsightGroupInfo.ID, LoadingState>()

var loadingCancellable: AnyCancellable?

@Published var groupsDictionary = [DTOv2.Group.ID: DTOv2.Group]()
@Published var groupsDictionary = [InsightGroupInfo.ID: InsightGroupInfo]()

init(api: APIClient, errors: ErrorService) {
self.api = api
Expand All @@ -43,26 +43,26 @@ class GroupService: ObservableObject {
return loadingState
}

func group(withID groupID: DTOv2.Group.ID) -> DTOv2.Group? {
func group(withID groupID: InsightGroupInfo.ID) -> InsightGroupInfo? {
return groupsDictionary[groupID]
}

func retrieveGroup(with groupID: DTOv2.Group.ID) {
func retrieveGroup(with groupID: InsightGroupInfo.ID) {
performRetrieval(ofGroupWithID: groupID)
}

func create(insightGroupNamed: String, for appID: UUID, callback: ((Result<DTOv2.Group, TransferError>) -> Void)? = nil) {
let url = api.urlForPath(apiVersion: .v2, "groups")
func create(insightGroupNamed: String, for appID: UUID, callback: ((Result<InsightGroupInfo, TransferError>) -> Void)? = nil) {
let url = api.urlForPath(apiVersion: .v3, "groups")

api.post(["title": insightGroupNamed, "appID": appID.uuidString], to: url) { (result: Result<DTOv2.Group, TransferError>) in
api.post(["title": insightGroupNamed, "appID": appID.uuidString], to: url) { (result: Result<InsightGroupInfo, TransferError>) in
callback?(result)
}
}

func update(insightGroup: DTOv2.Group, in appID: UUID, callback: ((Result<DTOv2.Group, TransferError>) -> Void)? = nil) {
let url = api.urlForPath(apiVersion: .v2, "groups", insightGroup.id.uuidString)
func update(insightGroup: InsightGroupInfo, in appID: UUID, callback: ((Result<InsightGroupInfo, TransferError>) -> Void)? = nil) {
let url = api.urlForPath(apiVersion: .v3, "groups", insightGroup.id.uuidString)

api.patch(insightGroup, to: url) { (result: Result<DTOv2.Group, TransferError>) in
api.patch(insightGroup, to: url) { (result: Result<InsightGroupInfo, TransferError>) in
callback?(result)
}
}
Expand All @@ -78,7 +78,7 @@ class GroupService: ObservableObject {
}

private extension GroupService {
func performRetrieval(ofGroupWithID groupID: DTOv2.Group.ID) {
func performRetrieval(ofGroupWithID groupID: InsightGroupInfo.ID) {
switch loadingState(for: groupID) {
case .loading, .error:
return
Expand All @@ -88,9 +88,9 @@ private extension GroupService {

loadingState[groupID] = .loading

let url = api.urlForPath(apiVersion: .v2, "groups", groupID.uuidString)
let url = api.urlForPath(apiVersion: .v3, "groups", groupID.uuidString)

api.get(url) { [weak self] (result: Result<DTOv2.Group, TransferError>) in
api.get(url) { [weak self] (result: Result<InsightGroupInfo, TransferError>) in

switch result {
case let .success(group):
Expand Down
Loading
Loading