Caveat:More of an opinion, I feel using Launch Darkly REST API as part of application logic is trashington but the API exists and I wanted to know how to use it. Potentially in the future I’ll have a valid use case.
My team needed to programatically remove keys from a Launch Darkly segment and it tickled my interest as this cannot be done with the SDK however Launch Darkly does provide a REST API (which is what I suspect SDK Client is probabaly using under the hood). I had a play in the past with Launch Darkly SDK Clients.
Launch Darkly segments are groups of values that can be applied to more than one rule, so you can have sweet segment 3 with the guids listed below applied to n feature flags. For my tests I just added the guids below to the feature sweet-feature-name-3
Create the segment using the GUI and add the values
Create the flag and apply the segment
Using the code described in this SDK post and pushed here, query Launch Darkly. The expected output is as follows
1
CheckByUserKey: featureName=sweet-feature-name-3 userId=e3dbd64b-f51d-4aff-a4e5-f334960b9045 returned allowed=True
Restful API - Change Segment From Code
The REST API uses an access token (Generate from the GUI -> Account settings -> Authorization -> Access tokens). This is not the same as an SDK Key which is associated with the project when you set it up.
These are the steps I followed to make use of the Restful API and change the segment described above from code.
Create the HTTP client and set its headers, in a real world application this will be injected into the pipeline with Microsoft.Extensions.DependencyInjection.AddHttpClient instead of being new HttpClient(); - remember new is glue :D
1 2 3
var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Add("Authorization", "api-00000000-0000-0000-0000-000000000000"); httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
Additionally I setup some serializer options - I really dont understand why this is not the default
1 2 3 4
var jsonOptions = new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
Lastly I created a user variable - this can be any string that represents your entity. Guids are the most common so thats what I used to represent a user.
1
var userId = "e3dbd64b-f51d-4aff-a4e5-f334960b9045";
privatestaticstringGetSegmentUri() { var projectKey = "default"; var environmentKey = "production"; var segmentKey = "sweet-segment-3"; var baseUrl = "https://app.launchdarkly.com/api/v2";
Get the segment, this will be the JSON representation of the segment created above. It was called sweet-segment-3. This code would need some defensive checks for null on responseBody but you knew that already 😊
1 2 3 4 5 6 7 8
privatestaticasync Task<Segment> GetSegment(HttpClient httpClient, JsonSerializerOptions options) { var response = await httpClient.GetAsync(GetSegmentUri()); response.EnsureSuccessStatusCode(); var responseBody = await response.Content.ReadAsStringAsync();
From there we itterate over the clauses and interrogate the values using ToHashSet. Should the userId exist in that hash we create a LdPatchOperation. From code this look like the below. The Op is an operation type requested. Example remove. This can be neatly wrapped up on a LaunchDarklyDto class.
privatestatic List<LdPatchOperation> GetPatchOperation(string userId, Segment segment) { var firstRule = segment.Rules.First(); var patchOperations = new List<LdPatchOperation>();
for (int clauseIndex = 0; clauseIndex < firstRule.Clauses.Count; clauseIndex++) { var currentClause = firstRule.Clauses[clauseIndex]; var guidsInClause = currentClause.Values.ToHashSet(); var exists = guidsInClause.Contains(userId);
if (exists) { var idIndex = currentClause.Values.IndexOf(userId); patchOperations.Add(new LdPatchOperation() { Op = "remove", Path = $"/rules/0/clauses/{clauseIndex}/values/{idIndex}", }); } }
return patchOperations; }
Update the segment by passing the operations as a Patch property. Here we must use the HTTP verb PATCH which we get implicitly by calling PatchAsync.
Its very important here that we check EnsureSuccessStatusCode as if we get something like 429 (Rate Limit) then the update was unsuccessful - potentially this is possible if you are using a script thats iterating over a large set of userIds.
var body = new LdPatchSegmentPayload() { Patch = operations, Comment = $"Removing userId : {userId}" }; var content = JsonSerializer.Serialize(body, options); var stringContent = new StringContent(content, Encoding.UTF8, "application/json");
var response = await httpClient.PatchAsync(GetSegmentUri(), stringContent); response.EnsureSuccessStatusCode(); }
Now using the same checks from Test Data And SDK Tests above, if we look for e3dbd64b-f51d-4aff-a4e5-f334960b9045 it would have been removed
1
CheckByUserKey: featureName=sweet-feature-name-3 userId=e3dbd64b-f51d-4aff-a4e5-f334960b9045 returned allowed=False
This can be confirmed from the GUI
The complete code call stack could look as follows
1 2 3 4 5
var userId = "e3dbd64b-f51d-4aff-a4e5-f334960b9045"; var segment = await GetSegment(httpClient, jsonOptions); var operations = GetPatchOperation(userId, segment);