growthbook / growthbook-proxy

:zap: GrowthBook Proxy Server - Caching, Streaming, Security, and Scalability
MIT License
25 stars 12 forks source link

[Bug] Payload from Cloudflare KV using webhook doesn't work with init #61

Closed arnarthor closed 2 months ago

arnarthor commented 2 months ago

Wasn't sure if I should file this issue here or in the growthbook/growthbook repo. Feel free to close this if the issue in the other repo is sufficient Link to other issue [Bug] Payload from Cloudflare KV using webhook doesn't work with init

Summary

When using a webhook to send data to Cloudflare KV and then using the getPayloadFromKV function to instantiate a Growthbook instance with the data the feature set is always empty.

This is due to the fact that the payload from KV includes webhook metadata that isn't parsed out. The payload read from KV looks like this

{
  type: 'payload.changed',
  timestamp: '2024-07-02T15:24:11.657Z',
  data: {
    payload: '{"features":{"phone-number-filter":{"defaultValue":"nova","rules":[{"condition":{"phone":{"$regex":"\\\\+62"}},"force":"twilio"},{"condition":{"phone":{"$regex":"\\\\+3547778"}},"force":"twilio"}]}},"dateUpdated":"2024-07-02T14:35:30.807Z"}'
  }
}

But the only thing that is supposed to be sent to the init function is the data part of that payload. Additionally the payload that should be passed in, the data part, is a string, since it's a JSON value in KV that hasn't been parsed before being returned.

Expected Behavior

const payload = await getPayloadFromKV(c.env);
const growthbook = new GrowthBook({
  apiHost: 'https://cdn.growthbook.io',
  clientKey: c.env.GROWTHBOOK_CLIENT_KEY,
});

await growthbook.init({
  payload,
});

This code snippet which is taken from this documentation should work

Current Behavior

To pre populate the feature set I need to add this additional code with relevant typescript hacks to make it work

const payloadFromKv = await getPayloadFromKV(c.env);
const growthbook = new GrowthBook({
  apiHost: 'https://cdn.growthbook.io',
  clientKey: c.env.GROWTHBOOK_CLIENT_KEY,
});
try {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const payload = JSON.parse((payloadFromKv as any).data.payload);
  await growthbook.init({
    payload,
    streaming: false,
  });
} catch (e) {
  debugLog({ message: 'Using default init with request' });
  await growthbook.init();
}
bryce-fitzsimons commented 2 months ago

Thanks for this feedback! It was definitely working even with payload metadata when I last tested it, but maybe I just got lucky. Let me think about a few solutions here... it's likely we'd use some sort of webhook formatter in the GB app to match the CF KV endpoint.

arnarthor commented 2 months ago

Yeah, I anticipated this having worked at some point and the person writing the docs wasn't just guessing. The main indicator to me that something has changed with the webhook or with the library since then is that the typescript type for the getPayloadFromKV doesn't match the actual type it returns at all.

export type FeatureApiResponse = {
  features?: FeatureDefinitions;
  dateUpdated?: string;
  encryptedFeatures?: string;
  experiments?: AutoExperiment[];
  encryptedExperiments?: string;
};

This looks like the type of payload.data rather then payload

bryce-fitzsimons commented 2 months ago

Hopefully this PR (webhook formatter) solves the problem. https://github.com/growthbook/growthbook/pull/2721

bryce-fitzsimons commented 2 months ago

Having deployed the webhook formatter, I'm closing this issue. Feel free to re-open if you're still having trouble here.