Skip to content

Commit 9deddf0

Browse files
authored
feat: Add wrapper_name and wrapper_version configuration options (#207)
1 parent d4ea3ee commit 9deddf0

File tree

6 files changed

+89
-8
lines changed

6 files changed

+89
-8
lines changed

src/LaunchDarkly/Impl/Integrations/CurlEventPublisher.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public function __construct(string $sdkKey, array $options = [])
4949
$this->_curl = $options['curl'];
5050
}
5151

52-
$this->_eventHeaders = Util::eventHeaders($sdkKey, $options['application_info'] ?? null);
52+
$this->_eventHeaders = Util::eventHeaders($sdkKey, $options);
5353
$this->_connectTimeout = $options['connect_timeout'];
5454
$this->_isWindows = PHP_OS_FAMILY == 'Windows';
5555
}

src/LaunchDarkly/Impl/Integrations/GuzzleEventPublisher.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public function __construct(string $sdkKey, array $options = [])
3434
$this->_eventsUri = \LaunchDarkly\Impl\Util::adjustBaseUri($baseUri);
3535

3636
$this->_requestOptions = [
37-
'headers' => Util::eventHeaders($this->_sdkKey, $options['application_info'] ?? null),
37+
'headers' => Util::eventHeaders($this->_sdkKey, $options),
3838
'timeout' => $options['timeout'],
3939
'connect_timeout' => $options['connect_timeout']
4040
];

src/LaunchDarkly/Impl/Integrations/GuzzleFeatureRequester.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public function __construct(string $baseUri, string $sdkKey, array $options)
4343
}
4444

4545
$defaults = [
46-
'headers' => Util::defaultHeaders($sdkKey, $options['application_info'] ?? null),
46+
'headers' => Util::defaultHeaders($sdkKey, $options),
4747
'timeout' => $options['timeout'],
4848
'connect_timeout' => $options['connect_timeout'],
4949
'handler' => $stack,

src/LaunchDarkly/Impl/Util.php

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,10 @@ public static function makeNullLogger(): LoggerInterface
9292
* made to LaunchDarkly servers.
9393
*
9494
* @param string $sdkKey
95-
* @param ApplicationInfo|null $applicationInfo
95+
* @params array<string, mixed> $options
9696
* @return array<string, string>
9797
*/
98-
public static function defaultHeaders(string $sdkKey, $applicationInfo): array
98+
public static function defaultHeaders(string $sdkKey, array $options): array
9999
{
100100
$headers = [
101101
'Content-Type' => 'application/json',
@@ -104,13 +104,22 @@ public static function defaultHeaders(string $sdkKey, $applicationInfo): array
104104
'User-Agent' => 'PHPClient/' . LDClient::VERSION,
105105
];
106106

107+
$applicationInfo = $options['application_info'] ?? null;
107108
if ($applicationInfo instanceof ApplicationInfo) {
108109
$headerValue = (string) $applicationInfo;
109110
if ($headerValue) {
110111
$headers['X-LaunchDarkly-Tags'] = $headerValue;
111112
}
112113
}
113114

115+
if (!empty($options['wrapper_name'])) {
116+
$headers['X-LaunchDarkly-Wrapper'] = $options['wrapper_name'];
117+
118+
if (!empty($options['wrapper_version'])) {
119+
$headers['X-LaunchDarkly-Wrapper'] .= '/' . $options['wrapper_version'];
120+
}
121+
}
122+
114123
return $headers;
115124
}
116125

@@ -119,12 +128,12 @@ public static function defaultHeaders(string $sdkKey, $applicationInfo): array
119128
* made to the LaunchDarkly Events API.
120129
*
121130
* @param string $sdkKey
122-
* @param ApplicationInfo|null $applicationInfo
131+
* @param array<string, mixed> $options
123132
* @return array
124133
*/
125-
public static function eventHeaders(string $sdkKey, $applicationInfo): array
134+
public static function eventHeaders(string $sdkKey, array $options): array
126135
{
127-
$headers = Util::defaultHeaders($sdkKey, $applicationInfo);
136+
$headers = Util::defaultHeaders($sdkKey, $options);
128137
$headers['X-LaunchDarkly-Event-Schema'] = EventPublisher::CURRENT_SCHEMA_VERSION;
129138
// Only the presence of this header is important. We encode a string
130139
// value of 'true' to ensure it isn't dropped along the way.

src/LaunchDarkly/LDClient.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ class LDClient
7878
* with this configuration active will have attributes with these names removed. You can also set private attributes on a
7979
* - `application_info`: An optional {@see \LaunchDarkly\Types\ApplicationInfo} instance.
8080
* per-user basis in the LDContext builder.
81+
* - `wrapper_name`: For use by wrapper libraries to set an identifying name for the wrapper being used. This will be sent in User-Agent headers during requests to the LaunchDarkly servers to allow recording metrics on the usage of these wrapper libraries.
82+
* - `wrapper_version`: For use by wrapper libraries to report the version of the library in use. If `wrapper_name` is not set, this field will be ignored. Otherwise the version string will be included in the User-Agent headers along with the `wrapper_name` during requests to the LaunchDarkly servers.
8183
* - Other options may be available depending on any features you are using from the `LaunchDarkly\Integrations` namespace.
8284
*
8385
* @return LDClient

tests/Impl/Integrations/GuzzleFeatureRequesterTest.php

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,74 @@ public function testSendsCorrectHeaders(): void
7272
$this->assertEquals('PHPClient/' . LDClient::VERSION, $headers['User-Agent']);
7373
$this->assertEquals('application-id/my-id application-version/my-version', $headers['X-LaunchDarkly-Tags']);
7474
}
75+
76+
public function wrapperProvider(): array
77+
{
78+
return [
79+
[null, null, null],
80+
['my-wrapper', null, 'my-wrapper'],
81+
['my-wrapper', '1.0.0', 'my-wrapper/1.0.0'],
82+
[null, '1.0.0', null],
83+
];
84+
}
85+
86+
/**
87+
* @dataProvider wrapperProvider
88+
*/
89+
public function testSendsCorrectWrapperNameHeaders(?string $wrapper_name, ?string $wrapper_version, ?string $expected_header): void
90+
{
91+
/** @var LoggerInterface **/
92+
$logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
93+
94+
$config = [
95+
'logger' => $logger,
96+
'timeout' => 3,
97+
'connect_timeout' => 3,
98+
];
99+
100+
if ($wrapper_name) {
101+
$config['wrapper_name'] = $wrapper_name;
102+
}
103+
if ($wrapper_version) {
104+
$config['wrapper_version'] = $wrapper_version;
105+
}
106+
107+
$requester = new GuzzleFeatureRequester('http://localhost:8080', 'sdk-key', $config);
108+
$requester->getFeature("flag-key");
109+
110+
$requests = [];
111+
$client = new Client();
112+
113+
// Provide time for the curl to execute
114+
$start = time();
115+
while (time() - $start < 5) {
116+
$response = $client->request('GET', 'http://localhost:8080/__admin/requests');
117+
$body = json_decode($response->getBody()->getContents(), true);
118+
$requests = $body['requests'];
119+
120+
if ($requests) {
121+
break;
122+
}
123+
usleep(100);
124+
}
125+
126+
if (!$requests) {
127+
$this->fail("Unable to connect to endpoint within specified timeout");
128+
}
129+
130+
$this->assertCount(1, $requests);
131+
132+
$request = $requests[0]['request'];
133+
134+
// Validate that we hit the right endpoint
135+
$this->assertEquals('/sdk/flags/flag-key', $request['url']);
136+
137+
// And validate that we provided all the correct headers
138+
$headers = $request['headers'];
139+
if ($expected_header) {
140+
$this->assertEquals($expected_header, $headers['X-LaunchDarkly-Wrapper']);
141+
} else {
142+
$this->assertNotContains('X-LaunchDarkly-Wrapper', $headers);
143+
}
144+
}
75145
}

0 commit comments

Comments
 (0)