<commit_before>/* eslint-env node, mocha */
/* eslint-disable no-console */
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const expect = require('chai').expect;
const fs = require('fs');
const sass = require('node-sass');
const path = require('path');

const globImporter = require('../');
const GlobImporterClass = require('../dist/GlobImporter.js');

chai.use(chaiAsPromised);

describe('globImporter', () => {
  it('should be a function', () => expect(globImporter).to.be.a('function'));

  it('should resolve glob import', (done) => {
    const expectedResult = fs.readFileSync('test/files/glob-reference.css', {
      encoding: 'utf8'
    });
    sass.render({
      file: 'test/files/glob.scss',
      importer: globImporter
    }, (error, result) => {
      if (!error) {
        expect(result.css.toString()).to.equal(expectedResult);
        done();
      } else {
        console.log(error);
      }
    });
  });
});

/** @test {GlobImporter} */
describe('GlobImporter', () => {
  it('should be a function', () => expect(GlobImporterClass).to.be.a('function'));

  /** @test {GlobImporter#resolveSync} */
  describe('resolveSync()', () => {
    it('should be a function', () => {
      const globImporterInstance = new GlobImporterClass();
      return expect(globImporterInstance.resolveSync).to.be.a('function');
    });
  });

  /** @test {GlobImporter#resolve} */
  describe('resolve()', () => {
    it('should be a function', () => {
      const globImporterInstance = new GlobImporterClass();
      return expect(globImporterInstance.resolve).to.be.a('function');
    });

    it('should return null', (done) => {
      const globImporterInstance = new GlobImporterClass();
      const url = 'path/without/glob/pattern.scss';
      const expectedResult = null;
      return expect(globImporterInstance.resolve(url))
        .to.eventually.deep.equal(expectedResult)
        .notify(done);
    });

    it('should return array of resolved urls', (done) => {
      const globImporterInstance = new GlobImporterClass();
      const url = 'files/test-resolve/**/*.scss';
      const includePath = path.join(process.cwd(), 'test');
      const expectedResult = [
        path.join(includePath, 'files/test-resolve/style1.scss'),
        path.join(includePath, 'files/test-resolve/style2.scss')
      ];
      return expect(globImporterInstance.resolve(url, [includePath]))
        .to.eventually.deep.equal(expectedResult)
        .notify(done);
    });
  });

  /** @test {GlobImporter#importer} */
  describe('importer()', () => {
    it('should be a function', () => {
      const globImporterInstance = new GlobImporterClass();
      return expect(globImporterInstance.importer).to.be.a('function');
    });

    it('should resolve glob import', (done) => {
      const globImporterInstance = new GlobImporterClass();
      const expectedResult = fs.readFileSync('test/files/glob-reference.css', {
        encoding: 'utf8'
      });
      sass.render({
        file: 'test/files/glob.scss',
        importer: globImporterInstance.importer()
      }, (error, result) => {
        if (!error) {
          expect(result.css.toString()).to.equal(expectedResult);
          done();
        } else {
          console.log(error);
        }
      });
    });

    it('should resolve glob import with include path', (done) => {
      const globImporterInstance = new GlobImporterClass();
      const expectedResult = fs.readFileSync('test/files/include-path-reference.css', {
        encoding: 'utf8'
      });
      sass.render({
        file: 'test/files/include-path.scss',
        importer: globImporterInstance.importer(),
        includePaths: [
          'some/other/include-path',
          'test/files/test-imports'
        ]
      }, (error, result) => {
        if (!error) {
          expect(result.css.toString()).to.equal(expectedResult);
          done();
        } else {
          console.log(error);
        }
      });
    });

    it('should resolve glob import with absolute include path', (done) => {
      const globImporterInstance = new GlobImporterClass();
      const expectedResult = fs.readFileSync('test/files/include-path-reference.css', {
        encoding: 'utf8'
      });
      sass.render({
        file: 'test/files/include-path.scss',
        importer: globImporterInstance.importer(),
        includePaths: [path.join(process.cwd(), 'test/files/test-imports')]
      }, (error, result) => {
        if (!error) {
          expect(result.css.toString()).to.equal(expectedResult);
          done();
        } else {
          console.log(error);
        }
      });
    });
  });
});
<commit_msg>Add test that tries to resolve files in an empty folder, fixes #11<commit_after>/* eslint-env node, mocha */
/* eslint-disable no-console */
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const expect = require('chai').expect;
const fs = require('fs');
const sass = require('node-sass');
const path = require('path');

const globImporter = require('../');
const GlobImporterClass = require('../dist/GlobImporter.js');

chai.use(chaiAsPromised);

describe('globImporter', () => {
  it('should be a function', () => expect(globImporter).to.be.a('function'));

  it('should resolve glob import', (done) => {
    const expectedResult = fs.readFileSync('test/files/glob-reference.css', {
      encoding: 'utf8'
    });
    sass.render({
      file: 'test/files/glob.scss',
      importer: globImporter
    }, (error, result) => {
      if (!error) {
        expect(result.css.toString()).to.equal(expectedResult);
        done();
      } else {
        console.log(error);
      }
    });
  });
});

/** @test {GlobImporter} */
describe('GlobImporter', () => {
  it('should be a function', () => expect(GlobImporterClass).to.be.a('function'));

  /** @test {GlobImporter#resolveSync} */
  describe('resolveSync()', () => {
    it('should be a function', () => {
      const globImporterInstance = new GlobImporterClass();
      return expect(globImporterInstance.resolveSync).to.be.a('function');
    });
  });

  /** @test {GlobImporter#resolve} */
  describe('resolve()', () => {
    it('should be a function', () => {
      const globImporterInstance = new GlobImporterClass();
      return expect(globImporterInstance.resolve).to.be.a('function');
    });

    it('should return null', (done) => {
      const globImporterInstance = new GlobImporterClass();
      const url = 'path/without/glob/pattern.scss';
      const expectedResult = null;
      return expect(globImporterInstance.resolve(url))
        .to.eventually.deep.equal(expectedResult)
        .notify(done);
    });

    it('should return array of resolved urls', (done) => {
      const globImporterInstance = new GlobImporterClass();
      const url = 'files/test-resolve/**/*.scss';
      const includePath = path.join(process.cwd(), 'test');
      const expectedResult = [
        path.join(includePath, 'files/test-resolve/style1.scss'),
        path.join(includePath, 'files/test-resolve/style2.scss')
      ];
      return expect(globImporterInstance.resolve(url, [includePath]))
        .to.eventually.deep.equal(expectedResult)
        .notify(done);
    });

    it('should return an empty array', (done) => {
      const globImporterInstance = new GlobImporterClass();
      const url = 'files/test-resolve/test-empty/**/*.scss';
      const includePath = path.join(process.cwd(), 'test');
      const expectedResult = [];
      return expect(globImporterInstance.resolve(url, [includePath]))
        .to.eventually.deep.equal(expectedResult)
        .notify(done);
    });
  });

  /** @test {GlobImporter#importer} */
  describe('importer()', () => {
    it('should be a function', () => {
      const globImporterInstance = new GlobImporterClass();
      return expect(globImporterInstance.importer).to.be.a('function');
    });

    it('should resolve glob import', (done) => {
      const globImporterInstance = new GlobImporterClass();
      const expectedResult = fs.readFileSync('test/files/glob-reference.css', {
        encoding: 'utf8'
      });
      sass.render({
        file: 'test/files/glob.scss',
        importer: globImporterInstance.importer()
      }, (error, result) => {
        if (!error) {
          expect(result.css.toString()).to.equal(expectedResult);
          done();
        } else {
          console.log(error);
        }
      });
    });

    it('should resolve glob import with include path', (done) => {
      const globImporterInstance = new GlobImporterClass();
      const expectedResult = fs.readFileSync('test/files/include-path-reference.css', {
        encoding: 'utf8'
      });
      sass.render({
        file: 'test/files/include-path.scss',
        importer: globImporterInstance.importer(),
        includePaths: [
          'some/other/include-path',
          'test/files/test-imports'
        ]
      }, (error, result) => {
        if (!error) {
          expect(result.css.toString()).to.equal(expectedResult);
          done();
        } else {
          console.log(error);
        }
      });
    });

    it('should resolve glob import with absolute include path', (done) => {
      const globImporterInstance = new GlobImporterClass();
      const expectedResult = fs.readFileSync('test/files/include-path-reference.css', {
        encoding: 'utf8'
      });
      sass.render({
        file: 'test/files/include-path.scss',
        importer: globImporterInstance.importer(),
        includePaths: [path.join(process.cwd(), 'test/files/test-imports')]
      }, (error, result) => {
        if (!error) {
          expect(result.css.toString()).to.equal(expectedResult);
          done();
        } else {
          console.log(error);
        }
      });
    });
  });
});
<|endoftext|>


<commit_before>var utils = require('./utils.js');

var ArrayType = Float64Array;

function Tensor(dims) {
	this.dims = dims;
	var size = 1;
	var n = dims.length;
	while (n--) size *= dims[n];
	this.length = size;
	this.data = new ArrayType(size);
	return this;
}

Object.defineProperties(Tensor.prototype, {
	rank: { get: function() { return this.dims.length; } },
});

Tensor.prototype.fill = function(val) {
	// TODO: Use TypedArray.fill, when it is more broadly supported
	var n = this.length;
	while (n--) this.data[n] = val;
	return this;
};

Tensor.prototype.zero = function() {
	return this.fill(0);
};

// Adapted from:
//    https://github.com/karpathy/convnetjs/blob/master/src/convnet_vol.js
Tensor.prototype.fillRandom = function() {
	var scale = 1/this.length;
	var n = this.length;
	while (n--) this.data[n] = utils.gaussianSample(0, scale);
	return this;
}

Tensor.prototype.copy = function(other) {
	this.data.set(other.data);
	return this;
};

Tensor.prototype.clone = function() {
	var copy = new Tensor(this.dims);
	return copy.copy(this);
};

// These are slow; don't use them inside any hot loops (i.e. they're good for
//    debgugging/translating data to/from other formats, and not much else)
Tensor.prototype.get = function(coords) {
	var idx = 0;
	var n = this.dims.length;
	for (var i = 0; i < n; i++) {
		idx = idx * this.dims[i] + coords[i];
	}
	return this.data[idx];
};
Tensor.prototype.set = function(coords, val) {
	var idx = 0;
	var n = this.dims.length;
	for (var i = 0; i < n; i++) {
		idx = idx * this.dims[i] + coords[i];
	}
	this.data[idx] = val;
};
function toArrayRec(tensor, coords) {
	if (coords.length === tensor.rank) {
		return tensor.get(coords);
	} else {
		var dim = coords.length;
		var arr = [];
		for (var i = 0; i < tensor.dims[dim]; i++) {
			arr.push(toArrayRec(tensor, coords.concat([i])));
		}
		return arr;
	}
}
Tensor.prototype.toArray = function() {
	return toArrayRec(this, []);
};
function fromArrayRec(tensor, coords, x) {
	if (!(x instanceof Array)) {
		tensor.set(coords, x);
	} else {
		var dim = coords.length;
		for (var i = 0; i < tensor.dims[dim]; i++) {
			fromArrayRec(tensor, coords.concat([i]), x[i]);
		}
	}
}
Tensor.prototype.fromArray = function(arr) {
	fromArrayRec(this, [], arr);
	return this;
};

Tensor.prototype.toString = function() {
	return this.toArray();
};

Tensor.prototype.toFlatArray = function() {
	return Array.prototype.slice.call(this.data);
}
Tensor.prototype.fromFlatArray = function(arr) {
	this.data.set(arr);
	return this;
}

function addUnaryMethod(name, fncode) {
	var fneq = new Function([
		'var n = this.data.length;',
		'while (n--) {',
		'	var x = this.data[n];',
		'	this.data[n] = ' + fncode + ';',
		'}',
		'return this;'
	].join('\n'));
	Tensor.prototype[name + 'eq'] = fneq;
	Tensor.prototype[name] = function() {
		var nt = this.clone();
		return fneq.call(nt);
	};
}

function addBinaryMethod(name, fncode) {
	var fneqS = new Function('s', [
		'var n = this.data.length;',
		'var b = s;',
		'while (n--) {',
		'	var a = this.data[n];',
		'	this.data[n] = ' + fncode + ';',
		'}',
		'return this;'
	].join('\n'));
	var fneqT = new Function('t', [
		'var n = this.data.length;',
		'while (n--) {',
		'	var a = this.data[n];',
		'	var b = t.data[n];',
		'	this.data[n] = ' + fncode + ';',
		'}',
		'return this;'
	].join('\n'));

	var fneq = function(x) {
		if (x.constructor === Tensor)
			return fneqT.call(this, x);
		else
			return fneqS.call(this, x);
	}
	Tensor.prototype[name + 'eq'] = fneq;
	Tensor.prototype[name] = function(x) {
		var nt = this.clone();
		return fneq.call(nt, x);
	};
}

function addReduction(name, initcode, fncode) {
	Tensor.prototype[name+'reduce'] = new Function([
		'var accum = ' + initcode + ';',
		'var n = this.data.length;',
		'while (n--) {',
		'	var x = this.data[n];',
		'	accum = ' + fncode + ';',
		'}',
		'return accum;'
	].join('\n'));
}

addUnaryMethod('neg', '-x');
addUnaryMethod('round', 'Math.round(x)');
addUnaryMethod('log', 'Math.log(x)');
addUnaryMethod('exp', 'Math.exp(x)');
addUnaryMethod('sqrt', 'Math.sqrt(x)');
addUnaryMethod('abs', 'Math.abs(x)');
addUnaryMethod('ceil', 'Math.ceil(x)');
addUnaryMethod('floor', 'Math.floor(x)');
addUnaryMethod('cos', 'Math.cos(x)');
addUnaryMethod('sin', 'Math.sin(x)');
addUnaryMethod('tan', 'Math.tan(x)');
addUnaryMethod('acos', 'Math.acos(x)');
addUnaryMethod('asin', 'Math.asin(x)');
addUnaryMethod('atan', 'Math.atan(x)');
addUnaryMethod('cosh', 'Math.cosh(x)');
addUnaryMethod('sinh', 'Math.sinh(x)');
addUnaryMethod('tanh', 'Math.tanh(x)');
addUnaryMethod('acosh', 'Math.acosh(x)');
addUnaryMethod('asinh', 'Math.asinh(x)');
addUnaryMethod('atanh', 'Math.atanh(x)');
addUnaryMethod('sigmoid', '1 / (1 + Math.exp(-x))');
addUnaryMethod('isFinite', 'isFinite(x)');
addUnaryMethod('isNaN', 'isNaN(x)');

addBinaryMethod('add', 'a + b');
addBinaryMethod('sub', 'a - b');
addBinaryMethod('mul', 'a * b');
addBinaryMethod('div', 'a / b');
addBinaryMethod('mod', 'a % b');
addBinaryMethod('min', 'Math.min(a, b)');
addBinaryMethod('max', 'Math.max(a, b)');
addBinaryMethod('pow', 'Math.pow(a, b)');
addBinaryMethod('atan2', 'Math.atan2(a, b)');
addBinaryMethod('eq', 'a === b');
addBinaryMethod('neq', 'a !== b');
addBinaryMethod('gt', 'a > b');
addBinaryMethod('ge', 'a >= b');
addBinaryMethod('lt', 'a < b');
addBinaryMethod('le', 'a <= b');

addReduction('sum', '0', 'accum + x');
addReduction('min', 'Infinity', 'Math.min(accum, x)');
addReduction('max', '-Infinity', 'Math.max(accum, x)');
addReduction('all', 'true', 'accum && (x !== 0)');
addReduction('any', 'false', 'accum || (x !== 0)');

module.exports = Tensor;

<commit_msg>Invert, invert-and-multiply-by operators for tensors.<commit_after>var utils = require('./utils.js');

var ArrayType = Float64Array;

function Tensor(dims) {
	this.dims = dims;
	var size = 1;
	var n = dims.length;
	while (n--) size *= dims[n];
	this.length = size;
	this.data = new ArrayType(size);
	return this;
}

Object.defineProperties(Tensor.prototype, {
	rank: { get: function() { return this.dims.length; } },
});

Tensor.prototype.fill = function(val) {
	// TODO: Use TypedArray.fill, when it is more broadly supported
	var n = this.length;
	while (n--) this.data[n] = val;
	return this;
};

Tensor.prototype.zero = function() {
	return this.fill(0);
};

// Adapted from:
//    https://github.com/karpathy/convnetjs/blob/master/src/convnet_vol.js
Tensor.prototype.fillRandom = function() {
	var scale = 1/this.length;
	var n = this.length;
	while (n--) this.data[n] = utils.gaussianSample(0, scale);
	return this;
}

Tensor.prototype.copy = function(other) {
	this.data.set(other.data);
	return this;
};

Tensor.prototype.clone = function() {
	var copy = new Tensor(this.dims);
	return copy.copy(this);
};

// These are slow; don't use them inside any hot loops (i.e. they're good for
//    debgugging/translating data to/from other formats, and not much else)
Tensor.prototype.get = function(coords) {
	var idx = 0;
	var n = this.dims.length;
	for (var i = 0; i < n; i++) {
		idx = idx * this.dims[i] + coords[i];
	}
	return this.data[idx];
};
Tensor.prototype.set = function(coords, val) {
	var idx = 0;
	var n = this.dims.length;
	for (var i = 0; i < n; i++) {
		idx = idx * this.dims[i] + coords[i];
	}
	this.data[idx] = val;
};
function toArrayRec(tensor, coords) {
	if (coords.length === tensor.rank) {
		return tensor.get(coords);
	} else {
		var dim = coords.length;
		var arr = [];
		for (var i = 0; i < tensor.dims[dim]; i++) {
			arr.push(toArrayRec(tensor, coords.concat([i])));
		}
		return arr;
	}
}
Tensor.prototype.toArray = function() {
	return toArrayRec(this, []);
};
function fromArrayRec(tensor, coords, x) {
	if (!(x instanceof Array)) {
		tensor.set(coords, x);
	} else {
		var dim = coords.length;
		for (var i = 0; i < tensor.dims[dim]; i++) {
			fromArrayRec(tensor, coords.concat([i]), x[i]);
		}
	}
}
Tensor.prototype.fromArray = function(arr) {
	fromArrayRec(this, [], arr);
	return this;
};

Tensor.prototype.toString = function() {
	return this.toArray();
};

Tensor.prototype.toFlatArray = function() {
	return Array.prototype.slice.call(this.data);
}
Tensor.prototype.fromFlatArray = function(arr) {
	this.data.set(arr);
	return this;
}

function addUnaryMethod(name, fncode) {
	var fneq = new Function([
		'var n = this.data.length;',
		'while (n--) {',
		'	var x = this.data[n];',
		'	this.data[n] = ' + fncode + ';',
		'}',
		'return this;'
	].join('\n'));
	Tensor.prototype[name + 'eq'] = fneq;
	Tensor.prototype[name] = function() {
		var nt = this.clone();
		return fneq.call(nt);
	};
}

function addBinaryMethod(name, fncode) {
	var fneqS = new Function('s', [
		'var n = this.data.length;',
		'var b = s;',
		'while (n--) {',
		'	var a = this.data[n];',
		'	this.data[n] = ' + fncode + ';',
		'}',
		'return this;'
	].join('\n'));
	var fneqT = new Function('t', [
		'var n = this.data.length;',
		'while (n--) {',
		'	var a = this.data[n];',
		'	var b = t.data[n];',
		'	this.data[n] = ' + fncode + ';',
		'}',
		'return this;'
	].join('\n'));

	var fneq = function(x) {
		if (x.constructor === Tensor)
			return fneqT.call(this, x);
		else
			return fneqS.call(this, x);
	}
	Tensor.prototype[name + 'eq'] = fneq;
	Tensor.prototype[name] = function(x) {
		var nt = this.clone();
		return fneq.call(nt, x);
	};
}

function addReduction(name, initcode, fncode) {
	Tensor.prototype[name+'reduce'] = new Function([
		'var accum = ' + initcode + ';',
		'var n = this.data.length;',
		'while (n--) {',
		'	var x = this.data[n];',
		'	accum = ' + fncode + ';',
		'}',
		'return accum;'
	].join('\n'));
}

addUnaryMethod('neg', '-x');
addUnaryMethod('round', 'Math.round(x)');
addUnaryMethod('log', 'Math.log(x)');
addUnaryMethod('exp', 'Math.exp(x)');
addUnaryMethod('sqrt', 'Math.sqrt(x)');
addUnaryMethod('abs', 'Math.abs(x)');
addUnaryMethod('ceil', 'Math.ceil(x)');
addUnaryMethod('floor', 'Math.floor(x)');
addUnaryMethod('cos', 'Math.cos(x)');
addUnaryMethod('sin', 'Math.sin(x)');
addUnaryMethod('tan', 'Math.tan(x)');
addUnaryMethod('acos', 'Math.acos(x)');
addUnaryMethod('asin', 'Math.asin(x)');
addUnaryMethod('atan', 'Math.atan(x)');
addUnaryMethod('cosh', 'Math.cosh(x)');
addUnaryMethod('sinh', 'Math.sinh(x)');
addUnaryMethod('tanh', 'Math.tanh(x)');
addUnaryMethod('acosh', 'Math.acosh(x)');
addUnaryMethod('asinh', 'Math.asinh(x)');
addUnaryMethod('atanh', 'Math.atanh(x)');
addUnaryMethod('sigmoid', '1 / (1 + Math.exp(-x))');
addUnaryMethod('isFinite', 'isFinite(x)');
addUnaryMethod('isNaN', 'isNaN(x)');
addUnaryMethod('invert', '1/x');

addBinaryMethod('add', 'a + b');
addBinaryMethod('sub', 'a - b');
addBinaryMethod('mul', 'a * b');
addBinaryMethod('div', 'a / b');
addBinaryMethod('invertAndMul', 'b / a');
addBinaryMethod('mod', 'a % b');
addBinaryMethod('min', 'Math.min(a, b)');
addBinaryMethod('max', 'Math.max(a, b)');
addBinaryMethod('pow', 'Math.pow(a, b)');
addBinaryMethod('atan2', 'Math.atan2(a, b)');
addBinaryMethod('eq', 'a === b');
addBinaryMethod('neq', 'a !== b');
addBinaryMethod('gt', 'a > b');
addBinaryMethod('ge', 'a >= b');
addBinaryMethod('lt', 'a < b');
addBinaryMethod('le', 'a <= b');

addReduction('sum', '0', 'accum + x');
addReduction('min', 'Infinity', 'Math.min(accum, x)');
addReduction('max', '-Infinity', 'Math.max(accum, x)');
addReduction('all', 'true', 'accum && (x !== 0)');
addReduction('any', 'false', 'accum || (x !== 0)');

module.exports = Tensor;

<|endoftext|>


<commit_before>// Initializes the `paintings` service on path `/paintings`
const createService = require('feathers-sequelize');
const createModel = require('../../models/paintings.model');
const hooks = require('./paintings.hooks');
const filters = require('./paintings.filters');

module.exports = function () {
  const app = this;
  const Model = createModel(app);
  const paginate = app.get('paginate');

  const options = {
    name: 'paintings',
    Model,
    paginate
  };

  // Initialize our service with any options it requires
  app.use('/api/paintings', createService(options));

  // Get our initialized service so that we can register hooks and filters
  const service = app.service('api/paintings');

  service.hooks(hooks);

  if (service.filter) {
    service.filter(filters);
  }
};
<commit_msg>removed pagination from paintings<commit_after>// Initializes the `paintings` service on path `/paintings`
const createService = require('feathers-sequelize');
const createModel = require('../../models/paintings.model');
const hooks = require('./paintings.hooks');
const filters = require('./paintings.filters');

module.exports = function () {
  const app = this;
  const Model = createModel(app);
  const paginate = app.get('paginate');

  const options = {
    name: 'paintings',
    Model,
    // paginate
  };

  // Initialize our service with any options it requires
  app.use('/api/paintings', createService(options));

  // Get our initialized service so that we can register hooks and filters
  const service = app.service('api/paintings');

  service.hooks(hooks);

  if (service.filter) {
    service.filter(filters);
  }
};
<|endoftext|>


<commit_before>'use strict'
var _ = require('lodash')
var AuthorisationError = require('../errors/authorisationError')
var NoSuchResourceError = require('../errors/noSuchResourceError')
var MalformedRequestError = require('../errors/malformedRequestError')
var ConflictingEditError = require('../errors/conflictingEditError')
var escapeForLike = require('./utilities/escapeForLike')
var authenticatedTransaction = require('./utilities/authenticatedTransaction')
var validate = require('../../utilities/validate')
var vf = validate.funcs
var ValidationError = validate.ValidationError

function transformAuthor(posts) {
  return _.map(posts, function (post) {
    return {
      id: post.id,
      title: post.title,
      postedTime: post.postedTime,
      body: post.body,
      preview: post.preview,
      active: post.active,
      author: {
        id: post.authorId,
        givenName: post.authorGivenName,
        familyName: post.authorFamilyName,
        active: post.authorActive
      }
    }
  })
}

module.exports = function (knex) {

  return {

    create: function (args) {
      return authenticatedTransaction(knex, args.auth, function (trx, authUser) {
        if (!authUser) {
          throw new AuthorisationError('Blog posts cannot be created anonymously.')
        }
        if (!authUser.authorisedToBlog && !authUser.admin) {
          throw new AuthorisationError('You are not authorised to create blog posts.')
        }

        return validate(args.params || {}, {
          postId: [
            vf.notUndefined('The post id is required.'),
            vf.notNull('The post id is required.'),
            vf.string('The post id must be a string.'),
            vf.matchesRegex(/^[A-Za-z0-9_\-]*$/),
            vf.minLength('The id must be at least 10 characters long.', 10),
            vf.maxLength('The id must be at most 255 characters long.', 255),
            function (val) {
              if (val === undefined || val === null) {
                return
              }
              // Check for case-insensitive uniqueness of ID.
              return trx
                .from('blogPosts')
                .select(['id'])
                .where('id', 'ilike', escapeForLike(val))
                .then(function (existingPosts) {
                  if (existingPosts && existingPosts.length) {
                    throw new ConflictingEditError('That id already belongs to another post.')
                  }
                })
            }, function (val) {
              if (val === undefined || val === null) {
                return
              }
              // Check that the ID starts with an ISO date.
              if (!val.substring(0, 10).match(/^\d\d\d\d\-\d\d\-\d\d$/)
                || isNaN(new Date(val.substring(0, 10)).getTime())) {
                throw new ValidationError('The id must begin with a date in the format yyyy-mm-dd.')
              }
            }
          ]
        }).then(function () {
          return validate(args.body, {
            title: [
              vf.notUndefined('The title is required.'),
              vf.notNull('The title is required.'),
              vf.string('The title must be a string.'),
              vf.notEmpty('The title must not be empty.'),
              vf.maxLength('The title must be at most 255 characters long', 255)
            ],
            body: [
              vf.notUndefined('The body is required.'),
              vf.notNull('The body is required.'),
              vf.string('The body must be a string.'),
              vf.maxLength('The body must be at most 100,000 characters long', 100000)
            ],
            preview: [
              vf.string('The preview must be a string.'),
              vf.maxLength('The preview must be at most 5,000 characters long', 5000)
            ],
            author: [
              vf.notUndefined('The author is required.'),
              vf.notNull('The author is required.'),
              vf.object('The author must be an object.', {
                id: [
                  vf.notUndefined('The author\'s id is required.'),
                  vf.notNull('The author\'s id is required.'),
                  vf.naturalNumber('The author\'s id must be a natural number.'),
                  // Check for existence of the author.
                  function (val) {
                    if (val === undefined || val === null) {
                      return
                    }
                    return trx
                      .from('users')
                      .select(['id'])
                      .where('id', val)
                      .then(function (users) {
                        if (!users || !users.length) {
                          throw new ValidationError('The given author does not exist.')
                        }
                      })
                  },
                  // Check that the author is the authenticated user or that the authenticated user is an admin.
                  function (val) {
                    if (val === undefined || val === null) {
                      return
                    }
                    if (val !== authUser.id && !authUser.admin) {
                      throw new ValidationError('You cannot set the ownership of a post to someone else.')
                    }
                  }
                ]
              })
            ],
            postedTime: [
              vf.notUndefined('The date is required.'),
              vf.notNull('The date is required.'),
              function (val) {
                if (val === undefined || val === null) {
                  return
                }
                if (isNaN(new Date(val).getTime())) {
                  throw new ValidationError('The date must be a dateTime.')
                }
              }
            ],
            active: [
              vf.boolean('The post must be set active or inactive.')
            ]
          })
        }).then(function () {
          return {
            id: args.params.postId,
            title: args.body.title,
            body: args.body.body,
            preview: args.body.preview,
            postedTime: new Date(args.body.postedTime),
            author: args.body.author.id
          }

        }).then(function (newPost) {

            // Do the insertion.
          return trx
            .into('blogPosts')
            .insert(newPost)
            .returning([
              'id',
              'title',
              'body',
              'preview',
              'author',
              'postedTime',
              'active'
            ])
        }).then(function (rows) {

          // Respond with the newly created post.
          return rows[0]
        })

      })
    },

    read: function (args) {
      return authenticatedTransaction(knex, args.auth, function (trx, authUser) {

        if (args.params && Object.keys(args.params).length && args.query && Object.keys(args.query).length) {
          throw new MalformedRequestError('A read against a specific blog post cannot filter by any other parameters.')
        }

        var query = trx
          .from('blogPosts')
          .leftJoin('users', 'users.id', 'blogPosts.author')
          .select([
            'blogPosts.id',
            'blogPosts.title',
            'blogPosts.postedTime',
            'blogPosts.body',
            'blogPosts.preview',
            'blogPosts.active',
            'users.id as authorId',
            'users.givenName as authorGivenName',
            'users.familyName as authorFamilyName',
            'users.active as authorActive'
          ])

        function hideContents(post) {
          return {
            id: post.id,
            active: post.active,
            author: post.author
          }
        }

        if (args.params && args.params.postId) {

          // Read a specific post.
          return query
            .where('blogPosts.id', 'ilike', escapeForLike(args.params.postId))
            .then(transformAuthor)
            .then(function (posts) {
              if (!posts.length) {
                throw new NoSuchResourceError()
              }
              var post = posts[0]

              // Remove the contents of inactive posts that don't belong to the authenticated user.
              if (!post.active &&
                (!authUser || (authUser.id !== post.author.id && !authUser.admin))) {
                post = hideContents(post)
              }
              return post
            })

        } else {

          // Add sorting.
          query = query.orderBy('postedTime', 'desc')

          // Add offset.
          query = query.limit(20)
          if (args.query && args.query.offset) {
            query = query.offset(args.query.offset)
          }

          // Add search parameters.
          var searchParams = _.omit(args.query, ['offset']) || {}
          return validate(searchParams, {
            // No filter is accepted yet.
          }).then(function () {
            // TODO Interpret tag, postedTime, and author filters here.
            query = query.where(searchParams)

            // The query is finished.
            return query
          }).then(transformAuthor)
          .then(function (posts) {

            // Remove the contents of inactive posts that don't belong to the authenticated user.
            return _.map(posts, function (post) {
              var authorisedToViewInactive = !!authUser && (authUser.id === post.author.id || !!authUser.admin)
              if (authorisedToViewInactive || post.active) {
                return post
              } else {
                return hideContents(post)
              }
            })
          })
        }
      }).then(function (result) {
        return JSON.parse(JSON.stringify(result))
      })
    },

    update: function (args) {
      return authenticatedTransaction(knex, args.auth, function (trx, authUser) {
        if (!authUser) {
          throw new AuthorisationError('Blog posts cannot be updated anonymously.')
        }
        if (!authUser.authorisedToBlog && !authUser.admin) {
          throw new AuthorisationError('You are not authorised to update blog posts.')
        }

        var notUniqueIdError = new ConflictingEditError('That id already belongs to another post.')
        var noSuchAuthorError = new ConflictingEditError('The given author does not exist.')

        return knex
          .from('blogPosts')
          .where('id', 'ilike', escapeForLike(args.params.postId))
          .select()
          .then(function (posts) {
            if (!posts.length) {
              throw new NoSuchResourceError()
            }
            var post = posts[0]
            if (authUser.id !== post.author && !authUser.admin) {
              throw new AuthorisationError('You are not authorised to update this blog post.')
            }
            return validate(args.body, {
              id: [
                vf.notNull('The id cannot be unset.'),
                vf.string('The post id must be a string.'),
                vf.matchesRegex(/^[A-Za-z0-9_\-]*$/),
                vf.minLength('The id must be at least 10 characters long.', 10),
                vf.maxLength('The id must be at most 255 characters long.', 255),
                function (val) {
                  if (val === undefined || val === null) {
                    return
                  }
                  // Check for case-insensitive uniqueness of ID if it's being changed.
                  if (val.toLowerCase() !== args.params.postId.toLowerCase()) {
                    return trx
                      .from('blogPosts')
                      .select(['id'])
                      .where('id', 'ilike', escapeForLike(val))
                      .then(function (existingPosts) {
                        if (existingPosts && existingPosts.length) {
                          throw notUniqueIdError
                        }
                      })
                  }
                }, function (val) {
                  if (val === undefined || val === null) {
                    return
                  }
                  // Check that the ID starts with an ISO date.
                  if (!val.substring(0, 10).match(/^\d\d\d\d\-\d\d\-\d\d$/)
                    || isNaN(new Date(val.substring(0, 10)).getTime())) {
                    throw new MalformedRequestError('The id must begin with a date in the format yyyy-mm-dd.')
                  }
                }
              ],
              title: [
                vf.notNull('The title cannot be unset.'),
                vf.string('The title must be a string.'),
                vf.notEmpty('The title must not be empty.'),
                vf.maxLength('The title must be at most 255 characters long', 255)
              ],
              body: [
                vf.notNull('The body is cannot be unset.'),
                vf.string('The body must be a string.'),
                vf.maxLength('The body must be at most 100,000 characters long', 100000)
              ],
              preview: [
                vf.string('The preview must be a string.'),
                vf.maxLength('The preview must be at most 5,000 characters long', 5000)
              ],
              author: [
                vf.notNull('The author is cannot be unset.'),
                vf.object('The author must be an object.', {
                  id: [
                    vf.notUndefined('The author\'s id is required.'),
                    vf.notNull('The author\'s id is required.'),
                    vf.naturalNumber('The author\'s id must be a natural number.'),
                    // Check for existence of the author.
                    function (val) {
                      if (val === undefined || val === null) {
                        return
                      }
                      return trx
                        .from('users')
                        .select(['id'])
                        .where('id', val)
                        .then(function (users) {
                          if (!users || !users.length) {
                            throw new ValidationError('The given author does not exist.')
                          }
                        })
                    },
                    // Check that the author is the authenticated user or that the authenticated user is an admin.
                    function (val) {
                      if (val === undefined || val === null) {
                        return
                      }
                      if (val !== authUser.id && !authUser.admin) {
                        throw new ValidationError('You cannot change the ownership of a post to someone else.')
                      }
                    }
                  ]
                })
              ],
              postedTime: [
                vf.notNull('The date cannot be unset.'),
                function (val) {
                  if (val === undefined || val === null) {
                    return
                  }
                  if (isNaN(new Date(val).getTime())) {
                    throw new ValidationError('The date must be a dateTime.')
                  }
                }
              ],
              active: [
                vf.notNull('The post must be set active or inactive.'),
                vf.boolean('The post must be set active or inactive.')
              ]
            })
          }).then(function () {
            var result = _.cloneDeep(args.body)
            if (result.postedTime) {
              result.postedTime = new Date(result.postedTime)
            }
            if (result.author && result.author.id) {
              result.author = result.author.id
            }
            return result

          }).then(function (newPost) {
            // Do the insertion.
            return trx
              .into('blogPosts')
              .where('id', 'ilike', escapeForLike(args.params.postId))
              .update(newPost)
              .returning('id')
          }).then(function (ids) {
            return trx
              .from('blogPosts')
              .leftJoin('users', 'users.id', 'blogPosts.author')
              .where('blogPosts.id', 'ilike', escapeForLike(ids[0]))
              .select([
                'blogPosts.id',
                'blogPosts.title',
                'blogPosts.postedTime',
                'blogPosts.body',
                'blogPosts.preview',
                'blogPosts.active',
                'users.id as authorId',
                'users.givenName as authorGivenName',
                'users.familyName as authorFamilyName',
                'users.active as authorActive'
              ])
          }).then(transformAuthor)
          .then(function (posts) {
            // Respond with the updated post.
            return posts[0]
          })
      })
    },

    delete: function (args) {
      return authenticatedTransaction(knex, args.auth, function (trx, authUser) {
        if (!authUser) {
          throw new AuthorisationError('Blog posts cannot be deleted anonymously.')
        }
        if (!authUser.authorisedToBlog && !authUser.admin) {
          throw new AuthorisationError('You are not authorised to delete blog posts.')
        }
        return knex
          .from('blogPosts')
          .where('id', 'ilike', escapeForLike(args.params.postId))
          .select()
          .then(function (posts) {
            if (!posts.length) {
              throw new NoSuchResourceError()
            }
            var post = posts[0]
            if (authUser.id !== post.author) {
              throw new AuthorisationError('You are not authorised to delete this blog post.')
            }
            return knex
              .from('blogPosts')
              .where('id', 'ilike', escapeForLike(args.params.postId))
              .del()
              .then(function () {
                return null
              })
          })
      })
    }

  }
}
<commit_msg>Fixed a problem with blog post creation.<commit_after>'use strict'
var _ = require('lodash')
var AuthorisationError = require('../errors/authorisationError')
var NoSuchResourceError = require('../errors/noSuchResourceError')
var MalformedRequestError = require('../errors/malformedRequestError')
var ConflictingEditError = require('../errors/conflictingEditError')
var escapeForLike = require('./utilities/escapeForLike')
var authenticatedTransaction = require('./utilities/authenticatedTransaction')
var validate = require('../../utilities/validate')
var vf = validate.funcs
var ValidationError = validate.ValidationError

function transformAuthor(posts) {
  return _.map(posts, function (post) {
    return {
      id: post.id,
      title: post.title,
      postedTime: post.postedTime,
      body: post.body,
      preview: post.preview,
      active: post.active,
      author: {
        id: post.authorId,
        givenName: post.authorGivenName,
        familyName: post.authorFamilyName,
        active: post.authorActive
      }
    }
  })
}

module.exports = function (knex) {

  return {

    create: function (args) {
      return authenticatedTransaction(knex, args.auth, function (trx, authUser) {
        if (!authUser) {
          throw new AuthorisationError('Blog posts cannot be created anonymously.')
        }
        if (!authUser.authorisedToBlog && !authUser.admin) {
          throw new AuthorisationError('You are not authorised to create blog posts.')
        }

        return validate(args.params || {}, {
          postId: [
            vf.notUndefined('The post id is required.'),
            vf.notNull('The post id is required.'),
            vf.string('The post id must be a string.'),
            vf.matchesRegex(/^[A-Za-z0-9_\-]*$/),
            vf.minLength('The id must be at least 10 characters long.', 10),
            vf.maxLength('The id must be at most 255 characters long.', 255),
            function (val) {
              if (val === undefined || val === null) {
                return
              }
              // Check for case-insensitive uniqueness of ID.
              return trx
                .from('blogPosts')
                .select(['id'])
                .where('id', 'ilike', escapeForLike(val))
                .then(function (existingPosts) {
                  if (existingPosts && existingPosts.length) {
                    throw new ConflictingEditError('That id already belongs to another post.')
                  }
                })
            }, function (val) {
              if (val === undefined || val === null) {
                return
              }
              // Check that the ID starts with an ISO date.
              if (!val.substring(0, 10).match(/^\d\d\d\d\-\d\d\-\d\d$/)
                || isNaN(new Date(val.substring(0, 10)).getTime())) {
                throw new ValidationError('The id must begin with a date in the format yyyy-mm-dd.')
              }
            }
          ]
        }).then(function () {
          return validate(args.body, {
            title: [
              vf.notUndefined('The title is required.'),
              vf.notNull('The title is required.'),
              vf.string('The title must be a string.'),
              vf.notEmpty('The title must not be empty.'),
              vf.maxLength('The title must be at most 255 characters long', 255)
            ],
            body: [
              vf.notUndefined('The body is required.'),
              vf.notNull('The body is required.'),
              vf.string('The body must be a string.'),
              vf.maxLength('The body must be at most 100,000 characters long', 100000)
            ],
            preview: [
              vf.string('The preview must be a string.'),
              vf.maxLength('The preview must be at most 5,000 characters long', 5000)
            ],
            author: [
              vf.notUndefined('The author is required.'),
              vf.notNull('The author is required.'),
              vf.object('The author must be an object.', {
                id: [
                  vf.notUndefined('The author\'s id is required.'),
                  vf.notNull('The author\'s id is required.'),
                  vf.naturalNumber('The author\'s id must be a natural number.'),
                  // Check for existence of the author.
                  function (val) {
                    if (val === undefined || val === null) {
                      return
                    }
                    return trx
                      .from('users')
                      .select(['id'])
                      .where('id', val)
                      .then(function (users) {
                        if (!users || !users.length) {
                          throw new ValidationError('The given author does not exist.')
                        }
                      })
                  },
                  // Check that the author is the authenticated user or that the authenticated user is an admin.
                  function (val) {
                    if (val === undefined || val === null) {
                      return
                    }
                    if (val !== authUser.id && !authUser.admin) {
                      throw new ValidationError('You cannot set the ownership of a post to someone else.')
                    }
                  }
                ]
              })
            ],
            postedTime: [
              vf.notUndefined('The date is required.'),
              vf.notNull('The date is required.'),
              function (val) {
                if (val === undefined || val === null) {
                  return
                }
                if (isNaN(new Date(val).getTime())) {
                  throw new ValidationError('The date must be a dateTime.')
                }
              }
            ],
            active: [
              vf.boolean('The post must be set active or inactive.')
            ]
          })
        }).then(function () {
          return {
            id: args.params.postId,
            title: args.body.title,
            body: args.body.body,
            preview: args.body.preview,
            postedTime: new Date(args.body.postedTime),
            author: args.body.author.id
          }

        }).then(function (newPost) {

            // Do the insertion.
          return trx
            .into('blogPosts')
            .insert(newPost)
            .returning('id')
        }).then(function (ids) {
          return trx
            .from('blogPosts')
            .leftJoin('users', 'users.id', 'blogPosts.author')
            .where('blogPosts.id', 'ilike', escapeForLike(ids[0]))
            .select([
              'blogPosts.id',
              'blogPosts.title',
              'blogPosts.postedTime',
              'blogPosts.body',
              'blogPosts.preview',
              'blogPosts.active',
              'users.id as authorId',
              'users.givenName as authorGivenName',
              'users.familyName as authorFamilyName',
              'users.active as authorActive'
            ])
        }).then(transformAuthor)
        .then(function (rows) {

          // Respond with the newly created post.
          return rows[0]
        })

      })
    },

    read: function (args) {
      return authenticatedTransaction(knex, args.auth, function (trx, authUser) {

        if (args.params && Object.keys(args.params).length && args.query && Object.keys(args.query).length) {
          throw new MalformedRequestError('A read against a specific blog post cannot filter by any other parameters.')
        }

        var query = trx
          .from('blogPosts')
          .leftJoin('users', 'users.id', 'blogPosts.author')
          .select([
            'blogPosts.id',
            'blogPosts.title',
            'blogPosts.postedTime',
            'blogPosts.body',
            'blogPosts.preview',
            'blogPosts.active',
            'users.id as authorId',
            'users.givenName as authorGivenName',
            'users.familyName as authorFamilyName',
            'users.active as authorActive'
          ])

        function hideContents(post) {
          return {
            id: post.id,
            active: post.active,
            author: post.author
          }
        }

        if (args.params && args.params.postId) {

          // Read a specific post.
          return query
            .where('blogPosts.id', 'ilike', escapeForLike(args.params.postId))
            .then(transformAuthor)
            .then(function (posts) {
              if (!posts.length) {
                throw new NoSuchResourceError()
              }
              var post = posts[0]

              // Remove the contents of inactive posts that don't belong to the authenticated user.
              if (!post.active &&
                (!authUser || (authUser.id !== post.author.id && !authUser.admin))) {
                post = hideContents(post)
              }
              return post
            })

        } else {

          // Add sorting.
          query = query.orderBy('postedTime', 'desc')

          // Add offset.
          query = query.limit(20)
          if (args.query && args.query.offset) {
            query = query.offset(args.query.offset)
          }

          // Add search parameters.
          var searchParams = _.omit(args.query, ['offset']) || {}
          return validate(searchParams, {
            // No filter is accepted yet.
          }).then(function () {
            // TODO Interpret tag, postedTime, and author filters here.
            query = query.where(searchParams)

            // The query is finished.
            return query
          }).then(transformAuthor)
          .then(function (posts) {

            // Remove the contents of inactive posts that don't belong to the authenticated user.
            return _.map(posts, function (post) {
              var authorisedToViewInactive = !!authUser && (authUser.id === post.author.id || !!authUser.admin)
              if (authorisedToViewInactive || post.active) {
                return post
              } else {
                return hideContents(post)
              }
            })
          })
        }
      }).then(function (result) {
        return JSON.parse(JSON.stringify(result))
      })
    },

    update: function (args) {
      return authenticatedTransaction(knex, args.auth, function (trx, authUser) {
        if (!authUser) {
          throw new AuthorisationError('Blog posts cannot be updated anonymously.')
        }
        if (!authUser.authorisedToBlog && !authUser.admin) {
          throw new AuthorisationError('You are not authorised to update blog posts.')
        }

        var notUniqueIdError = new ConflictingEditError('That id already belongs to another post.')

        return knex
          .from('blogPosts')
          .where('id', 'ilike', escapeForLike(args.params.postId))
          .select()
          .then(function (posts) {
            if (!posts.length) {
              throw new NoSuchResourceError()
            }
            var post = posts[0]
            if (authUser.id !== post.author && !authUser.admin) {
              throw new AuthorisationError('You are not authorised to update this blog post.')
            }
            return validate(args.body, {
              id: [
                vf.notNull('The id cannot be unset.'),
                vf.string('The post id must be a string.'),
                vf.matchesRegex(/^[A-Za-z0-9_\-]*$/),
                vf.minLength('The id must be at least 10 characters long.', 10),
                vf.maxLength('The id must be at most 255 characters long.', 255),
                function (val) {
                  if (val === undefined || val === null) {
                    return
                  }
                  // Check for case-insensitive uniqueness of ID if it's being changed.
                  if (val.toLowerCase() !== args.params.postId.toLowerCase()) {
                    return trx
                      .from('blogPosts')
                      .select(['id'])
                      .where('id', 'ilike', escapeForLike(val))
                      .then(function (existingPosts) {
                        if (existingPosts && existingPosts.length) {
                          throw notUniqueIdError
                        }
                      })
                  }
                }, function (val) {
                  if (val === undefined || val === null) {
                    return
                  }
                  // Check that the ID starts with an ISO date.
                  if (!val.substring(0, 10).match(/^\d\d\d\d\-\d\d\-\d\d$/)
                    || isNaN(new Date(val.substring(0, 10)).getTime())) {
                    throw new MalformedRequestError('The id must begin with a date in the format yyyy-mm-dd.')
                  }
                }
              ],
              title: [
                vf.notNull('The title cannot be unset.'),
                vf.string('The title must be a string.'),
                vf.notEmpty('The title must not be empty.'),
                vf.maxLength('The title must be at most 255 characters long', 255)
              ],
              body: [
                vf.notNull('The body is cannot be unset.'),
                vf.string('The body must be a string.'),
                vf.maxLength('The body must be at most 100,000 characters long', 100000)
              ],
              preview: [
                vf.string('The preview must be a string.'),
                vf.maxLength('The preview must be at most 5,000 characters long', 5000)
              ],
              author: [
                vf.notNull('The author is cannot be unset.'),
                vf.object('The author must be an object.', {
                  id: [
                    vf.notUndefined('The author\'s id is required.'),
                    vf.notNull('The author\'s id is required.'),
                    vf.naturalNumber('The author\'s id must be a natural number.'),
                    // Check for existence of the author.
                    function (val) {
                      if (val === undefined || val === null) {
                        return
                      }
                      return trx
                        .from('users')
                        .select(['id'])
                        .where('id', val)
                        .then(function (users) {
                          if (!users || !users.length) {
                            throw new ValidationError('The given author does not exist.')
                          }
                        })
                    },
                    // Check that the author is the authenticated user or that the authenticated user is an admin.
                    function (val) {
                      if (val === undefined || val === null) {
                        return
                      }
                      if (val !== authUser.id && !authUser.admin) {
                        throw new ValidationError('You cannot change the ownership of a post to someone else.')
                      }
                    }
                  ]
                })
              ],
              postedTime: [
                vf.notNull('The date cannot be unset.'),
                function (val) {
                  if (val === undefined || val === null) {
                    return
                  }
                  if (isNaN(new Date(val).getTime())) {
                    throw new ValidationError('The date must be a dateTime.')
                  }
                }
              ],
              active: [
                vf.notNull('The post must be set active or inactive.'),
                vf.boolean('The post must be set active or inactive.')
              ]
            })
          }).then(function () {
            var result = _.cloneDeep(args.body)
            if (result.postedTime) {
              result.postedTime = new Date(result.postedTime)
            }
            if (result.author && result.author.id) {
              result.author = result.author.id
            }
            return result

          }).then(function (newPost) {
            // Do the insertion.
            return trx
              .into('blogPosts')
              .where('id', 'ilike', escapeForLike(args.params.postId))
              .update(newPost)
              .returning('id')
          }).then(function (ids) {
            return trx
              .from('blogPosts')
              .leftJoin('users', 'users.id', 'blogPosts.author')
              .where('blogPosts.id', 'ilike', escapeForLike(ids[0]))
              .select([
                'blogPosts.id',
                'blogPosts.title',
                'blogPosts.postedTime',
                'blogPosts.body',
                'blogPosts.preview',
                'blogPosts.active',
                'users.id as authorId',
                'users.givenName as authorGivenName',
                'users.familyName as authorFamilyName',
                'users.active as authorActive'
              ])
          }).then(transformAuthor)
          .then(function (posts) {
            // Respond with the updated post.
            return posts[0]
          })
      })
    },

    delete: function (args) {
      return authenticatedTransaction(knex, args.auth, function (trx, authUser) {
        if (!authUser) {
          throw new AuthorisationError('Blog posts cannot be deleted anonymously.')
        }
        if (!authUser.authorisedToBlog && !authUser.admin) {
          throw new AuthorisationError('You are not authorised to delete blog posts.')
        }
        return knex
          .from('blogPosts')
          .where('id', 'ilike', escapeForLike(args.params.postId))
          .select()
          .then(function (posts) {
            if (!posts.length) {
              throw new NoSuchResourceError()
            }
            var post = posts[0]
            if (authUser.id !== post.author) {
              throw new AuthorisationError('You are not authorised to delete this blog post.')
            }
            return knex
              .from('blogPosts')
              .where('id', 'ilike', escapeForLike(args.params.postId))
              .del()
              .then(function () {
                return null
              })
          })
      })
    }

  }
}
<|endoftext|>


<commit_before>/**
 * Topic.js
 *
 * @description :: TODO: You might write a short summary of how this model works and what it represents here.
 * @docs        :: http://sailsjs.org/documentation/concepts/models-and-orm/models
 */

var _ = require('lodash');
var Promise = require('bluebird');
var sanitizeHtml = require('sanitize-html');

module.exports = {

  attributes: {
    name: {
      type: Sequelize.STRING,
      allowNull: false
    },

    title: {
      type: Sequelize.TEXT,
      allowNull: false
    },

    description: {
      type: Sequelize.TEXT
    },

    usage: {
      type: Sequelize.TEXT
    },

    details: {
      type: Sequelize.TEXT
    },

    value: {
      type: Sequelize.TEXT
    },

    references: {
      type: Sequelize.TEXT
    },

    note: {
      type: Sequelize.TEXT
    },

    author: {
      type: Sequelize.TEXT
    },

    seealso: {
      type: Sequelize.TEXT
    },

    examples: {
      type: Sequelize.TEXT
    },

    sourceJSON: {
      type: Sequelize.TEXT,
      allowNull: true
    }

  },
  associations: function() {
    Topic.belongsTo(PackageVersion,
      {
        as: 'package_version',
        foreignKey: {
          allowNull: false,
          required: true,
          name: 'package_version_id',
          as: 'package_version'
        }
      });

    Topic.hasMany(Argument, {as: 'arguments', foreignKey: 'topic_id'});

    Topic.hasMany(Section, {as: 'sections', foreignKey: 'topic_id'});

    Topic.hasMany(Alias, {as: 'aliases', foreignKey: 'topic_id'});

    Topic.hasMany(Review, {
      as: 'reviews',
      foreignKey: 'reviewable_id',
      constraints: false,
      scope: {
        reviewable: 'topic'
      }
    });

    Topic.hasMany(Example, { as: 'user_examples', foreignKey: 'topic_id' });

    Topic.belongsToMany(Tag, {as: 'keywords', through: 'TopicTags', foreignKey: 'topic_id', timestamps: false});
  },

  options: {
    underscored: true,

    indexes: [
      {
        type: 'UNIQUE',
        fields: ['name', 'package_version_id']
      }
    ],

    getterMethods: {
      uri: function()  {
        if (this.package_version) {
          return '/api/packages/:name/versions/:version/topics/:topic'
            .replace(':name', encodeURIComponent(this.package_version.package_name))
            .replace(':version', encodeURIComponent(this.package_version.version))
            .replace(':topic', encodeURIComponent(this.name))
            .replace('/api/', '/');
        } return sails.getUrlFor({ target: 'Topic.findById' })
          .replace(':id', encodeURIComponent(this.id))
          .replace('/api/', '/');
      },
      api_uri: function()  {
        if (this.package_version) {
          return '/api/packages/:name/versions/:version/topics/:topic'
            .replace(':name', encodeURIComponent(this.package_version.package_name))
            .replace(':version', encodeURIComponent(this.package_version.version))
            .replace(':topic', encodeURIComponent(this.name));
        } return sails.getUrlFor({ target: 'Topic.findById' })
          .replace(':id', encodeURIComponent(this.id));
      }
    },

    classMethods: {
      findOnePopulated: function(criteria, opts) {
        function customizer(objValue, srcValue) {
          if (_.isArray(objValue)) {
            return objValue.concat(srcValue);
          }
        }
        var options = _.mergeWith({
          where: criteria,
          include: [
            {model: Argument, as: 'arguments', attributes: ['name', 'description', 'topic_id'], separate: true },
            {model: Section, as: 'sections', attributes: ['name', 'description', 'topic_id'], separate: true },
            {model: Tag, as: 'keywords', attributes: ['name']},
            {model: Alias, as: 'aliases', attributes: ['name', 'topic_id'], separate: true },
            {model: Example, as: 'user_examples',
              include: [{model: User, as: 'user', attributes: ['username']}]
            },
            { model: PackageVersion,
              as: 'package_version',
              attributes: ['package_name', 'version']
            }
          ]
        }, opts, customizer);

        return Topic.findOne(options);
      },

      findByNameInPackage: function(packageName, name, version) {
        var packageCriteria = {
          package_name: packageName
        };

        if (version) { // if version is specified, find in this one
          packageCriteria.version = version;
        }

        var params = {
          where: { name: name },
          include: [
            { model: PackageVersion, as: 'package_version', attributes: ['package_name', 'version'], where: packageCriteria }
          ]
        };

        return Promise.join(
          Topic.findAll(params),
          Topic.findByAliasInPackage(packageName, name, version),
        function(topics, aliasesTopics) {
          return topics.concat(aliasesTopics).sort(PackageService.compareVersions('desc', function(topic) {
            return topic.package_version.version;
          })
          )[0];
        });
      },

      findByAliasInPackage: function(packageName, alias, version) {
        var packageCriteria = {
          package_name: packageName
        };

        if (version) { // if version is specified, find in this one
          packageCriteria.version = version;
        }

        var params = {
          include: [
            { model: PackageVersion, as: 'package_version', attributes: ['package_name', 'version'], where: packageCriteria },
            { model: Alias, as: 'aliases', attributes: ['name', 'topic_id'], where: { name: alias }}
          ]
        };

        return Topic.findAll(params).then(function(topicInstances) {
          if (topicInstances) {
            return topicInstances.sort(PackageService.compareVersions('desc', function(topic) {
              return topic.package_version.version;
            })
          )[0];
          }
          return topicInstances;
        });
      },

      createWithRdFile: function(opts) {
        var rdJSON = opts.input;
        return sequelize.transaction(function(t) {
          var attributes = [
            'name',
            'title',
            'description',
            'usage',
            'details',
            'value',
            'references',
            'note',
            'seealso',
            'examples'
          ];

          var topic = _.pick(rdJSON, attributes);

          if (topic.usage !== null && typeof topic.usage === 'object') {
            topic.usage = topic.usage.contents;
          }

          if (topic.description !== null && typeof topic.description === 'object') {
            topic.description = topic.description.contents;
          }

          topic.examples = sanitizeHtml(topic.examples, {
            allowedTags: ['a' ],
            allowedAttributes: {}
          });

          var arrayToString = function(val) {
            if (val instanceof Array) {
              if (_.isEmpty(val)) return '';
              return val.join('\n\n');
            } return val;
          };

          topic = _.mapValues(topic, arrayToString);

          var customSections = rdJSON.sections;

          customSections = _.map(customSections, function(section) {
            return _.mapValues(section, arrayToString);
          });

          topic.sourceJSON = JSON.stringify(rdJSON);

          var packageVersion = {
            package_name: opts.packageName,
            version: opts.packageVersion,
            description: '',
            license: ''
          };

          return PackageVersion.upsertPackageVersion(packageVersion, {
            transaction: t
          }).spread(function(version, created) {
            topic.package_version_id = version.id;

            var keywords = rdJSON.keywords && !(rdJSON.keywords instanceof Array) ? [rdJSON.keywords] : rdJSON.keywords;
            var keywordsRecords =  _.isEmpty(keywords) ? [] :
              _.chain(keywords)
                .map(function(entry) { return entry.split(','); })
                .flatten()
                .map(function(keyword) {
                  return {name: keyword};
                })
                .value();

            return Promise.join(
              Topic.findOrCreate({
                where: {
                  package_version_id: version.id,
                  name: topic.name
                },
                defaults: topic,
                transaction: t,
                include: [
                  {model: Tag, as: 'keywords'},
                  {model: Argument, as: 'arguments'},
                  {model: Section, as: 'sections'},
                  {model: Alias, as: 'aliases'}
                ]
              }),
              Tag.bulkCreate(keywordsRecords, {transaction: t, ignoreDuplicates: true})
              .then(function(instances) {
                var names = _.map(instances, function(inst) {
                  return inst.name;
                });
                return Tag.findAll({where: {name: {$in: names}}, transaction: t });
              }),
              function(instanceCreatedArray, keywordsInstances) {
                var topicInstance = instanceCreatedArray[0];
                topicInstance.set(_.pick(topic, ['title',
                  'description',
                  'usage',
                  'details',
                  'value',
                  'references',
                  'note',
                  'seealso',
                  'examples',
                  'author',
                  'sourceJSON'])
                );

                var topicArguments = _.isEmpty(rdJSON.arguments) ? [] : rdJSON.arguments.map(function(argument) {
                  var arg = _.mapValues(argument, arrayToString);
                  return _.merge({}, arg, {topic_id: topicInstance.id});
                });

                var aliases = rdJSON.aliases && !(rdJSON.aliases instanceof Array) ? [rdJSON.aliases] : rdJSON.aliases;
                var aliasesRecords = _.isEmpty(aliases) ? [] : aliases.map(function(alias) {
                  return {name: alias, topic_id: topicInstance.id};
                });

                var sections = customSections.map(function(section) {
                  var sect = _.mapValues(section, arrayToString);
                  return { name: sect.title, description: sect.contents, topic_id: topicInstance.id };
                });

                return Promise.all([
                  topicInstance.removeArguments(topicInstance.arguments),
                  topicInstance.removeSections(topicInstance.sections),
                  topicInstance.removeAliases(topicInstance.aliases),
                  Argument.bulkCreate(topicArguments, {transaction: t}),
                  Alias.bulkCreate(aliasesRecords, {transaction: t}),
                  Section.bulkCreate(sections, {transaction: t}),
                  topicInstance.setKeywords(keywordsInstances, {transaction: t }),
                  topicInstance.save({transaction: t})
                ]).then(_.partial(Topic.findOnePopulated, {id: topicInstance.id}, {transaction: t}));
              });
          });
        });
      }
    }
  }

};

<commit_msg>chore: remove how arguments are handled separately when the queue is processed<commit_after>/**
 * Topic.js
 *
 * @description :: TODO: You might write a short summary of how this model works and what it represents here.
 * @docs        :: http://sailsjs.org/documentation/concepts/models-and-orm/models
 */

var _ = require('lodash');
var Promise = require('bluebird');
var sanitizeHtml = require('sanitize-html');

module.exports = {

  attributes: {
    name: {
      type: Sequelize.STRING,
      allowNull: false
    },

    title: {
      type: Sequelize.TEXT,
      allowNull: false
    },

    description: {
      type: Sequelize.TEXT
    },

    usage: {
      type: Sequelize.TEXT
    },

    details: {
      type: Sequelize.TEXT
    },

    value: {
      type: Sequelize.TEXT
    },

    references: {
      type: Sequelize.TEXT
    },

    note: {
      type: Sequelize.TEXT
    },

    author: {
      type: Sequelize.TEXT
    },

    seealso: {
      type: Sequelize.TEXT
    },

    examples: {
      type: Sequelize.TEXT
    },

    sourceJSON: {
      type: Sequelize.TEXT,
      allowNull: true
    }

  },
  associations: function() {
    Topic.belongsTo(PackageVersion,
      {
        as: 'package_version',
        foreignKey: {
          allowNull: false,
          required: true,
          name: 'package_version_id',
          as: 'package_version'
        }
      });

    Topic.hasMany(Argument, {as: 'arguments', foreignKey: 'topic_id'});

    Topic.hasMany(Section, {as: 'sections', foreignKey: 'topic_id'});

    Topic.hasMany(Alias, {as: 'aliases', foreignKey: 'topic_id'});

    Topic.hasMany(Review, {
      as: 'reviews',
      foreignKey: 'reviewable_id',
      constraints: false,
      scope: {
        reviewable: 'topic'
      }
    });

    Topic.hasMany(Example, { as: 'user_examples', foreignKey: 'topic_id' });

    Topic.belongsToMany(Tag, {as: 'keywords', through: 'TopicTags', foreignKey: 'topic_id', timestamps: false});
  },

  options: {
    underscored: true,

    indexes: [
      {
        type: 'UNIQUE',
        fields: ['name', 'package_version_id']
      }
    ],

    getterMethods: {
      uri: function()  {
        if (this.package_version) {
          return '/api/packages/:name/versions/:version/topics/:topic'
            .replace(':name', encodeURIComponent(this.package_version.package_name))
            .replace(':version', encodeURIComponent(this.package_version.version))
            .replace(':topic', encodeURIComponent(this.name))
            .replace('/api/', '/');
        } return sails.getUrlFor({ target: 'Topic.findById' })
          .replace(':id', encodeURIComponent(this.id))
          .replace('/api/', '/');
      },
      api_uri: function()  {
        if (this.package_version) {
          return '/api/packages/:name/versions/:version/topics/:topic'
            .replace(':name', encodeURIComponent(this.package_version.package_name))
            .replace(':version', encodeURIComponent(this.package_version.version))
            .replace(':topic', encodeURIComponent(this.name));
        } return sails.getUrlFor({ target: 'Topic.findById' })
          .replace(':id', encodeURIComponent(this.id));
      }
    },

    classMethods: {
      findOnePopulated: function(criteria, opts) {
        function customizer(objValue, srcValue) {
          if (_.isArray(objValue)) {
            return objValue.concat(srcValue);
          }
        }
        var options = _.mergeWith({
          where: criteria,
          include: [
            {model: Argument, as: 'arguments', attributes: ['name', 'description', 'topic_id'], separate: true },
            {model: Section, as: 'sections', attributes: ['name', 'description', 'topic_id'], separate: true },
            {model: Tag, as: 'keywords', attributes: ['name']},
            {model: Alias, as: 'aliases', attributes: ['name', 'topic_id'], separate: true },
            {model: Example, as: 'user_examples',
              include: [{model: User, as: 'user', attributes: ['username']}]
            },
            { model: PackageVersion,
              as: 'package_version',
              attributes: ['package_name', 'version']
            }
          ]
        }, opts, customizer);

        return Topic.findOne(options);
      },

      findByNameInPackage: function(packageName, name, version) {
        var packageCriteria = {
          package_name: packageName
        };

        if (version) { // if version is specified, find in this one
          packageCriteria.version = version;
        }

        var params = {
          where: { name: name },
          include: [
            { model: PackageVersion, as: 'package_version', attributes: ['package_name', 'version'], where: packageCriteria }
          ]
        };

        return Promise.join(
          Topic.findAll(params),
          Topic.findByAliasInPackage(packageName, name, version),
        function(topics, aliasesTopics) {
          return topics.concat(aliasesTopics).sort(PackageService.compareVersions('desc', function(topic) {
            return topic.package_version.version;
          })
          )[0];
        });
      },

      findByAliasInPackage: function(packageName, alias, version) {
        var packageCriteria = {
          package_name: packageName
        };

        if (version) { // if version is specified, find in this one
          packageCriteria.version = version;
        }

        var params = {
          include: [
            { model: PackageVersion, as: 'package_version', attributes: ['package_name', 'version'], where: packageCriteria },
            { model: Alias, as: 'aliases', attributes: ['name', 'topic_id'], where: { name: alias }}
          ]
        };

        return Topic.findAll(params).then(function(topicInstances) {
          if (topicInstances) {
            return topicInstances.sort(PackageService.compareVersions('desc', function(topic) {
              return topic.package_version.version;
            })
          )[0];
          }
          return topicInstances;
        });
      },

      createWithRdFile: function(opts) {
        var rdJSON = opts.input;
        return sequelize.transaction(function(t) {
          var attributes = [
            'name',
            'title',
            'description',
            'usage',
            'details',
            'value',
            'references',
            'note',
            'seealso',
            'examples'
          ];

          var topic = _.pick(rdJSON, attributes);

          if (topic.usage !== null && typeof topic.usage === 'object') {
            topic.usage = topic.usage.contents;
          }

          if (topic.description !== null && typeof topic.description === 'object') {
            topic.description = topic.description.contents;
          }

          topic.examples = sanitizeHtml(topic.examples, {
            allowedTags: [],
            allowedAttributes: {}
          });

          var arrayToString = function(val) {
            if (val instanceof Array) {
              if (_.isEmpty(val)) return '';
              return val.join('\n\n');
            } return val;
          };

          topic = _.mapValues(topic, arrayToString);

          var customSections = rdJSON.sections;

          customSections = _.map(customSections, function(section) {
            return _.mapValues(section, arrayToString);
          });

          topic.sourceJSON = JSON.stringify(rdJSON);

          var packageVersion = {
            package_name: opts.packageName,
            version: opts.packageVersion,
            description: '',
            license: ''
          };

          return PackageVersion.upsertPackageVersion(packageVersion, {
            transaction: t
          }).spread(function(version, created) {
            topic.package_version_id = version.id;

            var keywords = rdJSON.keywords && !(rdJSON.keywords instanceof Array) ? [rdJSON.keywords] : rdJSON.keywords;
            var keywordsRecords =  _.isEmpty(keywords) ? [] :
              _.chain(keywords)
                .map(function(entry) { return entry.split(','); })
                .flatten()
                .map(function(keyword) {
                  return {name: keyword};
                })
                .value();

            return Promise.join(
              Topic.findOrCreate({
                where: {
                  package_version_id: version.id,
                  name: topic.name
                },
                defaults: topic,
                transaction: t,
                include: [
                  {model: Tag, as: 'keywords'},
                  {model: Argument, as: 'arguments'},
                  {model: Section, as: 'sections'},
                  {model: Alias, as: 'aliases'}
                ]
              }),
              Tag.bulkCreate(keywordsRecords, {transaction: t, ignoreDuplicates: true})
              .then(function(instances) {
                var names = _.map(instances, function(inst) {
                  return inst.name;
                });
                return Tag.findAll({where: {name: {$in: names}}, transaction: t });
              }),
              function(instanceCreatedArray, keywordsInstances) {
                var topicInstance = instanceCreatedArray[0];
                topicInstance.set(_.pick(topic, ['title',
                  'description',
                  'usage',
                  'details',
                  'value',
                  'references',
                  'note',
                  'seealso',
                  'examples',
                  'author',
                  'sourceJSON'])
                );

                var aliases = rdJSON.aliases && !(rdJSON.aliases instanceof Array) ? [rdJSON.aliases] : rdJSON.aliases;
                var aliasesRecords = _.isEmpty(aliases) ? [] : aliases.map(function(alias) {
                  return {name: alias, topic_id: topicInstance.id};
                });

                var sections = customSections.map(function(section) {
                  var sect = _.mapValues(section, arrayToString);
                  return { name: sect.title, description: sect.contents, topic_id: topicInstance.id };
                });

                return Promise.all([
                  topicInstance.removeSections(topicInstance.sections),
                  topicInstance.removeAliases(topicInstance.aliases),
                  Alias.bulkCreate(aliasesRecords, {transaction: t}),
                  Section.bulkCreate(sections, {transaction: t}),
                  topicInstance.setKeywords(keywordsInstances, {transaction: t }),
                  topicInstance.save({transaction: t})
                ]).then(_.partial(Topic.findOnePopulated, {id: topicInstance.id}, {transaction: t}));
              });
          });
        });
      }
    }
  }

};

<|endoftext|>


<commit_before>var mongoose	= require('mongoose');
var Schema		= mongoose.Schema;

var MediaSchema = new Schema({
	title: { type: String, required: true },				// Photo or image title
	type: { type: String, required: true },					// Photo or Video
	tags: Array,																		// Smiling, Sleeping, Disneyland, etc
	pubdate: { type: Date, default: Date.now },			// Initial publish date
	editdate: { type: Date, default: Date.now },		// Date of last update
	filename: { type: String, required: true },			// Photo or video filename
	description: { type: String, required: true }		// Description of photo or video
});

module.exports = mongoose.model('Media', MediaSchema);<commit_msg>Adding extension to schema<commit_after>var mongoose	= require('mongoose');
var Schema		= mongoose.Schema;

var MediaSchema = new Schema({
	title: { type: String, required: true },				// Photo or image title
	type: { type: String, required: true },					// Photo or Video
	tags: { type: Array },																		// Smiling, Sleeping, Disneyland, etc
	pubdate: { type: Date, default: Date.now },			// Initial publish date
	editdate: { type: Date, default: Date.now },		// Date of last update
	filename: { type: String, required: true },			// Photo or video filename
	extension: { type: Array, required: true },		// jpg, png, etc
	description: { type: String}		// Description of photo or video
});

module.exports = mongoose.model('Media', MediaSchema);<|endoftext|>


<commit_before><commit_msg>uses geoservice-api for parent images<commit_after><|endoftext|>


<commit_before>const infix = queryTerm => ({
  evaluate(searchText = '') {
    if (!searchText) {
      return false;
    }

    return searchText.indexOf(queryTerm) !== -1;
  },
  matches(searchText = '') {
    if (!searchText) {
      return [];
    }

    const splitString = searchText.split(queryTerm);
    const result = [];
    let currentPosition = 0;

    for (let x = 0; x < splitString.length; x++) {
      result.push({
        startIndex: currentPosition + splitString[x].length,
        length: queryTerm.length
      });

      currentPosition += splitString[x].length + queryTerm.length;
    }

    result.pop();

    return result;
  }
});

const prefix = queryTerm => ({
  evaluate(searchText = '') {
    if (!searchText) {
      return false;
    }

    return searchText.indexOf(queryTerm) === 0;
  },
  matches(searchText = '') {
    if (!searchText) {
      return [];
    }

    const prefixIndex = searchText.indexOf(queryTerm);

    if (prefixIndex === 0) {
      return [
        {
          startIndex: 0,
          length: queryTerm.length
        }
      ];
    }

    return [];
  }
});

export default {
  infix,
  prefix
};
<commit_msg>Taught infix and prefix strategies how to search in nested arrays. (Closes #211)<commit_after>const infix = queryTerm => ({
  evaluate(searchText = '') {
    if (!searchText) {
      return false;
    }

    if (Array.isArray(searchText)) {
      return searchText.some(v => this.evaluate(v));
    }

    return searchText.indexOf(queryTerm) !== -1;
  },
  matches(searchText = '') {
    if (!searchText) {
      return [];
    }

    if (Array.isArray(searchText)) {
      return searchText.reduce(
        (result, text, index) => {
          const search = this.matches(text);

          if (search.length) {
            result[index] = search; // eslint-disable-line no-param-reassign
          }

          return result;
        },
        new Array(searchText.length)
      );
    }

    const splitString = searchText.split(queryTerm);
    const result = [];
    let currentPosition = 0;

    for (let x = 0; x < splitString.length; x++) {
      result.push({
        startIndex: currentPosition + splitString[x].length,
        length: queryTerm.length
      });

      currentPosition += splitString[x].length + queryTerm.length;
    }

    result.pop();

    return result;
  }
});

const prefix = queryTerm => ({
  evaluate(searchText = '') {
    if (!searchText) {
      return false;
    }

    if (Array.isArray(searchText)) {
      return searchText.some(v => this.evaluate(v));
    }

    return searchText.indexOf(queryTerm) === 0;
  },
  matches(searchText = '') {
    if (!searchText) {
      return [];
    }

    if (Array.isArray(searchText)) {
      return searchText.reduce(
        (result, text, index) => {
          const search = this.matches(text);

          if (search.length) {
            result[index] = search; // eslint-disable-line no-param-reassign
          }

          return result;
        },
        new Array(searchText.length)
      );
    }

    const prefixIndex = searchText.indexOf(queryTerm);

    if (prefixIndex === 0) {
      return [
        {
          startIndex: 0,
          length: queryTerm.length
        }
      ];
    }

    return [];
  }
});

export default {
  infix,
  prefix
};
<|endoftext|>


<commit_before>'use strict';

const _ = require('lodash');
const { yup, formatYupErrors } = require('strapi-utils');

const ALLOWED_EVENTS = [
  'entry.create',
  'entry.update',
  'entry.delete',
  'media.create',
  'media.update',
  'media.delete',
];

const urlRegex = /^(?:([a-z0-9+.-]+):\/\/)(?:\S+(?::\S*)?@)?(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/;

const webhookValidator = yup
  .object({
    name: yup.string().required(),
    url: yup
      .string()
      .matches(urlRegex, 'url must be a valid URL')
      .required(),
    headers: yup.lazy(data => {
      if (typeof data !== 'object') {
        return yup.object().required();
      }

      return yup
        .object(
          _.mapValues(data, () => {
            yup
              .string()
              .min(1)
              .required();
          })
        )
        .required();
    }),
    events: yup
      .array()
      .of(
        yup
          .string()
          .oneOf(ALLOWED_EVENTS)
          .required()
      )
      .min(1)
      .required(),
  })
  .noUnknown();

const updateWebhookValidator = webhookValidator.shape({
  isEnabled: yup.boolean(),
});

module.exports = {
  async listWebhooks(ctx) {
    const webhooks = await strapi.webhookStore.findWebhooks();
    ctx.send({ data: webhooks });
  },

  async getWebhook(ctx) {
    const { id } = ctx.params;
    const webhook = await strapi.webhookStore.findWebhook(id);

    if (!webhook) {
      return ctx.notFound('webhook.notFound');
    }

    ctx.send({ data: webhook });
  },

  async createWebhook(ctx) {
    const { body } = ctx.request;

    try {
      await webhookValidator.validate(body, {
        strict: true,
        abortEarly: false,
      });
    } catch (error) {
      return ctx.badRequest('ValidationError', {
        errors: formatYupErrors(error),
      });
    }

    const webhook = await strapi.webhookStore.createWebhook(body);

    strapi.webhookRunner.add(webhook);

    ctx.created({ data: webhook });
  },

  async updateWebhook(ctx) {
    const { id } = ctx.params;
    const { body } = ctx.request;

    try {
      await updateWebhookValidator.validate(body, {
        strict: true,
        abortEarly: false,
      });
    } catch (error) {
      return ctx.badRequest('ValidationError', {
        errors: formatYupErrors(error),
      });
    }

    const webhook = await strapi.webhookStore.findWebhook(id);

    if (!webhook) {
      return ctx.notFound('webhook.notFound');
    }

    const updatedWebhook = await strapi.webhookStore.updateWebhook(id, {
      ...webhook,
      ...body,
    });

    if (!updatedWebhook) {
      return ctx.notFound('webhook.notFound');
    }

    strapi.webhookRunner.update(updatedWebhook);

    ctx.send({ data: updatedWebhook });
  },

  async deleteWebhook(ctx) {
    const { id } = ctx.params;
    const webhook = await strapi.webhookStore.findWebhook(id);

    if (!webhook) {
      return ctx.notFound('webhook.notFound');
    }

    await strapi.webhookStore.deleteWebhook(id);

    strapi.webhookRunner.remove(webhook);

    ctx.body = { data: webhook };
  },

  async deleteWebhooks(ctx) {
    const { ids } = ctx.request.body;

    if (!Array.isArray(ids) || ids.length === 0) {
      return ctx.badRequest('ids must be an array of id');
    }

    for (const id of ids) {
      const webhook = await strapi.webhookStore.findWebhook(id);

      if (!webhook) continue;

      await strapi.webhookStore.deleteWebhook(id);
      strapi.webhookRunner.remove(webhook);
    }

    ctx.send({ data: ids });
  },

  async triggerWebhook(ctx) {
    const { id } = ctx.params;

    const webhook = await strapi.webhookStore.findWebhook(id);

    const response = await strapi.webhookRunner.run(webhook, 'trigger-test', {});

    ctx.body = { data: response };
  },
};
<commit_msg>Updates webhooks RegExp to allow undescores, fixes #5543 (#7079)<commit_after>'use strict';

const _ = require('lodash');
const { yup, formatYupErrors } = require('strapi-utils');

const ALLOWED_EVENTS = [
  'entry.create',
  'entry.update',
  'entry.delete',
  'media.create',
  'media.update',
  'media.delete',
];

const urlRegex = /^(?:([a-z0-9+.-]+):\/\/)(?:\S+(?::\S*)?@)?(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9_]-*)*[a-z\u00a1-\uffff0-9_]+)(?:\.(?:[a-z\u00a1-\uffff0-9_]-*)*[a-z\u00a1-\uffff0-9_]+)*\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/;

const webhookValidator = yup
  .object({
    name: yup.string().required(),
    url: yup
      .string()
      .matches(urlRegex, 'url must be a valid URL')
      .required(),
    headers: yup.lazy(data => {
      if (typeof data !== 'object') {
        return yup.object().required();
      }

      return yup
        .object(
          _.mapValues(data, () => {
            yup
              .string()
              .min(1)
              .required();
          })
        )
        .required();
    }),
    events: yup
      .array()
      .of(
        yup
          .string()
          .oneOf(ALLOWED_EVENTS)
          .required()
      )
      .min(1)
      .required(),
  })
  .noUnknown();

const updateWebhookValidator = webhookValidator.shape({
  isEnabled: yup.boolean(),
});

module.exports = {
  async listWebhooks(ctx) {
    const webhooks = await strapi.webhookStore.findWebhooks();
    ctx.send({ data: webhooks });
  },

  async getWebhook(ctx) {
    const { id } = ctx.params;
    const webhook = await strapi.webhookStore.findWebhook(id);

    if (!webhook) {
      return ctx.notFound('webhook.notFound');
    }

    ctx.send({ data: webhook });
  },

  async createWebhook(ctx) {
    const { body } = ctx.request;

    try {
      await webhookValidator.validate(body, {
        strict: true,
        abortEarly: false,
      });
    } catch (error) {
      return ctx.badRequest('ValidationError', {
        errors: formatYupErrors(error),
      });
    }

    const webhook = await strapi.webhookStore.createWebhook(body);

    strapi.webhookRunner.add(webhook);

    ctx.created({ data: webhook });
  },

  async updateWebhook(ctx) {
    const { id } = ctx.params;
    const { body } = ctx.request;

    try {
      await updateWebhookValidator.validate(body, {
        strict: true,
        abortEarly: false,
      });
    } catch (error) {
      return ctx.badRequest('ValidationError', {
        errors: formatYupErrors(error),
      });
    }

    const webhook = await strapi.webhookStore.findWebhook(id);

    if (!webhook) {
      return ctx.notFound('webhook.notFound');
    }

    const updatedWebhook = await strapi.webhookStore.updateWebhook(id, {
      ...webhook,
      ...body,
    });

    if (!updatedWebhook) {
      return ctx.notFound('webhook.notFound');
    }

    strapi.webhookRunner.update(updatedWebhook);

    ctx.send({ data: updatedWebhook });
  },

  async deleteWebhook(ctx) {
    const { id } = ctx.params;
    const webhook = await strapi.webhookStore.findWebhook(id);

    if (!webhook) {
      return ctx.notFound('webhook.notFound');
    }

    await strapi.webhookStore.deleteWebhook(id);

    strapi.webhookRunner.remove(webhook);

    ctx.body = { data: webhook };
  },

  async deleteWebhooks(ctx) {
    const { ids } = ctx.request.body;

    if (!Array.isArray(ids) || ids.length === 0) {
      return ctx.badRequest('ids must be an array of id');
    }

    for (const id of ids) {
      const webhook = await strapi.webhookStore.findWebhook(id);

      if (!webhook) continue;

      await strapi.webhookStore.deleteWebhook(id);
      strapi.webhookRunner.remove(webhook);
    }

    ctx.send({ data: ids });
  },

  async triggerWebhook(ctx) {
    const { id } = ctx.params;

    const webhook = await strapi.webhookStore.findWebhook(id);

    const response = await strapi.webhookRunner.run(webhook, 'trigger-test', {});

    ctx.body = { data: response };
  },
};
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies
 */

/**
 * CORS hook
 */

const defaults = require('./defaults.json');

module.exports = strapi => {
  return {
    /**
     * Initialize the hook
     */

    initialize: function(cb) {
      strapi.app.use(
        async (ctx, next) => {
          if (ctx.request.admin) {
            return strapi.koaMiddlewares.kcors({
              origin: '*',
              exposeHeaders: defaults.cors.expose,
              maxAge: defaults.cors.maxAge,
              credentials: defaults.cors.credentials,
              allowMethods: defaults.cors.methods,
              allowHeaders: defaults.cors.headers,
              keepHeadersOnError: defaults.cors.keepHeadersOnError
            })(ctx, next);
          } else if (strapi.config.currentEnvironment.security.cors.enabled) {
            return strapi.koaMiddlewares.kcors({
              origin: function(ctx) {
                const whitelist = strapi.config.middleware.settings.cors.origin.split(/\s*,\s*/);
                const requestOrigin = ctx.accept.headers.origin;
		if(whitelist.includes('*')){
		  return '*';
		}
                if (!whitelist.includes(requestOrigin)) {
                  return ctx.throw(`${requestOrigin} is not a valid origin`);
                }
                return requestOrigin;
              },
              exposeHeaders: strapi.config.middleware.settings.cors.expose,
              maxAge: strapi.config.middleware.settings.cors.maxAge,
              credentials: strapi.config.middleware.settings.cors.credentials,
              allowMethods: strapi.config.middleware.settings.cors.methods,
              allowHeaders: strapi.config.middleware.settings.cors.headers,
              keepHeadersOnError: strapi.config.middleware.settings.cors.keepHeadersOnError
            })(ctx, next);
          }

          await next();
        }
      );

      cb();
    }
  };
};
<commit_msg>Fix indent<commit_after>'use strict';

/**
 * Module dependencies
 */

/**
 * CORS hook
 */

const defaults = require('./defaults.json');

module.exports = strapi => {
  return {
    /**
     * Initialize the hook
     */

    initialize: function(cb) {
      strapi.app.use(
        async (ctx, next) => {
          if (ctx.request.admin) {
            return strapi.koaMiddlewares.kcors({
              origin: '*',
              exposeHeaders: defaults.cors.expose,
              maxAge: defaults.cors.maxAge,
              credentials: defaults.cors.credentials,
              allowMethods: defaults.cors.methods,
              allowHeaders: defaults.cors.headers,
              keepHeadersOnError: defaults.cors.keepHeadersOnError
            })(ctx, next);
          } else if (strapi.config.currentEnvironment.security.cors.enabled) {
            return strapi.koaMiddlewares.kcors({
              origin: function(ctx) {
                const whitelist = strapi.config.middleware.settings.cors.origin.split(/\s*,\s*/);
                const requestOrigin = ctx.accept.headers.origin;
		if (whitelist.includes('*')) {
		  return '*';
		}
                if (!whitelist.includes(requestOrigin)) {
                  return ctx.throw(`${requestOrigin} is not a valid origin`);
                }
                return requestOrigin;
              },
              exposeHeaders: strapi.config.middleware.settings.cors.expose,
              maxAge: strapi.config.middleware.settings.cors.maxAge,
              credentials: strapi.config.middleware.settings.cors.credentials,
              allowMethods: strapi.config.middleware.settings.cors.methods,
              allowHeaders: strapi.config.middleware.settings.cors.headers,
              keepHeadersOnError: strapi.config.middleware.settings.cors.keepHeadersOnError
            })(ctx, next);
          }

          await next();
        }
      );

      cb();
    }
  };
};
<|endoftext|>


<commit_before>/**
 * Created by alberto on 29/10/16.
 */

var api = {
    post: function (req, res, next) {

        //this is how we access the table
        var tabla = req.azureMobile.tables('Notas');
        var query = {
            sql: "update Notas set puntos = 69"
        };

        tabla.update(query)
            .then(function (result) {
                res.json(result);
            });
    }
};

api.get.access = 'anonymous';

module.exports = api;<commit_msg>Api de update de votos<commit_after>/**
 * Created by alberto on 29/10/16.
 */

var api = {
    get: function (req, res, next) {
        if (req.param.length < 0) {
            return next();
        }

        var context = req.azureMobile;
        var query = {
            sql: "update Notas set puntos = " + req.query.puntos
        };

        context.data.execute(query)
            .then(function (result) {
                res.json(result);
            });
    }
};

api.get.access = 'anonymous';

module.exports = api;<|endoftext|>