Skip to content

Commit

Permalink
feat: support custom name resolver
Browse files Browse the repository at this point in the history
Signed-off-by: Faisal Rafiuddin <[email protected]>
  • Loading branch information
faisalBooking committed Feb 4, 2025
1 parent 9de707a commit 0497bd8
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 5 deletions.
12 changes: 12 additions & 0 deletions libs/providers/flagd/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Options can be defined in the constructor or as environment variables. Construct
| selector | FLAGD_SOURCE_SELECTOR | string | - | |
| cache | FLAGD_CACHE | string | lru | lru, disabled |
| maxCacheSize | FLAGD_MAX_CACHE_SIZE | int | 1000 | |
| defaultAuthority | FLAGD_DEFAULT_AUTHORITY | string | - | rpc, in-process |

#### Resolver type-specific Defaults

Expand Down Expand Up @@ -93,6 +94,17 @@ To enable this mode, you should provide a valid flag configuration file with the
Offline mode uses `fs.watchFile` and polls every 5 seconds for changes to the file.
This mode is useful for local development, test cases, and for offline applications.

### Default Authority usage (optional)

This is useful for complex routing or service-discovery usecases that involve a proxy (e.g., Envoy). Please refer to this [link](https://github.com/open-feature/js-sdk-contrib/issues/1187) for more information.

```ts
OpenFeature.setProvider(new FlagdProvider({
resolverType: 'in-process',
defaultAuthority: 'b-target-api.service',
}))
```

### Supported Events

The flagd provider emits `PROVIDER_READY`, `PROVIDER_ERROR` and `PROVIDER_CONFIGURATION_CHANGED` events.
Expand Down
5 changes: 5 additions & 0 deletions libs/providers/flagd/src/lib/configuration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ describe('Configuration', () => {
const resolverType = 'in-process';
const selector = 'app=weather';
const offlineFlagSourcePath = '/tmp/flags.json';
const defaultAuthority = 'test-authority';

process.env['FLAGD_HOST'] = host;
process.env['FLAGD_PORT'] = `${port}`;
Expand All @@ -41,6 +42,7 @@ describe('Configuration', () => {
process.env['FLAGD_SOURCE_SELECTOR'] = `${selector}`;
process.env['FLAGD_RESOLVER'] = `${resolverType}`;
process.env['FLAGD_OFFLINE_FLAG_SOURCE_PATH'] = offlineFlagSourcePath;
process.env['FLAGD_DEFAULT_AUTHORITY'] = defaultAuthority;

expect(getConfig()).toStrictEqual({
host,
Expand All @@ -52,6 +54,7 @@ describe('Configuration', () => {
resolverType,
selector,
offlineFlagSourcePath,
defaultAuthority,
});
});

Expand All @@ -64,11 +67,13 @@ describe('Configuration', () => {
cache: 'lru',
resolverType: 'rpc',
selector: '',
defaultAuthority: '',
};

process.env['FLAGD_HOST'] = 'override';
process.env['FLAGD_PORT'] = '8080';
process.env['FLAGD_TLS'] = 'false';
process.env['FLAGD_DEFAULT_AUTHORITY'] = 'test-authority-override';

expect(getConfig(options)).toStrictEqual(options);
});
Expand Down
11 changes: 11 additions & 0 deletions libs/providers/flagd/src/lib/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ export interface Config {
* @default 1000
*/
maxCacheSize?: number;

/**
* The target host (authority) when routing requests through a proxy (e.g. Envoy)
*
* @default ''
*/
defaultAuthority?: string;
}

export type FlagdProviderOptions = Partial<Config>;
Expand All @@ -94,6 +101,7 @@ enum ENV_VAR {
FLAGD_SOURCE_SELECTOR = 'FLAGD_SOURCE_SELECTOR',
FLAGD_RESOLVER = 'FLAGD_RESOLVER',
FLAGD_OFFLINE_FLAG_SOURCE_PATH = 'FLAGD_OFFLINE_FLAG_SOURCE_PATH',
FLAGD_DEFAULT_AUTHORITY = 'FLAGD_DEFAULT_AUTHORITY',
}

const getEnvVarConfig = (): Partial<Config> => ({
Expand Down Expand Up @@ -124,6 +132,9 @@ const getEnvVarConfig = (): Partial<Config> => ({
...(process.env[ENV_VAR.FLAGD_OFFLINE_FLAG_SOURCE_PATH] && {
offlineFlagSourcePath: process.env[ENV_VAR.FLAGD_OFFLINE_FLAG_SOURCE_PATH],
}),
...(process.env[ENV_VAR.FLAGD_DEFAULT_AUTHORITY] && {
defaultAuthority: process.env[ENV_VAR.FLAGD_DEFAULT_AUTHORITY],
}),
});

export function getConfig(options: FlagdProviderOptions = {}) {
Expand Down
12 changes: 10 additions & 2 deletions libs/providers/flagd/src/lib/service/grpc/grpc-service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ClientReadableStream, ClientUnaryCall, ServiceError, credentials, status } from '@grpc/grpc-js';
import { ClientReadableStream, ClientUnaryCall, ServiceError, credentials, status, ClientOptions } from '@grpc/grpc-js';
import { ConnectivityState } from '@grpc/grpc-js/build/src/connectivity-state';
import {
EvaluationContext,
Expand Down Expand Up @@ -80,12 +80,20 @@ export class GRPCService implements Service {
client?: ServiceClient,
private logger?: Logger,
) {
const { host, port, tls, socketPath } = config;
const { host, port, tls, socketPath, defaultAuthority } = config;
let clientOptions: ClientOptions | undefined;
if (defaultAuthority) {
clientOptions = {
'grpc.default_authority': defaultAuthority,
};
}

this._client = client
? client
: new ServiceClient(
socketPath ? `unix://${socketPath}` : `${host}:${port}`,
tls ? credentials.createSsl() : credentials.createInsecure(),
clientOptions,
);

if (config.cache === 'lru') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const serviceMock: FlagSyncServiceClient = {
} as unknown as FlagSyncServiceClient;

describe('grpc fetch', () => {
const cfg: Config = { host: 'localhost', port: 8000, tls: false, socketPath: '' };
const cfg: Config = { host: 'localhost', port: 8000, tls: false, socketPath: '', defaultAuthority: 'test-authority' };

afterEach(() => {
jest.clearAllMocks();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ClientReadableStream, ServiceError, credentials } from '@grpc/grpc-js';
import { ClientReadableStream, ServiceError, credentials, ClientOptions } from '@grpc/grpc-js';
import { GeneralError, Logger } from '@openfeature/server-sdk';
import { FlagSyncServiceClient, SyncFlagsRequest, SyncFlagsResponse } from '../../../../proto/ts/flagd/sync/v1/sync';
import { Config } from '../../../configuration';
Expand Down Expand Up @@ -27,13 +27,20 @@ export class GrpcFetch implements DataFetch {
private _isConnected = false;

constructor(config: Config, syncServiceClient?: FlagSyncServiceClient, logger?: Logger) {
const { host, port, tls, socketPath, selector } = config;
const { host, port, tls, socketPath, selector, defaultAuthority } = config;
let clientOptions: ClientOptions | undefined;
if (defaultAuthority) {
clientOptions = {
'grpc.default_authority': defaultAuthority,
};
}

this._syncClient = syncServiceClient
? syncServiceClient
: new FlagSyncServiceClient(
socketPath ? `unix://${socketPath}` : `${host}:${port}`,
tls ? credentials.createSsl() : credentials.createInsecure(),
clientOptions,
);

this._logger = logger;
Expand Down

0 comments on commit 0497bd8

Please sign in to comment.