You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

141 lines
5.4 KiB
JavaScript

"use strict";
const util = require("util");
const lmdb = require("lmdb");
const mapObj = require("map-obj");
const createMemoryBackend = require("./memory");
const createLMDBBackend = require("./lmdb");
const reduceMigrations = require("../schema/reducer");
const createRecordCoder = require("../storage-encoder/record-coder");
function stringifyIterator(iterator) {
// return "[" + Array.from(iterator).join(", ") + "]";
return util.inspect(Array.from(iterator).map((item) => item.value), { colors: true });
// return util.inspect(Array.from(iterator));
}
function buf(number) {
return Buffer.from([ number ]);
}
// let backendChoice = "memory";
let backendChoice = "lmdb";
let dummyMigrations = [
{ id: 1, operations: [
{ type: "createCollection", name: "users", operations: [
{ type: "createField", name: "username", operations: [
{ type: "setFieldType", fieldType: "string" },
{ type: "setAttribute", attribute: "required", value: true }
]},
{ type: "createField", name: "passwordHash", operations: [
{ type: "setFieldType", fieldType: "string" },
{ type: "setAttribute", attribute: "required", value: true }
]},
{ type: "createField", name: "emailAddress", operations: [
{ type: "setFieldType", fieldType: "string" },
{ type: "setAttribute", attribute: "required", value: false }
]},
{ type: "createField", name: "isActive", operations: [
{ type: "setFieldType", fieldType: "boolean" },
{ type: "setAttribute", attribute: "required", value: true }
]},
{ type: "createField", name: "registrationDate", operations: [
{ type: "setFieldType", fieldType: "date" },
{ type: "setAttribute", attribute: "required", value: true },
{ type: "setAttribute", attribute: "withTimezone", value: false },
]},
{ type: "createField", name: "invitesLeft", operations: [
{ type: "setFieldType", fieldType: "integer" },
{ type: "setAttribute", attribute: "required", value: true },
]},
]}
]},
{ id: 2, operations: [
{ type: "modifyCollection", name: "users", operations: [
{ type: "modifyField", name: "emailAddress", operations: [
{ type: "setAttribute", attribute: "required", value: true },
]},
// FIXME: Disallow no-ops for attribute changes?
// { type: "modifyField", name: "isActive", operations: [
// { type: "setAttribute", attribute: "required", value: true },
// ]},
{ type: "modifyField", name: "registrationDate", operations: [
{ type: "setAttribute", attribute: "withTimezone", value: true },
{ type: "rollbackTo", transformer: (value) => value.toUTC() }
]},
{ type: "modifyField", name: "invitesLeft", operations: [
{ type: "setAttribute", attribute: "signed", value: false },
]},
{ type: "createField", name: "sendNewsletter", operations: [
{ type: "setFieldType", fieldType: "boolean" },
{ type: "setAttribute", attribute: "required", value: true }, // FIXME: Enforce a default in this case! Otherwise existing columns would be invalid -- actually this should be handled by 'migration defaults' specifically, without requiring a default for new records
// FIXME: The below lazy function is currently getting evaluated at schema reduce time, because of the immutable deep merge. *Really* need to work this into merge-by-template instead to prevent cases like this!
{ type: "setAttribute", attribute: "defaultValue", value: () => false }, // FIXME: Always specified as a value-producing function, or also allow literals?
]},
]}
]},
];
let schema = reduceMigrations(dummyMigrations);
console.dir({schema}, {depth:null});
function createRecord(collectionName, data) {
let collectionSchema = schema.collections[collectionName];
let fields = mapObj(collectionSchema.fields, (key, value) => {
return [
key,
value.schema // Extract only the *current* schema, not the transforms
];
});
let coder = createRecordCoder(fields);
return coder.encode(data);
}
// console.log(schema);
// MARKER: createRecord takes a schema *array*, because fields need to have a well-defined order for consistent encoding. Need to convert from object format to array format, and also update recordCoder so that it can deal with the new internal schema representation format (eg. no longer a nested `attributes` object)
console.log(createRecord("users", {
username: "joepie91",
passwordHash: "foo",
emailAddress: "admin@cryto.net",
isActive: true,
registrationDate: new Date(),
invitesLeft: 10
}));
return;
(async function() {
// FIXME: match-value
let db = lmdb.open({ keyEncoding: "binary" });
let backend = (backendChoice === "lmdb")
? createLMDBBackend(db)
: createMemoryBackend();
await backend.putKey(buf(1), "one");
await backend.putKey(buf(2), "two");
await backend.putKey(buf(3), "three");
await backend.putKey(buf(4), "four");
await backend.putKey(buf(5), "five");
await backend.putKey(buf(6), "six");
await backend.putKey(buf(7), "seven");
await backend.putKey(buf(8), "eight");
await backend.putKey(buf(9), "nine");
await backend.putKey(buf(10), "ten");
console.log(backend.getKey(buf(4)));
console.log(Array.from(db.getRange()));
console.log(Array.from(db.getRange({ start: buf(2), end: buf(6) })));
console.log(stringifyIterator(backend.getKeyRange(buf(2), true, buf(6), true)));
console.log(stringifyIterator(backend.getKeyRange(buf(2), false, buf(6), true)));
console.log(stringifyIterator(backend.getKeyRange(buf(2), true, buf(6), false)));
console.log(stringifyIterator(backend.getKeyRange(buf(2), false, buf(6), false)));
})();