UNPKG

@featurevisor/core

Version:

Core package of Featurevisor for Node.js usage

236 lines (183 loc) 5.79 kB
import type { ParsedFeature, Segment, Attribute, Group, FeatureKey, Test, EnvironmentKey, ExistingState, SegmentKey, AttributeKey, DatafileContent, EntityType, } from "@featurevisor/types"; import { ProjectConfig, CustomParser } from "../config"; import { Adapter, DatafileOptions } from "./adapter"; export class Datasource { private adapter: Adapter; constructor( private config: ProjectConfig, private rootDirectoryPath?: string, ) { this.adapter = new config.adapter(config, rootDirectoryPath); } // @NOTE: only site generator needs it, find a way to get it out of here later getExtension() { return (this.config.parser as CustomParser).extension; } /** * State */ readState(environment: EnvironmentKey | false): Promise<ExistingState> { return this.adapter.readState(environment); } writeState(environment: EnvironmentKey | false, existingState: ExistingState) { return this.adapter.writeState(environment, existingState); } /** * Revision */ readRevision() { return this.adapter.readRevision(); } writeRevision(revision: string) { return this.adapter.writeRevision(revision); } /** * Datafile */ readDatafile(options: DatafileOptions) { return this.adapter.readDatafile(options); } writeDatafile(datafileContent: DatafileContent, options: DatafileOptions) { return this.adapter.writeDatafile(datafileContent, options); } /** * Entity specific methods */ // features listFeatures() { return this.adapter.listEntities("feature"); } featureExists(featureKey: FeatureKey) { return this.adapter.entityExists("feature", featureKey); } readFeature(featureKey: FeatureKey) { return this.adapter.readEntity<ParsedFeature>("feature", featureKey); } writeFeature(featureKey: FeatureKey, feature: ParsedFeature) { return this.adapter.writeEntity<ParsedFeature>("feature", featureKey, feature); } deleteFeature(featureKey: FeatureKey) { return this.adapter.deleteEntity("feature", featureKey); } async getRequiredFeaturesChain( featureKey: FeatureKey, chain = new Set<FeatureKey>(), ): Promise<Set<FeatureKey>> { chain.add(featureKey); if (!this.adapter.entityExists("feature", featureKey)) { throw new Error(`Feature not found: ${featureKey}`); } const feature = await this.readFeature(featureKey); if (!feature.required) { return chain; } for (const r of feature.required) { const requiredKey = typeof r === "string" ? r : r.key; if (chain.has(requiredKey)) { throw new Error(`Circular dependency detected: ${chain.toString()}`); } await this.getRequiredFeaturesChain(requiredKey, chain); } return chain; } // segments listSegments() { return this.adapter.listEntities("segment"); } segmentExists(segmentKey: SegmentKey) { return this.adapter.entityExists("segment", segmentKey); } readSegment(segmentKey: SegmentKey) { return this.adapter.readEntity<Segment>("segment", segmentKey); } writeSegment(segmentKey: SegmentKey, segment: Segment) { return this.adapter.writeEntity<Segment>("segment", segmentKey, segment); } deleteSegment(segmentKey: SegmentKey) { return this.adapter.deleteEntity("segment", segmentKey); } // attributes listAttributes() { return this.adapter.listEntities("attribute"); } async listFlattenedAttributes() { const attributes = await this.listAttributes(); const result: string[] = []; for (const key of attributes) { const attribute = await this.readAttribute(key); result.push(key); if (attribute.type === "object") { // @NOTE: in future, this can be recursive const propertyKeys = Object.keys(attribute.properties || {}); for (const propertyKey of propertyKeys) { result.push(`${key}.${propertyKey}`); } } } return result; } attributeExists(attributeKey: AttributeKey) { return this.adapter.entityExists("attribute", attributeKey); } readAttribute(attributeKey: AttributeKey) { return this.adapter.readEntity<Attribute>("attribute", attributeKey); } writeAttribute(attributeKey: AttributeKey, attribute: Attribute) { return this.adapter.writeEntity<Attribute>("attribute", attributeKey, attribute); } deleteAttribute(attributeKey: AttributeKey) { return this.adapter.deleteEntity("attribute", attributeKey); } // groups listGroups() { return this.adapter.listEntities("group"); } groupExists(groupKey: string) { return this.adapter.entityExists("group", groupKey); } readGroup(groupKey: string) { return this.adapter.readEntity<Group>("group", groupKey); } writeGroup(groupKey: string, group: Group) { return this.adapter.writeEntity<Group>("group", groupKey, group); } deleteGroup(groupKey: string) { return this.adapter.deleteEntity("group", groupKey); } // tests listTests() { return this.adapter.listEntities("test"); } readTest(testKey: string) { return this.adapter.readEntity<Test>("test", testKey); } writeTest(testKey: string, test: Test) { return this.adapter.writeEntity<Test>("test", testKey, test); } deleteTest(testKey: string) { return this.adapter.deleteEntity("test", testKey); } getTestSpecName(testKey: string) { return `${testKey}.${this.getExtension()}`; } // history listHistoryEntries(entityType?: EntityType, entityKey?: string) { return this.adapter.listHistoryEntries(entityType, entityKey); } readCommit(commitHash: string, entityType?: EntityType, entityKey?: string) { return this.adapter.readCommit(commitHash, entityType, entityKey); } }