Added insert and update methods for all properties

This commit is contained in:
Stefan Zermatten
2019-03-13 14:03:03 +02:00
parent 572aca5906
commit 18e3f653f3
23 changed files with 1049 additions and 154 deletions

View File

@@ -26,3 +26,46 @@ export function assertViewPermission(creature, userId) {
creature = getCreature(creature, {owner: 1, writers: 1, public: 1});
viewPermission(creature, userId);
};
// Checks if the method has permission to run on the document. If the document
// has a charId, that creature is checked, otherwise if it has an _id and the
// collection is defined in the method options, that document is fetched to
// determine its charId, otherwise a getCharId method can be defined to perform
// a special search for the required creature
export function creaturePermissionMixin(methodOptions){
let assertPermission;
if (methodOptions.permission === 'owner'){
assertPermission = assertOwnership;
} else if (methodOptions.permission === 'edit'){
assertPermission = assertEditPermission;
} else if (methodOptions.permission === 'view'){
assertPermission = assertViewPermission;
} else {
throw "`permission` missing in method options";
}
let getCharId;
if (methodOptions.getCharId){
getCharId = methodOptions.getCharId
} else if (methodOptions.collection) {
getCharId = function({_id}){
methodOptions.collection.findOne(_id, {
fields: {charId: 1}
}).charId;
};
} else {
getCharId = function(){
throw "`getCharId` or `collection` missing in method options," +
" or {charId} missing in call";
}
}
let runFunc = methodOptions.run;
methodOptions.run = function(doc, ...rest){
// Store the charId on the doc for other mixins if it had to be fetched
doc.charId = doc.charId || getCharId.apply(this, arguments);
assertPermission(charId, this.userId)
return runFunc.call(this, doc, ...rest);
};
return methodOptions;
};

View File

@@ -6,12 +6,23 @@ import PropertySchema from '/imports/api/creature/subSchemas/PropertySchema.js';
import ChildSchema from '/imports/api/parenting/ChildSchema.js';
import ColorSchema from '/imports/api/creature/subSchemas/ColorSchema.js';
// Mixins
import { creaturePermissionMixin } from '/imports/api/creature/creaturePermissions.js';
import { setDocToLastMixin } from '/imports/api/order.js';
import { setDocAncestryMixin, ensureAncestryContainsCharIdMixin } from '/imports/api/parenting/parenting.js';
import simpleSchemaMixin from '/imports/api/simpleSchemaMixin.js';
let Actions = new Mongo.Collection('actions');
/*
* Actions are things a character can do
*/
let ActionSchema = schema({
name: {
type: String,
optional: true,
},
description: {
type: String,
optional: true,
@@ -73,5 +84,39 @@ Actions.attachSchema(ActionSchema);
Actions.attachSchema(PropertySchema);
Actions.attachSchema(ChildSchema);
const insertAction = new ValidatedMethod({
name: 'Actions.methods.insert',
mixins: [
creaturePermissionMixin,
setDocToLastMixin,
setDocAncestryMixin,
ensureAncestryContainsCharIdMixin,
simpleSchemaMixin,
],
collection: Actions,
permission: 'edit',
schema: ActionSchema,
run(action) {
return Actions.insert(action);
},
});
const updateAction = new ValidatedMethod({
name: 'Actions.methods.update',
mixins: [
creaturePermissionMixin,
simpleSchemaMixin,
],
collection: Actions,
permission: 'edit',
schema: new SimpleSchema({
_id: SimpleSchema.RegEx.Id,
update: ActionSchema.omit('name'),
}),
run({_id, update}) {
return Actions.update(_id, {$set: update});
},
});
export default Actions;
export { ActionSchema };
export { ActionSchema, insertAction, updateAction };

View File

@@ -3,10 +3,14 @@ import ChildSchema from '/imports/api/parenting/ChildSchema.js';
import ColorSchema from '/imports/api/creature/subSchemas/ColorSchema.js';
import SimpleSchema from 'simpl-schema';
import schema from '/imports/api/schema.js';
import { assertEditPermission } from '/imports/api/creature/creaturePermissions.js';
import { recomputeCreatureById } from '/imports/api/creature/creatureComputation.js'
import { getHighestOrder } from '/imports/api/order.js';
import pickKeysAsOptional from '/imports/api/pickKeysAsOptional.js';
import VARIABLE_NAME_REGEX from '/imports/constants/VARIABLE_NAME_REGEX.js';
// Mixins
import recomputeCreatureMixin from '/imports/api/creature/recomputeCreatureMixin.js';
import { creaturePermissionMixin } from '/imports/api/creature/creaturePermissions.js';
import { setDocToLastMixin } from '/imports/api/order.js';
import { setDocAncestryMixin, ensureAncestryContainsCharIdMixin } from '/imports/api/parenting/parenting.js';
import simpleSchemaMixin from '/imports/api/simpleSchemaMixin.js';
let Attributes = new Mongo.Collection('attributes');
@@ -14,11 +18,14 @@ let Attributes = new Mongo.Collection('attributes');
* Attributes are numbered stats of a character
*/
let AttributeSchema = schema({
name: {
type: String,
optional: true,
},
// The technical, lowercase, single-word name used in formulae
variableName: {
type: String,
// Must contain a letter, and be made of word characters only
regEx: /^\w*[a-z]\w*$/i,
regEx: VARIABLE_NAME_REGEX,
},
// How it is displayed and computed is determined by type
type: {
@@ -85,119 +92,88 @@ Attributes.attachSchema(PropertySchema);
Attributes.attachSchema(ChildSchema);
const insertAttribute = new ValidatedMethod({
name: 'Attributes.methods.insert',
validate: AttributeSchema.validator({ clean: true }),
run({attribute}) {
const charId = attribute.charId;
assertEditPermission(charId, this.userId);
attribute.order = getHighestOrder({
collection: Attributes,
charId,
}) + 1;
attribute.parent = {
id: charId,
collection: 'Creatures',
};
let attId = Attributes.insert(attribute);
recomputeCreatureById(charId);
return attId;
mixins: [
creaturePermissionMixin,
setDocToLastMixin,
setDocAncestryMixin,
ensureAncestryContainsCharIdMixin,
recomputeCreatureMixin,
simpleSchemaMixin,
],
collection: Attributes,
permission: 'edit',
schema: AttributeSchema,
run(attribute) {
return Attributes.insert(attribute);
},
});
const updateAttribute = new ValidatedMethod({
name: 'Attributes.methods.update',
validate: schema({
_id: {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
update: AttributeSchema,
}).validator(),
run({_id, update}) {
let currentAttribute = Attributes.findOne(_id, {fields: {value: 1, charId: 1}});
if (!currentAttribute){
throw new Meteor.Error('Attributes.methods.update.denied',
`No attributes exist with the id: ${_id}`);
}
let charId = currentAttribute.charId;
assertEditPermission(charId, this.userId)
if (typeof update.adjustment === 'number'){
let val = currentAttribute.value;
if (update.adjustment < -val) update.adjustment = -val;
if (update.adjustment > 0) update.adjustment = 0;
}
Attributes.update(_id, {$set: update});
recomputeCreatureById(charId);
mixins: [
creaturePermissionMixin,
recomputeCreatureMixin,
simpleSchemaMixin,
],
collection: Attributes,
permission: 'edit',
schema: new SimpleSchema({
_id: SimpleSchema.RegEx.Id,
update: AttributeSchema.omit('adjustment', 'name'),
}),
skipRecompute({update}){
return !('variableName' in update) &&
!('type' in update) &&
!('baseValue' in update)
},
run({_id, update}) {
return Attributes.update(_id, {$set: update});
},
});
const adjustAttribute = new ValidatedMethod({
name: 'Attributes.methods.adjust',
validate: new SimpleSchema({
_id: {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
increment: {
type: Number,
optional: true
},
set: {
type: Number,
optional: true,
custom() {
if (!this.isSet && !this.field('increment').isSet) {
// either set or increment must exist
return SimpleSchema.ErrorTypes.REQUIRED;
} else if (this.isSet && this.field('increment').isSet){
return 'Can\'t increment and set an attritbute adjustment in one operation';
}
},
},
}).validator(),
run({_id, increment, set}) {
let currentAttribute = Attributes.findOne(_id);
if (!currentAttribute){
throw new Meteor.Error('Attributes.methods.update.denied',
`No attributes exist with the id: ${_id}`);
}
let charId = currentAttribute.charId;
assertEditPermission(charId, this.userId)
if (typeof set === 'number'){
let val = currentAttribute.value;
mixins: [
creaturePermissionMixin,
simpleSchemaMixin,
],
collection: Attributes,
permission: 'edit',
schema: new SimpleSchema({
_id: SimpleSchema.RegEx.Id,
type: {
type: String,
allowedValues: ['set', 'increment']
},
value: Number,
}),
run({_id, type, value}) {
if (type === 'set'){
let currentValue = currentAttribute.value;
// Set represents what we want the value to be after adjustment
// So we need the actual adjustment to get to that value
let adjustment = set - val;
let adjustment = value - currentValue;
// Ajustment can't exceed total value
if (-adjustment > val) adjustment = -val;
if (-adjustment > currentValue) adjustment = -currentValue;
// Adjustment must be negative
if (adjustment > 0) adjustment = 0;
Attributes.update(_id, {$set: {adjustment}});
} else if (typeof increment === 'number'){
return Attributes.update(_id, {$set: {adjustment}});
} else if (type === 'increment'){
let remaining = currentAttribute.value + (currentAttribute.adjustment || 0);
let adj = currentAttribute.adjustment;
// Can't decrease adjustment below remaining value
let increment = value;
if (-increment > remaining) increment = -remaining;
// Can't increase adjustment above zero
if (increment > -adj) increment = -adj;
if (typeof currentAttribute.adjustment === 'number'){
Attributes.update(_id, {$inc: {adjustment: increment}});
return Attributes.update(_id, {$inc: {adjustment: increment}});
} else {
Attributes.update(_id, {$set: {adjustment: increment}});
return Attributes.update(_id, {$set: {adjustment: increment}});
}
}
},
});
export default Attributes;

View File

@@ -4,9 +4,19 @@ import PropertySchema from '/imports/api/creature/subSchemas/PropertySchema.js';
import ChildSchema from '/imports/api/parenting/ChildSchema.js';
import { EffectSchema } from '/imports/api/creature/properties/Effects.js';
// Mixins
import { creaturePermissionMixin } from '/imports/api/creature/creaturePermissions.js';
import { setDocToLastMixin } from '/imports/api/order.js';
import { setDocAncestryMixin, ensureAncestryContainsCharIdMixin } from '/imports/api/parenting/parenting.js';
import simpleSchemaMixin from '/imports/api/simpleSchemaMixin.js';
let Buffs = new Mongo.Collection('buffs');
let BuffSchema = new SimpleSchema({
name: {
type: String,
optional: true,
},
description: {
type: String,
optional: true,
@@ -32,9 +42,9 @@ let StoredBuffSchema = new SimpleSchema({
target: {
type: String,
allowedValues: [
// the character who took the action
// the character who took the buff
'self',
// the singular `target` of the action
// the singular `target` of the buff
'target',
// rolled once for `each` target
'each',
@@ -69,5 +79,39 @@ Buffs.attachSchema(AppliedBuffSchema);
Buffs.attachSchema(PropertySchema);
Buffs.attachSchema(ChildSchema);
const insertBuff = new ValidatedMethod({
name: 'Buffs.methods.insert',
mixins: [
creaturePermissionMixin,
setDocToLastMixin,
setDocAncestryMixin,
ensureAncestryContainsCharIdMixin,
simpleSchemaMixin,
],
collection: Buffs,
permission: 'edit',
schema: BuffSchema,
run(buff) {
return Buffs.insert(buff);
},
});
const updateBuff = new ValidatedMethod({
name: 'Buffs.methods.update',
mixins: [
creaturePermissionMixin,
simpleSchemaMixin,
],
collection: Buffs,
permission: 'edit',
schema: new SimpleSchema({
_id: SimpleSchema.RegEx.Id,
update: BuffSchema.omit('name'),
}),
run({_id, update}) {
return Buffs.update(_id, {$set: update});
},
});
export default Buffs;
export { AppliedBuffSchema, StoredBuffSchema };
export { AppliedBuffSchema, StoredBuffSchema, insertBuff, updateBuff };

View File

@@ -2,10 +2,39 @@ import SimpleSchema from 'simpl-schema';
import schema from '/imports/api/schema.js';
import PropertySchema from '/imports/api/creature/subSchemas/PropertySchema.js';
import ChildSchema from '/imports/api/parenting/ChildSchema.js';
import VARIABLE_NAME_REGEX from '/imports/constants/VARIABLE_NAME_REGEX.js';
// Mixins
import { creaturePermissionMixin } from '/imports/api/creature/creaturePermissions.js';
import { setDocToLastMixin } from '/imports/api/order.js';
import { setDocAncestryMixin, ensureAncestryContainsCharIdMixin } from '/imports/api/parenting/parenting.js';
import simpleSchemaMixin from '/imports/api/simpleSchemaMixin.js';
let ClassLevels = new Mongo.Collection("classLevels");
let ClassLevelSchema = schema({
name: {
type: String,
optional: true,
},
// The name of this class level's variable
variableName: {
type: String,
regEx: VARIABLE_NAME_REGEX,
},
// The variable name of the base class this level is attached to
baseClass: {
type: String,
regEx: VARIABLE_NAME_REGEX,
},
// The name of the class level that needs to preceed this class level
// So a totemWarrior level 5 must be preceded by a totemWarrior level 4
// If it's not set, any level below with the same baseClass is matched
previousClassLevel: {
type: String,
regEx: VARIABLE_NAME_REGEX,
optional: true,
},
level: {
type: SimpleSchema.Integer,
},
@@ -15,5 +44,41 @@ ClassLevels.attachSchema(ClassLevelSchema);
ClassLevels.attachSchema(PropertySchema);
ClassLevels.attachSchema(ChildSchema);
// Todo ensure the class level is being parented to a compatible class, and that
// previous level requirements are met
const insertClassLevel = new ValidatedMethod({
name: 'ClassLevels.methods.insert',
mixins: [
creaturePermissionMixin,
setDocToLastMixin,
setDocAncestryMixin,
ensureAncestryContainsCharIdMixin,
simpleSchemaMixin,
],
collection: ClassLevels,
permission: 'edit',
schema: ClassLevelSchema,
run(classLevel) {
return ClassLevels.insert(classLevel);
},
});
const updateClassLevel = new ValidatedMethod({
name: 'ClassLevels.methods.update',
mixins: [
creaturePermissionMixin,
simpleSchemaMixin,
],
collection: ClassLevels,
permission: 'edit',
schema: new SimpleSchema({
_id: SimpleSchema.RegEx.Id,
update: ClassLevelSchema.omit('name'),
}),
run({_id, update}) {
return ClassLevels.update(_id, {$set: update});
},
});
export default ClassLevels;
export { ClassLevelSchema };
export { ClassLevelSchema, insertClassLevel, updateClassLevel };

View File

@@ -1,11 +1,29 @@
import SimpleSchema from 'simpl-schema';
import schema from '/imports/api/schema.js';
import PropertySchema from '/imports/api/creature/subSchemas/PropertySchema.js';
import ChildSchema from '/imports/api/parenting/ChildSchema.js';
import ColorSchema from "/imports/api/creature/subSchemas/ColorSchema.js";
import VARIABLE_NAME_REGEX from '/imports/constants/VARIABLE_NAME_REGEX.js';
// Mixins
import { creaturePermissionMixin } from '/imports/api/creature/creaturePermissions.js';
import { setDocToLastMixin } from '/imports/api/order.js';
import { setDocAncestryMixin, ensureAncestryContainsCharIdMixin } from '/imports/api/parenting/parenting.js';
import simpleSchemaMixin from '/imports/api/simpleSchemaMixin.js';
let Classes = new Mongo.Collection("classes");
let ClassSchema = schema({});
// TODO use variable name in computation engine, rather than a generated one
let ClassSchema = schema({
name: {
type: String,
optional: true,
},
variableName: {
type: String,
regEx: VARIABLE_NAME_REGEX,
},
});
ClassSchema.extend(ColorSchema);
@@ -13,5 +31,39 @@ Classes.attachSchema(ClassSchema);
Classes.attachSchema(PropertySchema);
Classes.attachSchema(ChildSchema);
const insertClass = new ValidatedMethod({
name: 'Classes.methods.insert',
mixins: [
creaturePermissionMixin,
setDocToLastMixin,
setDocAncestryMixin,
ensureAncestryContainsCharIdMixin,
simpleSchemaMixin,
],
collection: Classes,
permission: 'edit',
schema: ClassSchema,
run(cls) {
return Classes.insert(cls);
},
});
const updateClass = new ValidatedMethod({
name: 'Classes.methods.update',
mixins: [
creaturePermissionMixin,
simpleSchemaMixin,
],
collection: Classes,
permission: 'edit',
schema: new SimpleSchema({
_id: SimpleSchema.RegEx.Id,
update: ClassSchema.omit('name'),
}),
run({_id, update}) {
return Classes.update(_id, {$set: update});
},
});
export default Classes;
export { ClassSchema };
export { ClassSchema, insertClass, updateClass };

View File

@@ -4,12 +4,24 @@ import PropertySchema from '/imports/api/creature/subSchemas/PropertySchema.js';
import ChildSchema from '/imports/api/parenting/ChildSchema.js';
import DAMAGE_TYPES from '/imports/constants/DAMAGE_TYPES.js';
// Mixins
import recomputeCreatureMixin from '/imports/api/creature/recomputeCreatureMixin.js';
import { creaturePermissionMixin } from '/imports/api/creature/creaturePermissions.js';
import { setDocToLastMixin } from '/imports/api/order.js';
import { setDocAncestryMixin, ensureAncestryContainsCharIdMixin } from '/imports/api/parenting/parenting.js';
import simpleSchemaMixin from '/imports/api/simpleSchemaMixin.js';
let DamageMultipliers = new Mongo.Collection("damageMultipliers");
/*
* DamageMultipliers are multipliers that affect
* DamageMultipliers are multipliers that affect how much damage is taken from
* a given damage type
*/
let DamageMultiplierSchema = schema({
name: {
type: String,
optional: true,
},
// The technical, lowercase, single-word name used in formulae
damageType: {
type: String,
@@ -27,5 +39,41 @@ DamageMultipliers.attachSchema(DamageMultiplierSchema);
DamageMultipliers.attachSchema(PropertySchema);
DamageMultipliers.attachSchema(ChildSchema);
const insertDamageMultiplier = new ValidatedMethod({
name: 'DamageMultipliers.methods.insert',
mixins: [
creaturePermissionMixin,
setDocToLastMixin,
setDocAncestryMixin,
ensureAncestryContainsCharIdMixin,
recomputeCreatureMixin,
simpleSchemaMixin,
],
collection: DamageMultipliers,
permission: 'edit',
schema: DamageMultiplierSchema,
run(dm) {
return DamageMultipliers.insert(dm);
},
});
const updateDamageMultiplier = new ValidatedMethod({
name: 'DamageMultipliers.methods.update',
mixins: [
creaturePermissionMixin,
recomputeCreatureMixin,
simpleSchemaMixin,
],
collection: DamageMultipliers,
permission: 'edit',
schema: new SimpleSchema({
_id: SimpleSchema.RegEx.Id,
update: DamageMultiplierSchema.omit('name'),
}),
run({_id, update}) {
return DamageMultipliers.update(_id, {$set: update});
},
});
export default DamageMultipliers;
export { DamageMultiplierSchema };
export { DamageMultiplierSchema, insertDamageMultiplier, updateDamageMultiplier };

View File

@@ -3,6 +3,13 @@ import schema from '/imports/api/schema.js';
import PropertySchema from '/imports/api/creature/subSchemas/PropertySchema.js';
import ChildSchema from '/imports/api/parenting/ChildSchema.js';
// Mixins
import recomputeCreatureMixin from '/imports/api/creature/recomputeCreatureMixin.js';
import { creaturePermissionMixin } from '/imports/api/creature/creaturePermissions.js';
import { setDocToLastMixin } from '/imports/api/order.js';
import { setDocAncestryMixin, ensureAncestryContainsCharIdMixin } from '/imports/api/parenting/parenting.js';
import simpleSchemaMixin from '/imports/api/simpleSchemaMixin.js';
let Effects = new Mongo.Collection('effects');
/*
@@ -10,6 +17,10 @@ let Effects = new Mongo.Collection('effects');
* that modify their final value or presentation in some way
*/
let EffectSchema = schema({
name: {
type: String,
optional: true,
},
operation: {
type: String,
defaultValue: 'add',
@@ -49,5 +60,41 @@ Effects.attachSchema(PropertySchema);
Effects.attachSchema(ChildSchema);
Effects.attachSchema(EffectComputedSchema);
const insertEffect = new ValidatedMethod({
name: 'Effects.methods.insert',
mixins: [
creaturePermissionMixin,
setDocToLastMixin,
setDocAncestryMixin,
ensureAncestryContainsCharIdMixin,
recomputeCreatureMixin,
simpleSchemaMixin,
],
collection: Effects,
permission: 'edit',
schema: EffectSchema,
run(effect) {
return Effects.insert(effect);
},
});
const updateEffect = new ValidatedMethod({
name: 'Effects.methods.update',
mixins: [
creaturePermissionMixin,
recomputeCreatureMixin,
simpleSchemaMixin,
],
collection: Effects,
permission: 'edit',
schema: new SimpleSchema({
_id: SimpleSchema.RegEx.Id,
update: EffectSchema.omit('name'),
}),
run({_id, update}) {
return Effects.update(_id, {$set: update});
},
});
export default Effects;
export { EffectSchema };

View File

@@ -1,10 +1,22 @@
import SimpleSchema from 'simpl-schema';
import schema from '/imports/api/schema.js';
import PropertySchema from '/imports/api/creature/subSchemas/PropertySchema.js';
import recomputeCreatureXP from '/imports/api/creature/creatureComputation.js';
// Mixins
import recomputeCreatureMixin from '/imports/api/creature/recomputeCreatureMixin.js';
import { creaturePermissionMixin } from '/imports/api/creature/creaturePermissions.js';
import { setDocToLastMixin } from '/imports/api/order.js';
import { setDocAncestryMixin, ensureAncestryContainsCharIdMixin } from '/imports/api/parenting/parenting.js';
import simpleSchemaMixin from '/imports/api/simpleSchemaMixin.js';
let Experiences = new Mongo.Collection("experience");
let ExperienceSchema = schema({
name: {
type: String,
optional: true,
},
// Potentially long description of the event
description: {
type: String,
@@ -37,5 +49,55 @@ let ExperienceSchema = schema({
Experiences.attachSchema(PropertySchema);
Experiences.attachSchema(ExperienceSchema);
const insertExperience = new ValidatedMethod({
name: 'Experiences.methods.insert',
mixins: [
creaturePermissionMixin,
setDocToLastMixin,
setDocAncestryMixin,
ensureAncestryContainsCharIdMixin,
recomputeCreatureMixin,
simpleSchemaMixin,
],
collection: Experiences,
permission: 'edit',
schema: ExperienceSchema,
skipRecompute(experience){
return !experience.value;
},
run(experience) {
let result = Experiences.insert(experience);
if (experience.value){
recomputeCreatureXP(charId);
}
return result;
},
});
const updateExperience = new ValidatedMethod({
name: 'Experiences.methods.update',
mixins: [
creaturePermissionMixin,
recomputeCreatureMixin,
simpleSchemaMixin,
],
collection: Experiences,
permission: 'edit',
schema: new SimpleSchema({
_id: SimpleSchema.RegEx.Id,
update: ExperienceSchema.omit('name'),
}),
skipRecompute({update}){
return !('value' in update);
},
run({_id, update, charId}) {
let result = Experiences.update(_id, {$set: update});
if ('value' in update){
recomputeCreatureXP(charId);
}
return result;
},
});
export default Experiences;
export { ExperienceSchema };
export { ExperienceSchema, insertExperience, updateExperience };

View File

@@ -7,9 +7,20 @@ import PropertySchema from '/imports/api/creature/subSchemas/PropertySchema.js';
import ChildSchema from '/imports/api/parenting/ChildSchema.js';
import ColorSchema from '/imports/api/creature/subSchemas/ColorSchema.js';
// Mixins
import recomputeCreatureMixin from '/imports/api/creature/recomputeCreatureMixin.js';
import { creaturePermissionMixin } from '/imports/api/creature/creaturePermissions.js';
import { setDocToLastMixin } from '/imports/api/order.js';
import { setDocAncestryMixin, ensureAncestryContainsCharIdMixin } from '/imports/api/parenting/parenting.js';
import simpleSchemaMixin from '/imports/api/simpleSchemaMixin.js';
let Features = new Mongo.Collection('features');
let FeatureSchema = schema({
name: {
type: String,
optional: true,
},
description: {
type: String,
optional: true,
@@ -27,33 +38,38 @@ Features.attachSchema(PropertySchema);
Features.attachSchema(ChildSchema);
const insertFeature = new ValidatedMethod({
name: 'Features.methods.insert',
mixins: [
creaturePermissionMixin,
setDocToLastMixin,
setDocAncestryMixin,
ensureAncestryContainsCharIdMixin,
simpleSchemaMixin,
],
collection: Features,
permission: 'edit',
schema: FeatureSchema,
run(feature) {
return Features.insert(feature);
},
});
validate: FeatureSchema.validator({clean: true}),
run({feature}) {
const charId = feature.charId;
assertEditPermission(charId, this.userId);
// Set order
feature.order = getHighestOrder({
collection: Features,
charId,
}) + 1;
// Set parent
feature.parent = {
id: charId,
collection: 'Creatures',
};
// Insert
let featureId = Features.insert(feature);
recomputeCreatureById(charId);
return featureId;
const updateFeature = new ValidatedMethod({
name: 'Features.methods.update',
mixins: [
creaturePermissionMixin,
simpleSchemaMixin,
],
collection: Features,
permission: 'edit',
schema: new SimpleSchema({
_id: SimpleSchema.RegEx.Id,
update: FeatureSchema.omit('name'),
}),
run({_id, update}) {
return Features.update(_id, {$set: update});
},
});
export default Features;
export { FeatureSchema, insertFeature }
export { FeatureSchema, insertFeature, updateFeature }

View File

@@ -1,14 +1,60 @@
import SimpleSchema from 'simpl-schema';
import schema from '/imports/api/schema.js';
import PropertySchema from '/imports/api/creature/subSchemas/PropertySchema.js';
import ChildSchema from '/imports/api/parenting/ChildSchema.js';
// Mixins
import { creaturePermissionMixin } from '/imports/api/creature/creaturePermissions.js';
import { setDocToLastMixin } from '/imports/api/order.js';
import { setDocAncestryMixin, ensureAncestryContainsCharIdMixin } from '/imports/api/parenting/parenting.js';
import simpleSchemaMixin from '/imports/api/simpleSchemaMixin.js';
let Folders = new Mongo.Collection('folders');
let FolderSchema = schema({});
let FolderSchema = schema({
name: {
type: String,
optional: true,
},
});
Folders.attachSchema(FolderSchema);
Folders.attachSchema(PropertySchema);
Folders.attachSchema(ChildSchema);
const insertFolder = new ValidatedMethod({
name: 'Folders.methods.insert',
mixins: [
creaturePermissionMixin,
setDocToLastMixin,
setDocAncestryMixin,
ensureAncestryContainsCharIdMixin,
simpleSchemaMixin,
],
collection: Folders,
permission: 'edit',
schema: FolderSchema,
run(folder) {
return Folders.insert(folder);
},
});
const updateFolder = new ValidatedMethod({
name: 'Folders.methods.update',
mixins: [
creaturePermissionMixin,
simpleSchemaMixin,
],
collection: Folders,
permission: 'edit',
schema: new SimpleSchema({
_id: SimpleSchema.RegEx.Id,
update: FolderSchema.omit('name'),
}),
run({_id, update}) {
return Folders.update(_id, {$set: update});
},
});
export default Folders;
export { FolderSchema };
export { FolderSchema, insertFolder, updateFolder };

View File

@@ -3,9 +3,19 @@ import schema from '/imports/api/schema.js';
import ColorSchema from "/imports/api/creature/subSchemas/ColorSchema.js";
import PropertySchema from '/imports/api/creature/subSchemas/PropertySchema.js';
// Mixins
import { creaturePermissionMixin } from '/imports/api/creature/creaturePermissions.js';
import { setDocToLastMixin } from '/imports/api/order.js';
import { setDocAncestryMixin, ensureAncestryContainsCharIdMixin } from '/imports/api/parenting/parenting.js';
import simpleSchemaMixin from '/imports/api/simpleSchemaMixin.js';
let Notes = new Mongo.Collection("notes");
let NoteSchema = schema({
name: {
type: String,
optional: true,
},
description: {
type: String,
optional: true,
@@ -17,5 +27,39 @@ NoteSchema.extend(ColorSchema);
Notes.attachSchema(NoteSchema);
Notes.attachSchema(PropertySchema);
const insertNote = new ValidatedMethod({
name: 'Notes.methods.insert',
mixins: [
creaturePermissionMixin,
setDocToLastMixin,
setDocAncestryMixin,
ensureAncestryContainsCharIdMixin,
simpleSchemaMixin,
],
collection: Notes,
permission: 'edit',
schema: NoteSchema,
run(note) {
return Notes.insert(note);
},
});
const updateNote = new ValidatedMethod({
name: 'Notes.methods.update',
mixins: [
creaturePermissionMixin,
simpleSchemaMixin,
],
collection: Notes,
permission: 'edit',
schema: new SimpleSchema({
_id: SimpleSchema.RegEx.Id,
update: NoteSchema.omit('name'),
}),
run({_id, update}) {
return Notes.update(_id, {$set: update});
},
});
export default Notes;
export { NoteSchema };
export { NoteSchema, insertNote, updateNote };

View File

@@ -1,10 +1,22 @@
import SimpleSchema from 'simpl-schema';
import schema from '/imports/api/schema.js';
import PropertySchema from '/imports/api/creature/subSchemas/PropertySchema.js';
import ChildSchema from '/imports/api/parenting/ChildSchema.js';
// Mixins
import recomputeCreatureMixin from '/imports/api/creature/recomputeCreatureMixin.js';
import { creaturePermissionMixin } from '/imports/api/creature/creaturePermissions.js';
import { setDocToLastMixin } from '/imports/api/order.js';
import { setDocAncestryMixin, ensureAncestryContainsCharIdMixin } from '/imports/api/parenting/parenting.js';
import simpleSchemaMixin from '/imports/api/simpleSchemaMixin.js';
let Proficiencies = new Mongo.Collection("proficiencies");
let ProficiencySchema = schema({
name: {
type: String,
optional: true,
},
// A number representing how proficient the character is
value: {
type: Number,
@@ -22,5 +34,41 @@ Proficiencies.attachSchema(ProficiencySchema);
Proficiencies.attachSchema(PropertySchema);
Proficiencies.attachSchema(ChildSchema);
const insertProficiency = new ValidatedMethod({
name: 'Proficiencies.methods.insert',
mixins: [
creaturePermissionMixin,
setDocToLastMixin,
setDocAncestryMixin,
ensureAncestryContainsCharIdMixin,
recomputeCreatureMixin,
simpleSchemaMixin,
],
collection: Proficiencies,
permission: 'edit',
schema: ProficiencySchema,
run(prof) {
return Proficiencies.insert(prof);
},
});
const updateProficiency = new ValidatedMethod({
name: 'Proficiencies.methods.update',
mixins: [
creaturePermissionMixin,
recomputeCreatureMixin,
simpleSchemaMixin,
],
collection: Proficiencies,
permission: 'edit',
schema: new SimpleSchema({
_id: SimpleSchema.RegEx.Id,
update: ProficiencySchema.omit('name'),
}),
run({_id, update}) {
return Proficiencies.update(_id, {$set: update});
},
});
export default Proficiencies;
export { ProficiencySchema };
export { ProficiencySchema, insertProficiency, updateProficiency };

View File

@@ -4,9 +4,19 @@ import ChildSchema from '/imports/api/parenting/ChildSchema.js';
import AdjustmentSchema from '/imports/api/creature/subSchemas/AdjustmentSchema.js';
import StoredBuffSchema from '/imports/api/creature/properties/Buffs.js';
// Mixins
import { creaturePermissionMixin } from '/imports/api/creature/creaturePermissions.js';
import { setDocToLastMixin } from '/imports/api/order.js';
import { setDocAncestryMixin, ensureAncestryContainsCharIdMixin } from '/imports/api/parenting/parenting.js';
import simpleSchemaMixin from '/imports/api/simpleSchemaMixin.js';
let Rolls = new Mongo.Collection('rolls');
let RollChildrenSchema = new SimpleSchema({
name: {
type: String,
optional: true,
},
// The adjustments to be applied
adjustments: {
type: Array,
@@ -93,5 +103,39 @@ Rolls.attachSchema(RollSchema);
Rolls.attachSchema(PropertySchema);
Rolls.attachSchema(ChildSchema);
const insertRoll = new ValidatedMethod({
name: 'Rolls.methods.insert',
mixins: [
creaturePermissionMixin,
setDocToLastMixin,
setDocAncestryMixin,
ensureAncestryContainsCharIdMixin,
simpleSchemaMixin,
],
collection: Rolls,
permission: 'edit',
schema: RollSchema,
run(roll) {
return Rolls.insert(roll);
},
});
const updateRoll = new ValidatedMethod({
name: 'Rolls.methods.update',
mixins: [
creaturePermissionMixin,
simpleSchemaMixin,
],
collection: Rolls,
permission: 'edit',
schema: new SimpleSchema({
_id: SimpleSchema.RegEx.Id,
update: RollSchema.omit('name'),
}),
run({_id, update}) {
return Rolls.update(_id, {$set: update});
},
});
export default Rolls;
export { RollSchema };
export { RollSchema, insertRoll, updateRoll };

View File

@@ -4,6 +4,13 @@ import PropertySchema from '/imports/api/creature/subSchemas/PropertySchema.js';
import ChildSchema from '/imports/api/parenting/ChildSchema.js';
import ColorSchema from '/imports/api/creature/subSchemas/ColorSchema.js';
// Mixins
import recomputeCreatureMixin from '/imports/api/creature/recomputeCreatureMixin.js';
import { creaturePermissionMixin } from '/imports/api/creature/creaturePermissions.js';
import { setDocToLastMixin } from '/imports/api/order.js';
import { setDocAncestryMixin, ensureAncestryContainsCharIdMixin } from '/imports/api/parenting/parenting.js';
import simpleSchemaMixin from '/imports/api/simpleSchemaMixin.js';
let Skills = new Mongo.Collection("skills");
/*
@@ -11,6 +18,10 @@ let Skills = new Mongo.Collection("skills");
* Skills have an ability score modifier that they use as their basis
*/
let SkillSchema = schema({
name: {
type: String,
optional: true,
},
// The technical, lowercase, single-word name used in formulae
variableName: {
type: String,
@@ -92,5 +103,41 @@ Skills.attachSchema(ComputedSkillSchema);
Skills.attachSchema(PropertySchema);
Skills.attachSchema(ChildSchema);
const insertSkill = new ValidatedMethod({
name: 'Skills.methods.insert',
mixins: [
creaturePermissionMixin,
setDocToLastMixin,
setDocAncestryMixin,
ensureAncestryContainsCharIdMixin,
recomputeCreatureMixin,
simpleSchemaMixin,
],
collection: Skills,
permission: 'edit',
schema: SkillSchema,
run(skill) {
return Skills.insert(skill);
},
});
const updateSkill = new ValidatedMethod({
name: 'Skills.methods.update',
mixins: [
creaturePermissionMixin,
recomputeCreatureMixin,
simpleSchemaMixin,
],
collection: Skills,
permission: 'edit',
schema: new SimpleSchema({
_id: SimpleSchema.RegEx.Id,
update: SkillSchema.omit('name'),
}),
run({_id, update}) {
return Skills.update(_id, {$set: update});
},
});
export default Skills;
export { SkillSchema };

View File

@@ -1,11 +1,22 @@
import SimpleSchema from 'simpl-schema';
import schema from '/imports/api/schema.js';
import ColorSchema from "/imports/api/creature/subSchemas/ColorSchema.js";
import PropertySchema from '/imports/api/creature/subSchemas/PropertySchema.js';
import ChildSchema from '/imports/api/parenting/ChildSchema.js';
// Mixins
import { creaturePermissionMixin } from '/imports/api/creature/creaturePermissions.js';
import { setDocToLastMixin } from '/imports/api/order.js';
import { setDocAncestryMixin, ensureAncestryContainsCharIdMixin } from '/imports/api/parenting/parenting.js';
import simpleSchemaMixin from '/imports/api/simpleSchemaMixin.js';
let SpellLists = new Mongo.Collection("spellLists");
let SpellListSchema = schema({
name: {
type: String,
optional: true,
},
description: {
type: String,
optional: true,
@@ -33,4 +44,38 @@ SpellLists.attachSchema(SpellListSchema);
SpellLists.attachSchema(PropertySchema);
SpellLists.attachSchema(ChildSchema);
const insertSpellList = new ValidatedMethod({
name: 'SpellLists.methods.insert',
mixins: [
creaturePermissionMixin,
setDocToLastMixin,
setDocAncestryMixin,
ensureAncestryContainsCharIdMixin,
simpleSchemaMixin,
],
collection: SpellLists,
permission: 'edit',
schema: SpellListSchema,
run(spellList) {
return SpellLists.insert(spellList);
},
});
const updateSpellList = new ValidatedMethod({
name: 'SpellLists.methods.update',
mixins: [
creaturePermissionMixin,
simpleSchemaMixin,
],
collection: SpellLists,
permission: 'edit',
schema: new SimpleSchema({
_id: SimpleSchema.RegEx.Id,
update: SpellListSchema.omit('name'),
}),
run({_id, update}) {
return SpellLists.update(_id, {$set: update});
},
});
export default SpellLists;

View File

@@ -4,6 +4,12 @@ import schema from '/imports/api/schema.js';
import PropertySchema from '/imports/api/creature/subSchemas/PropertySchema.js';
import ChildSchema from '/imports/api/parenting/ChildSchema.js';
// Mixins
import { creaturePermissionMixin } from '/imports/api/creature/creaturePermissions.js';
import { setDocToLastMixin } from '/imports/api/order.js';
import { setDocAncestryMixin, ensureAncestryContainsCharIdMixin } from '/imports/api/parenting/parenting.js';
import simpleSchemaMixin from '/imports/api/simpleSchemaMixin.js';
const magicSchools = [
'Abjuration',
'Conjuration',
@@ -18,6 +24,10 @@ const magicSchools = [
let Spells = new Mongo.Collection('spells');
let SpellSchema = schema({
name: {
type: String,
optional: true,
},
prepared: {
type: String,
defaultValue: 'prepared',
@@ -78,5 +88,39 @@ Spells.attachSchema(SpellSchema);
Spells.attachSchema(PropertySchema);
Spells.attachSchema(ChildSchema);
const insertSpell = new ValidatedMethod({
name: 'Spells.methods.insert',
mixins: [
creaturePermissionMixin,
setDocToLastMixin,
setDocAncestryMixin,
ensureAncestryContainsCharIdMixin,
simpleSchemaMixin,
],
collection: Spells,
permission: 'edit',
schema: SpellSchema,
run(spell) {
return Spells.insert(spell);
},
});
const updateSpell = new ValidatedMethod({
name: 'Spells.methods.update',
mixins: [
creaturePermissionMixin,
simpleSchemaMixin,
],
collection: Spells,
permission: 'edit',
schema: new SimpleSchema({
_id: SimpleSchema.RegEx.Id,
update: SpellSchema.omit('name'),
}),
run({_id, update}) {
return Spells.update(_id, {$set: update});
},
});
export default Spells;
export { SpellSchema };
export { SpellSchema, insertSpell, updateSpell };

View File

@@ -0,0 +1,13 @@
export default function recomputeCreatureMixin(methodOptions){
let runFunc = methodOptions.run;
methodOptions.run = function({charId}){
const result = runFunc.apply(this, arguments);
if (
methodOptions.skipRecompute &&
methodOptions.skipRecompute.apply(this, arguments)
) return result;
recomputeCreatureById(charId);
return result;
};
return methodOptions;
};

View File

@@ -6,24 +6,28 @@ import librarySchemas from '/imports/api/library/librarySchemas.js';
let LibraryNodes = new Mongo.Collection('libraryNodes');
let LibraryNodeSchema = schema({
type: {
name: {
type: String,
optional: true,
},
libraryNodeType: {
type: String,
allowedValues: Object.keys(librarySchemas),
},
data: {
type: Object,
custom(){
let type = this.field('type');
let schema = librarySchemas[type];
schema.validate(this.value)
},
},
});
LibraryNodeSchema.extend(SharingSchema);
LibraryNodes.attachSchema(LibraryNodeSchema);
LibraryNodes.attachSchema(ChildSchema);
for (key in librarySchemas){
let schema = new SimpleSchema({});
schema.extend(librarySchemas[key]);
schema.extend(LibraryNodeSchema);
schema.extend(ChildSchema);
if (key === 'folder'){
schema.extend(SharingSchema);
}
LibraryNodes.attachSchema(schema, {
selector: {libraryNodeType: key}
});
}
export default LibraryNodes;
export { LibraryNodeSchema };

View File

@@ -1,4 +1,6 @@
const getHighestOrder = function({collection, charId}){
import SimpleSchema from 'simpl-schema';
export function getHighestOrder({collection, charId}){
const highestOrderedDoc = collection.findOne({
charId
}, {
@@ -6,9 +8,37 @@ const getHighestOrder = function({collection, charId}){
sort: {order: -1},
});
return (highestOrderedDoc && highestOrderedDoc.order) || 0;
};
export function setDocToLastOrder({collection, doc}){
doc.order = getHighestOrder({
collection,
charId: doc.charId,
}) + 1;
};
export function setDocToLastMixin(methodOptions){
// Make sure the doc has a charId
// This mixin should come before simpleSchemaMixin
methodOptions.schema.extend({
charId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
});
let collection = methodOptions.collection
if (!collection){
throw "`collection` required in method options for setDocToLastMixin"
}
let runFunc = methodOptions.run;
methodOptions.run = function(doc){
setDocToLastOrder({collection, doc});
return runFunc.apply(this, arguments);
};
return methodOptions;
}
const moveDocToOrder = function({collection, doc, order}){
export function setDocOrder({collection, doc, order}){
const currentOrder = doc.order;
if (currentOrder === order){
return;
@@ -42,7 +72,7 @@ const moveDocToOrder = function({collection, doc, order}){
}
};
const reorderDocs = function({collection, charId}){
export function reorderDocs({collection, charId}){
let bulkWrite = [];
collection.find({
charId
@@ -67,5 +97,3 @@ const reorderDocs = function({collection, charId}){
});
}
};
export { getHighestOrder, moveDocToOrder, reorderDocs };

View File

@@ -1,5 +1,6 @@
import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js';
import getCollectionByName from '/imports/api/parenting/getCollectionByName.js';
import SimpleSchema from 'simpl-schema';
// n = collections.length
let collections = [];
@@ -64,8 +65,7 @@ export function forEachDecendent({ancestorId, filter = {}, options}, callback){
};
// 1 database read
export function getParenting({id, collection}){
export function getAncestry({id, collection}){
// Get the parent ref
let parentDoc = fetchDocByRef({id, collection}, {fields: {
name: 1,
@@ -86,6 +86,71 @@ export function getParenting({id, collection}){
return {parent, ancestors};
}
export function setDocAncestryMixin(methodOptions){
// Extend the method's schema to require the needed properties
// This mixin should come before simpleschema mixin
methodOptions.schema.extend({
parent: {
type: Object,
},
'parent.id': {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
'parent.collection': {
type: String,
},
});
// Change the doc's ancestry before running
let runFunc = methodOptions.run;
methodOptions.run = function(doc, ...rest){
let {parent, ancestors} = getAncestry(doc.parent);
doc.parent = parent;
doc.ancestors = ancestors;
return runFunc.call(this, doc, ...rest);
};
return methodOptions;
};
function ensureAncestryContainsId(ancestors, id){
if (!id){
throw new Meteor.Error('ancestor-check-failed',
`Expected charId, got ${id}`
);
}
if (!ancestors){
throw new Meteor.Error('ancestor-check-failed',
`Expected ancestors array, got ${ancestors}`
);
}
for (let ancestor of ancestors){
if (ancestor.id === id){
return;
}
}
throw new Meteor.Error('ancestor-check-failed',
`Ancestors did not contain id: ${id}`
);
}
export function ensureAncestryContainsCharIdMixin(methodOptions){
// Extend the method's schema to require the needed properties
// This mixin should come before simpleSchemaMixin
methodOptions.schema.extend({
charId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
});
let runFunc = methodOptions.run;
methodOptions.run = function({charId, ancestors}){
ensureAncestryContainsId(ancestors, charId);
return runFunc.apply(this, arguments);
};
return methodOptions;
};
export function updateParent(docRef, parentRef){
let collection = getCollectionByName(docRef.collection);
let oldDoc = fetchDocByRef(docRef, {fields: {
@@ -97,7 +162,7 @@ export function updateParent(docRef, parentRef){
if (oldDoc.parent.id === parentRef.id) return;
// update the document's parenting
let {parent, ancestors} = getParenting(parentRef);
let {parent, ancestors} = getAncestry(parentRef);
collection.update(docRef.id, {$set: {parent, ancestors}});
// Remove the old ancestors from the decendents
@@ -123,7 +188,7 @@ export function updateParent(docRef, parentRef){
export function setInheritedField({id, collection, fieldName, fieldValue}){
// Update the doc
let collection = getCollectionByName(collection);
collection = getCollectionByName(collection);
collection.update(id, {$set: {
[`${fieldName}`]: fieldValue,
}});
@@ -163,3 +228,17 @@ export function setName({id, collection, name}){
fieldValue: name,
});
};
export function findEnabled(collection, query, options){
query['enabled'] = true;
query['ancestors.$.enabled'] = {$not: false};
return collection.find(query, options);
};
export function getName(doc){
if (doc.name) return name;
var i = doc.ancestors.length;
while(i--) {
if (ancestors[i].name) return ancestors[i].name;
}
}

View File

@@ -0,0 +1,51 @@
// Copied from https://github.com/sethjgore/meteor-simple-schema-mixin
// and updated to simpl-schema npm package
import SimpleSchema from 'simpl-schema';
export default function simpleSchemaMixin(methodOptions) {
// If the user didn't give us a schema and they did give us a validate, assume
// that they are choosing to use the validate way of doing things in this call.
// If they've built a wrapper around ValidateMethod that includes this mixin
// all the time, this could happen semi-"intentionally". There may be times they
// just don't want to use a schema and have specified a "validate" option. So
// returning the unchanged options instead of an error seems proper.
if ((typeof methodOptions.schema === 'undefined'
&& typeof methodOptions.validate !== 'undefined')
|| (typeof methodOptions.schema !== 'undefined' && methodOptions.schema === null
&& typeof methodOptions.validate !== 'undefined' && methodOptions.validate !== null)) {
return methodOptions;
}
// If they truly gave us both... that just doesn't seem proper.
if (methodOptions.validate && methodOptions.validate !== null) {
throw new Meteor.Error(
'simpleSchemaMixin.options',
'"schema" and "validate" options cannot be used together');
}
// Note that setting them both null will make it through, defaulting to the
// schema = null behavior (enforce no args) instead of the validate = null
// behavior (do no validation).
const newOptions = methodOptions;
newOptions.schemaValidatorOptions =
newOptions.schemaValidatorOptions ||
{ clean: true, filter: false };
let simpleSchema;
if (!newOptions.schema || newOptions.schema === null) {
// Allow simply leaving off both the schema and validate specifications
// or setting them to "null" as a shorthand. In this case, unlike
// the straight default validate or typical coder's call to validator,
// we will ENFORCE the Method be called without parameters because of
// the "filter: false" above.
simpleSchema = new SimpleSchema({});
} else if (newOptions.schema instanceof SimpleSchema) {
// In this one case, we can save ourselves the time to make a schema out
// of the schema.
simpleSchema = newOptions.schema;
} else {
simpleSchema = new SimpleSchema(newOptions.schema);
}
newOptions.validate = simpleSchema.validator(newOptions.schemaValidatorOptions);
return newOptions;
};

View File

@@ -0,0 +1,4 @@
// Must contain a letter, and be made of word characters only
const VARIABLE_NAME_REGEX = /^\w*[a-z]\w*$/i;
export default VARIABLE_NAME_REGEX;