diff --git a/README.md b/README.md index 4d1ab21..251a0e1 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,19 @@ npm install @shubhodeep9/localstorm import { Model, Schema } from '@shubhodeep9/localstorm'; const UserSchema = new Schema({ - id: "number", - name: "string", - phone: "string" + id: { + required: true, // validates the field as mandatory + type: Number + }, + name: { + required: true, + type: String + }, + phone: { + required: true, + type: String + }, + hobbies: Array // will assume field as optional }); const UserStore = Model('user', UserSchema); @@ -26,9 +36,18 @@ const UserStore = Model('user', UserSchema); UserStore.save({ id: 123, name: "Shubho", - phone: "91xxxxx934" + phone: "91xxxxx934", + hobbies: ['sleeping'] }); +// if you want to bypass validations +UserStore.save({ + id: 123, + name: "Shubho", + phone: "91xxxxx934", + hobbies: ['sleeping'] +}, true) // pass skipValidation as true + // to retrieve const response = UserStore.get(); @@ -37,7 +56,6 @@ UserStore.flush(); ``` ## To-Do -- Add more type support - Global Store fetch and flush - Dynamic Schema - Triggers diff --git a/package.json b/package.json index 31a9d16..054b31a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@shubhodeep9/localstorm", - "version": "0.0.3", + "version": "0.0.4", "description": "An ORM for localstorage", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -18,7 +18,7 @@ }, "author": "Shubhodeep Mukherjee", "license": "MIT", - "dependencies": { + "devDependencies": { "typescript": "^4.9.3" }, "bugs": { diff --git a/src/entity/entry.ts b/src/entity/entry.ts index 184d1dd..6666e22 100644 --- a/src/entity/entry.ts +++ b/src/entity/entry.ts @@ -1,4 +1,4 @@ -import { Schema } from "./schema"; +import { isDefinedType, Schema, SchemaPropGenericTypes } from "./schema"; /** * Entry is layer over localstorage with structuring support, @@ -14,19 +14,39 @@ export class Entry { } validate(saveObj: any) { + // validate required fields + const requiredProps = this.schema.getRequiredProps(); + for (let requiredProp of requiredProps) { + if (saveObj[requiredProp] === undefined) { + throw Error(`Missing required field "${requiredProp}"`); + } + } for (let x in saveObj) { const propType = this.schema.get(x); - - if (propType != typeof saveObj[x]) { - throw Error(`Schema mismatch for ${x}`); + let intendedType: SchemaPropGenericTypes; + if (isDefinedType(propType)) { + intendedType = propType.type; + } else { + intendedType = propType; + } + + if (saveObj[x].constructor != intendedType) { + throw Error(`Schema mismatch for ${x}, expected ${intendedType.name} got ${saveObj[x].constructor.name}`); } } } - save(saveObj: Object) { - this.validate(saveObj); + /** + * + * @param saveObj pass the object to be saved in localstorage, it will be validated and stringified + * @param skipValidation if you want to skip schema validations, pass this param as true + */ + save(saveObj: Object, skipValidation?: boolean) { + if (!skipValidation) { + this.validate(saveObj); + } - window.localStorage.setItem(this.key, JSON.stringify(saveObj)); + // window.localStorage.setItem(this.key, JSON.stringify(saveObj)); } get() { diff --git a/src/entity/model.ts b/src/entity/model.ts index 1f7c3b5..45dfe76 100644 --- a/src/entity/model.ts +++ b/src/entity/model.ts @@ -4,6 +4,8 @@ import { Entry } from "./entry"; /** * Model function takes in key-pair's key, and structure in Schema * returns Entry with functionalities. + * @param key name for localstorage `K` + * @param schema an object of Schema class with required structured for validations */ export function Model(key: string, schema: Schema) { return new Entry(key, schema); diff --git a/src/entity/schema.ts b/src/entity/schema.ts index ec1b37c..8f35f12 100644 --- a/src/entity/schema.ts +++ b/src/entity/schema.ts @@ -1,9 +1,19 @@ /** * PropTypes currently supports number and string - * @TODO: support more structures, or build support for constructors + * @TODO: support more structures */ + +export type SchemaPropGenericTypes = NumberConstructor | StringConstructor | ArrayConstructor +export type SchemaPropDefinedType = { + required: boolean, + type: SchemaPropGenericTypes +} interface PropTypes { - [key: string]: "number" | "string" + [key: string]: SchemaPropDefinedType | SchemaPropGenericTypes; +} + +export function isDefinedType(propType: SchemaPropDefinedType | SchemaPropGenericTypes): propType is SchemaPropDefinedType { + return (propType).type != undefined; } /** @@ -19,6 +29,17 @@ export class Schema { return this.properties[key]; } -} - - + getRequiredProps(): Array { + let requiredProps = [] as Array; + for (let x in this.properties) { + const prop = this.properties[x]; + if (isDefinedType(prop)) { + const isRequired = prop.required; + if (isRequired) { + requiredProps.push(x); + } + } + } + return requiredProps; + } +} \ No newline at end of file