Skip to content

Commit

Permalink
Use metric hashes for API
Browse files Browse the repository at this point in the history
  • Loading branch information
christophhagen committed Feb 6, 2023
1 parent 4ad7632 commit f37bc51
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 24 deletions.
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ let observer = MetricObserver(logFolder: url, authenticator: authenticator, logM
Now that the metrics are protected, they can be accessed by authorized entities.
There are currently four main entry points.
All requests are `POST` requests, and require authentication.
**Note**: If the included clients are used, then the API is already correctly implemented and not important.

#### `/list`

Expand All @@ -172,9 +173,13 @@ The request calls the function `metricListAccess(isAllowedForRequest:)` or `metr

The response is an array of `MetricDescription`, encoded with the binary encoder assigned to the `MetricObserver`.

#### `/last/<METRIC_ID>`
#### `/last/<METRIC_ID_HASH>`

Get the last value of the metric.
Get the last value of the metric. The `<METRIC_ID_HASH>` are the first 16 bytes of the SHA256 hash of the metric `ID` as a hex string (32 characters). Authentication of the request depends on the chosen implementation.

#### `history/<METRIC_ID_HASH>`

Get the logged values of a metric in a specified time interval. The time interval is provided in the request body as a binary encoding of a `ClosedRange<Date>`. Authentication of the request depends on the chosen implementation.

### Pushing to other servers

Expand Down
4 changes: 2 additions & 2 deletions Sources/Clairvoyant/Client/MetricConsumer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public final class MetricConsumer {

func lastValueData(for metric: MetricId) async throws -> Data? {
do {
return try await post(path: "last/\(metric)")
return try await post(path: "last/\(metric.hashed())")
} catch MetricError.noValueAvailable {
return nil
}
Expand All @@ -64,7 +64,7 @@ public final class MetricConsumer {

func historyData(for metric: MetricId, in range: ClosedRange<Date>) async throws -> Data {
let body = try encode(range)
return try await post(path: "history/\(metric)", body: body)
return try await post(path: "history/\(metric.hashed())", body: body)
}

public func history<T>(for metric: MetricId, in range: ClosedRange<Date>, type: T.Type = T.self) async throws -> [Timestamped<T>] where T: MetricValue {
Expand Down
1 change: 1 addition & 0 deletions Sources/Clairvoyant/Metric/MetricId.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Foundation
import Crypto

public typealias MetricId = String
typealias MetricIdHash = String

extension MetricId {

Expand Down
40 changes: 20 additions & 20 deletions Sources/Clairvoyant/Metric/MetricObserver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import Vapor
import FoundationNetworking
#endif

typealias MetricIdHash = String

public final class MetricObserver {

private let hashParameterName = "hash"

/**
The default observer, to which created metrics are added.

Expand Down Expand Up @@ -40,16 +40,16 @@ public final class MetricObserver {
/**
The metrics observed by this instance.

The key is the metric `name`
The key is the metric `idHash`
*/
private var observedMetrics: [MetricId : AbstractMetric] = [:]
private var observedMetrics: [MetricIdHash : AbstractMetric] = [:]

/**
The remote observers of metrics logged with this instance.

All updates to metrics are pushed to each remote observer.
*/
private var remoteObservers: [MetricId : Set<RemoteMetricObserver>] = [:]
private var remoteObservers: [MetricIdHash : Set<RemoteMetricObserver>] = [:]

/**
Create a new observer.
Expand Down Expand Up @@ -151,14 +151,14 @@ public final class MetricObserver {
}

func observe(metric: AbstractMetric) -> Bool {
guard observedMetrics[metric.id] == nil else {
guard observedMetrics[metric.idHash] == nil else {
return false
}
if let oldObserver = metric.observer {
oldObserver.remove(metric)
}
metric.observer = self
observedMetrics[metric.id] = metric
observedMetrics[metric.idHash] = metric
return true
}

Expand All @@ -177,7 +177,7 @@ public final class MetricObserver {
guard metric.observer == self else {
return
}
observedMetrics[metric.id] = nil
observedMetrics[metric.idHash] = nil
metric.observer = nil
}

Expand Down Expand Up @@ -207,8 +207,8 @@ public final class MetricObserver {

// MARK: Update metric values

func getMetric(with id: MetricId) throws -> AbstractMetric {
guard let metric = observedMetrics[id] else {
private func getMetric(with idHash: MetricIdHash) throws -> AbstractMetric {
guard let metric = observedMetrics[idHash] else {
throw MetricError.unknownMetric
}
return metric
Expand Down Expand Up @@ -485,15 +485,15 @@ public final class MetricObserver {
// MARK: Remote observers

func push<T>(_ metric: AnyMetric<T>, to remote: RemoteMetricObserver) {
if remoteObservers[metric.id] == nil {
remoteObservers[metric.id] = [remote]
if remoteObservers[metric.idHash] == nil {
remoteObservers[metric.idHash] = [remote]
} else {
remoteObservers[metric.id]!.insert(remote)
remoteObservers[metric.idHash]!.insert(remote)
}
}

private func pushValueToRemoteObservers(_ data: TimestampedValueData, for metric: AbstractMetric) {
guard let observers = remoteObservers[metric.id] else {
guard let observers = remoteObservers[metric.idHash] else {
return
}

Expand Down Expand Up @@ -542,11 +542,11 @@ public final class MetricObserver {
}

private func getAccessibleMetric(_ request: Request) throws -> AbstractMetric {
guard let metricId = request.parameters.get("id", as: String.self) else {
guard let metricIdHash = request.parameters.get(hashParameterName, as: String.self) else {
throw Abort(.badRequest)
}
let metric = try getMetric(with: metricId)
try accessManager.metricAccess(to: metricId, isAllowedForRequest: request)
let metric = try getMetric(with: metricIdHash)
try accessManager.metricAccess(to: metric.id, isAllowedForRequest: request)
return metric
}

Expand Down Expand Up @@ -585,7 +585,7 @@ public final class MetricObserver {
return try self.encode(result)
}

app.post(subPath, "last", ":id") { [weak self] request in
app.post(subPath, "last", .parameter(hashParameterName)) { [weak self] request in
guard let self else {
throw Abort(.internalServerError)
}
Expand All @@ -598,7 +598,7 @@ public final class MetricObserver {
return data
}

app.post(subPath, "history", ":id") { [weak self] request in
app.post(subPath, "history", .parameter(hashParameterName)) { [weak self] request in
guard let self else {
throw Abort(.internalServerError)
}
Expand All @@ -608,7 +608,7 @@ public final class MetricObserver {
return try self.getHistoryFromLog(forMetric: metric, in: range)
}

app.post(subPath, "push", ":id") { [weak self] request -> Void in
app.post(subPath, "push", .parameter(hashParameterName)) { [weak self] request -> Void in
guard let self else {
throw Abort(.internalServerError)
}
Expand Down

0 comments on commit f37bc51

Please sign in to comment.