Skip to content

Conversation

leoromanovsky
Copy link
Member

@leoromanovsky leoromanovsky commented Apr 26, 2024

Motivation and Context

1️⃣ the axios package cannot be supported in chrome extensions v3 because it is based on APIs that do not exist within a service worker. dogfooding eppo's js sdk within an v3 extension throws this error:

http-client.js:32 Uncaught (in promise) Error: There is no suitable adapter to dispatch the request since :
- adapter xhr is not supported by the environment
- adapter http is not available in the build
    at HttpClient.handleHttpError (http-client.js:32:1)
    at HttpClient.get (http-client.js:25:1)
    at async ExperimentConfigurationRequestor.fetchAndStoreConfigurations (experiment-configura…n-requestor.js:10:1)
    at async Object.start (poller.js:22:1)
    at async EppoJSClient.fetchFlagConfigurations (eppo-client.js:52:1)
    at async init (index.js:90:1)
    at async index.js:12:1
Show 2 more frames

2️⃣ axios is a heavy weight package and is something like 50% of our asset size. removing it will improve our customers' applications

Description

  • replace axios with fetch - supporting a base url and timeouts
  • adds IHttpClient interface and implementing FetchHttpClient
➜  js-client-sdk-common git:(lr/ff-1976/axios-fetch) yarn why axios
yarn why v1.22.22
[1/4] 🤔  Why do we have the module "axios"...?
[2/4] 🚚  Initialising dependency graph...
[3/4] 🔍  Finding dependency...
error We couldn't find a match!
✨  Done in 0.11s.

Decisions

Since the HttpClient is instantiated inside the EppoClient, instead of being injected in, I have no choice but to continue mocking the http API. Changing from xhr-mock to mocking fetch itself. In the future if we decide passing the http client is useful then we can mock that object directly.

👉 Could use a set of eyes on the beforeAlls in the unit tests - everything passes but something feels a little off to me.

How has this been tested?

@leoromanovsky leoromanovsky force-pushed the lr/ff-1976/axios-fetch branch from 19bbc01 to 4b6f32c Compare April 26, 2024 22:25
@leoromanovsky leoromanovsky force-pushed the lr/ff-1976/axios-fetch branch from 4b6f32c to 29135d4 Compare April 26, 2024 22:32

describe('Eppo Client constructed with configuration request parameters', () => {
let client: EppoClient;
let storage: IConfigurationStore;
Copy link
Member Author

Choose a reason for hiding this comment

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

I was finding that this variable seemed to be impacted by other tests - there is a bit of spaghetti here that I would love to unpack so things can run in a more isolated fashion.

Copy link
Contributor

Choose a reason for hiding this comment

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

Interesting. Yeah test interdependency is not good.

import { Evaluator, FlagEvaluation, noneResult } from '../evaluator';
import ExperimentConfigurationRequestor from '../flag-configuration-requestor';
import HttpClient from '../http-client';
import FlagConfigurationRequestor from '../flag-configuration-requestor';
Copy link
Member Author

Choose a reason for hiding this comment

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

the default exports in nodejs are kind of funny to me; noticed this wasn't renamed.

@leoromanovsky leoromanovsky marked this pull request as ready for review April 26, 2024 22:34
Copy link
Contributor

@aarsilv aarsilv left a comment

Choose a reason for hiding this comment

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

Nice work! 👏 This is a great modernization.

I agree the mocking got a bit all over. I think basically we separate that test into separate describe blocks for when we want the unobfuscated UFC, obfuscated UFC, or per-test. The first two just need to make one mock for all tests returning the appropriate UFC. The latter can not mock anything in a beforeAll(), leaving it up to the tests, but resetting in an afterEach() or afterAll(). I think that would clean things up.

Also, this would be a good time to sneak in changing the default for throwing error on initialization.


describe('EppoClient E2E test', () => {
const storage = new TestConfigurationStore();
global.fetch = jest.fn(() => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Jest runs each file in it's own process so I don't think it's necessary to clean this up. But you may want to, just in case. Also not necessary, but consider putting it in beforeAll() just to cluster that one-time stuff together.

beforeAll(async () => {
  const original fetch = fetch;
  global.fetch = jest.fn(() => {
    ...
   });   
  await init(storage);
});

afterAll(()=> {
  global.fetch = originalFetch;
})

Update: I see you do it later on. I think you could just ax this one entirely and just save off and restore fetch() at the top level. Then have two nested describes for unobfuscated and obfuscated responses, where you mock fetch()

Copy link
Contributor

@schmit schmit left a comment

Choose a reason for hiding this comment

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

🚀

@leoromanovsky leoromanovsky merged commit 863a24a into main Apr 30, 2024
@leoromanovsky leoromanovsky deleted the lr/ff-1976/axios-fetch branch April 30, 2024 20:38
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.

4 participants