Skip to content
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

fix: calling getVariableValue in server actions #1014

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions e2e/nextjs/app-router/app/app/normal/ClientComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
useAllFeatures,
renderIfEnabled,
} from '@devcycle/nextjs-sdk'
import { useState } from 'react'
import { testAction } from './action'

const ConditionalComponent = renderIfEnabled(
'enabled-feature',
Expand All @@ -16,6 +18,7 @@ export const ClientComponent = () => {
const disabledVar = useVariableValue('disabled-feature', false)
const allVariables = useAllVariables()
const allFeatures = useAllFeatures()
const [actionResult, setActionResult] = useState<boolean | null>(null)

return (
<div>
Expand All @@ -24,6 +27,14 @@ export const ClientComponent = () => {
<p>Client Disabled Variable: {JSON.stringify(disabledVar)}</p>
<p>Client All Variables: {JSON.stringify(allVariables)}</p>
<p>Client All Features: {JSON.stringify(allFeatures)}</p>
<p>Server Function Result: {JSON.stringify(actionResult)}</p>
<button
onClick={() =>
testAction().then((result) => setActionResult(result))
}
>
Test Action
</button>
<ConditionalComponent />
</div>
)
Expand Down
6 changes: 6 additions & 0 deletions e2e/nextjs/app-router/app/app/normal/ServerComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { headers } from 'next/headers'
import { getAllFeatures, getAllVariables, getVariableValue } from './devcycle'
export const ServerComponent = async () => {
const enabledVar = await getVariableValue('enabled-feature', false)
const disabledVar = await getVariableValue('disabled-feature', false)
const allVariables = await getAllVariables()
const allFeatures = await getAllFeatures()
const reqHeaders = await headers()

return (
<div>
Expand All @@ -12,6 +14,10 @@ export const ServerComponent = async () => {
<p>Server Disabled Variable: {JSON.stringify(disabledVar)}</p>
<p>Server All Variables: {JSON.stringify(allVariables)}</p>
<p>Server All Features: {JSON.stringify(allFeatures)}</p>
<p>
Middleware Enabled Feature:{' '}
{reqHeaders.get('Middleware-Enabled-Feature')}
</p>
</div>
)
}
8 changes: 8 additions & 0 deletions e2e/nextjs/app-router/app/app/normal/action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use server'

import { getVariableValue } from './devcycle'

export const testAction = async () => {
const result = await getVariableValue('enabled-feature', false)
return result
}
2 changes: 1 addition & 1 deletion e2e/nextjs/app-router/app/app/normal/devcycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const {
userGetter: async () => {
const reqHeaders = await headers()
return {
user_id: '123',
user_id: 'normal-user',
customData: {
// set a dummy field here so that the headers call stays in the build output
someKey: reqHeaders.get('some-key'),
Expand Down
19 changes: 19 additions & 0 deletions e2e/nextjs/app-router/app/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { NextResponse } from 'next/server'
import { getVariableValue } from './app/normal/devcycle'

export const middleware = async () => {
const response = NextResponse.next()
const variableValue = await getVariableValue('enabled-feature', false)

// Add custom header
response.headers.set(
'Middleware-Enabled-Feature',
JSON.stringify(variableValue),
)

return response
}

export const config = {
matcher: '/normal',
}
49 changes: 25 additions & 24 deletions e2e/nextjs/app-router/app/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6,67 +6,68 @@ __metadata:
cacheKey: 10

"@devcycle/bucketing@file:../../../../dist/lib/shared/bucketing::locator=app%40workspace%3A.":
version: 1.23.0
resolution: "@devcycle/bucketing@file:../../../../dist/lib/shared/bucketing#../../../../dist/lib/shared/bucketing::hash=3760ab&locator=app%40workspace%3A."
version: 1.25.0
resolution: "@devcycle/bucketing@file:../../../../dist/lib/shared/bucketing#../../../../dist/lib/shared/bucketing::hash=f620ff&locator=app%40workspace%3A."
dependencies:
"@devcycle/types": "npm:^1.18.0"
"@devcycle/types": "npm:^1.20.0"
lodash: "npm:^4.17.21"
murmurhash: "npm:^2.0.0"
ua-parser-js: "npm:^1.0.36"
checksum: 44395904d0770308ce33a41e7bf5aa82523b98ddf6a2e44d0a66e62a01ffbe76b0e931ad28def059afd260f296ca0f93c36e3960cc5f0a7f3d85b7a91b5fccd0
checksum: 26b227546ed21ed61ccbf917eb5f6d60e7c5a31fd0b61d015f92dd95b4c51b1f79182ef0cf9bf54ddbd423f4eda496a79af77d75460126ef59b543a8dbeab358
languageName: node
linkType: hard

"@devcycle/js-client-sdk@file:../../../../dist/sdk/js::locator=app%40workspace%3A.":
version: 1.31.0
resolution: "@devcycle/js-client-sdk@file:../../../../dist/sdk/js#../../../../dist/sdk/js::hash=40e7b0&locator=app%40workspace%3A."
version: 1.33.0
resolution: "@devcycle/js-client-sdk@file:../../../../dist/sdk/js#../../../../dist/sdk/js::hash=5d8b0d&locator=app%40workspace%3A."
dependencies:
"@devcycle/types": "npm:^1.18.0"
"@devcycle/types": "npm:^1.20.0"
fetch-retry: "npm:^5.0.6"
lodash: "npm:^4.17.21"
ua-parser-js: "npm:^1.0.36"
uuid: "npm:^8.3.2"
checksum: 3acdcbf8b351c08cf6b482e7a3e56aa9560a7e3589cfaf9fcd330624e101c3001479b68c457fa3a54384d1248b572f3ddb71364e6bf5ffbb385cd2f02dabb915
checksum: d27d6f6952f363ea71a3eb6865021d1af175d696a81284f36dd6e7dd134c00996f8506cf5c8c35cc7b9c5044dc30edbf9308d580c330ed6bad7623c98dc91e7a
languageName: node
linkType: hard

"@devcycle/nextjs-sdk@file:../../../../dist/sdk/nextjs::locator=app%40workspace%3A.":
version: 2.6.0
resolution: "@devcycle/nextjs-sdk@file:../../../../dist/sdk/nextjs#../../../../dist/sdk/nextjs::hash=f2c38c&locator=app%40workspace%3A."
version: 2.8.0
resolution: "@devcycle/nextjs-sdk@file:../../../../dist/sdk/nextjs#../../../../dist/sdk/nextjs::hash=7ea780&locator=app%40workspace%3A."
dependencies:
"@devcycle/bucketing": "npm:^1.23.0"
"@devcycle/js-client-sdk": "npm:^1.31.0"
"@devcycle/react-client-sdk": "npm:^1.29.0"
"@devcycle/types": "npm:^1.18.0"
"@devcycle/bucketing": "npm:^1.25.0"
"@devcycle/js-client-sdk": "npm:^1.33.0"
"@devcycle/react-client-sdk": "npm:^1.31.0"
"@devcycle/types": "npm:^1.20.0"
class-transformer: "npm:^0.5.1"
hoist-non-react-statics: "npm:^3.3.2"
server-only: "npm:^0.0.1"
checksum: 9d905c89316e3e87fa1ec663ef29298abca0d40d7603bb156f4942568a322cf45e581d84481d7f0c2957f73f5fb72fbb5818307042ebd1a6d4ba796317f5ce2a
checksum: a97ec2cd27c27cdef68d9b87bd99e32a4babe7a8bdbdb33941463a7651402b95864202c1a028870a471925a3aa5644d7fdfacdd730072a76cf2a429df8e89329
languageName: node
linkType: hard

"@devcycle/react-client-sdk@file:../../../../dist/sdk/react::locator=app%40workspace%3A.":
version: 1.29.0
resolution: "@devcycle/react-client-sdk@file:../../../../dist/sdk/react#../../../../dist/sdk/react::hash=53291e&locator=app%40workspace%3A."
version: 1.31.0
resolution: "@devcycle/react-client-sdk@file:../../../../dist/sdk/react#../../../../dist/sdk/react::hash=3e2fc6&locator=app%40workspace%3A."
dependencies:
"@devcycle/js-client-sdk": "npm:^1.31.0"
"@devcycle/types": "npm:^1.18.0"
"@devcycle/js-client-sdk": "npm:^1.33.0"
"@devcycle/types": "npm:^1.20.0"
hoist-non-react-statics: "npm:^3.3.2"
peerDependencies:
react: ">=16.8.0"
checksum: bc8140acca4d07fd4c5adba4e3b750b52314eaf2f2b8794b073d5878dc99a6904594ef1c10702420a29e3712ea3d0238b72a511e3d731adc1fe7fc0c053cba67
checksum: f022ea564ad4ef722b33afc295ccdd61eb44c03dec21f47a368d418297da3ba7ad398b175016d3ad9bc32f051159fc8c5d6a1875c4bca7bca01176cf133d3a2f
languageName: node
linkType: hard

"@devcycle/types@file:../../../../dist/lib/shared/types::locator=app%40workspace%3A.":
version: 1.18.0
resolution: "@devcycle/types@file:../../../../dist/lib/shared/types#../../../../dist/lib/shared/types::hash=4eb545&locator=app%40workspace%3A."
version: 1.20.0
resolution: "@devcycle/types@file:../../../../dist/lib/shared/types#../../../../dist/lib/shared/types::hash=49328d&locator=app%40workspace%3A."
dependencies:
class-transformer: "npm:0.5.1"
class-validator: "npm:0.14.1"
iso-639-1: "npm:^2.1.13"
lodash: "npm:^4.17.21"
reflect-metadata: "npm:^0.1.13"
checksum: 9422caa1a8dd9cdd356ac213b968faca1e656381a195efdc12118bf65a672f84930d0866716c0717507ea9b54b031c779d3d1427fd608a4f511b5ee4a93ceb0c
checksum: 37a261b629edf04b9225ac3a29001c4c7694269d78221533b401ceb351ebfcf16602e18049797fe0af3baab17e84ffe1d7834227eeb4850f67d00925bc001437
languageName: node
linkType: hard

Expand Down Expand Up @@ -378,7 +379,7 @@ __metadata:
languageName: node
linkType: hard

"class-transformer@npm:0.5.1":
"class-transformer@npm:0.5.1, class-transformer@npm:^0.5.1":
version: 0.5.1
resolution: "class-transformer@npm:0.5.1"
checksum: 750327e3e9a5cf233c5234252f4caf6b06c437bf68a24acbdcfb06c8e0bfff7aa97c30428184813e38e08111b42871f20c5cf669ea4490f8ae837c09f08b31e7
Expand Down
9 changes: 9 additions & 0 deletions e2e/nextjs/app-router/tests/app-router.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,15 @@
await expect(
page.getByText('Client Component Conditionally Bundled'),
).toBeVisible()

// test server action flagging
await page.getByText('Test Action').click()
await expect(page.getByText('Server Function Result: true')).toBeVisible()

// test middleware flagging
await expect(
page.getByText('Middleware Enabled Feature: true'),
).toBeVisible()
})

test('works after a client side navigation', async ({ page }) => {
Expand Down Expand Up @@ -127,18 +136,18 @@
).toBeVisible()
await expect(
page.getByText(
/Server All Variables: .*"key":"dvc_obfs_0f647e752bcd0586b2844212f720d0f3c9f892431a9b6f6a4076bb4e4ee68a5e","type":"Boolean"/,

Check warning on line 139 in e2e/nextjs/app-router/tests/app-router.spec.ts

View workflow job for this annotation

GitHub Actions / build (22.12)

This line has a length of 137. Maximum allowed is 120
),
).toBeVisible()
await expect(
page.getByText(
/Server All Features: .*"key":"dvc_obfs_6553a2aa0a2db40ce71b5fa1ecba4773236249e1e3b1b2270507c6b819e256f8","type":"permission"/,

Check warning on line 144 in e2e/nextjs/app-router/tests/app-router.spec.ts

View workflow job for this annotation

GitHub Actions / build (22.12)

This line has a length of 139. Maximum allowed is 120
),
).toBeVisible()

await expect(
page.getByText(
/Client All Variables: .*"key":"dvc_obfs_0f647e752bcd0586b2844212f720d0f3c9f892431a9b6f6a4076bb4e4ee68a5e","type":"Boolean"/,

Check warning on line 150 in e2e/nextjs/app-router/tests/app-router.spec.ts

View workflow job for this annotation

GitHub Actions / build (22.12)

This line has a length of 137. Maximum allowed is 120
),
).toBeVisible()
await expect(
Expand Down
14 changes: 0 additions & 14 deletions sdk/nextjs/src/server/allFeatures.ts

This file was deleted.

14 changes: 0 additions & 14 deletions sdk/nextjs/src/server/getAllVariables.ts

This file was deleted.

27 changes: 0 additions & 27 deletions sdk/nextjs/src/server/getVariableValue.ts

This file was deleted.

91 changes: 40 additions & 51 deletions sdk/nextjs/src/server/initialize.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { DevCycleUser, initializeDevCycle } from '@devcycle/js-client-sdk'
import { getClient, setClient } from './requestContext'
import {
DevCycleClient,
DevCycleUser,
initializeDevCycle,
} from '@devcycle/js-client-sdk'
import { getUserAgent } from './userAgent'
import { DevCycleNextOptions, DevCycleServerData } from '../common/types'
import { cache } from 'react'
Expand All @@ -23,61 +26,47 @@ const cachedUserGetter = cache(
},
)

export const initialize = async (
sdkKey: string,
clientSDKKey: string,
userGetter: () => DevCycleUser | Promise<DevCycleUser>,
options: DevCycleNextOptions = {},
): Promise<DevCycleServerData> => {
const [userAgent, user, configData] = await Promise.all([
getUserAgent(options),
cachedUserGetter(userGetter),
getConfigFromSource(sdkKey, clientSDKKey, options),
])

if (!user || typeof user.user_id !== 'string') {
throw new Error('DevCycle user getter must return a user')
}

const initializeAlreadyCalled = !!getClient()

if (!initializeAlreadyCalled) {
setClient(
initializeDevCycle(sdkKey, user, {
...options,
deferInitialization: true,
...jsClientOptions,
}),
)
}
export const initialize = cache(
async (
sdkKey: string,
clientSDKKey: string,
userGetter: () => DevCycleUser | Promise<DevCycleUser>,
options: DevCycleNextOptions = {},
): Promise<DevCycleServerData & { client: DevCycleClient }> => {
const [userAgent, user, configData] = await Promise.all([
getUserAgent(options),
cachedUserGetter(userGetter),
getConfigFromSource(sdkKey, clientSDKKey, options),
])

let config = null
try {
config = await getBucketedConfig(
configData.config,
configData.lastModified,
user,
options,
userAgent,
)
} catch (e) {
console.error('Error fetching DevCycle config', e)
}
if (!user || typeof user.user_id !== 'string') {
throw new Error('DevCycle user getter must return a user')
}

const client = getClient()
const client = initializeDevCycle(sdkKey, user, {
...options,
deferInitialization: true,
...jsClientOptions,
})

if (!client) {
throw new Error(
"React 'cache' function not working as expected. Please contact DevCycle support.",
)
}
let config = null
try {
config = await getBucketedConfig(
configData.config,
configData.lastModified,
user,
options,
userAgent,
)
} catch (e) {
console.error('Error fetching DevCycle config', e)
}

if (!initializeAlreadyCalled) {
client.synchronizeBootstrapData(config, user, userAgent)
}

return { config, user, userAgent }
}
return { config, user, userAgent, client }
},
)

export const validateSDKKey = (
sdkKey: string,
Expand Down
Loading
Loading