Skip to content

[RUM-16084] Fetch and store remote configuration file at SDK init#2919

Open
saraSr5 wants to merge 10 commits into
feature/remote-configfrom
saraSr5/RUM-16084/remote-config
Open

[RUM-16084] Fetch and store remote configuration file at SDK init#2919
saraSr5 wants to merge 10 commits into
feature/remote-configfrom
saraSr5/RUM-16084/remote-config

Conversation

@saraSr5
Copy link
Copy Markdown
Contributor

@saraSr5 saraSr5 commented May 13, 2026

What and why?

Implements the first building block of the Remote configuration: fetching the remote config document from the Datadog CDN at SDK startup and caching the raw JSON to disk.

When a remoteConfigurationID is provided in Datadog.Configuration, the SDK asynchronously fetches the corresponding config document and writes it atomically to remote-config.json inside the SDK's private core directory. On any failure (network error, non-2xx, empty body, invalid JSON), a telemetry error is reported and any existing cached file is left untouched.
Parsing the cached file and applying its values to SDK configuration is out of scope for this PR.

How?

  • Added remoteConfigurationID: String? to Datadog.Configuration (defaults to nil, no behaviour change for existing apps)
  • Added DatadogSite.remoteConfigurationURL(for:) to build the CDN URL from the ID and resolved site
  • Added RemoteConfigurationCache — thin wrapper around an atomic file write/read in the core directory
  • Added RemoteConfigurationFetcher — fires a one-shot URLSession dataTask, validates the response (2xx, non-empty, valid JSON), and delegates storage to the cache
  • Wired everything in Datadog.initialize() — fire-and-forget, does not block SDK init

Review checklist

  • Feature or bugfix MUST have appropriate tests (unit, integration)
  • Make sure each commit and the PR mention the Issue number or JIRA reference
  • Add CHANGELOG entry for user facing changes
  • Add Objective-C interface for public APIs - see our guidelines (internal)
  • Run make api-surface when adding new APIs

@saraSr5
Copy link
Copy Markdown
Contributor Author

saraSr5 commented May 13, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fe567dc579

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

/// The URLSession used for remote configuration fetching. Replaceable in tests.
/// TODO: Build this session from `proxyConfiguration` so remote config fetches respect
/// proxy settings in restricted network environments (same as the main HTTP client).
internal var remoteConfigurationSession = URLSession(configuration: .ephemeral)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Honor proxy settings for remote config fetch session

Create the remote-config URLSession from proxyConfiguration before startup fetches run; using a hard-coded ephemeral session here bypasses the SDK’s proxy setup, so apps in environments that require an explicit proxy can upload telemetry successfully but will consistently fail to retrieve remote config. This is a functional regression for customers relying on Datadog.Configuration.proxyConfiguration.

Useful? React with 👍 / 👎.

/// the two in sync.
func save(_ data: Data) {
do {
try data.write(to: fileURL, options: .atomic)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Encrypt cached remote config before writing to disk

Apply the configured DataEncryption to this payload before persisting it: the new cache currently writes raw JSON bytes directly to disk, which bypasses the SDK’s on-disk encryption contract and leaves remote-config.json in plaintext even when apps opt into encryption for persisted SDK data.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@saraSr5
Copy link
Copy Markdown
Contributor Author

saraSr5 commented May 13, 2026

@codex review

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@saraSr5 saraSr5 marked this pull request as ready for review May 13, 2026 12:29
@saraSr5 saraSr5 requested review from a team as code owners May 13, 2026 12:29
@saraSr5 saraSr5 requested a review from Copilot May 13, 2026 14:10
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

DatadogInternal/Sources/Context/DatadogSite.swift:66

  • The force unwrap of endpoint.host! can still crash if endpoint ever changes (or is constructed differently) and lacks a host. Since this method already returns URL?, it would be safer to guard let host = endpoint.host and return nil (and let the caller report telemetry) rather than force-unwrapping.
        let pathSegmentAllowed = CharacterSet.urlPathAllowed.subtracting(CharacterSet(charactersIn: "/?#"))
        guard let encoded = id.addingPercentEncoding(withAllowedCharacters: pathSegmentAllowed) else {
            return nil
        }
        // swiftlint:disable:next force_unwrapping
        return URL(string: "https://sdk-configuration.\(endpoint.host!)/v1/\(encoded).json")

Comment on lines +54 to +66
/// - Returns: URL to GET the config JSON from, or `nil` if `id` cannot be percent-encoded.
@_spi(Internal)
public func remoteConfigurationURL(for id: String) -> URL? {
// Format: https://sdk-configuration.browser-intake-{site}/v1/{id}.json
// `.urlPathAllowed` leaves `/`, `?`, and `#` unencoded (they are legal in a URL).
// Subtract them so an ID containing those characters doesn't produce extra path
// segments, a query string, or a fragment.
let pathSegmentAllowed = CharacterSet.urlPathAllowed.subtracting(CharacterSet(charactersIn: "/?#"))
guard let encoded = id.addingPercentEncoding(withAllowedCharacters: pathSegmentAllowed) else {
return nil
}
// swiftlint:disable:next force_unwrapping
return URL(string: "https://sdk-configuration.\(endpoint.host!)/v1/\(encoded).json")
Comment on lines +302 to +316
// Trigger remote config fetch if an ID was provided.
// The fetch is async — init returns immediately and does not wait for it.
if let id = configuration.remoteConfigurationID {
if let endpoint = configuration.site.remoteConfigurationURL(for: id) {
let session = configuration.remoteConfigurationSession ?? {
let sessionConfig: URLSessionConfiguration = .ephemeral
sessionConfig.urlCache = nil
sessionConfig.connectionProxyDictionary = configuration.proxyConfiguration
return URLSession(configuration: sessionConfig)
}()
core.fetchRemoteConfiguration(from: endpoint, session: session)
} else {
core.telemetry.error("[RemoteConfig] Could not build CDN URL for remoteConfigurationID '\(id)'")
}
}
Comment on lines +59 to +60
/// Created at init using this core's directory. `data` is `nil` until a
/// successful CDN fetch has been written and the app relaunched.
Comment on lines +54 to +62
func save(_ data: Data) {
do {
try data.write(to: fileURL, options: .atomic)
self.data = data
} catch {
// Best-effort: disk write failures are silently ignored.
// self.data is intentionally NOT updated so in-memory state stays
// consistent with what is actually on disk.
}
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 4 comments.

Comment on lines +58 to +61
// `.urlPathAllowed` leaves `/`, `?`, and `#` unencoded (they are legal in a URL).
// Subtract them so an ID containing those characters doesn't produce extra path
// segments, a query string, or a fragment.
let pathSegmentAllowed = CharacterSet.urlPathAllowed.subtracting(CharacterSet(charactersIn: "/?#"))

/// Constructs the CDN URL for fetching the remote configuration document.
/// - Parameter id: The value of `Datadog.Configuration.remoteConfigurationID`.
/// - Returns: URL to GET the config JSON from, or `nil` if `id` cannot be percent-encoded.
Comment on lines +53 to +64
/// - Returns: `true` if the write succeeded, `false` otherwise.
@discardableResult
func save(_ data: Data) -> Bool {
do {
try data.write(to: fileURL, options: .atomic)
self.data = data
return true
} catch {
// self.data is intentionally NOT updated so in-memory state stays
// consistent with what is actually on disk.
return false
}
Comment thread api-surface-swift
Comment on lines 1317 to +1339
public protocol FlagsClientProtocol: AnyObject
var state: FlagsStateObservable
func setEvaluationContext(_ context: FlagsEvaluationContext,completion: @escaping (Result<Void, FlagsError>) -> Void)
func getDetails<T>(key: String, defaultValue: T) -> FlagDetails<T> where T: Equatable, T: FlagValue
[?] extension FlagsClientProtocol
public var state: FlagsStateObservable
[?] extension FlagsClientProtocol
public func setEvaluationContext(_ context: FlagsEvaluationContext)
public func setEvaluationContext(_ context: FlagsEvaluationContext) async throws
[?] extension FlagsClientProtocol
public func getValue<T>(key: String, defaultValue: T) -> T where T: Equatable, T: FlagValue
public func getBooleanValue(key: String, defaultValue: Bool) -> Bool
public func getStringValue(key: String, defaultValue: String) -> String
public func getIntegerValue(key: String, defaultValue: Int) -> Int
public func getDoubleValue(key: String, defaultValue: Double) -> Double
public func getObjectValue(key: String, defaultValue: AnyValue) -> AnyValue
[?] extension FlagsClientProtocol
public func getBooleanDetails(key: String, defaultValue: Bool) -> FlagDetails<Bool>
public func getStringDetails(key: String, defaultValue: String) -> FlagDetails<String>
public func getIntegerDetails(key: String, defaultValue: Int) -> FlagDetails<Int>
public func getDoubleDetails(key: String, defaultValue: Double) -> FlagDetails<Double>
public func getObjectDetails(key: String, defaultValue: AnyValue) -> FlagDetails<AnyValue>
public enum FlagsClientState: Equatable
public enum FlagsClientState: Sendable
@saraSr5
Copy link
Copy Markdown
Contributor Author

saraSr5 commented May 14, 2026

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 3 comments.

Comment on lines +66 to +67
// swiftlint:disable:next force_unwrapping
return URL(string: "https://sdk-configuration.\(endpoint.host!)/v1/\(encoded).json")
Comment on lines +59 to +62
// `.urlPathAllowed` leaves `/`, `?`, and `#` unencoded (they are legal in a URL).
// Subtract them so an ID containing those characters doesn't produce extra path
// segments, a query string, or a fragment.
let pathSegmentAllowed = CharacterSet.urlPathAllowed.subtracting(CharacterSet(charactersIn: "/?#"))
Comment thread api-surface-swift
Comment on lines 1337 to 1340
public func getDoubleDetails(key: String, defaultValue: Double) -> FlagDetails<Double>
public func getObjectDetails(key: String, defaultValue: AnyValue) -> FlagDetails<AnyValue>
public enum FlagsClientState: Equatable
public enum FlagsClientState: Sendable
case notReady
@saraSr5
Copy link
Copy Markdown
Contributor Author

saraSr5 commented May 14, 2026

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Can't wait for the next one!

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants