/** * Copyright 2024-2026 Wingify Software Pvt. Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { Struct, array, boolean, number, object, optional, refine, string, type, union, validate } from 'superstruct'; import { dynamic } from '../../types/Common'; import { SettingsModel } from '../settings/SettingsModel'; export class SettingsSchema { private campaignMetricSchema: Struct; private variableObjectSchema: Struct; private campaignVariationSchema: Struct; private campaignObjectSchema: Struct; private settingsSchema: Struct; private featureSchema: Struct; private ruleSchema: Struct; private holdoutSchema: Struct; constructor() { this.initializeSchemas(); } private initializeSchemas(): void { this.campaignMetricSchema = type({ id: union([number(), string()]), type: string(), identifier: string(), mca: optional(union([number(), string()])), hasProps: optional(boolean()), revenueProp: optional(string()), }); this.variableObjectSchema = type({ id: union([number(), string()]), type: string(), key: string(), value: union([number(), string(), boolean(), object()]), }); this.campaignVariationSchema = type({ id: union([number(), string()]), name: string(), weight: union([number(), string()]), segments: optional(object()), variables: optional(array(this.variableObjectSchema)), startRangeVariation: optional(number()), endRangeVariation: optional(number()), salt: optional(string()), }); this.campaignObjectSchema = type({ id: union([number(), string()]), type: string(), key: string(), percentTraffic: optional(number()), status: string(), variations: array(this.campaignVariationSchema), segments: object(), isForcedVariationEnabled: optional(boolean()), isAlwaysCheckSegment: optional(boolean()), name: string(), salt: optional(string()), }); this.ruleSchema = type({ type: string(), ruleKey: string(), campaignId: number(), variationId: optional(number()), }); this.featureSchema = type({ id: union([number(), string()]), key: string(), status: string(), name: string(), type: string(), metrics: array(this.campaignMetricSchema), impactCampaign: optional(object()), rules: optional(array(this.ruleSchema)), variables: optional(array(this.variableObjectSchema)), }); this.holdoutSchema = type({ metrics: array(this.campaignMetricSchema), segments: object(), featureIds: array(number()), isGlobal: boolean(), name: string(), id: union([number(), string()]), percentTraffic: number(), }); // holdouts: optional. Backend sends {} when no holdouts; [] or [holdout, ...] when 1+ present. // Union: either an array (each item validated by holdoutSchema) or a strict empty object {}. const holdoutsSchema = union([ array(this.holdoutSchema), refine(object(), 'EmptyObject', (v) => Object.keys(v).length === 0), ]); this.settingsSchema = type({ sdkKey: optional(string()), version: union([number(), string()]), accountId: union([number(), string()]), usageStatsAccountId: optional(number()), features: optional(array(this.featureSchema)), holdouts: optional(holdoutsSchema), campaigns: array(this.campaignObjectSchema), groups: optional(object()), campaignGroups: optional(object()), collectionPrefix: optional(string()), sdkMetaInfo: optional(object({ wasInitializedEarlier: optional(boolean()) })), pollInterval: optional(number()), }); } isSettingsValid(settings: any | SettingsModel): boolean { if (!settings) { return false; } const [error] = validate(settings, this.settingsSchema); return !error; } }