diff --git a/desktop/app/src/utils/flipperLibImplementation.tsx b/desktop/app/src/utils/flipperLibImplementation.tsx index 742990bcc..0028685ef 100644 --- a/desktop/app/src/utils/flipperLibImplementation.tsx +++ b/desktop/app/src/utils/flipperLibImplementation.tsx @@ -11,6 +11,7 @@ import type {FlipperLib} from 'flipper-plugin'; import type {Logger} from '../fb-interfaces/Logger'; import type {Store} from '../reducers'; import createPaste from '../fb-stubs/createPaste'; +import GK from '../fb-stubs/GK'; let flipperLibInstance: FlipperLib | undefined; @@ -25,6 +26,9 @@ export function initializeFlipperLibImplementation( addSandyPluginEntries(entries); }, createPaste, + GK(gatekeeper: string) { + return GK.get(gatekeeper); + }, }; } diff --git a/desktop/flipper-plugin/src/__tests__/test-utils.node.tsx b/desktop/flipper-plugin/src/__tests__/test-utils.node.tsx index cd222bc06..449f29aa7 100644 --- a/desktop/flipper-plugin/src/__tests__/test-utils.node.tsx +++ b/desktop/flipper-plugin/src/__tests__/test-utils.node.tsx @@ -401,3 +401,31 @@ test('available methods can be overridden', async () => { ); expect(await plugin.instance.checkEnabled()).toBeFalsy(); }); + +test('GKs are supported', () => { + const pluginModule = { + plugin(client: PluginClient<{}, {}>) { + return { + isTest() { + return client.GK('bla'); + }, + }; + }, + Component() { + return null; + }, + }; + + { + const plugin = TestUtils.startPlugin(pluginModule); + expect(plugin.instance.isTest()).toBe(false); + } + { + const plugin = TestUtils.startPlugin(pluginModule, {GKs: ['bla']}); + expect(plugin.instance.isTest()).toBe(true); + } + { + const plugin = TestUtils.startPlugin(pluginModule, {GKs: ['x']}); + expect(plugin.instance.isTest()).toBe(false); + } +}); diff --git a/desktop/flipper-plugin/src/plugin/FlipperLib.tsx b/desktop/flipper-plugin/src/plugin/FlipperLib.tsx index 4e6ecdff6..485fdfb3b 100644 --- a/desktop/flipper-plugin/src/plugin/FlipperLib.tsx +++ b/desktop/flipper-plugin/src/plugin/FlipperLib.tsx @@ -15,4 +15,5 @@ import {NormalizedMenuEntry} from './MenuEntry'; export interface FlipperLib { enableMenuEntries(menuEntries: NormalizedMenuEntry[]): void; createPaste(input: string): Promise; + GK(gatekeeper: string): boolean; } diff --git a/desktop/flipper-plugin/src/plugin/PluginBase.tsx b/desktop/flipper-plugin/src/plugin/PluginBase.tsx index 15e6f3723..97481ca53 100644 --- a/desktop/flipper-plugin/src/plugin/PluginBase.tsx +++ b/desktop/flipper-plugin/src/plugin/PluginBase.tsx @@ -48,6 +48,12 @@ export interface BasePluginClient { * Facebook only function. Resolves to undefined if creating a paste failed. */ createPaste(input: string): Promise; + + /** + * Returns true if the user is taking part in the given gatekeeper. + * Always returns `false` in open source. + */ + GK(gkName: string): boolean; } let currentPluginInstance: BasePluginInstance | undefined = undefined; @@ -155,6 +161,7 @@ export abstract class BasePluginInstance { } }, createPaste: this.flipperLib.createPaste, + GK: this.flipperLib.GK, }; } diff --git a/desktop/flipper-plugin/src/test-utils/test-utils.tsx b/desktop/flipper-plugin/src/test-utils/test-utils.tsx index d01de0fee..1b43662b3 100644 --- a/desktop/flipper-plugin/src/test-utils/test-utils.tsx +++ b/desktop/flipper-plugin/src/test-utils/test-utils.tsx @@ -46,6 +46,10 @@ interface StartPluginOptions { startUnactivated?: boolean; /** Provide a set of unsupported methods to simulate older clients that don't support certain methods yet */ unsupportedMethods?: string[]; + /** + * Provide a set of GKs that are enabled in this test. + */ + GKs?: string[]; } type ExtractClientType> = Parameters< @@ -179,7 +183,7 @@ export function startPlugin>( } const sendStub = jest.fn(); - const flipperUtils = createMockFlipperLib(); + const flipperUtils = createMockFlipperLib(options); const testDevice = createMockDevice(options); const appName = 'TestApplication'; const deviceName = 'TestDevice'; @@ -290,7 +294,7 @@ export function startDevicePlugin( ); } - const flipperLib = createMockFlipperLib(); + const flipperLib = createMockFlipperLib(options); const testDevice = createMockDevice(options); const pluginInstance = new SandyDevicePluginInstance( flipperLib, @@ -340,10 +344,13 @@ export function renderDevicePlugin( }; } -export function createMockFlipperLib(): FlipperLib { +export function createMockFlipperLib(options?: StartPluginOptions): FlipperLib { return { enableMenuEntries: jest.fn(), createPaste: jest.fn(), + GK(gk: string) { + return options?.GKs?.includes(gk) || false; + }, }; } diff --git a/docs/extending/flipper-plugin.mdx b/docs/extending/flipper-plugin.mdx index 8e593c1b5..59573fa73 100644 --- a/docs/extending/flipper-plugin.mdx +++ b/docs/extending/flipper-plugin.mdx @@ -218,6 +218,14 @@ The returned promise either contains a string with the URL of the paste, or `und Details of the failure will be communicated back directly to the user through Flipper notifications. For example if the user is currently not signed in. +#### `GK` + +Facebook only API. + +Usage: `client.GK(gatekeeper: string): boolean` + +Returns `true` if the current user is part of the given GK. `false` in all other cases. + ## DevicePluginClient ### Properties @@ -435,6 +443,7 @@ The `options` argument is optional, but can specify the following fields: * `isArchived: boolean`: Setting this flag, will set the `isArchived` on the mocked device as well. Set it if you want to test the behavior of your plugin for imported devices (see also [`Device.isArchived`](#isarchived)). Defaults to `false`. * `isBackgroundPlugin`: This makes sure the test runner emits life-cycle events in a way that is typical for background plugins. Defaults to `false`. The notable difference in behavior is that calling `.active()` on the test runner won't trigger the `connect` event to be fired, nor the `.deactivate()` the `disconnect` event. * `startUnactivated`: This does not activate the plugin; `connect` needs to be explicitly called. This can be used in case setting mock implementation for `onSend` is required to make sure Client plugin works as expected. Defaults to `false`. +* `GKs`: A string array of gatekeeper names for which `client.GK` will `true` inside the test. By default GKs are assumed to be disabled inside unit tests. #### The test runner object