Advanced
Datasource & Adapters
By default, Featurevisor CLI uses the file system for reading and writing data in your project, given it's a Git repository after all. But the configuration API allows you to switch to any source via adapters.
Accessing datasource#
It's unlikely that you will make use of the Datasource API yourself directly, unless you are a plugin developer.
The datasource object allows you to read and write data from/to the Featurevisor project, so that you don't have to deal with the file system (or any other custom source of your project data) directly.
You can refer to the full datasource API for more details.
Datasource methods#
Once you have access to the datasource object, you can use the following methods from its instance.
Most definitions follow the same pattern of list, exists, read, write, and delete methods.
Revision#
See state files for more details.
const revision = await datasource.readRevision()await datasource.writeRevision(revision + 1)Features#
See features for more details.
const features = await datasource.listFeatures()const fooFeatureExists = await datasource.featureExists('foo')const fooFeature = await datasource.readFeature('foo')await datasource.writeFeature('foo', { ...fooFeature, ...newData })await datasource.deleteFeature('foo')To collect the full chain of features that a feature depends on via its required property:
const requiredKeys = await datasource.getRequiredFeaturesChain('foo')It returns a Set of feature keys, and it also covers dependencies of dependencies.
Segments#
See segments for more details.
const segments = await datasource.listSegments()const fooSegmentExists = await datasource.segmentExists('foo')const fooSegment = await datasource.readSegment('foo')await datasource.writeSegment('foo', { ...fooSegment, ...newData })await datasource.deleteSegment('foo')Attributes#
See attributes for more details.
const attributes = await datasource.listAttributes()const fooAttributeExists = await datasource.attributeExists('foo')const fooAttribute = await datasource.readAttribute('foo')await datasource.writeAttribute('foo', { ...fooAttribute, ...newData })await datasource.deleteAttribute('foo')Attributes of type object can declare nested properties. To list every attribute key together with those nested properties in their dot separated form:
const keys = await datasource.listFlattenedAttributes()// ["userId", "device", "device.os", "device.version"]Groups#
See groups for more details.
const groups = await datasource.listGroups()const fooGroupExists = await datasource.groupExists('foo')const fooGroup = await datasource.readGroup('foo')await datasource.writeGroup('foo', { ...fooGroup, ...newData })await datasource.deleteGroup('foo')Schemas#
See schemas for more details.
const schemas = await datasource.listSchemas()const fooSchemaExists = await datasource.schemaExists('foo')const fooSchema = await datasource.readSchema('foo')await datasource.writeSchema('foo', { ...fooSchema, ...newData })await datasource.deleteSchema('foo')Targets#
See targets for more details.
const targets = await datasource.listTargets()const fooTargetExists = await datasource.targetExists('foo')const fooTarget = await datasource.readTarget('foo')await datasource.writeTarget('foo', { ...fooTarget, ...newData })await datasource.deleteTarget('foo')Tests#
See testing for more details.
const tests = await datasource.listTests()const fooTest = await datasource.readTest('foo')await datasource.writeTest('foo', { ...fooTest, ...newData })await datasource.deleteTest('foo')To find the spec file name that belongs to a test key:
const specName = await datasource.getTestSpecName('foo')State#
See state files for more details.
const existingState = await datasource.readState(environment)datasource.writeState(environment, { ...existingState, ...newState })If your project has no environments configured, pass false instead of an environment name:
const existingState = await datasource.readState(false)Datafiles#
See building datafiles for more details.
const datafiles = await datasource.listDatafiles()Each entry contains the datafile path relative to the datafiles directory, together with its size and gzipSize in bytes.
Individual datafiles are read and written with an options object, where environment is either an environment name or false for projects without environments:
const datafile = await datasource.readDatafile({ environment: 'production', target: 'web',})await datasource.writeDatafile(datafileContent, { environment: 'production', target: 'web',})An optional datafilesDir property overrides the configured datafilesDirectoryPath for that call.
History#
To get history of changes made to a specific entity:
const fooChanges = await datasource.listHistoryEntries('feature', 'foo')The first argument for entity type can be one of:
featuresegmentattributegroupschematargettest
Both arguments are optional. Omitting them lists the history of the whole project.
A specific commit can also be read, optionally narrowed down to one entity:
const commit = await datasource.readCommit(commitHash)const fooCommit = await datasource.readCommit(commitHash, 'feature', 'foo')Sets#
If your project uses sets, the datasource is always scoped to a single set. A datasource created without a set reads and writes the root project.
const sets = await datasource.listSets()const currentSet = datasource.getSet()const storefront = datasource.forSet('storefront')const storefrontFeatures = await storefront.listFeatures()forSet() returns a new datasource instance with that set's own configuration and directory paths applied. Plugins that support sets should honour the --set option and scope their datasource accordingly, otherwise they end up reading the wrong tree.
Project configuration#
const config = datasource.getConfig()const extension = datasource.getExtension()getConfig() returns the fully processed configuration that applies to this datasource, including any set specific overrides. getExtension() returns the file extension of the configured parser, like yml or json.
Adapters#
Because a Featurevisor project is a Git repository by default, Featurevisor CLI ships with a default adapter that reads and writes data from/to the file system which is called FilesystemAdapter.
You don't have to configure this adapter explicitly anywhere, unless you are writing a custom one.
Writing a custom adapter#
You can write your own custom datasource adapter as follows:
import { Adapter } from '@featurevisor/core'export class CustomAdapter extends Adapter { // ...implement the methods here}Refer to the implementation of FilesystemAdapter to understand more.
Using a custom adapter#
You can swap out the default file system adapter with your custom adapter via you configuration file as found in featurevisor.config.js:
const { CustomAdapter } = require('./adapters/custom-adapter')module.exports = { environments: ['staging', 'production'], tags: ['web', 'mobile'], adapter: CustomAdapter,}
