Skip to content
This repository has been archived by the owner on May 30, 2024. It is now read-only.

Commit

Permalink
prepare 6.3.1 release (#240)
Browse files Browse the repository at this point in the history
* rm nock

* Node 6 compatibility

* remove redundant helper, misc cleanup

* Node 6 compatibility

* fix comment

* change asyncify to promisifySingle

* misc fixes

* add Windows CircleCI job

* fix config

* syntax

* diagnostic events, part 2: initial event and stats, except for stream inits

* comments

* add test for stats event

* capture stream connection stats in diagnostic events

* fix test

* remove eventReportingDisabled from diagnostic event; only create diagnosticsManager if needed

* revise tests to use new helper package

* misc cleanup

* use launchdarkly-js-test-helpers 1.0.0

* fix package reference

* minor fixes to config validation messages + add comment

* diagnostic eventsInQueue counter should be # of events at last flush

* rename eventsInQueue to eventsInLastBatch

* don't let user fall outside of last bucket in rollout

* add unit tests for basic bucketing logic and edge case

* avoid redundant property lookups

* fix Redis client parameter to match TS declaration (but still support old incorrect parameter)

* add event payload ID

* remove mistakenly checked-in test code (note, this SDK key was only valid on staging)

* add mention of singleton usage

* update diagnostic event info for OS name, data store type, Node version

* standardize linting

* disallow window and document

* fix null/undef checks

* misc linting fixes

* inlineUsersInEvents is not an unknown option

* drop node-sha1 dependency

* don't omit streamInits.failed when it's false

* bump request dependency to get security patch; loosen some exact dependencies

* remove request package; improve polling cache logic + add test

* bump typescript version to fix build error in Node 6

* update @types/node to fix TypeScript check step

* lint

* make sure we keep polling regardless of whether we got new data

* use launchdarkly-eventsource, make stream retry behavior consistent

* stream retry delay option should be in seconds & should be included in diagnostics

* minor test fix

* fix: Throw an error on malformed user-supplied logger

* don't call unref() on Redis client; ensure that database integration tests close the store

* update Redis driver to major version 3

* add test case

* allow redisOpts parameter to be omitted

* add logger adapter shim + tests

* minor cleanup and comments for ch74741 fix (logger wrapper)

* fix proxy tunnel configuration and make sure it's used in streaming

* change some string concatenation expressions to use interpolation

* feat: upgrade winston (#189)

* fix merge

* remove support for indirect/patch and indirect/put (#182)

* reuse same Promise and same event listeners for all waitForInitialization calls

* better docs for waitForInitialization + misc doc cleanup (#184)

* update js-eventsource to 1.3.1 for stream parsing bugfix (#185)

* fix broken logger format (#186)

* retroactively update changelog for bugfix in 5.13.2 release

* allow get/getAll Redis queries to be queued if Redis client hasn't yet connected

* set stream read timeout

* adding the alias functionality (#190)

* Removed the guides link

* remove monkey-patching of setImmediate

* Persist contextKind property during feature and custom event transformations (#194)

* add inlineUsersInEvents option in TypeScript

* Add support for seed to bucketUser

* Add note for incorporating seed into evaluation

* Send events when the evaluation is from an experiment

* Use seed to evaluate.

* Clean up test descriptions

* Rename variable to be less confusing

* Use ternary to eliminate mutation

* Make return signature more consistent

* Un-prettier the tests

* redis lower bounds bump (#199)

* update launchdarkly-js-test-helpers to fix TLS tests (#200)

* update js-eventsource to remove vulnerability warning (#201)

* add CI jobs for all compatible Node versions

* CI fixes

* more CI fixes

* comment

* use default value to simplify config

* (6.0 - #1) stop saying we're compatible with Node <12 (#203)

* add CI jobs for all compatible Node versions (#202)

* (6.0 - #2) remove Redis integration (#204)

* allow feature store to be specified as a factory (so it can get our logger)

* (6.0 - #3) remove Winston (#205)

* remove deprecated things for 6.0 (#206)

* update node-cache to 5.x (drops old Node compat)

* update semver to 7.x (drops old Node compat)

* update uuid to 8.x (Node compat, perf improvements, bugfixes)

* update dev dependencies

* linter

* replace lrucache package with lru-cache (#209)

* make yaml dependency optional (#210)

* update release metadata to include maintenance branch

* remove package-lock.json (#211)

* rm prerelease changelog

* (big segments #1) add interfaces for big segments (#212)

* (big segments #2) add all components for big segments except evaluation (#213)

* (big segments #3) implement big segments in flag evaluation (#214)

* (big segments #4) add standard test suite for big segment store tests + refactor feature store tests (#215)

* move new interfaces to a module instead of a namespace (#216)

* fix TS export of CachingStoreWrapper

* use Releaser v2 config

* fix overly specific test expectation that breaks in Node 17

* Initial work on FlagBuilder (#219)

* Add TestData factory(with some dummy methods); Initial work on FlagBuilder

* fixed indentation and linter errors; fixed an error in update; fixed incorrect test label

* fixed typo in TestData store

* converted boolean variation constants to be file variables instead of class variables

Co-authored-by: charukiewicz <[email protected]>
Co-authored-by: belevy <[email protected]>

* implemented FlagRuleBuilder; added .build() methods to FlagBuilder/FlagRuleBuilder and changed tests to avoid using private interface

* converted _targets to be Map instead of object literal; changed variationForBoolean to be a module-scoped function instead class-scoped

* Implement stream processor(data source) interface for test data

* Add TestData to index.js and write out the types for TestData and friends

* added testdata documentation to index.d.ts; fix linter errors; changed flag default behavior to create boolean flag

* Fix the interface file: reindented to 2 spaces, corrected definition of functions from properties to functions in interfaces; corrected issues in JSDoc comments

* modify tests to fix capitalization and actually test the test datasource works as an LDClient updateProcessor.

* Fix linter error on defaulted callback

* explicitly enable JSDOM types in TypeScript build to avoid errors when jsdom is referenced for some reason

* capitalize Big Segments in docs & logs

* documentation comment fixes for TestData

* pin TypeScript to 4.4.x

* move TestData and FIleDataSource to integrations module

* lint

* rename types used by TestData for clarity (#229)

* use varargs semantics for TestFlagBuilder.variations() and add it to the TS interface (#230)

* don't ever use for...in (#232)

* don't ever use for...in

* add null guard

* bump launchdarkly-eventsource dependency for sc-136154 fix

* use TestData in our own tests (#231)

* use TestData in our own tests

* update TS interface

* lint

* typo

* fix allFlagsState behavior regarding experimentation

* lint

* allow "secondary" to be referenced in clauses

* don't throw an exception for non-string in semver comparison

* correctly handle "client not ready" condition in allFlagsState

* lint

* Flags with a version of 0 reported as 'unknown' in summary events. (#239)

* implement contract test service, not including big segments (#242)

Co-authored-by: Eli Bishop <[email protected]>

* Implement Application tags for the node SDK. (#241)

* update js-eventsource to 1.4.4 for security fix

Co-authored-by: Eli Bishop <[email protected]>
Co-authored-by: LaunchDarklyCI <[email protected]>
Co-authored-by: Ben Woskow <[email protected]>
Co-authored-by: Maxwell Gerber <[email protected]>
Co-authored-by: Chris West <[email protected]>
Co-authored-by: Ben Woskow <[email protected]>
Co-authored-by: Mike Zorn <[email protected]>
Co-authored-by: Robert J. Neal <[email protected]>
Co-authored-by: Ben Levy <[email protected]>
Co-authored-by: charukiewicz <[email protected]>
Co-authored-by: belevy <[email protected]>
Co-authored-by: charukiewicz <[email protected]>
Co-authored-by: LaunchDarklyReleaseBot <[email protected]>
Co-authored-by: Ryan Lamb <[email protected]>
  • Loading branch information
15 people authored Mar 10, 2022
1 parent 6adcdd0 commit ddfcd80
Show file tree
Hide file tree
Showing 20 changed files with 470 additions and 12 deletions.
11 changes: 11 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ jobs:
default: false
docker-image:
type: string
run-contract-tests:
type: boolean
default: true
docker:
- image: <<parameters.docker-image>>
steps:
Expand All @@ -56,6 +59,14 @@ jobs:
condition: <<parameters.run-lint>>
steps:
- run: npm run lint
- when:
condition: <<parameters.run-contract-tests>>
steps:
- run:
command: npm run contract-test-service
background: true
- run: mkdir -p reports/junit
- run: TEST_HARNESS_PARAMS="-junit reports/junit/contract-test-results.xml -skip-from contract-tests/testharness-suppressions.txt" npm run contract-test-harness
- run:
name: dependency audit
command: ./scripts/better-audit.sh
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package-lock.json
/docs/build/
/local/
/node_modules/
**/node_modules/
junit.xml
npm-debug.log
test-types.js
Expand Down
73 changes: 69 additions & 4 deletions configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ module.exports = (function () {
const typesForPropertiesWithNoDefault = {
// Add a value here if we add a configuration property whose type cannot be determined by looking
// in baseDefaults (for instance, the default is null but if the value isn't null it should be a
// string). The allowable values are 'boolean', 'string', 'number', 'object', 'function', or
// string). The allowable values are 'boolean', 'string', 'number', 'object', 'function', 'array' or
// 'factory' (the last one means it can be either a function or an object).
bigSegments: 'object',
eventProcessor: 'object',
Expand All @@ -48,6 +48,42 @@ module.exports = (function () {
wrapperVersion: 'string',
};

/**
* Expression to validate characters that are allowed in tag keys and values.
*/
const allowedTagCharacters = /^(\w|\.|-)+$/;

/**
* Verify that a value meets the requirements for a tag value.
* @param {Object} config
* @param {string} tagValue
*/
function validateTagValue(name, config, tagValue) {
if (typeof tagValue !== 'string' || !tagValue.match(allowedTagCharacters)) {
config.logger.warn(messages.invalidTagValue(name));
return undefined;
}
return tagValue;
}

const optionsWithValidatorsOrConversions = {
// Add a value here if we add a configuration property which requires custom validation
// and/or type conversion.
// The validator should log a message for any validation issues encountered.
// The validator should return undefined, or the validated value.

application: (name, config, value) => {
const validated = {};
if (value.id) {
validated.id = validateTagValue(`${name}.id`, config, value.id);
}
if (value.version) {
validated.version = validateTagValue(`${name}.version`, config, value.version);
}
return validated;
},
};

/* eslint-disable camelcase */
const deprecatedOptions = {};
/* eslint-enable camelcase */
Expand Down Expand Up @@ -102,8 +138,16 @@ module.exports = (function () {
if (value !== null && value !== undefined) {
const defaultValue = defaultConfig[name];
const typeDesc = typesForPropertiesWithNoDefault[name];
if (defaultValue === undefined && typeDesc === undefined) {
const validator = optionsWithValidatorsOrConversions[name];
if (defaultValue === undefined && typeDesc === undefined && validator === undefined) {
config.logger.warn(messages.unknownOption(name));
} else if (validator !== undefined) {
const validated = validator(name, config, config[name]);
if (validated !== undefined) {
config[name] = validated;
} else {
delete config[name];
}
} else {
const expectedType = typeDesc || typeDescForValue(defaultValue);
const actualType = typeDescForValue(value);
Expand Down Expand Up @@ -156,8 +200,29 @@ module.exports = (function () {
return config;
}

/**
* Get tags for the specified configuration.
*
* If any additional tags are added to the configuration, then the tags from
* this method should be extended with those.
* @param {Object} config The already valiated configuration.
* @returns {Object} The tag configuration.
*/
function getTags(config) {
const tags = {};
if (config.application && config.application.id !== undefined && config.application.id !== null) {
tags['application-id'] = [config.application.id];
}
if (config.application && config.application.version !== undefined && config.application.id !== null) {
tags['application-version'] = [config.application.version];
}

return tags;
}

return {
validate: validate,
defaults: defaults,
validate,
defaults,
getTags,
};
})();
7 changes: 7 additions & 0 deletions contract-tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# SDK contract test service

This directory contains an implementation of the cross-platform SDK testing protocol defined by https://github.com/launchdarkly/sdk-test-harness. See that project's `README` for details of this protocol, and the kinds of SDK capabilities that are relevant to the contract tests. This code should not need to be updated unless the SDK has added or removed such capabilities.

To run these tests locally, run `npm run contract-tests` from the SDK project root directory. This will start the test service, download the correct version of the test harness tool, and run the tests.

Or, to test against an in-progress local version of the test harness, run `npm run contract-test-service` from the SDK project root directory; then, in the root directory of the `sdk-test-harness` project, build the test harness and run it from the command line.
108 changes: 108 additions & 0 deletions contract-tests/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
const express = require('express');
const bodyParser = require('body-parser');

const { Log } = require('./log');
const { newSdkClientEntity, badCommandError } = require('./sdkClientEntity');

const app = express();
let server = null;

const port = 8000;

let clientCounter = 0;
const clients = {};

const mainLog = Log('service');

app.use(bodyParser.json());

app.get('/', (req, res) => {
res.header('Content-Type', 'application/json');
res.json({
capabilities: [
'server-side',
'all-flags-client-side-only',
'all-flags-details-only-for-tracked-flags',
'all-flags-with-reasons',
'tags',
],
});
});

app.delete('/', (req, res) => {
mainLog.info('Test service has told us to exit');
res.status(204);
res.send();

// Defer the following actions till after the response has been sent
setTimeout(() => {
server.close(() => process.exit());
// We force-quit with process.exit because, even after closing the server, there could be some
// scheduled tasks lingering if an SDK instance didn't get cleaned up properly, and we don't want
// that to prevent us from quitting.
}, 1);
});

app.post('/', async (req, res) => {
const options = req.body;

clientCounter += 1;
const clientId = clientCounter.toString();
const resourceUrl = `/clients/${clientId}`;

try {
const client = await newSdkClientEntity(options);
clients[clientId] = client;

res.status(201);
res.set('Location', resourceUrl);
} catch (e) {
res.status(500);
const message = e.message || JSON.stringify(e);
mainLog.error('Error creating client: ' + message);
res.write(message);
}
res.send();
});

app.post('/clients/:id', async (req, res) => {
const client = clients[req.params.id];
if (!client) {
res.status(404);
} else {
try {
const respValue = await client.doCommand(req.body);
if (respValue) {
res.status(200);
res.write(JSON.stringify(respValue));
} else {
res.status(204);
}
} catch (e) {
const isBadRequest = e === badCommandError;
res.status(isBadRequest ? 400 : 500);
res.write(e.message || JSON.stringify(e));
if (!isBadRequest && e.stack) {
console.log(e.stack);
}
}
}
res.send();
});

app.delete('/clients/:id', async (req, res) => {
const client = clients[req.params.id];
if (!client) {
res.status(404);
res.send();
} else {
client.close();
delete clients[req.params.id];
res.status(204);
res.send();
}
});

server = app.listen(port, () => {
console.log('Listening on port %d', port);
});
23 changes: 23 additions & 0 deletions contract-tests/log.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const ld = require('launchdarkly-node-server-sdk');

function Log(tag) {
function doLog(level, message) {
console.log(new Date().toISOString() + ` [${tag}] ${level}: ${message}`);
}
return {
info: message => doLog('info', message),
error: message => doLog('error', message),
};
}

function sdkLogger(tag) {
return ld.basicLogger({
level: 'debug',
destination: line => {
console.log(new Date().toISOString() + ` [${tag}.sdk] ${line}`);
},
});
}

module.exports.Log = Log;
module.exports.sdkLogger = sdkLogger;
15 changes: 15 additions & 0 deletions contract-tests/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "node-server-sdk-contract-tests",
"version": "0.0.0",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"author": "",
"license": "Apache-2.0",
"dependencies": {
"body-parser": "^1.19.0",
"express": "^4.17.1",
"launchdarkly-node-server-sdk": "file:.."
}
}
Loading

0 comments on commit ddfcd80

Please sign in to comment.