- Notifications
You must be signed in to change notification settings - Fork 20
feat: Added ODPSegmentManager #321
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
Changes from 1 commit
d9db9a5 e5fcd9a 61d3e0f c4db70a b79861e bc60e1f 38281ab 164b9d6 4f62cd7 a614e9f a42ed21 7efe971 8b4f002 3d27572 1c5a914 9d61e43 40f2fdf 3f91f81 be49cb1 892cd3f 77d56dd eedf603 ec81354 fe6a6d8 File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,35 @@ | ||
| using System.Collections.Generic; | ||
| /* | ||
| * Copyright 2022 Optimizely | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * https://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
| | ||
| using System.Collections.Generic; | ||
| | ||
| namespace OptimizelySDK.Odp | ||
| { | ||
| /// <summary> | ||
| /// Interface to schedule connections to ODP for audience segmentation and caches the results. | ||
| /// </summary> | ||
| public interface IOdpSegmentManager | ||
| { | ||
| List<string> GetQualifiedSegments(string fsUserId, List<OdpSegmentOption> options = null); | ||
| /// <summary> | ||
| /// Attempts to fetch and return a list of a user's qualified segments from the local segments cache. | ||
| /// If no cached data exists for the target user, this fetches and caches data from the ODP server instead. | ||
| /// </summary> | ||
| /// <param name="fsUserId">The FS User ID identifying the user</param> | ||
| /// <param name="options">An array of OptimizelySegmentOption used to ignore and/or reset the cache.</param> | ||
| /// <returns>Qualified segments for the user from the cache or the ODP server if the cache is empty.</returns> | ||
| List<string> FetchQualifiedSegments(string fsUserId, List<OdpSegmentOption> options = null); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,19 +1,54 @@ | ||
| using OptimizelySDK.Logger; | ||
| /* | ||
| * Copyright 2022 Optimizely | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * https://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
| | ||
| using OptimizelySDK.Logger; | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| | ||
| namespace OptimizelySDK.Odp | ||
| { | ||
| public class OdpSegmentManager: IOdpSegmentManager | ||
| /// <summary> | ||
| /// Concrete implementation that schedules connections to ODP for audience segmentation | ||
| /// and caches the results. | ||
| /// </summary> | ||
| public class OdpSegmentManager : IOdpSegmentManager | ||
| { | ||
| /// <summary> | ||
| /// Logger used to record messages that occur within the ODP client | ||
| /// </summary> | ||
| private readonly ILogger _logger; | ||
| | ||
| /// <summary> | ||
| /// ODP segment API manager to communicate with ODP | ||
| /// </summary> | ||
| private readonly IOdpSegmentApiManager _apiManager; | ||
| | ||
| /// <summary> | ||
| /// ODP configuration containing the connection parameters | ||
| /// </summary> | ||
| private readonly IOdpConfig _odpConfig; | ||
| | ||
| private readonly Cache<List<string>> _segmentsCache; | ||
| /// <summary> | ||
| /// Cached segments | ||
| /// </summary> | ||
| private readonly LruCache<List<string>> _segmentsCache; | ||
| | ||
| public OdpSegmentManager(IOdpConfig odpConfig, IOdpSegmentApiManager apiManager, | ||
| int cacheSize = Constants.DEFAULT_MAX_CACHE_SIZE, TimeSpan? itemTimeout = default, | ||
| int cacheSize = Constants.DEFAULT_MAX_CACHE_SIZE, TimeSpan? itemTimeout = null, | ||
| ILogger logger = null | ||
| ) | ||
| { | ||
| | @@ -32,7 +67,16 @@ public OdpSegmentManager(IOdpConfig odpConfig, IOdpSegmentApiManager apiManager, | |
| _segmentsCache = new LruCache<List<string>>(cacheSize, timeout, logger); | ||
| } | ||
| | ||
| public List<string> GetQualifiedSegments(string fsUserId, List<OdpSegmentOption> options = null) | ||
| /// <summary> | ||
| /// Attempts to fetch and return a list of a user's qualified segments from the local segments cache. | ||
| /// If no cached data exists for the target user, this fetches and caches data from the ODP server instead. | ||
| /// </summary> | ||
| /// <param name="fsUserId">The FS User ID identifying the user</param> | ||
| /// <param name="options">An array of OptimizelySegmentOption used to ignore and/or reset the cache.</param> | ||
| /// <returns>Qualified segments for the user from the cache or the ODP server if the cache is empty.</returns> | ||
| public List<string> FetchQualifiedSegments(string fsUserId, | ||
| List<OdpSegmentOption> options = null | ||
| ) | ||
| { | ||
| if (!_odpConfig.IsReady()) | ||
| { | ||
| | @@ -46,45 +90,51 @@ public List<string> GetQualifiedSegments(string fsUserId, List<OdpSegmentOption> | |
| "No Segments are used in the project, Not Fetching segments. Returning empty list"); | ||
| ||
| return new List<string>(); | ||
| } | ||
| | ||
| | ||
| options = options ?? new List<OdpSegmentOption>(); | ||
| | ||
| List<string> qualifiedSegments; | ||
| var cacheKey = GetCacheKey(OdpUserKeyType.FS_USER_ID.ToString().ToLower(), fsUserId); | ||
| | ||
| if (options.Contains(OdpSegmentOption.ResetCache)) | ||
| { | ||
| _segmentsCache.reset(); | ||
| _segmentsCache.Reset(); | ||
| } | ||
| else if (!options.Contains(OdpSegmentOption.IgnoreCache)) | ||
mikechu-optimizely marked this conversation as resolved. Outdated Show resolved Hide resolved | ||
| { | ||
| qualifiedSegments = _segmentsCache.lookup(cacheKey); | ||
| qualifiedSegments = _segmentsCache.Lookup(cacheKey); | ||
| if (qualifiedSegments != null) | ||
| { | ||
| _logger.Log(LogLevel.DEBUG,"ODP Cache Hit. Returning segments from Cache."); | ||
| _logger.Log(LogLevel.DEBUG, "ODP Cache Hit. Returning segments from Cache."); | ||
| return qualifiedSegments; | ||
| } | ||
| } | ||
| | ||
| _logger.Log(LogLevel.DEBUG, "ODP Cache Miss. Making a call to ODP Server."); | ||
| | ||
| var qualifiedSegmentsResponse = _apiManager.FetchSegments( | ||
| _odpConfig.ApiKey, | ||
| _odpConfig.ApiHost + Constants.ODP_GRAPHQL_API_ENDPOINT_PATH, | ||
| OdpUserKeyType.FS_USER_ID, fsUserId, _odpConfig.SegmentsToCheck); | ||
| | ||
| var parser = ResponseJsonParserFactory.getParser(); | ||
| qualifiedSegments = parser.parseQualifiedSegments(qualifiedSegmentsResponse); | ||
| qualifiedSegments = _apiManager.FetchSegments( | ||
| _odpConfig.ApiKey, | ||
| _odpConfig.ApiHost + Constants.ODP_GRAPHQL_API_ENDPOINT_PATH, | ||
| OdpUserKeyType.FS_USER_ID, fsUserId, _odpConfig.SegmentsToCheck) | ||
| .ToList(); | ||
| | ||
| if (qualifiedSegments != null && !options.Contains(OdpSegmentOption.IgnoreCache)) | ||
| if (!options.Contains(OdpSegmentOption.IgnoreCache)) | ||
| { | ||
| _segmentsCache.save(cacheKey, qualifiedSegments); | ||
| _segmentsCache.Save(cacheKey, qualifiedSegments); | ||
mikechu-optimizely marked this conversation as resolved. Show resolved Hide resolved | ||
| } | ||
| | ||
| return qualifiedSegments; | ||
| } | ||
| | ||
| /// <summary> | ||
| /// Creates a key used to identify which user fetchQualifiedSegments should lookup and save to in the segments cache | ||
| /// </summary> | ||
| /// <param name="userKey">Always 'fs_user_id' (parameter for consistency with other SDKs)</param> | ||
| /// <param name="userValue">Arbitrary string representing the full stack user ID</param> | ||
| /// <returns>Concatenates inputs and returns the string "{userKey}-$-{userValue}"</returns> | ||
| private static string GetCacheKey(string userKey, string userValue) | ||
| { | ||
| return userKey + "-$-" + userValue; | ||
| return $"{userKey}-$-{userValue}"; | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.