Skip to content

EDSC-4678: Adds GrowthBook SDK for A/B testing - #2079

Open
macrouch wants to merge 4 commits into
mainfrom
EDSC-4678
Open

EDSC-4678: Adds GrowthBook SDK for A/B testing#2079
macrouch wants to merge 4 commits into
mainfrom
EDSC-4678

Conversation

@macrouch

Copy link
Copy Markdown
Contributor

Overview

What is the feature?

Adds GrowthBook SDK for A/B testing

What is the Solution?

Adds new Zustand slice to hold GrowthBook details
Creates a random user id and session id for users
Wraps the GrowthBook code around a deployment variable to turn on/off new code
Adds a new call to LoggerRequest for experiment event logging
Adds a new lambda to send experiment events to AWS Data Firehose

What areas of the application does this impact?

Adds new pieces

Testing

You can set up GrowthBook to run locally with a docker compose file. Create a user, create a feature flag (probably more things you have to create but I can't remember). Name the feature flag nlpSearch and have it service a boolean with a default value of true or false, and you can see it enable/disable the nlpSearch on the home page.

Add the logEvent util to the code in changeQuery to see it submit to the lambda to be logged.

Checklist

  • I have added automated tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • I have run npm audit fix and made note of any changes in this PR
  • My changes generate no new warnings

@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown

Bundle Size Comparison

Full build details

> earthdata-search@1.0.0 build
> vite build

vite v6.4.3 building for production...
transforming...
✓ 4044 modules transformed.
rendering chunks...
computing gzip size...
static/dist/assets/logo-Bl7CmmEi.webp                                                      0.98 kB
static/dist/assets/logo-CZz0VEVL.webp                                                      1.14 kB
static/dist/assets/logo-Cycj4Zl7.webp                                                      1.33 kB
static/dist/assets/logo-U_2b2F1P.webp                                                      1.37 kB
static/dist/assets/logo-DrQyzv8O.webp                                                      1.54 kB
static/dist/assets/logo-BtXPiD8D.webp                                                      1.81 kB
static/dist/assets/logo-CBoC93bb.webp                                                      2.16 kB
static/dist/assets/logo-DjzD1LMz.webp                                                      2.39 kB
static/dist/assets/orange-bars-circle-D7AglAGM.webp                                        2.77 kB
static/dist/assets/logo-mTUaHBfB.webp                                                      3.11 kB
static/dist/assets/blue-bars-circle-BoIdEwIS.webp                                          3.67 kB
static/dist/assets/logo-BRHnty9h.webp                                                      4.09 kB
static/dist/assets/cryosphere-icon-DSWEvPIt.svg                                            4.11 kB │ gzip:   1.64 kB
static/dist/assets/logo-DPcWY5XI.webp                                                      4.25 kB
static/dist/assets/logo-BI7QOwpp.webp                                                      4.72 kB
static/dist/assets/logo-CjKnSDvE.webp                                                      7.06 kB
static/dist/index.html                                                                    13.24 kB │ gzip:   3.10 kB
static/dist/assets/plate_carree_earth_scaled-RfqGhO39.png                                 18.14 kB
static/dist/assets/tour-video-thumbnail-BxsZeqvq.webp                                     18.84 kB
static/dist/assets/plate_carree_earth_scaled@2x-BIj0Wstc.png                              45.03 kB
static/dist/assets/image-unavailable-CcXgOtQr.svg                                         52.20 kB │ gzip:  20.77 kB
static/dist/assets/earthdata-search-og-image-Cs5RILJ1.webp                                92.82 kB
static/dist/assets/MODIS-Terra-Swirling-Clouds-In-Atlantic-800x600@2x-DdSR_KXb.webp      132.70 kB
static/dist/assets/MODIS-Terra-Swirling-Clouds-In-Atlantic-2560x1440@2x-CZbMhKXi.webp    224.17 kB
static/dist/assets/MODIS-Terra-Swirling-Clouds-In-Atlantic-800x600@2x-DFWWupvf.webp      381.18 kB
static/dist/assets/MODIS-Terra-Swirling-Clouds-In-Atlantic-2560x1440@2x-BwDp59vN.webp    464.37 kB
static/dist/assets/MODIS-Terra-Swirling-Clouds-In-Atlantic-2560x1440@2x-CeYlmMCD.webp    737.44 kB
static/dist/assets/MODIS-Terra-Swirling-Clouds-In-Atlantic-2560x1440@2x-tQ4uTIyn.webp  1,255.87 kB
static/dist/assets/MODIS-Terra-Swirling-Clouds-In-Atlantic-2560x1440@2x-BqbXRJmQ.webp  1,516.81 kB
static/dist/assets/AdminRetrieval-nNSujMnn.css                                             0.06 kB │ gzip:   0.08 kB
static/dist/assets/AdminPreferencesMetrics-GkGryAtC.css                                    0.10 kB │ gzip:   0.11 kB
static/dist/assets/ChunkedOrderModal-DIli9vpQ.css                                          0.11 kB │ gzip:   0.10 kB
static/dist/assets/TooManyPointsModal-D8ufPyjz.css                                         0.14 kB │ gzip:   0.12 kB
static/dist/assets/AdminRetrievalsMetrics-CT7bgXZt.css                                     0.22 kB │ gzip:   0.13 kB
static/dist/assets/AdminPage-D19229XC.css                                                  0.23 kB │ gzip:   0.16 kB
static/dist/assets/ContactInfo-C7Pr11wu.css                                                0.28 kB │ gzip:   0.18 kB
static/dist/assets/useDeleteSubscription-YPccXDF3.css                                      0.46 kB │ gzip:   0.23 kB
static/dist/assets/ShapefileDropzoneContainer-BG7JE7Dj.css                                 0.53 kB │ gzip:   0.26 kB
static/dist/assets/GranuleFiltersContainer-CfwvM6Mp.css                                    0.59 kB │ gzip:   0.27 kB
static/dist/assets/ShapefileUploadModal-CMzmPyoZ.css                                       0.66 kB │ gzip:   0.32 kB
static/dist/assets/Preferences-DGkx-xES.css                                                0.68 kB │ gzip:   0.24 kB
static/dist/assets/Skeleton-DtMOdDsM.css                                                   0.88 kB │ gzip:   0.34 kB
static/dist/assets/TextWindowActions-aNefZur7.css                                          0.91 kB │ gzip:   0.38 kB
static/dist/assets/RelatedCollection-BaShQZdF.css                                          0.93 kB │ gzip:   0.40 kB
static/dist/assets/CollectionDetailsHighlights-CHDYEfjF.css                                1.24 kB │ gzip:   0.44 kB
static/dist/assets/GranuleResultsHighlights-BIY0O9bb.css                                   1.24 kB │ gzip:   0.41 kB
static/dist/assets/Subscriptions-CYrzsucf.css                                              1.26 kB │ gzip:   0.40 kB
static/dist/assets/EDSCModalContainer-D_JXXJBf.css                                         1.77 kB │ gzip:   0.59 kB
static/dist/assets/AdminProjects-CZeT2VXI.css                                              1.98 kB │ gzip:   0.46 kB
static/dist/assets/AdminRetrievals-DIdJ2AD-.css                                            2.02 kB │ gzip:   0.46 kB
static/dist/assets/DownloadHistory-C2TrZbdc.css                                            2.05 kB │ gzip:   0.47 kB
static/dist/assets/DeprecatedParameterModal-nutVvbLm.css                                   2.12 kB │ gzip:   0.93 kB
static/dist/assets/Projects-5m-RoZO0.css                                                   2.13 kB │ gzip:   0.51 kB
static/dist/assets/EchoForm-VVUehB8V.css                                                   3.47 kB │ gzip:   1.06 kB
static/dist/assets/SearchTour-AJompJPK.css                                                 4.82 kB │ gzip:   1.22 kB
static/dist/assets/index-BOElThXL.css                                                      6.47 kB │ gzip:   1.24 kB
static/dist/assets/OrderStatus-qyLNmnfS.css                                                8.49 kB │ gzip:   1.93 kB
static/dist/assets/createSpatialDisplay-CiT4b-tQ.css                                      10.62 kB │ gzip:   2.41 kB
static/dist/assets/MapContainer-GerI1QoD.css                                              17.00 kB │ gzip:   5.89 kB
static/dist/assets/Project-RbLZgqcX.css                                                   20.04 kB │ gzip:   4.07 kB
static/dist/assets/Search-DWJQ45Qd.css                                                    69.43 kB │ gzip:  10.66 kB
static/dist/assets/index-CgZEHIcM.css                                                    342.64 kB │ gzip: 110.16 kB
static/dist/assets/logo-BHeuky8i.js                                                        0.06 kB │ gzip:   0.08 kB
static/dist/assets/logo-C2M9lK3U.js                                                        0.06 kB │ gzip:   0.08 kB
static/dist/assets/logo-BbJQ0fWs.js                                                        0.06 kB │ gzip:   0.08 kB
static/dist/assets/logo-CW6PQqmQ.js                                                        0.06 kB │ gzip:   0.08 kB
static/dist/assets/logo-CM8gX-og.js                                                        0.06 kB │ gzip:   0.08 kB
static/dist/assets/logo-CGJHAKVD.js                                                        0.06 kB │ gzip:   0.08 kB
static/dist/assets/logo-DOLER09h.js                                                        0.06 kB │ gzip:   0.08 kB
static/dist/assets/logo-BXFnLkSI.js                                                        0.06 kB │ gzip:   0.08 kB
static/dist/assets/logo-J7t-4CBp.js                                                        0.06 kB │ gzip:   0.08 kB
static/dist/assets/logo-CxvnVTz2.js                                                        0.06 kB │ gzip:   0.08 kB
static/dist/assets/logo-C9HDQHSy.js                                                        0.06 kB │ gzip:   0.08 kB
static/dist/assets/logo-IHV3gtAZ.js                                                        0.06 kB │ gzip:   0.08 kB
static/dist/assets/logo-Dedvhxzp.js                                                        0.06 kB │ gzip:   0.08 kB
static/dist/assets/logo--kH-hKRS.js                                                        0.06 kB │ gzip:   0.08 kB
static/dist/assets/logo-CBz1b7O8.js                                                        0.06 kB │ gzip:   0.08 kB
static/dist/assets/logo-nOuRrv94.js                                                        0.06 kB │ gzip:   0.08 kB
static/dist/assets/logo-DIxV4RPX.js                                                        0.06 kB │ gzip:   0.08 kB
static/dist/assets/logo-uABdcgsF.js                                                        0.06 kB │ gzip:   0.08 kB
static/dist/assets/pluralize-49vSaAHF.js                                                   0.08 kB │ gzip:   0.09 kB
static/dist/assets/accessMethodTypes-CY5JnOnd.js                                           0.13 kB │ gzip:   0.14 kB
static/dist/assets/commafy-BfQYQOA4.js                                                     0.14 kB │ gzip:   0.14 kB
static/dist/assets/requestDebounceDuration-B92It3cW.js                                     0.16 kB │ gzip:   0.11 kB
static/dist/assets/subscriptions-BCwAKWn8.js                                               0.41 kB │ gzip:   0.21 kB
static/dist/assets/DownloadsLayout-BWuNgiSU.js                                             0.44 kB │ gzip:   0.25 kB
static/dist/assets/Table-CPuf_6EE.js                                                       0.59 kB │ gzip:   0.38 kB
static/dist/assets/DefinitionList-B3Ux0AG_.js                                              0.63 kB │ gzip:   0.39 kB
static/dist/assets/AdminLayout-C43BaqL3.js                                                 0.63 kB │ gzip:   0.36 kB
static/dist/assets/AdminPage-4O2Hdwvm.js                                                   0.68 kB │ gzip:   0.36 kB
static/dist/assets/TooManyPointsModal-DRKX-RJT.js                                          0.74 kB │ gzip:   0.49 kB
static/dist/assets/humanizedQueryValueFormatters-z-QpfuHK.js                               1.00 kB │ gzip:   0.54 kB
static/dist/assets/KeyboardShortcutsModal-UV4RzQwV.js                                      1.24 kB │ gzip:   0.72 kB
static/dist/assets/metricsDataAccess-B0dv9eSs.js                                           1.26 kB │ gzip:   0.35 kB
static/dist/assets/Skeleton-BDEbBmOU.js                                                    1.28 kB │ gzip:   0.58 kB
static/dist/assets/AdminIndex-Bd2V7-Pp.js                                                  1.36 kB │ gzip:   0.71 kB
static/dist/assets/index-Zi8wnWhN.js                                                       1.64 kB │ gzip:   0.94 kB
static/dist/assets/AdminProject-hrsO0gCj.js                                                1.68 kB │ gzip:   0.86 kB
static/dist/assets/useLazyQuery-CrOrtEOm.js                                                1.70 kB │ gzip:   0.89 kB
static/dist/assets/ShapefileUploadModal-BbEpBeH0.js                                        1.83 kB │ gzip:   0.81 kB
static/dist/assets/DeprecatedParameterModal-DUc6X8XR.js                                    2.07 kB │ gzip:   1.16 kB
static/dist/assets/EditSubscriptionModal-Cpwz4xi4.js                                       2.23 kB │ gzip:   1.13 kB
static/dist/assets/EarthdataDownloadRedirect-BVnln5ql.js                                   2.24 kB │ gzip:   0.90 kB
static/dist/assets/AboutCSDAModal-DFJMU1_l.js                                              2.25 kB │ gzip:   0.99 kB
static/dist/assets/ChunkedOrderModal-Bb-tJlRw.js                                           2.34 kB │ gzip:   1.20 kB
static/dist/assets/AdminPreferencesMetrics-BvICheZX.js                                     2.41 kB │ gzip:   0.84 kB
static/dist/assets/useCreateRetrieval-D1NoFrv_.js                                          3.26 kB │ gzip:   1.59 kB
static/dist/assets/DownloadHistory-BxWf2N23.js                                             3.41 kB │ gzip:   1.42 kB
static/dist/assets/GranuleResultsHighlights-BxaJ9Yjl.js                                    3.66 kB │ gzip:   1.35 kB
static/dist/assets/CollectionDetailsHighlights-F9qUS8Z8.js                                 3.77 kB │ gzip:   0.99 kB
static/dist/assets/EDSCModalContainer-DveIhU86.js                                          4.24 kB │ gzip:   1.46 kB
static/dist/assets/AdminRetrieval-DYB18td5.js                                              4.29 kB │ gzip:   1.78 kB
static/dist/assets/AdminProjects-CNB1fZvq.js                                               4.35 kB │ gzip:   1.59 kB
static/dist/assets/Projects-BW0wtwrU.js                                                    4.40 kB │ gzip:   1.72 kB
static/dist/assets/AdminRetrievals-su2hqmE1.js                                             4.60 kB │ gzip:   1.65 kB
static/dist/assets/AboutCwicModal-BfSvJhhl.js                                              4.69 kB │ gzip:   1.92 kB
static/dist/assets/ContactInfo-DyTdHvj2.js                                                 4.71 kB │ gzip:   1.55 kB
static/dist/assets/Subscriptions-SYVNk5kW.js                                               4.79 kB │ gzip:   1.71 kB
static/dist/assets/AdminRetrievalsMetrics-bCQAxRHp.js                                      4.82 kB │ gzip:   1.64 kB
static/dist/assets/useDeleteSubscription-Du9fyihm.js                                       6.35 kB │ gzip:   2.44 kB
static/dist/assets/RelatedCollection-CgneoFbA.js                                          10.41 kB │ gzip:   3.99 kB
static/dist/assets/Modal-C0nr3L4G.js                                                      10.74 kB │ gzip:   4.05 kB
static/dist/assets/GranuleFiltersContainer-B1hLCicv.js                                    21.83 kB │ gzip:   5.91 kB
static/dist/assets/styles-DxjJPldU.js                                                     32.69 kB │ gzip:  10.77 kB
static/dist/assets/index-BhlC0V5o.js                                                      35.34 kB │ gzip:  12.82 kB
static/dist/assets/moment-BAtM24Ew.js                                                     60.92 kB │ gzip:  19.77 kB
static/dist/assets/createSpatialDisplay-uCW6tWAs.js                                       60.95 kB │ gzip:  17.02 kB
static/dist/assets/OrderStatus-D1DDkCcH.js                                                63.29 kB │ gzip:  17.62 kB
static/dist/assets/lodash-DR7EQ6YQ.js                                                     92.24 kB │ gzip:  31.91 kB
static/dist/assets/EchoForm-BrPudv-6.js                                                  108.53 kB │ gzip:  33.67 kB
static/dist/assets/SearchTour-C-Y9Goc2.js                                                117.44 kB │ gzip:  35.77 kB
static/dist/assets/ShapefileDropzoneContainer-e16JTVy9.js                                119.66 kB │ gzip:  40.21 kB
static/dist/assets/edscUtils-6LLLH5LD.js                                                 141.73 kB │ gzip:  46.95 kB
static/dist/assets/react-D1lqFcfa.js                                                     230.00 kB │ gzip:  74.34 kB
static/dist/assets/Project-CpVxGEi9.js                                                   232.42 kB │ gzip:  83.87 kB
static/dist/assets/Preferences-D5yFWD6K.js                                               362.82 kB │ gzip: 115.23 kB
static/dist/assets/Search-Cx8KsYH1.js                                                    493.88 kB │ gzip: 143.94 kB
static/dist/assets/MapContainer-CKFBm8cl.js                                            1,199.70 kB │ gzip: 233.16 kB
static/dist/assets/index-D2x2h4pv.js                                                   1,280.99 kB │ gzip: 400.42 kB
✓ built in 35.66s

The full bundle is larger than main by 46.45 kB. ❗

The index.js is larger than main by 46.45 kB. ❗

Run npx vite-bundle-visualizer to review the bundle in more detail.

File Main Branch Diff
Total 10227.59 kB 10274.04 kB 46.45 kB
index.js 1271.52 kB 1317.97 kB 46.45 kB
index.js (gzip) 399.99 kB 414.18 kB 14.19 kB
index.css 349.11 kB 349.11 kB 0 kB
index.css (gzip) 111.4 kB 111.4 kB 0 kB
Number of files 139 139 0
Build Time 24.72 s 35.66 s 10.94 s

@codecov

codecov Bot commented Aug 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.45455% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.84%. Comparing base (33e07e1) to head (9849c34).

Files with missing lines Patch % Lines
...components/GrowthBookWrapper/GrowthBookWrapper.tsx 83.33% 2 Missing ⚠️
static/src/js/util/metrics/experiments/logEvent.ts 93.33% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2079      +/-   ##
==========================================
- Coverage   95.84%   95.84%   -0.01%     
==========================================
  Files         756      761       +5     
  Lines       17776    17840      +64     
  Branches     4978     5026      +48     
==========================================
+ Hits        17037    17098      +61     
- Misses        687      690       +3     
  Partials       52       52              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread serverless/src/experimentLogger/handler.js Outdated
Comment thread bin/deploy-bamboo.sh
config="`jq '.application.nlpSearch = $newValue' --arg newValue $bamboo_NLP_SEARCH <<< $config`"
config="`jq '.application.numberOfGranules = $newValue' --arg newValue "$bamboo_NUMBER_OF_GRANULES" <<< $config`"
config="`jq '.application.placeLabelsStyleUrl = $newValue' --arg newValue $bamboo_PLACE_LABELS_STYLE_URL <<< $config`"
config="`jq '.application.growthbookEnabled = $newValue' --arg newValue $bamboo_GROWTHBOOK_ENABLED <<< $config`"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

--argjson, --arg evaluates everything to a string

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I like the idea of switching this, but I wouldn't only change this field, I'd want every boolean to behave the same way.

But that also means that environment variables used in a lambda would behave differently than those in React, because they are set within the dockerRun function in this file.

We should put a little thought into that separately

event_type: eventType,
event_data: eventData,
session_id: gbSessionId,
user_id: gbUserId

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

we'll probably tweak this quite a bit, but I think it's worth adding the window.location.pathname as well

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm just guessing on most of these, so I'm not going to change it for this PR. On the next ticket when we learn more that would be when we need to change these


expect(localStorageSetItemSpy).toHaveBeenCalledTimes(2)
expect(localStorageSetItemSpy).toHaveBeenNthCalledWith(1, 'gbUserId', expect.any(String))
expect(localStorageSetItemSpy).toHaveBeenNthCalledWith(2, 'gbSessionId', expect.any(String))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we make these more specific? Hard to be certain userId is actually the userId and not the sessionId

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure how to be more specific. This is ensuring that we are setting gbUserId and gbSessionId. We don't need to test that crypto.randomUUID() is giving us a specific string

})
})

test('renders the spinner then the children', async () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this just tests the spinner

useEffect(() => {
const gbUserId = window.localStorage.getItem('gbUserId')
if (!gbUserId) {
const newGbUserId = crypto.randomUUID()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thinking about the test, could make this user-${crypto.randomUUID} to help differentiate it from the sessionId

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We don't like writing code specifically for tests if we can avoid it. Adding a prefix to this value would only add meaningless characters to every row of data we end up querying downstream. We know its a user or session because they are labeled correctly in the places they are stored

const growthbook = new GrowthBook({
apiHost: growthbookApiHost,
clientKey: growthbookClientKey,
enableDevMode: true,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

base off of env instead

})
}
})
growthbook.init({})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we check to see if growthbook has already been intialized first?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

There is annoying little documentation on this method, so I don't know if you can check.

But this component will only be rendered once. The only trigger it has to re-render is if the children prop changes, which can't happen


import useEdscStore from '../../../zustand/useEdscStore'

const logEvent = async (eventKey: string, eventType: string, eventData: string) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

eventData: object?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

My guess here is that we just need a simple data type to query downstream, so I went with string thinking that it would be which filter was changed. But in the next ticket when we know more it could change

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.

3 participants