Started on DBv1 migration
This commit is contained in:
@@ -51,3 +51,4 @@ peerlibrary:subscription-data
|
||||
seba:minifiers-autoprefixer
|
||||
akryum:vue-component
|
||||
akryum:vue-sass
|
||||
percolate:migrations
|
||||
|
||||
@@ -93,6 +93,7 @@ peerlibrary:reactive-mongo@0.4.0
|
||||
peerlibrary:reactive-publish@0.10.0
|
||||
peerlibrary:server-autorun@0.8.0
|
||||
peerlibrary:subscription-data@0.8.0
|
||||
percolate:migrations@1.0.3
|
||||
percolate:synced-cron@1.3.2
|
||||
promise@0.11.2
|
||||
raix:eventemitter@1.0.0
|
||||
|
||||
@@ -10,6 +10,10 @@ import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||
let CreatureProperties = new Mongo.Collection('creatureProperties');
|
||||
|
||||
let CreaturePropertySchema = new SimpleSchema({
|
||||
_migrationError: {
|
||||
type: String,
|
||||
optional: true,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
allowedValues: Object.keys(propertySchemasIndex),
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
||||
import InlineComputationSchema from '/imports/api/properties/subSchemas/InlineComputationSchema.js';
|
||||
import { storedIconsSchema } from '/imports/api/icons/Icons.js';
|
||||
import {
|
||||
InlineCalculationFieldToComputeSchema,
|
||||
ComputedOnlyInlineCalculationFieldSchema,
|
||||
InlineCalculationFieldSchema,
|
||||
} from '/imports/api/properties/subSchemas/InlineCalculationFieldSchema.js';
|
||||
import {
|
||||
FieldToComputeSchema,
|
||||
ComputedOnlyFieldSchema,
|
||||
ComputedFieldSchema,
|
||||
} from '/imports/api/properties/subSchemas/ComputedFieldSchema.js';
|
||||
import {
|
||||
ResourcesSchema,
|
||||
ResourcesComputedOnlySchema,
|
||||
ResourcesComputedSchema,
|
||||
} from '/imports/api/properties/subSchemas/ResourcesSchema.js';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||
|
||||
/*
|
||||
@@ -11,186 +23,75 @@ import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||
* to this action
|
||||
*/
|
||||
let ActionSchema = new SimpleSchema({
|
||||
name: {
|
||||
type: String,
|
||||
optional: true,
|
||||
name: {
|
||||
type: String,
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.name,
|
||||
},
|
||||
summary: {
|
||||
type: String,
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.summary,
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.description,
|
||||
},
|
||||
// What time-resource is used to take the action in combat
|
||||
// long actions take longer than 1 round to cast
|
||||
actionType: {
|
||||
type: String,
|
||||
allowedValues: ['action', 'bonus', 'attack', 'reaction', 'free', 'long'],
|
||||
defaultValue: 'action',
|
||||
},
|
||||
// Who is the action directed at
|
||||
target: {
|
||||
type: String,
|
||||
defaultValue: 'singleTarget',
|
||||
allowedValues: [
|
||||
},
|
||||
summary: {
|
||||
type: InlineCalculationFieldToComputeSchema,
|
||||
optional: true,
|
||||
},
|
||||
description: {
|
||||
type: InlineCalculationFieldToComputeSchema,
|
||||
optional: true,
|
||||
},
|
||||
// What time-resource is used to take the action in combat
|
||||
// long actions take longer than 1 round to cast
|
||||
actionType: {
|
||||
type: String,
|
||||
allowedValues: ['action', 'bonus', 'attack', 'reaction', 'free', 'long'],
|
||||
defaultValue: 'action',
|
||||
},
|
||||
// Who is the action directed at
|
||||
target: {
|
||||
type: String,
|
||||
defaultValue: 'singleTarget',
|
||||
allowedValues: [
|
||||
'self',
|
||||
'singleTarget',
|
||||
'multipleTargets',
|
||||
'multipleTargets',
|
||||
],
|
||||
},
|
||||
// Duplicate the ResourceSchema here so we can extend it elegantly.
|
||||
},
|
||||
// Resources schema changes for between standard, computed, and computedOnly
|
||||
resources: {
|
||||
type: Object,
|
||||
type: ResourcesSchema,
|
||||
defaultValue: {},
|
||||
},
|
||||
'resources.itemsConsumed': {
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
maxCount: STORAGE_LIMITS.resourcesCount,
|
||||
},
|
||||
'resources.itemsConsumed.$': {
|
||||
type: Object,
|
||||
},
|
||||
'resources.itemsConsumed.$._id': {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
autoValue(){
|
||||
if (!this.isSet) return Random.id();
|
||||
}
|
||||
},
|
||||
'resources.itemsConsumed.$.tag': {
|
||||
type: String,
|
||||
// Calculation of how many times this action can be used
|
||||
uses: {
|
||||
type: FieldToComputeSchema,
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.tagLength,
|
||||
},
|
||||
'resources.itemsConsumed.$.quantity': {
|
||||
type: Number,
|
||||
defaultValue: 1,
|
||||
},
|
||||
'resources.itemsConsumed.$.itemId': {
|
||||
type: String,
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.name,
|
||||
},
|
||||
'resources.attributesConsumed': {
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
maxCount: STORAGE_LIMITS.resourcesCount,
|
||||
},
|
||||
'resources.attributesConsumed.$': {
|
||||
type: Object,
|
||||
},
|
||||
'resources.attributesConsumed.$._id': {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
autoValue(){
|
||||
if (!this.isSet) return Random.id();
|
||||
}
|
||||
},
|
||||
'resources.attributesConsumed.$.variableName': {
|
||||
type: String,
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.variableName,
|
||||
},
|
||||
'resources.attributesConsumed.$.quantity': {
|
||||
type: Number,
|
||||
defaultValue: 1,
|
||||
},
|
||||
// Calculation of how many times this action can be used
|
||||
uses: {
|
||||
type: String,
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.calculation,
|
||||
},
|
||||
// Integer of how many times it has already been used
|
||||
usesUsed: {
|
||||
type: SimpleSchema.Integer,
|
||||
optional: true,
|
||||
},
|
||||
// How this action's uses are reset automatically
|
||||
reset: {
|
||||
type: String,
|
||||
allowedValues: ['longRest', 'shortRest'],
|
||||
optional: true,
|
||||
},
|
||||
});
|
||||
|
||||
const ComputedOnlyActionSchema = new SimpleSchema({
|
||||
summaryCalculations: {
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
maxCount: STORAGE_LIMITS.inlineCalculationCount,
|
||||
},
|
||||
'summaryCalculations.$': InlineComputationSchema,
|
||||
|
||||
descriptionCalculations: {
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
maxCount: STORAGE_LIMITS.inlineCalculationCount,
|
||||
},
|
||||
'descriptionCalculations.$': InlineComputationSchema,
|
||||
|
||||
usesResult: {
|
||||
// Integer of how many times it has already been used
|
||||
usesUsed: {
|
||||
type: SimpleSchema.Integer,
|
||||
optional: true,
|
||||
},
|
||||
usesErrors: {
|
||||
type: Array,
|
||||
optional: true,
|
||||
maxCount: STORAGE_LIMITS.errorCount,
|
||||
},
|
||||
'usesErrors.$':{
|
||||
type: ErrorSchema,
|
||||
},
|
||||
resources: Object,
|
||||
'resources.itemsConsumed': Array,
|
||||
'resources.itemsConsumed.$': Object,
|
||||
'resources.itemsConsumed.$.available': {
|
||||
type: Number,
|
||||
optional: true,
|
||||
},
|
||||
// This appears both in the computed and uncomputed schema because it can be
|
||||
// set by both a computation or a form
|
||||
'resources.itemsConsumed.$.itemId': {
|
||||
// How this action's uses are reset automatically
|
||||
reset: {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
allowedValues: ['longRest', 'shortRest'],
|
||||
optional: true,
|
||||
},
|
||||
'resources.itemsConsumed.$.itemName': {
|
||||
type: String,
|
||||
max: STORAGE_LIMITS.name,
|
||||
});
|
||||
|
||||
const ComputedOnlyActionSchema = new SimpleSchema({
|
||||
summary: {
|
||||
type: ComputedOnlyInlineCalculationFieldSchema,
|
||||
optional: true,
|
||||
},
|
||||
'resources.itemsConsumed.$.itemIcon': {
|
||||
type: storedIconsSchema,
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.icon,
|
||||
},
|
||||
'resources.itemsConsumed.$.itemColor': {
|
||||
type: String,
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.color,
|
||||
},
|
||||
'resources.attributesConsumed': Array,
|
||||
'resources.attributesConsumed.$': Object,
|
||||
'resources.attributesConsumed.$.available': {
|
||||
type: Number,
|
||||
description: {
|
||||
type: ComputedOnlyInlineCalculationFieldSchema,
|
||||
optional: true,
|
||||
},
|
||||
'resources.attributesConsumed.$.statId': {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
uses: {
|
||||
type: ComputedOnlyFieldSchema,
|
||||
optional: true,
|
||||
},
|
||||
'resources.attributesConsumed.$.statName': {
|
||||
type: String,
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.name,
|
||||
resources: {
|
||||
type: ResourcesComputedOnlySchema,
|
||||
defaultValue: {},
|
||||
},
|
||||
// True if the uses left is zero, or any item or attribute consumed is
|
||||
// insufficient
|
||||
@@ -202,6 +103,24 @@ const ComputedOnlyActionSchema = new SimpleSchema({
|
||||
|
||||
const ComputedActionSchema = new SimpleSchema()
|
||||
.extend(ActionSchema)
|
||||
.extend(ComputedOnlyActionSchema);
|
||||
.extend(ComputedOnlyActionSchema)
|
||||
.extend({
|
||||
uses: {
|
||||
type: ComputedFieldSchema,
|
||||
optional: true,
|
||||
},
|
||||
summary: {
|
||||
type: InlineCalculationFieldSchema,
|
||||
optional: true,
|
||||
},
|
||||
description: {
|
||||
type: InlineCalculationFieldSchema,
|
||||
optional: true,
|
||||
},
|
||||
resources: {
|
||||
type: ResourcesComputedSchema,
|
||||
defaultValue: {},
|
||||
},
|
||||
});
|
||||
|
||||
export { ActionSchema, ComputedOnlyActionSchema, ComputedActionSchema};
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { Random } from 'meteor/random';
|
||||
import {
|
||||
FieldToComputeSchema,
|
||||
ComputedOnlyFieldSchema,
|
||||
ComputedFieldSchema,
|
||||
} from '/imports/api/properties/subSchemas/ComputedFieldSchema.js';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||
|
||||
const AttributeConsumedSchema = new SimpleSchema({
|
||||
_id: {
|
||||
@@ -12,11 +18,47 @@ const AttributeConsumedSchema = new SimpleSchema({
|
||||
variableName: {
|
||||
type: String,
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.variableName,
|
||||
},
|
||||
quantity: {
|
||||
type: Number,
|
||||
defaultValue: 1,
|
||||
type: FieldToComputeSchema,
|
||||
optional: true,
|
||||
},
|
||||
});
|
||||
|
||||
export default AttributeConsumedSchema;
|
||||
const ComputedOnlyAttributeConsumedSchema = new SimpleSchema({
|
||||
available: {
|
||||
type: Number,
|
||||
optional: true,
|
||||
},
|
||||
statId: {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
optional: true,
|
||||
},
|
||||
statName: {
|
||||
type: String,
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.name,
|
||||
},
|
||||
quantity: {
|
||||
type: ComputedOnlyFieldSchema,
|
||||
optional: true,
|
||||
},
|
||||
});
|
||||
|
||||
const ComputedAttributeConsumedSchema = new SimpleSchema()
|
||||
.extend(AttributeConsumedSchema)
|
||||
.extend(ComputedOnlyAttributeConsumedSchema)
|
||||
.extend({
|
||||
quantity: {
|
||||
type: ComputedFieldSchema,
|
||||
optional: true,
|
||||
},
|
||||
});
|
||||
|
||||
export {
|
||||
AttributeConsumedSchema,
|
||||
ComputedOnlyAttributeConsumedSchema,
|
||||
ComputedAttributeConsumedSchema
|
||||
};
|
||||
|
||||
35
app/imports/api/properties/subSchemas/ComputedFieldSchema.js
Normal file
35
app/imports/api/properties/subSchemas/ComputedFieldSchema.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||
|
||||
const FieldToComputeSchema = new SimpleSchema({
|
||||
// This is required, if we don't have a calculation delete the whole object
|
||||
calculation: {
|
||||
type: String,
|
||||
},
|
||||
});
|
||||
|
||||
const ComputedOnlyFieldSchema = new SimpleSchema({
|
||||
value: {
|
||||
type: SimpleSchema.oneOf(String, Number),
|
||||
optional: true,
|
||||
},
|
||||
errors: {
|
||||
type: Array,
|
||||
optional: true,
|
||||
maxCount: STORAGE_LIMITS.errorCount,
|
||||
},
|
||||
'errors.$':{
|
||||
type: ErrorSchema,
|
||||
},
|
||||
});
|
||||
|
||||
const ComputedFieldSchema = new SimpleSchema()
|
||||
.extend(FieldToComputeSchema)
|
||||
.extend(ComputedOnlyFieldSchema)
|
||||
|
||||
export {
|
||||
FieldToComputeSchema,
|
||||
ComputedOnlyFieldSchema,
|
||||
ComputedFieldSchema
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import InlineComputationSchema from '/imports/api/properties/subSchemas/InlineComputationSchema.js';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||
|
||||
const InlineCalculationFieldToComputeSchema = new SimpleSchema({
|
||||
text: {
|
||||
type: String,
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.inlineCalculationField,
|
||||
},
|
||||
});
|
||||
|
||||
const ComputedOnlyInlineCalculationFieldSchema = new SimpleSchema({
|
||||
'inlineCalculations': {
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
maxCount: STORAGE_LIMITS.inlineCalculationCount,
|
||||
},
|
||||
'inlineCalculations.$': {
|
||||
type: InlineComputationSchema,
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.inlineCalculationField,
|
||||
},
|
||||
});
|
||||
|
||||
const InlineCalculationFieldSchema = new SimpleSchema()
|
||||
.extend(InlineCalculationFieldToComputeSchema)
|
||||
.extend(ComputedOnlyInlineCalculationFieldSchema)
|
||||
|
||||
export {
|
||||
InlineCalculationFieldToComputeSchema,
|
||||
ComputedOnlyInlineCalculationFieldSchema,
|
||||
InlineCalculationFieldSchema,
|
||||
};
|
||||
@@ -8,8 +8,8 @@ const InlineComputationSchema = new SimpleSchema({
|
||||
type: String,
|
||||
max: STORAGE_LIMITS.calculation,
|
||||
},
|
||||
result: {
|
||||
type: String,
|
||||
value: {
|
||||
type: SimpleSchema.oneOf(String, Number),
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.calculation,
|
||||
},
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { Random } from 'meteor/random';
|
||||
import {
|
||||
FieldToComputeSchema,
|
||||
ComputedOnlyFieldSchema,
|
||||
ComputedFieldSchema,
|
||||
} from '/imports/api/properties/subSchemas/ComputedFieldSchema.js';
|
||||
import { storedIconsSchema } from '/imports/api/icons/Icons.js';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||
|
||||
const ItemConsumedSchema = new SimpleSchema({
|
||||
_id: {
|
||||
@@ -14,13 +21,61 @@ const ItemConsumedSchema = new SimpleSchema({
|
||||
optional: true,
|
||||
},
|
||||
quantity: {
|
||||
type: Number,
|
||||
defaultValue: 1,
|
||||
type: FieldToComputeSchema,
|
||||
optional: true,
|
||||
},
|
||||
itemId: {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
optional: true,
|
||||
},
|
||||
});
|
||||
|
||||
export default ItemConsumedSchema;
|
||||
const ComputedOnlyItemConsumedSchema = new SimpleSchema({
|
||||
available: {
|
||||
type: Number,
|
||||
optional: true,
|
||||
},
|
||||
quantity: {
|
||||
type: ComputedOnlyFieldSchema,
|
||||
optional: true,
|
||||
},
|
||||
// This appears both in the computed and uncomputed schema because it can be
|
||||
// set by both a computation or a form
|
||||
itemId: {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
optional: true,
|
||||
},
|
||||
itemName: {
|
||||
type: String,
|
||||
max: STORAGE_LIMITS.name,
|
||||
optional: true,
|
||||
},
|
||||
itemIcon: {
|
||||
type: storedIconsSchema,
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.icon,
|
||||
},
|
||||
itemColor: {
|
||||
type: String,
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.color,
|
||||
},
|
||||
})
|
||||
|
||||
const ComputedItemConsumedSchema = new SimpleSchema()
|
||||
.extend(ItemConsumedSchema)
|
||||
.extend(ComputedOnlyItemConsumedSchema)
|
||||
.extend({
|
||||
quantity: {
|
||||
type: ComputedFieldSchema,
|
||||
optional: true,
|
||||
},
|
||||
});
|
||||
|
||||
export {
|
||||
ItemConsumedSchema,
|
||||
ComputedOnlyItemConsumedSchema,
|
||||
ComputedItemConsumedSchema
|
||||
};
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import ItemConsumedSchema from '/imports/api/properties/subSchemas/ItemConsumedSchema.js';
|
||||
import AttributeConsumedSchema from '/imports/api/properties/subSchemas/AttributeConsumedSchema.js';
|
||||
import {
|
||||
ItemConsumedSchema,
|
||||
ComputedOnlyItemConsumedSchema,
|
||||
ComputedItemConsumedSchema
|
||||
} from '/imports/api/properties/subSchemas/ItemConsumedSchema.js';
|
||||
import {
|
||||
AttributeConsumedSchema,
|
||||
ComputedOnlyAttributeConsumedSchema,
|
||||
ComputedAttributeConsumedSchema
|
||||
} from '/imports/api/properties/subSchemas/AttributeConsumedSchema.js';
|
||||
|
||||
const ResourcesSchema = new SimpleSchema({
|
||||
itemsConsumed: {
|
||||
@@ -19,4 +27,42 @@ const ResourcesSchema = new SimpleSchema({
|
||||
},
|
||||
});
|
||||
|
||||
export default ResourcesSchema;
|
||||
const ResourcesComputedOnlySchema = new SimpleSchema({
|
||||
itemsConsumed: {
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
},
|
||||
'itemsConsumed.$': {
|
||||
type: ComputedOnlyItemConsumedSchema,
|
||||
},
|
||||
attributesConsumed: {
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
},
|
||||
'attributesConsumed.$': {
|
||||
type: ComputedOnlyAttributeConsumedSchema,
|
||||
},
|
||||
});
|
||||
|
||||
const ResourcesComputedSchema = new SimpleSchema({
|
||||
itemsConsumed: {
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
},
|
||||
'itemsConsumed.$': {
|
||||
type: ComputedItemConsumedSchema,
|
||||
},
|
||||
attributesConsumed: {
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
},
|
||||
'attributesConsumed.$': {
|
||||
type: ComputedAttributeConsumedSchema,
|
||||
},
|
||||
});
|
||||
|
||||
export {
|
||||
ResourcesSchema,
|
||||
ResourcesComputedOnlySchema,
|
||||
ResourcesComputedSchema,
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ const STORAGE_LIMITS = Object.freeze({
|
||||
collectionName: 64,
|
||||
color: 10000,
|
||||
description: 49473, //the length of the Bee Movie script
|
||||
inlineCalculationField: 49473,
|
||||
errorMessage: 256,
|
||||
icon: 10000,
|
||||
name: 128,
|
||||
|
||||
186
app/imports/migrations/2.0-beta.33-dbv1.js
Normal file
186
app/imports/migrations/2.0-beta.33-dbv1.js
Normal file
@@ -0,0 +1,186 @@
|
||||
import { Migrations } from 'meteor/percolate:migrations';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import { get, merge } from 'lodash';
|
||||
|
||||
// Git version 2.0-beta.33
|
||||
// Database version 1
|
||||
Migrations.add({
|
||||
version: 1,
|
||||
name: 'Unifies calculated field schema',
|
||||
up(){
|
||||
CreatureProperties.find({}).forEach(prop => {
|
||||
const modifier = getUpPropModifier(prop);
|
||||
if (!modifier) return;
|
||||
updateOrStoreError(CreatureProperties, prop, modifier);
|
||||
});
|
||||
},
|
||||
down(){
|
||||
CreatureProperties.find({}).forEach(prop => {
|
||||
const modifier = getDownPropModifier(prop);
|
||||
if (!modifier) return;
|
||||
updateOrStoreError(CreatureProperties, prop, modifier);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
function updateOrStoreError(collection, prop, modifier){
|
||||
try {
|
||||
collection.update(prop._id, modifier, {
|
||||
bypassCollection2: true,
|
||||
//selector: {type: prop.type},
|
||||
});
|
||||
} catch(e){
|
||||
let errorString = e.toString();
|
||||
if (errorString){
|
||||
console.warn(errorString, prop._id);
|
||||
collection.update(prop._id, {
|
||||
$set: {_migrationError: e.toString()}
|
||||
}, {
|
||||
bypassCollection2: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getUpPropModifier(prop){
|
||||
const modifiers = typeUpModifiers[prop.type]?.(prop);
|
||||
if (!modifiers) return;
|
||||
return cleanModifier(merge(...modifiers));
|
||||
}
|
||||
|
||||
function getDownPropModifier(prop){
|
||||
const modifiers = typeDownModifiers[prop.type]?.(prop);
|
||||
if (!modifiers) return;
|
||||
return cleanModifier(merge(...modifiers));
|
||||
}
|
||||
|
||||
function cleanModifier(modifier){
|
||||
if (modifier.$set && !Object.keys(modifier.$set).length){
|
||||
delete modifier.$set;
|
||||
}
|
||||
if (modifier.$unset && !Object.keys(modifier.$unset).length){
|
||||
delete modifier.$unset;
|
||||
}
|
||||
if (!modifier.$set && !modifier.$unset) return;
|
||||
return modifier;
|
||||
}
|
||||
|
||||
const typeUpModifiers = {
|
||||
action(prop){
|
||||
return [
|
||||
convertComputedField(prop, 'uses'),
|
||||
// TODO: This doesn't work on itemsConsumed because it is an array field
|
||||
// Need to iterate over every item consumed
|
||||
convertComputedField(prop, 'resources.itemsConsumed.quantity'),
|
||||
convertComputedField(prop, 'resources.attributesConsumed.quantity'),
|
||||
convertInlineComputationField(prop, 'summary'),
|
||||
convertInlineComputationField(prop, 'description'),
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
const typeDownModifiers = {
|
||||
action(prop){
|
||||
const modifiers = [
|
||||
unConvertComputedField(prop, 'uses'),
|
||||
unConvertComputedField(prop, 'resources.itemsConsumed.quantity'),
|
||||
unConvertComputedField(prop, 'resources.attributesConsumed.quantity'),
|
||||
unConvertInlineComputationField(prop, 'summary'),
|
||||
unConvertInlineComputationField(prop, 'description'),
|
||||
];
|
||||
return modifiers;
|
||||
},
|
||||
};
|
||||
|
||||
function convertComputedField(object, field){
|
||||
const calculation = get(object, field);
|
||||
if (!calculation) return {
|
||||
$unset: {
|
||||
[field]: 1,
|
||||
[field + 'Errors']: 1,
|
||||
[field + 'Result']: 1,
|
||||
}
|
||||
};
|
||||
const errors = get(object, field + 'Errors');
|
||||
let value = get(object, field + 'Result');
|
||||
// If the calculation can be cast to number, use that for value
|
||||
if (value === undefined && Number.isFinite(+calculation)){
|
||||
value = +calculation;
|
||||
}
|
||||
const modifier = {
|
||||
$unset:{
|
||||
[field + 'Errors']: 1,
|
||||
[field + 'Result']: 1,
|
||||
},
|
||||
$set: {
|
||||
[field]: {
|
||||
value,
|
||||
calculation,
|
||||
errors,
|
||||
}
|
||||
}
|
||||
};
|
||||
return modifier;
|
||||
}
|
||||
|
||||
function unConvertComputedField(object, field){
|
||||
const calculation = get(object, field)?.calculation;
|
||||
if (!calculation) return {
|
||||
$unset: {
|
||||
[field]: 1,
|
||||
}
|
||||
};
|
||||
const errors = get(object, field).errors;
|
||||
let value = get(object, field).value;
|
||||
// If the calculation can be cast to number, use that for value
|
||||
if (value === undefined && Number.isFinite(+calculation)){
|
||||
value = +calculation;
|
||||
}
|
||||
const modifier = {
|
||||
$set:{
|
||||
[field]: calculation,
|
||||
[field + 'Errors']: errors,
|
||||
[field + 'Result']: value,
|
||||
},
|
||||
};
|
||||
return modifier;
|
||||
}
|
||||
|
||||
function convertInlineComputationField(object, field){
|
||||
const text = get(object, field);
|
||||
const inlineCalculations = get(object, field + 'Calculations');
|
||||
if (inlineCalculations){
|
||||
inlineCalculations.forEach(calc => {
|
||||
calc.value = calc.result;
|
||||
delete calc.result;
|
||||
});
|
||||
}
|
||||
return {
|
||||
$unset: {
|
||||
[field + 'Calculations']: 1,
|
||||
},
|
||||
$set: {
|
||||
[field]: {
|
||||
text,
|
||||
inlineCalculations,
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function unConvertInlineComputationField(object, field){
|
||||
const text = get(object, field)?.text;
|
||||
const inlineCalculations = get(object, field)?.inlineCalculations;
|
||||
if (inlineCalculations) {
|
||||
inlineCalculations.forEach(calc => {
|
||||
calc.result = calc.value;
|
||||
delete calc.value;
|
||||
});
|
||||
}
|
||||
return {
|
||||
$set: {
|
||||
[field]: text,
|
||||
[field + 'Calculations']: inlineCalculations,
|
||||
},
|
||||
};
|
||||
}
|
||||
2
app/imports/migrations/index.js
Normal file
2
app/imports/migrations/index.js
Normal file
@@ -0,0 +1,2 @@
|
||||
import './2.0-beta.33-dbv1.js';
|
||||
import './methods/index.js';
|
||||
30
app/imports/migrations/methods/getVersion.js
Normal file
30
app/imports/migrations/methods/getVersion.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import { assertAdmin } from '/imports/api/sharing/sharingPermissions.js';
|
||||
import { Migrations } from 'meteor/percolate:migrations';
|
||||
|
||||
const dbVersionToGitVersion = {
|
||||
0: '2.0-beta.32 and lower',
|
||||
1: '2.0-beta.33',
|
||||
}
|
||||
|
||||
const getVersion = new ValidatedMethod({
|
||||
name: 'admin.getVersion',
|
||||
validate: null,
|
||||
mixins: [RateLimiterMixin],
|
||||
rateLimit: {
|
||||
numRequests: 5,
|
||||
timeInterval: 5000,
|
||||
},
|
||||
run() {
|
||||
if (Meteor.isClient) return;
|
||||
assertAdmin(this.userId);
|
||||
const dbVersion = Migrations.getVersion();
|
||||
return {
|
||||
dbVersion,
|
||||
gitVersion: dbVersionToGitVersion[dbVersion],
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export default getVersion;
|
||||
2
app/imports/migrations/methods/index.js
Normal file
2
app/imports/migrations/methods/index.js
Normal file
@@ -0,0 +1,2 @@
|
||||
import './migrateTo.js';
|
||||
import './getVersion.js';
|
||||
29
app/imports/migrations/methods/migrateTo.js
Normal file
29
app/imports/migrations/methods/migrateTo.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { assertAdmin } from '/imports/api/sharing/sharingPermissions.js';
|
||||
import { Migrations } from 'meteor/percolate:migrations';
|
||||
|
||||
const migrateTo = new ValidatedMethod({
|
||||
name: 'admin.migrateTo',
|
||||
validate: new SimpleSchema({
|
||||
version: {
|
||||
type: SimpleSchema.oneOf(
|
||||
SimpleSchema.Integer,
|
||||
String
|
||||
),
|
||||
},
|
||||
}).validator(),
|
||||
mixins: [RateLimiterMixin],
|
||||
rateLimit: {
|
||||
numRequests: 5,
|
||||
timeInterval: 5000,
|
||||
},
|
||||
run({version}) {
|
||||
if (Meteor.isClient) return;
|
||||
assertAdmin(this.userId);
|
||||
Migrations.migrateTo(version);
|
||||
},
|
||||
});
|
||||
|
||||
export default migrateTo;
|
||||
87
app/imports/ui/pages/Admin.vue
Normal file
87
app/imports/ui/pages/Admin.vue
Normal file
@@ -0,0 +1,87 @@
|
||||
<template lang="html">
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-card>
|
||||
<v-card-text>
|
||||
<h4>Database version: {{ versions && versions.dbVersion }}</h4>
|
||||
<h4>Git version: {{ versions && versions.gitVersion }}</h4>
|
||||
<v-alert
|
||||
v-if="versionError"
|
||||
type="error"
|
||||
>
|
||||
{{ versionError }}
|
||||
</v-alert>
|
||||
<v-btn
|
||||
icon
|
||||
@click="refreshVersions"
|
||||
>
|
||||
<v-icon>mdi-refresh</v-icon>
|
||||
</v-btn>
|
||||
<v-text-field
|
||||
v-model="migrationInput"
|
||||
label="Database version to migrate to"
|
||||
/>
|
||||
<v-btn
|
||||
:disabled="!migrationInput"
|
||||
:loading="loadingMigration"
|
||||
@click="migrate"
|
||||
>
|
||||
Migrate
|
||||
</v-btn>
|
||||
<v-alert
|
||||
v-if="migrateError"
|
||||
type="error"
|
||||
>
|
||||
{{ migrateError }}
|
||||
</v-alert>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import getVersion from '/imports/migrations/methods/getVersion.js';
|
||||
import migrateTo from '/imports/migrations/methods/migrateTo.js';
|
||||
|
||||
export default {
|
||||
data(){return {
|
||||
loadingVersion: false,
|
||||
versions: {},
|
||||
migrationInput: undefined,
|
||||
versionError: undefined,
|
||||
migrateError: undefined,
|
||||
loadingMigration: false,
|
||||
}},
|
||||
mounted(){
|
||||
this.refreshVersions();
|
||||
},
|
||||
methods: {
|
||||
refreshVersions(){
|
||||
getVersion.call((error, result) => {
|
||||
this.versionError = error;
|
||||
this.versions = result;
|
||||
});
|
||||
},
|
||||
migrate(){
|
||||
let version = this.migrationInput;
|
||||
if (Number.isFinite(+version)){
|
||||
version = +version;
|
||||
}
|
||||
this.loadingMigration = true;
|
||||
migrateTo.call({
|
||||
version,
|
||||
}, error => {
|
||||
this.loadingMigration = false;
|
||||
this.migrateError = error;
|
||||
this.refreshVersions();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
</style>
|
||||
@@ -24,6 +24,7 @@ import PatreonLevelTooLow from '/imports/ui/pages/PatreonLevelTooLow.vue';
|
||||
import Tabletops from '/imports/ui/pages/Tabletops.vue';
|
||||
import Tabletop from '/imports/ui/pages/Tabletop.vue';
|
||||
import TabletopToolbar from '/imports/ui/tabletop/TabletopToolbar.vue';
|
||||
import Admin from '/imports/ui/pages/Admin.vue';
|
||||
|
||||
let userSubscription = Meteor.subscribe('user');
|
||||
|
||||
@@ -242,6 +243,11 @@ RouterFactory.configure(factory => {
|
||||
name: 'iconAdmin',
|
||||
component: IconAdmin,
|
||||
beforeEnter: ensureAdmin,
|
||||
},{
|
||||
path: '/admin',
|
||||
name: 'admin',
|
||||
component: Admin,
|
||||
beforeEnter: ensureAdmin,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -6,3 +6,4 @@ import '/imports/server/publications/index.js';
|
||||
import '/imports/server/cron/deleteSoftRemovedDocuments.js';
|
||||
import '/imports/api/parenting/organizeMethods.js';
|
||||
import '/imports/api/users/patreon/updatePatreonOnLogin.js';
|
||||
import '/imports/migrations/index.js';
|
||||
|
||||
Reference in New Issue
Block a user