<commit_before>window.GeneDetailsView = Backbone.View.extend({

    initialize: function() {
        this.gene = this.options.gene;
    },

    template: _.template($('#tpl-gene-modal-content').html()),

    render: function(width) {
        var that = this;
        $(this.el).html(this.template({
            gene: that.gene,
        }));
        this.drawExpressionDisplay(1100);
        return this;
    },

    resize: function() {
    },

    drawExpressionDisplay: function(width) {

        var that = this;

        if (this.gene.expression == null) {
            this.$('#expression_plot').hide();
            this.$('#no-gene-expression').show();
            return;
        } else {
            this.$('#expression_plot').show();
            this.$('#no-gene-expression').hide();
        }

        // rename to tissue_names
        var expression_names = [];
        var expression_slugs = [];
        for (var i=0; i<DICTIONARY.tissue_types.length; i++) {
            expression_slugs.push(DICTIONARY.tissue_types[i].slug);
            expression_names.push(DICTIONARY.tissue_types[i].name);
        }

        //var width = that.$('#expression_plot').width();

        var row_height = 25;
        var scatter_offset = 200;
        var scatter_horizontal_padding = 10;

        var scatter_width = width-scatter_offset-2*scatter_horizontal_padding;
        var min_exponent = -10;
        var max_exponent = 12;

        var x = d3.scale.linear().domain([min_exponent, max_exponent]).range([0, scatter_width]);
        var row_offset = function(i) { return i*row_height + 30 }

	// Define the div for the tooltip
	var tooltip_div = d3.select("body").append("div").attr("class", "d3-tooltip").style("opacity", 0);

        var xcoord = function(d) {
            var e = min_exponent;
            if (d>0) {
                e = Math.log(d)/Math.log(2); // convert to base 2
            }
            // don't allow outside bounds
            if (e < min_exponent) e = min_exponent;
            if (e > max_exponent) e = max_exponent;

            return scatter_offset + scatter_horizontal_padding + x(e);

        }

        var vis = d3.selectAll(that.$("#expression_plot"))
            .append("svg")
            .attr("width", width)
            .attr("height", row_height*expression_slugs.length + 50);

        var labels = vis.selectAll('text')
            .data(expression_names)
            .enter()
            .append('text')
            .attr("text-anchor", "end")
            .attr('x', scatter_offset-25) // 12 px margin between text and scatter
            .attr('y', function(d,i) { return row_offset(i) + 15 } )
            .style('font-weight', 'bold')
            .text(function(d) { return d; });

        var colors = d3.scale.category10();

        var axis = d3.svg.axis()
            .scale(x)
            .tickSize(0)
            .tickPadding(6)
            .orient("bottom");

        vis.append("g")
            .attr("class", "axis axis-label")
            .attr("transform", "translate(" + scatter_offset + "," + 3 + ")")
            .call(axis);

        vis.append("g")
            .append("text")
            .attr("class", "axis axis-title")
            .text("LOG 2 EXPRESSION")
            .attr("text-anchor", "end")
            .attr("transform", "translate(" + (scatter_offset - 25) + "," + 17 + ")");

        var colorLabels = vis.selectAll('rect.color-label')
            .data(expression_names)
            .enter()
            .append('rect')
            .attr('class', 'color-label')
            .attr('x', scatter_offset-15 )
            .attr('y', function(d,i) { return row_offset(i) + 4 } )
            .attr('width', 6)
            .attr('height', 16)
            .attr('fill', function(d, i) { return colors(i); } )
            .attr('fill-opacity', .7);

        for (var i=0; i<expression_slugs.length; i++) {
            var slug = expression_slugs[i];
            //window.expr = this.gene.expression;
	    var gene_rpkms_array = this.gene.expression[slug].filter(function(x) { return x > 0 });
	    var gene_expression_data = vis.selectAll('circle[data-expression="' + slug + '"]').data(gene_rpkms_array);
            gene_expression_data.enter()
                .append('circle')
                .attr('data-expression', slug)
                .attr('r', 5)
                .attr('fill-opacity', .12)
                .attr('fill', function(d, j) { return colors(i); } )
                .attr('transform', function(d, j) { return "translate(" + xcoord(d) + "," + (row_offset(i) + 13) + ")"; })
		.on("mouseover", (function() {
			var name = expression_names[i];
			var num_samples = gene_rpkms_array.length;
			return function(d) {
			    var e = Math.log(d)/Math.log(2); // convert to base 2
			    tooltip_div.transition()
				.duration(10)
				.style("opacity", 0.95);
			    tooltip_div.html(""+name+"
"+ num_samples + " samples
" + e.toFixed(2) + " log2RPKM
")
				.style("left", (d3.event.pageX - 50) + "px")
				.style("top", (d3.event.pageY - 60) + "px");
			};
		    })())
		.on("mouseout", function(d) {
			tooltip_div.transition()
			    .duration(500)
			    .style("opacity", 0);
		    });
        }
    }
});

window.GeneModalView = Backbone.View.extend({

    initialize: function() {
        this.gene = {};
    },

    content_template: _.template($('#tpl-gene-modal-content').html()),

    template: _.template(
        $('#tpl-gene-modal').html()
    ),

    render: function(eventName) {

        var that = this;

        $(this.el).html(this.template({
            gene_id: this.options.gene_id,
        }));

        this.setLoading();

        $.get(URL_PREFIX + 'api/gene-info/' + this.options.gene_id, {},
            function(data) {
                if (data.found_gene == true) {
                    that.gene = data.gene;
                    that.setLoaded();
                } else {
                    that.gene = null;
                    that.setNotFound();
                }
            }
        );

        return this;
    },

    setLoading: function() {
        this.$('#modal-content-container').hide();
        this.$('#modal-loading').show();
    },

    setNotFound: function() {
        this.$('#modal-content-container').html("Gene not found");
        this.$('#modal-content-container').show();
        this.$('#modal-loading').hide();
    },

    setLoaded: function() {
        var that = this;
        var detailsView = new GeneDetailsView({gene: this.gene});

        this.$('#modal-content-container').html(detailsView.render($(that.el).width()).el);
        detailsView.resize();

        this.$('#modal-content-container').show();
        this.$('#modal-loading').hide();
    },

});
<commit_msg>switch gtex plot to log10. Fixes #205.<commit_after>window.GeneDetailsView = Backbone.View.extend({

    initialize: function() {
        this.gene = this.options.gene;
    },

    template: _.template($('#tpl-gene-modal-content').html()),

    render: function(width) {
        var that = this;
        $(this.el).html(this.template({
            gene: that.gene,
        }));
        this.drawExpressionDisplay(1100);
        return this;
    },

    resize: function() {
    },

    drawExpressionDisplay: function(width) {

        var that = this;

        if (this.gene.expression == null) {
            this.$('#expression_plot').hide();
            this.$('#no-gene-expression').show();
            return;
        } else {
            this.$('#expression_plot').show();
            this.$('#no-gene-expression').hide();
        }

        // rename to tissue_names
        var expression_names = [];
        var expression_slugs = [];
        for (var i=0; i<DICTIONARY.tissue_types.length; i++) {
            expression_slugs.push(DICTIONARY.tissue_types[i].slug);
            expression_names.push(DICTIONARY.tissue_types[i].name);
        }

        //var width = that.$('#expression_plot').width();

        var row_height = 25;
        var scatter_offset = 200;
        var scatter_horizontal_padding = 10;

        var scatter_width = width-scatter_offset-2*scatter_horizontal_padding;
        var min_exponent = -10;
        var max_exponent = 12;

        var x = d3.scale.linear().domain([min_exponent, max_exponent]).range([0, scatter_width]);
        var row_offset = function(i) { return i*row_height + 30 }

	// Define the div for the tooltip
	var tooltip_div = d3.select("body").append("div").attr("class", "d3-tooltip").style("opacity", 0);

        var xcoord = function(d) {
            var e = min_exponent;
            if (d>0) {
                e = Math.log(d)/Math.log(10); // convert to base 2
            }
            // don't allow outside bounds
            if (e < min_exponent) e = min_exponent;
            if (e > max_exponent) e = max_exponent;

            return scatter_offset + scatter_horizontal_padding + x(e);

        }

        var vis = d3.selectAll(that.$("#expression_plot"))
            .append("svg")
            .attr("width", width)
            .attr("height", row_height*expression_slugs.length + 50);

        var labels = vis.selectAll('text')
            .data(expression_names)
            .enter()
            .append('text')
            .attr("text-anchor", "end")
            .attr('x', scatter_offset-25) // 12 px margin between text and scatter
            .attr('y', function(d,i) { return row_offset(i) + 15 } )
            .style('font-weight', 'bold')
            .text(function(d) { return d; });

        var colors = d3.scale.category10();

        var axis = d3.svg.axis()
            .scale(x)
            .tickSize(0)
            .tickPadding(6)
            .orient("bottom");

        vis.append("g")
            .attr("class", "axis axis-label")
            .attr("transform", "translate(" + scatter_offset + "," + 3 + ")")
            .call(axis);

        vis.append("g")
            .append("text")
            .attr("class", "axis axis-title")
            .text("LOG 10 EXPRESSION")
            .attr("text-anchor", "end")
            .attr("transform", "translate(" + (scatter_offset - 25) + "," + 17 + ")");

        var colorLabels = vis.selectAll('rect.color-label')
            .data(expression_names)
            .enter()
            .append('rect')
            .attr('class', 'color-label')
            .attr('x', scatter_offset-15 )
            .attr('y', function(d,i) { return row_offset(i) + 4 } )
            .attr('width', 6)
            .attr('height', 16)
            .attr('fill', function(d, i) { return colors(i); } )
            .attr('fill-opacity', .7);

        for (var i=0; i<expression_slugs.length; i++) {
            var slug = expression_slugs[i];
            //window.expr = this.gene.expression;
	    var gene_rpkms_array = this.gene.expression[slug].filter(function(x) { return x > 0 });
	    var gene_expression_data = vis.selectAll('circle[data-expression="' + slug + '"]').data(gene_rpkms_array);
            gene_expression_data.enter()
                .append('circle')
                .attr('data-expression', slug)
                .attr('r', 5)
                .attr('fill-opacity', .12)
                .attr('fill', function(d, j) { return colors(i); } )
                .attr('transform', function(d, j) { return "translate(" + xcoord(d) + "," + (row_offset(i) + 13) + ")"; })
		.on("mouseover", (function() {
			var name = expression_names[i];
			var num_samples = gene_rpkms_array.length;
			return function(d) {
			    var e = Math.log(d)/Math.log(10); // convert to base 2
			    tooltip_div.transition()
				.duration(10)
				.style("opacity", 0.95);
			    tooltip_div.html(""+name+"
"+ num_samples + " samples
" + e.toFixed(2) + " log10RPKM
")
				.style("left", (d3.event.pageX - 50) + "px")
				.style("top", (d3.event.pageY - 60) + "px");
			};
		    })())
		.on("mouseout", function(d) {
			tooltip_div.transition()
			    .duration(500)
			    .style("opacity", 0);
		    });
        }
    }
});

window.GeneModalView = Backbone.View.extend({

    initialize: function() {
        this.gene = {};
    },

    content_template: _.template($('#tpl-gene-modal-content').html()),

    template: _.template(
        $('#tpl-gene-modal').html()
    ),

    render: function(eventName) {

        var that = this;

        $(this.el).html(this.template({
            gene_id: this.options.gene_id,
        }));

        this.setLoading();

        $.get(URL_PREFIX + 'api/gene-info/' + this.options.gene_id, {},
            function(data) {
                if (data.found_gene == true) {
                    that.gene = data.gene;
                    that.setLoaded();
                } else {
                    that.gene = null;
                    that.setNotFound();
                }
            }
        );

        return this;
    },

    setLoading: function() {
        this.$('#modal-content-container').hide();
        this.$('#modal-loading').show();
    },

    setNotFound: function() {
        this.$('#modal-content-container').html("Gene not found");
        this.$('#modal-content-container').show();
        this.$('#modal-loading').hide();
    },

    setLoaded: function() {
        var that = this;
        var detailsView = new GeneDetailsView({gene: this.gene});

        this.$('#modal-content-container').html(detailsView.render($(that.el).width()).el);
        detailsView.resize();

        this.$('#modal-content-container').show();
        this.$('#modal-loading').hide();
    },

});
<|endoftext|>


<commit_before>'use strict';

var exec = require('child_process').exec,
    sysPath = require('path'),
    fs = require('fs'),
    mode = process.argv[2],
    fsExistsSync = fs.existsSync || sysPath.existsSync,
    emberUrls = {
      common: [
        'http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars-latest.js'
      ],
      development: [
        'http://builds.emberjs.com/release/ember.js',
        'http://builds.emberjs.com.s3.amazonaws.com/ember-data-latest.js'
      ],
      production: [
        'http://builds.emberjs.com/release/ember.prod.js',
        'http://builds.emberjs.com.s3.amazonaws.com/ember-data-latest.prod.js'
      ]
    };

var getBinaryPath = function(binary) {
  var path;
  if (fsExistsSync(
      path = sysPath.join('node_modules', '.bin', binary))) return path;
  if (fsExistsSync(path = sysPath.join('..', '.bin', binary))) return path;
  return binary;
};

var execute = function(path, params, callback) {
  if (callback == null) callback = function() {};
  var command = path + ' ' + params;
  console.log('Executing', command);
  exec(command, function(error, stdout, stderr) {
    if (error != null) return process.stderr.write(stderr.toString());
    console.log(stdout.toString());
    callback();
  });
};

switch (mode) {
  case 'update:ember':
    for (var env in emberUrls) {
      for (var file in emberUrls[env]) {
        var download = emberUrls[env][file],
            filename = download.split('/').reverse()[0]
                       .replace('.prod', '').replace('-latest', '');

        execute('curl ' + emberUrls[env][file],
                ' > vendor/scripts/' + env + '/' + filename);
      }
    }
    break;

  case 'update:skeleton':
    var downloadUrl = 'https://codeload.github.com/gcollazo/brunch-with-ember-reloaded/zip/master';

    execute('curl ' + downloadUrl, '> master.zip', function() {
      execute('unzip', 'master.zip', function() {
        execute('cat brunch-with-ember-reloaded-master/config.js', '> config.js');
        execute('cat brunch-with-ember-reloaded-master/karma.conf.js', '> karma.conf.js');
        execute('cat brunch-with-ember-reloaded-master/package.json', '> packages.json');
        execute('cat brunch-with-ember-reloaded-master/README.md', '> README.md');
        execute('cat brunch-with-ember-reloaded-master/setup.js', '> setup.js');
        execute('rm', '-rf generators', function() {
          execute('mv brunch-with-ember-reloaded-master/generators/', 'generators/', function() {
            execute('rm', '-rf brunch-with-ember-reloaded-master');
            execute('rm', '-r master.zip');
          });
        });
      });
    });
    break;
}
<commit_msg>fix typo<commit_after>'use strict';

var exec = require('child_process').exec,
    sysPath = require('path'),
    fs = require('fs'),
    mode = process.argv[2],
    fsExistsSync = fs.existsSync || sysPath.existsSync,
    emberUrls = {
      common: [
        'http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars-latest.js'
      ],
      development: [
        'http://builds.emberjs.com/release/ember.js',
        'http://builds.emberjs.com.s3.amazonaws.com/ember-data-latest.js'
      ],
      production: [
        'http://builds.emberjs.com/release/ember.prod.js',
        'http://builds.emberjs.com.s3.amazonaws.com/ember-data-latest.prod.js'
      ]
    };

var getBinaryPath = function(binary) {
  var path;
  if (fsExistsSync(
      path = sysPath.join('node_modules', '.bin', binary))) return path;
  if (fsExistsSync(path = sysPath.join('..', '.bin', binary))) return path;
  return binary;
};

var execute = function(path, params, callback) {
  if (callback == null) callback = function() {};
  var command = path + ' ' + params;
  console.log('Executing', command);
  exec(command, function(error, stdout, stderr) {
    if (error != null) return process.stderr.write(stderr.toString());
    console.log(stdout.toString());
    callback();
  });
};

switch (mode) {
  case 'update:ember':
    for (var env in emberUrls) {
      for (var file in emberUrls[env]) {
        var download = emberUrls[env][file],
            filename = download.split('/').reverse()[0]
                       .replace('.prod', '').replace('-latest', '');

        execute('curl ' + emberUrls[env][file],
                ' > vendor/scripts/' + env + '/' + filename);
      }
    }
    break;

  case 'update:skeleton':
    var downloadUrl = 'https://codeload.github.com/gcollazo/brunch-with-ember-reloaded/zip/master';

    execute('curl ' + downloadUrl, '> master.zip', function() {
      execute('unzip', 'master.zip', function() {
        execute('cat brunch-with-ember-reloaded-master/config.js', '> config.js');
        execute('cat brunch-with-ember-reloaded-master/karma.conf.js', '> karma.conf.js');
        execute('cat brunch-with-ember-reloaded-master/package.json', '> package.json');
        execute('cat brunch-with-ember-reloaded-master/README.md', '> README.md');
        execute('cat brunch-with-ember-reloaded-master/setup.js', '> setup.js');
        execute('rm', '-rf generators', function() {
          execute('mv brunch-with-ember-reloaded-master/generators/', 'generators/', function() {
            execute('rm', '-rf brunch-with-ember-reloaded-master');
            execute('rm', '-r master.zip');
          });
        });
      });
    });
    break;
}
<|endoftext|>


<commit_before>'use strict';

const Path = require('upath');
const expect = require('unexpected')
  .clone()
  .installPlugin(require('unexpected-express'))
  .installPlugin(require('unexpected-sinon'));
const connect = require('connect');
const sinon = require('sinon');
const pitm = require('../lib/middleware');
const babyTolk = require('@pingy/compile');
const fs = require('fs');

function getPath(path) {
  return Path.join(process.cwd(), 'examples', 'site', path || '');
}

describe('middleware', function () {
  this.timeout(5000);
  let app;
  let compileCount = 0;
  let cssFileChanged = 0;
  let cssFileChangedExpectation = 0;

  const startup = function () {
    const pitmInstance = pitm(getPath());
    app = connect().use(pitmInstance);

    pitmInstance.events.on('fileChanged', (serverPath, sourcePath) => {
      if (
        serverPath === '/styles/main.css' &&
        (sourcePath === 'styles/main.styl' || sourcePath === 'styles/_headings.styl')
      ) {
        cssFileChanged += 1;
      }
    });
  };

  before(() => {
    // babyTolk.read is called on compile so spy on this to see if
    // compilation has taken place
    babyTolk.read = sinon.spy(babyTolk, 'read');
  });

  function expectCssFileChangedEvent() {
    cssFileChangedExpectation += 1;
    return expect(cssFileChanged, 'to be', cssFileChangedExpectation);
  }

  function expectCompiled() {
    compileCount += 1;
    return expect(babyTolk.read, 'was called times', compileCount);
  }

  function expectCached() {
    return expect(babyTolk.read, 'was called times', compileCount);
  }

  describe('inner source file added after startup', () => {
    const pathToCSS = getPath('styles/main.styl');
    let fileContentsCSS;
    let importRemovedCSS;

    before(function(done) {
      fs.readFile(pathToCSS, (err, contents) => {
        fileContentsCSS = contents;

        importRemovedCSS = fileContentsCSS.toString().split('\n'); // split into lines
        importRemovedCSS.shift(); // remove first line
        importRemovedCSS = importRemovedCSS.join('\n'); // join up the string again
        fs.writeFile(pathToCSS, importRemovedCSS, () => {
          startup();
          done();
        });
      });
    });

    it('should compile styl file', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map',
          },
          body: expect.it('not to contain', 'h1 {'),
        },
      }).then(() => expectCompiled());
    });

    it('should have styl sourcemap', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css.map' },
        response: {
          statusCode: 200,
          headers: {
            'Content-Type': 'application/json; charset=UTF-8',
          },
          body: {
            sources: ['/styles/main.styl'],
            mappings: expect.it('to be non-empty'),
          },
        },
      }).then(() => expectCached());
    }); // Loading source files also caches source map

    describe('add inner import', () => {
      before(function(done) {
        // Revert file back to original contents with import
        // Allow 2000ms for chokidar to notice the change
        fs.writeFile(pathToCSS, fileContentsCSS, () => setTimeout(done, 2000));
      });

      it('should cause a file change event', () => {
        return expectCssFileChangedEvent();
      });

      it('should compile styl file', () => {
        return expect(app, 'to yield exchange', {
          request: { url: '/styles/main.css' },
          response: {
            headers: {
              'Content-Type': 'text/css; charset=UTF-8',
              'X-SourceMap': 'main.css.map',
            },
            body: expect.it('to contain', 'h1 {'),
          },
        }).then(() => expectCompiled());
      });

      it('should have styl sourcemap', () => {
        return expect(app, 'to yield exchange', {
          request: { url: '/styles/main.css.map' },
          response: {
            statusCode: 200,
            headers: {
              'Content-Type': 'application/json; charset=UTF-8',
            },
            body: {
              sources: expect.it('to contain', '/styles/_headings.styl', '/styles/main.styl'),
              mappings: expect.it('to be non-empty'),
            },
          },
        }).then(() => expectCached());
      });

      describe('edit inner import file', () => {
        const pathToInnerCSS = getPath('styles/_headings.styl');
        let fileContentsInnerCSS;

        before(function(done) {
          fs.readFile(pathToInnerCSS, (err, contents) => {
            fileContentsInnerCSS = contents;
            const newContents = `${contents}\nh2\n  color blue`;

            fs.writeFile(pathToInnerCSS, newContents, () => setTimeout(done, 2000));
          });
        });

        after(function(done) {
          // Revert file back to original contents with import
          // Allow 2000ms for chokidar to notice the change
          fs.writeFile(pathToInnerCSS, fileContentsInnerCSS, () => setTimeout(done, 2000));
        });

        it('should compile styl file', () => {
          return expect(app, 'to yield exchange', {
            request: { url: '/styles/main.css' },
            response: {
              headers: {
                'Content-Type': 'text/css; charset=UTF-8',
                'X-SourceMap': 'main.css.map',
              },
              body: expect.it('to contain', 'h2 {'),
            },
          }).then(() => expectCompiled());
        });

        it('should cause a file change event', () => {
          return expectCssFileChangedEvent();
        });
      });

      describe('after cleanup', () => {
        it('should cause a file change event', () => {
          return expectCssFileChangedEvent();
        });
      });
    });
  });

  describe('first requests', () => {
    it('should compile jade file', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/' },
        response: {
          statusCode: 200,
          headers: {
            'Content-Type': 'text/html; charset=UTF-8',
          },
          body: expect.it('to contain', '
Piggy In The Middle
'),
        },
      }).then(() => expectCompiled());
    });

    it('should compile styl file', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map',
          },
          body: expect.it('to contain', 'h1 {'),
        },
      }).then(() => expectCompiled());
    });

    it('should have styl sourcemap', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css.map' },
        response: {
          statusCode: 200,
          headers: {
            'Content-Type': 'application/json; charset=UTF-8',
          },
          body: {
            sources: expect.it('to contain', '/styles/_headings.styl', '/styles/main.styl'),
            mappings: expect.it('to be non-empty'),
          },
        },
      }).then(() => expectCached());
    }); // Loading source files also caches source map

    it('should compile coffee file', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/scripts/main.js' },
        response: {
          headers: {
            'Content-Type': 'application/javascript; charset=UTF-8',
            'X-SourceMap': 'main.js.map',
          },
          body: expect.it('to contain', 'console.log('),
        },
      }).then(() => expectCompiled());
    });

    it('should have coffee sourcemap', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/scripts/main.js.map' },
        response: {
          statusCode: 200,
          headers: {
            'Content-Type': 'application/json; charset=UTF-8',
          },
          body: {
            sources: ['/scripts/main.coffee'],
            mappings: expect.it('to be non-empty'),
          },
        },
      }).then(() => expectCached());
    }); // Loading source files also caches source map
  });

  describe('second (cached) requests', () => {
    it('should *not* compile jade file', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/' },
        response: {
          headers: {
            'Content-Type': 'text/html; charset=UTF-8',
          },
          body: expect.it('to contain', '
Piggy In The Middle
'),
        },
      }).then(() => expectCached());
    });

    it('should *not* compile styl file', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map',
          },
          body: expect.it('to contain', 'body {'),
        },
      }).then(() => expectCached());
    });

    it('should *not* compile coffee file', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/scripts/main.js' },
        response: {
          headers: {
            'Content-Type': 'application/javascript; charset=UTF-8',
            'X-SourceMap': 'main.js.map',
          },
          body: expect.it('to contain', 'console.log('),
        },
      }).then(() => expectCached());
    });
  });

  describe('third requests (after editing a watched file)', () => {
    const pathToCSS = getPath('styles/main.styl');
    let fileContents;

    before(function(done) {
      fs.readFile(pathToCSS, (err, contents) => {
        fileContents = contents;
        // Add space to end of file
        // Allow 2000ms for chokidar to notice the change
        fs.writeFile(pathToCSS, `${contents} `, () => setTimeout(done, 2000));
      });
    });

    after(function(done) {
      // Revert file back to original contents
      // Allow 2000ms for chokidar to notice the change
      fs.writeFile(pathToCSS, fileContents, () => setTimeout(done, 2000));
    });

    it('should recompile styl file', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map',
          },
          body: expect.it('to contain', 'body {'),
        },
      }).then(() => expectCompiled());
    });

    it('should cause a file change event', () => {
      return expectCssFileChangedEvent();
    });
  });

  describe('fourth requests', () => {
    // Probably bit pointless doing this again but just in case
    it('should recompile styl file', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map',
          },
          body: expect.it('to contain', 'body {'),
        },
      }).then(() => expectCompiled());
    });

    it('should cause a file change event', () => {
      return expectCssFileChangedEvent();
    });
  });

  describe('fifth (cached) requests', () => {
    // Probably bit pointless doing this again but just in case
    it('should *not* compile styl file', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map',
          },
          body: expect.it('to contain', 'body {'),
        },
      }).then(() => expectCached());
    });

    it('should *not* cause a file change event', () => {
      return expect(cssFileChangedExpectation, 'to be', cssFileChanged);
    });
  });

  describe('errors', () => {
    const pathToCSS = getPath('styles/main.styl');
    const pathToJS = getPath('scripts/main.coffee');
    let fileContentsCSS;
    let fileContentsJS;

    before(function(done) {
      let numDone = 0;
      function halfDone() {
        numDone++;
        if (numDone === 2) done();
      }
      fs.readFile(pathToCSS, (err, contents) => {
        fileContentsCSS = contents;
        // Add space to end of file
        // Allow 2000ms for chokidar to notice the change
        fs.writeFile(pathToCSS, 'sodifj5ij%$:@', () => setTimeout(halfDone, 2000));
      });
      fs.readFile(pathToJS, (err, contents) => {
        fileContentsJS = contents;
        fs.writeFile(pathToJS, '"sodifj5ij%$:@', halfDone);
      });
    });

    after(function(done) {
      let numDone = 0;
      function halfDone() {
        numDone++;
        if (numDone === 2) done();
      }
      // Revert file back to original contents
      // Allow 2000ms for chokidar to notice the change
      fs.writeFile(pathToCSS, fileContentsCSS, () => setTimeout(done, 2000));
      fs.writeFile(pathToJS, fileContentsJS, halfDone);
    });

    it('should return 404 when file not found', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/foo/bar.css' },
        response: {
          statusCode: 404,
          body: expect.it('to be undefined'),
        },
      });
    });

    it('should output error on css error', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        headers: expect.it('to not have keys', 'X-SourceMap'),
        response: {
          statusCode: 200,
          body: expect.it('to contain', 'body * {').and('to contain', 'ParseError'),
        },
      });
    });

    it('should output error on js error', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/scripts/main.js' },
        headers: expect.it('to not have keys', 'X-SourceMap'),
        response: {
          statusCode: 200,
          body: expect.it('to contain', 'body * {').and('to contain', 'error: missing'),
        },
      });
    });
  });
});
<commit_msg>[middleware] On mac we are sometimes getting multiple file change events, so let's ignore it for the moment<commit_after>'use strict';

const Path = require('upath');
const expect = require('unexpected')
  .clone()
  .installPlugin(require('unexpected-express'))
  .installPlugin(require('unexpected-sinon'));
const connect = require('connect');
const sinon = require('sinon');
const pitm = require('../lib/middleware');
const babyTolk = require('@pingy/compile');
const fs = require('fs');
const os = require('os');

// TODO: On mac we are sometimes getting multiple file change events, so let's ignore it for the moment
const fileChangeEqual = os.platform() === 'darwin' ? 'to be greater than or equal to' : 'to be';

function getPath(path) {
  return Path.join(process.cwd(), 'examples', 'site', path || '');
}

describe('middleware', function () {
  this.timeout(5000);
  let app;
  let compileCount = 0;
  let cssFileChanged = 0;
  let cssFileChangedExpectation = 0;

  const startup = function () {
    const pitmInstance = pitm(getPath());
    app = connect().use(pitmInstance);

    pitmInstance.events.on('fileChanged', (serverPath, sourcePath) => {
      if (
        serverPath === '/styles/main.css' &&
        (sourcePath === 'styles/main.styl' || sourcePath === 'styles/_headings.styl')
      ) {
        cssFileChanged += 1;
      }
    });
  };

  before(() => {
    // babyTolk.read is called on compile so spy on this to see if
    // compilation has taken place
    babyTolk.read = sinon.spy(babyTolk, 'read');
  });

  function expectCssFileChangedEvent() {
    cssFileChangedExpectation += 1;
    return expect(cssFileChanged, fileChangeEqual, cssFileChangedExpectation);
  }

  function expectCompiled() {
    compileCount += 1;
    return expect(babyTolk.read, 'was called times', compileCount);
  }

  function expectCached() {
    return expect(babyTolk.read, 'was called times', compileCount);
  }

  describe('inner source file added after startup', () => {
    const pathToCSS = getPath('styles/main.styl');
    let fileContentsCSS;
    let importRemovedCSS;

    before(function(done) {
      fs.readFile(pathToCSS, (err, contents) => {
        fileContentsCSS = contents;

        importRemovedCSS = fileContentsCSS.toString().split('\n'); // split into lines
        importRemovedCSS.shift(); // remove first line
        importRemovedCSS = importRemovedCSS.join('\n'); // join up the string again
        fs.writeFile(pathToCSS, importRemovedCSS, () => {
          startup();
          done();
        });
      });
    });

    it('should compile styl file', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map',
          },
          body: expect.it('not to contain', 'h1 {'),
        },
      }).then(() => expectCompiled());
    });

    it('should have styl sourcemap', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css.map' },
        response: {
          statusCode: 200,
          headers: {
            'Content-Type': 'application/json; charset=UTF-8',
          },
          body: {
            sources: ['/styles/main.styl'],
            mappings: expect.it('to be non-empty'),
          },
        },
      }).then(() => expectCached());
    }); // Loading source files also caches source map

    describe('add inner import', () => {
      before(function(done) {
        // Revert file back to original contents with import
        // Allow 2000ms for chokidar to notice the change
        fs.writeFile(pathToCSS, fileContentsCSS, () => setTimeout(done, 2000));
      });

      it('should cause a file change event', () => {
        return expectCssFileChangedEvent();
      });

      it('should compile styl file', () => {
        return expect(app, 'to yield exchange', {
          request: { url: '/styles/main.css' },
          response: {
            headers: {
              'Content-Type': 'text/css; charset=UTF-8',
              'X-SourceMap': 'main.css.map',
            },
            body: expect.it('to contain', 'h1 {'),
          },
        }).then(() => expectCompiled());
      });

      it('should have styl sourcemap', () => {
        return expect(app, 'to yield exchange', {
          request: { url: '/styles/main.css.map' },
          response: {
            statusCode: 200,
            headers: {
              'Content-Type': 'application/json; charset=UTF-8',
            },
            body: {
              sources: expect.it('to contain', '/styles/_headings.styl', '/styles/main.styl'),
              mappings: expect.it('to be non-empty'),
            },
          },
        }).then(() => expectCached());
      });

      describe('edit inner import file', () => {
        const pathToInnerCSS = getPath('styles/_headings.styl');
        let fileContentsInnerCSS;

        before(function(done) {
          fs.readFile(pathToInnerCSS, (err, contents) => {
            fileContentsInnerCSS = contents;
            const newContents = `${contents}\nh2\n  color blue`;

            fs.writeFile(pathToInnerCSS, newContents, () => setTimeout(done, 2000));
          });
        });

        after(function(done) {
          // Revert file back to original contents with import
          // Allow 2000ms for chokidar to notice the change
          fs.writeFile(pathToInnerCSS, fileContentsInnerCSS, () => setTimeout(done, 2000));
        });

        it('should compile styl file', () => {
          return expect(app, 'to yield exchange', {
            request: { url: '/styles/main.css' },
            response: {
              headers: {
                'Content-Type': 'text/css; charset=UTF-8',
                'X-SourceMap': 'main.css.map',
              },
              body: expect.it('to contain', 'h2 {'),
            },
          }).then(() => expectCompiled());
        });

        it('should cause a file change event', () => {
          return expectCssFileChangedEvent();
        });
      });

      describe('after cleanup', () => {
        it('should cause a file change event', () => {
          return expectCssFileChangedEvent();
        });
      });
    });
  });

  describe('first requests', () => {
    it('should compile jade file', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/' },
        response: {
          statusCode: 200,
          headers: {
            'Content-Type': 'text/html; charset=UTF-8',
          },
          body: expect.it('to contain', '
Piggy In The Middle
'),
        },
      }).then(() => expectCompiled());
    });

    it('should compile styl file', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map',
          },
          body: expect.it('to contain', 'h1 {'),
        },
      }).then(() => expectCompiled());
    });

    it('should have styl sourcemap', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css.map' },
        response: {
          statusCode: 200,
          headers: {
            'Content-Type': 'application/json; charset=UTF-8',
          },
          body: {
            sources: expect.it('to contain', '/styles/_headings.styl', '/styles/main.styl'),
            mappings: expect.it('to be non-empty'),
          },
        },
      }).then(() => expectCached());
    }); // Loading source files also caches source map

    it('should compile coffee file', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/scripts/main.js' },
        response: {
          headers: {
            'Content-Type': 'application/javascript; charset=UTF-8',
            'X-SourceMap': 'main.js.map',
          },
          body: expect.it('to contain', 'console.log('),
        },
      }).then(() => expectCompiled());
    });

    it('should have coffee sourcemap', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/scripts/main.js.map' },
        response: {
          statusCode: 200,
          headers: {
            'Content-Type': 'application/json; charset=UTF-8',
          },
          body: {
            sources: ['/scripts/main.coffee'],
            mappings: expect.it('to be non-empty'),
          },
        },
      }).then(() => expectCached());
    }); // Loading source files also caches source map
  });

  describe('second (cached) requests', () => {
    it('should *not* compile jade file', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/' },
        response: {
          headers: {
            'Content-Type': 'text/html; charset=UTF-8',
          },
          body: expect.it('to contain', '
Piggy In The Middle
'),
        },
      }).then(() => expectCached());
    });

    it('should *not* compile styl file', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map',
          },
          body: expect.it('to contain', 'body {'),
        },
      }).then(() => expectCached());
    });

    it('should *not* compile coffee file', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/scripts/main.js' },
        response: {
          headers: {
            'Content-Type': 'application/javascript; charset=UTF-8',
            'X-SourceMap': 'main.js.map',
          },
          body: expect.it('to contain', 'console.log('),
        },
      }).then(() => expectCached());
    });
  });

  describe('third requests (after editing a watched file)', () => {
    const pathToCSS = getPath('styles/main.styl');
    let fileContents;

    before(function(done) {
      fs.readFile(pathToCSS, (err, contents) => {
        fileContents = contents;
        // Add space to end of file
        // Allow 2000ms for chokidar to notice the change
        fs.writeFile(pathToCSS, `${contents} `, () => setTimeout(done, 2000));
      });
    });

    after(function(done) {
      // Revert file back to original contents
      // Allow 2000ms for chokidar to notice the change
      fs.writeFile(pathToCSS, fileContents, () => setTimeout(done, 2000));
    });

    it('should recompile styl file', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map',
          },
          body: expect.it('to contain', 'body {'),
        },
      }).then(() => expectCompiled());
    });

    it('should cause a file change event', () => {
      return expectCssFileChangedEvent();
    });
  });

  describe('fourth requests', () => {
    // Probably bit pointless doing this again but just in case
    it('should recompile styl file', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map',
          },
          body: expect.it('to contain', 'body {'),
        },
      }).then(() => expectCompiled());
    });

    it('should cause a file change event', () => {
      return expectCssFileChangedEvent();
    });
  });

  describe('fifth (cached) requests', () => {
    // Probably bit pointless doing this again but just in case
    it('should *not* compile styl file', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map',
          },
          body: expect.it('to contain', 'body {'),
        },
      }).then(() => expectCached());
    });

    it('should *not* cause a file change event', () => {
      return expect(cssFileChanged, fileChangeEqual, cssFileChangedExpectation);
    });
  });

  describe('errors', () => {
    const pathToCSS = getPath('styles/main.styl');
    const pathToJS = getPath('scripts/main.coffee');
    let fileContentsCSS;
    let fileContentsJS;

    before(function(done) {
      let numDone = 0;
      function halfDone() {
        numDone++;
        if (numDone === 2) done();
      }
      fs.readFile(pathToCSS, (err, contents) => {
        fileContentsCSS = contents;
        // Add space to end of file
        // Allow 2000ms for chokidar to notice the change
        fs.writeFile(pathToCSS, 'sodifj5ij%$:@', () => setTimeout(halfDone, 2000));
      });
      fs.readFile(pathToJS, (err, contents) => {
        fileContentsJS = contents;
        fs.writeFile(pathToJS, '"sodifj5ij%$:@', halfDone);
      });
    });

    after(function(done) {
      let numDone = 0;
      function halfDone() {
        numDone++;
        if (numDone === 2) done();
      }
      // Revert file back to original contents
      // Allow 2000ms for chokidar to notice the change
      fs.writeFile(pathToCSS, fileContentsCSS, () => setTimeout(done, 2000));
      fs.writeFile(pathToJS, fileContentsJS, halfDone);
    });

    it('should return 404 when file not found', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/foo/bar.css' },
        response: {
          statusCode: 404,
          body: expect.it('to be undefined'),
        },
      });
    });

    it('should output error on css error', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        headers: expect.it('to not have keys', 'X-SourceMap'),
        response: {
          statusCode: 200,
          body: expect.it('to contain', 'body * {').and('to contain', 'ParseError'),
        },
      });
    });

    it('should output error on js error', () => {
      return expect(app, 'to yield exchange', {
        request: { url: '/scripts/main.js' },
        headers: expect.it('to not have keys', 'X-SourceMap'),
        response: {
          statusCode: 200,
          body: expect.it('to contain', 'body * {').and('to contain', 'error: missing'),
        },
      });
    });
  });
});
<|endoftext|>


<commit_before>const fs  = require('fs')
const uri = require('amazon-s3-uri')
const AWS = require('aws-sdk/global')
const S3 = require('aws-sdk/clients/s3')

let regions = {}
let endpoints = {}

/* return a credentials object if possible, otherwise return false */
const getCredentials = params => {
    if (params && params.profile) {
        // will throw if the profile is not configured
        return new AWS.SharedIniFileCredentials({ profile: params.profile })
    } else if (params && params.accessKeyId && params.secretAccessKey) {
        return { accessKeyId: params.accessKeyId, secretAccessKey: params.secretAccessKey }
    // Cross-account role access pattern
    } else if (params && params.RoleArn && params.RoleSessionName) {
        return new AWS.ChainableTemporaryCredentials({ params: { ...params }})
    } else if (process.env.AWS_PROFILE) { // prioritize any explicitly set params before env variables
        // will throw if the profile is not configured
        return new AWS.SharedIniFileCredentials({ profile: process.env.AWS_PROFILE })
    } else if (process.env.AWS_ACCESS_KEY && process.env.AWS_SECRET_KEY) {
        return { accessKeyId: process.env.AWS_ACCESS_KEY, secretAccessKey: process.env.AWS_SECRET_KEY }
    }
}

/* create or get api instance with region */
const s3instanceWithRegion = (region, credentials) => {
    const key = region || 0

    if (!regions.hasOwnProperty(key)) {
        const options = { region: region }

        if (credentials) options.credentials = credentials

        regions[key] = new S3(options)
    }

    return regions[key]
}

const s3instanceWithEndpoint = (endpoint, credentials) => {
    const key = endpoint || 0

    if (!endpoints.hasOwnProperty(key)) {
        const options = { endpoint: endpoint }

        if (credentials) options.credentials = credentials

        endpoints[key] = new S3(options)
    }

    return endpoints[key]
}

/* define public methods */
const download = (job, settings, src, dest, params, type) => {
    src = src.replace('s3://', 'http://')

    if (src.indexOf('digitaloceanspaces.com') !== -1) {
        throw new Error('nexrender: Digital Ocean Spaces is not yet supported by the package: amazon-s3-uri')
    }

    const parsed = uri(src)
    const file = fs.createWriteStream(dest)

    if (!parsed.bucket) {
        return Promise.reject(new Error('S3 bucket not provided.'))
    }
    if (!parsed.key) {
        return Promise.reject(new Error('S3 key not provided.'))
    }

    return new Promise((resolve, reject) => {
        file.on('close', resolve);

        const awsParams = {
            Bucket: parsed.bucket,
            Key: parsed.key,
        }

        const credentials = getCredentials(params.credentials)

        const s3instance = params.endpoint ?
            s3instanceWithEndpoint(params.endpoint, credentials) :
            s3instanceWithRegion(params.region, credentials)

        s3instance
            .getObject(awsParams)
            .createReadStream()
            .on('error', reject)
            .pipe(file)
    })
}

const upload = (job, settings, src, params, onProgress, onComplete) => {
    const file = fs.createReadStream(src);

    if (!params.endpoint && !params.region) {
        return Promise.reject(new Error('S3 region or endpoint not provided.'))
    }
    if (!params.bucket) {
        return Promise.reject(new Error('S3 bucket not provided.'))
    }
    if (!params.key) {
        return Promise.reject(new Error('S3 key not provided.'))
    }
    if (!params.acl) {
        return Promise.reject(new Error('S3 ACL not provided.'))
    }

    const onUploadProgress = (e) => {
        const progress = Math.ceil(e.loaded / e.total * 100)
        if (typeof onProgress == 'function') {
            onProgress(job, progress);
        }
        settings.logger.log(`[${job.uid}] action-upload: upload progress ${progress}%...`)
    }

    const onUploadComplete = (file) => {
        if (typeof onComplete == 'function') {
            onComplete(job, file);
        }
        settings.logger.log(`[${job.uid}] action-upload: upload complete: ${file}`)
    }

    const output = params.endpoint ?
        `${params.endpoint}/${params.bucket}/${params.key}` :
        `https://s3-${params.region}.amazonaws.com/${params.bucket}/${params.key}`;
    settings.logger.log(`[${job.uid}] action-upload: input file ${src}`)
    settings.logger.log(`[${job.uid}] action-upload: output file ${output}`)

    return new Promise((resolve, reject) => {
        file.on('error', (err) => reject(err))

        const awsParams = {
            Bucket: params.bucket,
            Key: params.key,
            ACL: params.acl,
            Body: file,
        }
        if (params.metadata) awsParams.Metadata = params.metadata;

        const credentials = getCredentials(params.credentials)

        const s3instance = params.endpoint ?
            s3instanceWithEndpoint(params.endpoint, credentials) :
            s3instanceWithRegion(params.region, credentials)

        s3instance
            .upload(awsParams, (err, data) => {
                if (err) {
                    reject(err)
                }
                else
                {
                    onUploadComplete(data.Location)
                    resolve()
                }
            })
            .on('httpUploadProgress', onUploadProgress)
    })
}

module.exports = {
    download,
    upload,
}

/* tests */
// download({}, {}, 's3://BUCKET.s3.REGION.amazonaws.com/KEY', 'test.txt')
// download({}, {}, 's3://BUCKET.REGION.digitaloceanspaces.com/KEY', 'test.txt')
<commit_msg>added support param content type on  Action Upload Provider-S3<commit_after>const fs  = require('fs')
const uri = require('amazon-s3-uri')
const AWS = require('aws-sdk/global')
const S3 = require('aws-sdk/clients/s3')

let regions = {}
let endpoints = {}

/* return a credentials object if possible, otherwise return false */
const getCredentials = params => {
    if (params && params.profile) {
        // will throw if the profile is not configured
        return new AWS.SharedIniFileCredentials({ profile: params.profile })
    } else if (params && params.accessKeyId && params.secretAccessKey) {
        return { accessKeyId: params.accessKeyId, secretAccessKey: params.secretAccessKey }
    // Cross-account role access pattern
    } else if (params && params.RoleArn && params.RoleSessionName) {
        return new AWS.ChainableTemporaryCredentials({ params: { ...params }})
    } else if (process.env.AWS_PROFILE) { // prioritize any explicitly set params before env variables
        // will throw if the profile is not configured
        return new AWS.SharedIniFileCredentials({ profile: process.env.AWS_PROFILE })
    } else if (process.env.AWS_ACCESS_KEY && process.env.AWS_SECRET_KEY) {
        return { accessKeyId: process.env.AWS_ACCESS_KEY, secretAccessKey: process.env.AWS_SECRET_KEY }
    }
}

/* create or get api instance with region */
const s3instanceWithRegion = (region, credentials) => {
    const key = region || 0

    if (!regions.hasOwnProperty(key)) {
        const options = { region: region }

        if (credentials) options.credentials = credentials

        regions[key] = new S3(options)
    }

    return regions[key]
}

const s3instanceWithEndpoint = (endpoint, credentials) => {
    const key = endpoint || 0

    if (!endpoints.hasOwnProperty(key)) {
        const options = { endpoint: endpoint }

        if (credentials) options.credentials = credentials

        endpoints[key] = new S3(options)
    }

    return endpoints[key]
}

/* define public methods */
const download = (job, settings, src, dest, params, type) => {
    src = src.replace('s3://', 'http://')

    if (src.indexOf('digitaloceanspaces.com') !== -1) {
        throw new Error('nexrender: Digital Ocean Spaces is not yet supported by the package: amazon-s3-uri')
    }

    const parsed = uri(src)
    const file = fs.createWriteStream(dest)

    if (!parsed.bucket) {
        return Promise.reject(new Error('S3 bucket not provided.'))
    }
    if (!parsed.key) {
        return Promise.reject(new Error('S3 key not provided.'))
    }

    return new Promise((resolve, reject) => {
        file.on('close', resolve);

        const awsParams = {
            Bucket: parsed.bucket,
            Key: parsed.key,
        }

        const credentials = getCredentials(params.credentials)

        const s3instance = params.endpoint ?
            s3instanceWithEndpoint(params.endpoint, credentials) :
            s3instanceWithRegion(params.region, credentials)

        s3instance
            .getObject(awsParams)
            .createReadStream()
            .on('error', reject)
            .pipe(file)
    })
}

const upload = (job, settings, src, params, onProgress, onComplete) => {
    const file = fs.createReadStream(src);

    if (!params.endpoint && !params.region) {
        return Promise.reject(new Error('S3 region or endpoint not provided.'))
    }
    if (!params.bucket) {
        return Promise.reject(new Error('S3 bucket not provided.'))
    }
    if (!params.key) {
        return Promise.reject(new Error('S3 key not provided.'))
    }
    if (!params.acl) {
        return Promise.reject(new Error('S3 ACL not provided.'))
    }

    const onUploadProgress = (e) => {
        const progress = Math.ceil(e.loaded / e.total * 100)
        if (typeof onProgress == 'function') {
            onProgress(job, progress);
        }
        settings.logger.log(`[${job.uid}] action-upload: upload progress ${progress}%...`)
    }

    const onUploadComplete = (file) => {
        if (typeof onComplete == 'function') {
            onComplete(job, file);
        }
        settings.logger.log(`[${job.uid}] action-upload: upload complete: ${file}`)
    }

    const output = params.endpoint ?
        `${params.endpoint}/${params.bucket}/${params.key}` :
        `https://s3-${params.region}.amazonaws.com/${params.bucket}/${params.key}`;
    settings.logger.log(`[${job.uid}] action-upload: input file ${src}`)
    settings.logger.log(`[${job.uid}] action-upload: output file ${output}`)

    return new Promise((resolve, reject) => {
        file.on('error', (err) => reject(err))

        const awsParams = {
            Bucket: params.bucket,
            Key: params.key,
            ACL: params.acl,
            Body: file,
            ContentType: params.contentType ?? "application/octet-stream"
        }
        if (params.metadata) awsParams.Metadata = params.metadata;

        const credentials = getCredentials(params.credentials)

        const s3instance = params.endpoint ?
            s3instanceWithEndpoint(params.endpoint, credentials) :
            s3instanceWithRegion(params.region, credentials)

        s3instance
            .upload(awsParams, (err, data) => {
                if (err) {
                    reject(err)
                }
                else
                {
                    onUploadComplete(data.Location)
                    resolve()
                }
            })
            .on('httpUploadProgress', onUploadProgress)
    })
}

module.exports = {
    download,
    upload,
}

/* tests */
// download({}, {}, 's3://BUCKET.s3.REGION.amazonaws.com/KEY', 'test.txt')
// download({}, {}, 's3://BUCKET.REGION.digitaloceanspaces.com/KEY', 'test.txt')
<|endoftext|>


<commit_before>var _ = require("lodash");
var λ = require("contra");
var DB = require("./DB");
var cocb = require("co-callback");
var runKRL = require("./runKRL");
var Modules = require("./modules");
var PicoQueue = require("./PicoQueue");
var krl_stdlib = require("krl-stdlib");
var KRLClosure = require("./KRLClosure");
var SymbolTable = require("symbol-table");
var EventEmitter = require("events");
var processEvent = require("./processEvent");
var processQuery = require("./processQuery");

var log_levels = {
    "info": true,
    "debug": true,
    "warn": true,
    "error": true,
};

module.exports = function(conf, callback){
    var db = DB(conf.db);
    _.each(db, function(val, key){
        if(_.isFunction(val)){
            db[key + "Yieldable"] = cocb.toYieldable(val);
        }
    });
    var host = conf.host;
    var compileAndLoadRuleset = conf.compileAndLoadRuleset;

    var rulesets = {};
    var salience_graph = {};
    var keys_module_data = {};

    var core = {
        db: db,
        host: host,
        rulesets: rulesets,
        salience_graph: salience_graph,
        registerRulesetSrc: registerRulesetSrc,
    };

    var emitter = new EventEmitter();
    var modules = Modules(core);

    var mkCTX = function(ctx){
        ctx.getMyKey = function(id){
            var rid = ctx.rid;
            return _.get(keys_module_data, ["used_keys", rid, id]);
        };

        ctx.modules = modules;
        ctx.KRLClosure = KRLClosure;
        ctx.emit = function(type, val, message){//for stdlib
            var info = {};
            if(ctx.rid){
                info.rid = ctx.rid;
            }
            if(ctx.pico_id){
                info.pico_id = ctx.pico_id;
            }
            if(ctx.event){
                info.event = {
                    eci: ctx.event.eci,
                    eid: ctx.event.eid,
                    domain: ctx.event.domain,
                    type: ctx.event.type,
                };
                if(!info.eci){
                    info.eci = ctx.event.eci;
                }
            }
            if(ctx.query){
                info.query = {
                    eci: ctx.query.eci,
                    rid: ctx.query.rid,
                    name: ctx.query.name,
                    args: ctx.query.args
                };
                if(!info.rid){
                    info.rid = ctx.query.rid;
                }
                if(!info.eci){
                    info.eci = ctx.query.eci;
                }
            }
            emitter.emit(type, info, val, message);
        };
        ctx.log = function(level, val){
            if(!_.has(log_levels, level)){
                throw new Error("Unsupported log level: " + level);
            }
            //this 'log-' prefix distinguishes user declared log events from other system generated events
            ctx.emit("log-" + level, val);
        };
        ctx.callKRLstdlib = function(fn_name){
            var args = _.toArray(arguments);
            args[0] = ctx;
            var fn = krl_stdlib[fn_name];
            if(cocb.isGeneratorFunction(fn)){
                return cocb.promiseRun(function*(){
                    return yield fn.apply(void 0, args);
                });
            }
            return new Promise(function(resolve, reject){
                try{
                    resolve(fn.apply(void 0, args));
                }catch(err){
                    reject(err);
                }
            });
        };

        //don't allow anyone to mutate ctx on the fly
        Object.freeze(ctx);
        return ctx;
    };
    core.mkCTX = mkCTX;

    var initializeRulest = cocb.wrap(function*(rs, loadDepRS){
        rs.scope = SymbolTable();
        var ctx = mkCTX({
            rid: rs.rid,
            scope: rs.scope
        });
        if(_.isFunction(rs.meta && rs.meta.configure)){
            yield runKRL(rs.meta.configure, ctx);
        }
        if(_.isFunction(rs.global)){
            yield runKRL(rs.global, ctx);
        }
        rs.modules_used = {};
        var use_array = _.values(rs.meta && rs.meta.use);
        var i, use, dep_rs, ctx2;
        for(i = 0; i < use_array.length; i++){
            use = use_array[i];
            if(use.kind !== "module"){
                throw new Error("Unsupported 'use' kind: " + use.kind);
            }
            dep_rs = loadDepRS(use.rid);
            if(!dep_rs){
                throw new Error("Dependant module not loaded: " + use.rid);
            }
            ctx2 = mkCTX({
                rid: dep_rs.rid,
                scope: SymbolTable()
            });
            if(_.isFunction(dep_rs.meta && dep_rs.meta.configure)){
                yield runKRL(dep_rs.meta.configure, ctx2);
            }
            if(_.isFunction(use["with"])){
                yield runKRL(use["with"], mkCTX({
                    rid: rs.rid,//switch rid
                    scope: ctx2.scope//must share scope
                }));
            }
            if(_.isFunction(dep_rs.global)){
                yield runKRL(dep_rs.global, ctx2);
            }
            rs.modules_used[use.alias] = {
                rid: use.rid,
                scope: ctx2.scope,
                provides: dep_rs.meta.provides
            };
            if(_.has(keys_module_data, ["provided", use.rid, rs.rid])){
                _.set(keys_module_data, [
                    "used_keys",
                    rs.rid,
                ], keys_module_data.provided[use.rid][rs.rid]);
            }
        }
    });

    var registerRuleset = function(rs, loadDepRS, callback){
        cocb.run(initializeRulest(rs, loadDepRS), function(err){
            if(err) return callback(err);

            if(true
                && _.has(rs, "meta.keys")
                && _.has(rs, "meta.provides_keys")
            ){
                _.each(rs.meta.provides_keys, function(p, key){
                    _.each(p.to, function(to_rid){
                        _.set(keys_module_data, [
                            "provided",
                            rs.rid,
                            to_rid,
                            key
                        ], _.cloneDeep(rs.meta.keys[key]));
                    });
                });
            }

            if(_.has(rs, "meta.keys")){
                //"remove" keys so they don't leak out
                //don't use delete b/c it mutates the loaded rs
                rs = _.assign({}, rs, {
                    meta: _.omit(rs.meta, "keys")
                });
            }

            //now setup `salience_graph` and `rulesets`
            _.each(rs.rules, function(rule){
                rule.rid = rs.rid;
                _.each(rule.select && rule.select.graph, function(g, domain){
                    _.each(g, function(exprs, type){
                        _.set(salience_graph, [domain, type, rule.rid, rule.name], true);
                    });
                });
            });
            rulesets[rs.rid] = rs;

            callback();
        });
    };

    var getRulesetForRID = function(rid, callback){
        db.getEnabledRuleset(rid, function(err, data){
            if(err) return callback(err);
            compileAndLoadRuleset({
                rid: rid,
                src: data.src,
                hash: data.hash
            }, function(err, rs){
                if(err){
                    db.disableRuleset(rid, function(){
                        callback(err);
                    });
                    return;
                }
                callback(void 0, rs);
            });
        });
    };

    var registerRulesetSrc = function(krl_src, meta_data, callback){
        db.storeRuleset(krl_src, meta_data, function(err, hash){
            if(err) return callback(err);
            compileAndLoadRuleset({
                src: krl_src,
                hash: hash
            }, function(err, rs){
                if(err) return callback(err);
                db.enableRuleset(hash, function(err){
                    if(err) return callback(err);
                    registerRuleset(rs, function(rid){
                        return rulesets[rid];
                    }, function(err){
                        if(err){
                            db.disableRuleset(rs.rid, _.noop);//undo enable if failed
                        }
                        callback(err, {
                            rid: rs.rid,
                            hash: hash
                        });
                    });
                });
            });
        });
    };

    var picoQ = PicoQueue(function(pico_id, data, callback){
        if(data.type === "event"){
            var event = data.event;
            event.timestamp = new Date(event.timestamp);//convert from JSON string to date
            processEvent(core, mkCTX({
                event: event,
                pico_id: pico_id
            }), function(err, data){
                if(err) return callback(err);
                if(_.has(data, "event:send")){
                    _.each(data["event:send"], function(o){
                        signalEvent(o.event);
                    });
                    data = _.omit(data, "event:send");
                }
                callback(void 0, data);
            });
        }else if(data.type === "query"){
            processQuery(core, mkCTX({
                query: data.query,
                pico_id: pico_id
            }), callback);
        }else{
            callback(new Error("invalid PicoQueue type:" + data.type));
        }
    });

    var signalEvent = function(event, callback){
        if(!_.isDate(event.timestamp) || !conf.allow_event_time_override){
            event.timestamp = new Date();
        }

        var emit = mkCTX({event: event}).emit;
        emit("episode_start");
        emit("debug", "event received: " + event.domain + "/" + event.type);

        if(!_.isFunction(callback)){
            //if interal signalEvent or just no callback was given...
            callback = function(err, resp){
                if(err){
                    emit("error", err);
                }else{
                    emit("debug", resp);
                }
            };
        }

        db.getPicoIDByECI(event.eci, function(err, pico_id){
            if(err) return callback(err);
            picoQ.enqueue(pico_id, {
                type: "event",
                event: event
            }, callback);
            emit("debug", "event added to pico queue: " + pico_id);
        });
    };

    var runQuery = function(query, callback){
        var emit = mkCTX({query: query}).emit;
        emit("episode_start");
        emit("debug", "query received: " + query.rid + "/" + query.name);

        db.getPicoIDByECI(query.eci, function(err, pico_id){
            if(err) return callback(err);
            var emit = mkCTX({
                query: query,
                pico_id: pico_id,
            }).emit;
            picoQ.enqueue(pico_id, {
                type: "query",
                query: query
            }, function(err, data){
                emit("episode_stop");
                callback(err, data);
            });
            emit("debug", "query added to pico queue: " + pico_id);
        });
    };

    var registerAllEnabledRulesets = function(callback){
        db.getAllEnabledRulesets(function(err, rids){
            if(err)return callback(err);
            λ.map(rids, getRulesetForRID, function(err, rs_list){
                if(err)return callback(err);
                var rs_by_rid = {};
                _.each(rs_list, function(rs){
                    rs_by_rid[rs.rid] = rs;
                });
                var loadDepRS = function(rid){
                    return rs_by_rid[rid];
                };
                λ.each.series(rs_list, function(rs, next){
                    registerRuleset(rs, loadDepRS, next);
                }, callback);
            });
        });
    };

    registerAllEnabledRulesets(function(err){
        if(err) return callback(err);
        callback(void 0, {
            db: db,
            emitter: emitter,
            registerRuleset: registerRulesetSrc,
            signalEvent: signalEvent,
            runQuery: runQuery
        });
    });
};
<commit_msg>cleanup<commit_after>var _ = require("lodash");
var λ = require("contra");
var DB = require("./DB");
var cocb = require("co-callback");
var runKRL = require("./runKRL");
var Modules = require("./modules");
var PicoQueue = require("./PicoQueue");
var krl_stdlib = require("krl-stdlib");
var KRLClosure = require("./KRLClosure");
var SymbolTable = require("symbol-table");
var EventEmitter = require("events");
var processEvent = require("./processEvent");
var processQuery = require("./processQuery");

var log_levels = {
    "info": true,
    "debug": true,
    "warn": true,
    "error": true,
};

module.exports = function(conf, callback){
    var db = DB(conf.db);
    _.each(db, function(val, key){
        if(_.isFunction(val)){
            db[key + "Yieldable"] = cocb.toYieldable(val);
        }
    });
    var host = conf.host;
    var compileAndLoadRuleset = conf.compileAndLoadRuleset;

    var rulesets = {};
    var salience_graph = {};
    var keys_module_data = {};

    var core = {
        db: db,
        host: host,
        rulesets: rulesets,
        salience_graph: salience_graph,
        registerRulesetSrc: registerRulesetSrc,
    };

    var emitter = new EventEmitter();
    var modules = Modules(core);

    var mkCTX = function(ctx){
        ctx.getMyKey = (function(rid){
            //we do it this way so all the keys are not leaked out to other built in modules or rulesets
            return function(id){
                return _.get(keys_module_data, ["used_keys", rid, id]);
            };
        }(ctx.rid));//pass in the rid at mkCTX creation so it is not later mutated

        ctx.modules = modules;
        ctx.KRLClosure = KRLClosure;
        ctx.emit = function(type, val, message){//for stdlib
            var info = {};
            if(ctx.rid){
                info.rid = ctx.rid;
            }
            if(ctx.pico_id){
                info.pico_id = ctx.pico_id;
            }
            if(ctx.event){
                info.event = {
                    eci: ctx.event.eci,
                    eid: ctx.event.eid,
                    domain: ctx.event.domain,
                    type: ctx.event.type,
                };
                if(!info.eci){
                    info.eci = ctx.event.eci;
                }
            }
            if(ctx.query){
                info.query = {
                    eci: ctx.query.eci,
                    rid: ctx.query.rid,
                    name: ctx.query.name,
                    args: ctx.query.args
                };
                if(!info.rid){
                    info.rid = ctx.query.rid;
                }
                if(!info.eci){
                    info.eci = ctx.query.eci;
                }
            }
            emitter.emit(type, info, val, message);
        };
        ctx.log = function(level, val){
            if(!_.has(log_levels, level)){
                throw new Error("Unsupported log level: " + level);
            }
            //this 'log-' prefix distinguishes user declared log events from other system generated events
            ctx.emit("log-" + level, val);
        };
        ctx.callKRLstdlib = function(fn_name){
            var args = _.toArray(arguments);
            args[0] = ctx;
            var fn = krl_stdlib[fn_name];
            if(cocb.isGeneratorFunction(fn)){
                return cocb.promiseRun(function*(){
                    return yield fn.apply(void 0, args);
                });
            }
            return new Promise(function(resolve, reject){
                try{
                    resolve(fn.apply(void 0, args));
                }catch(err){
                    reject(err);
                }
            });
        };

        //don't allow anyone to mutate ctx on the fly
        Object.freeze(ctx);
        return ctx;
    };
    core.mkCTX = mkCTX;

    var initializeRulest = cocb.wrap(function*(rs, loadDepRS){
        rs.scope = SymbolTable();
        var ctx = mkCTX({
            rid: rs.rid,
            scope: rs.scope
        });
        if(_.isFunction(rs.meta && rs.meta.configure)){
            yield runKRL(rs.meta.configure, ctx);
        }
        if(_.isFunction(rs.global)){
            yield runKRL(rs.global, ctx);
        }
        rs.modules_used = {};
        var use_array = _.values(rs.meta && rs.meta.use);
        var i, use, dep_rs, ctx2;
        for(i = 0; i < use_array.length; i++){
            use = use_array[i];
            if(use.kind !== "module"){
                throw new Error("Unsupported 'use' kind: " + use.kind);
            }
            dep_rs = loadDepRS(use.rid);
            if(!dep_rs){
                throw new Error("Dependant module not loaded: " + use.rid);
            }
            ctx2 = mkCTX({
                rid: dep_rs.rid,
                scope: SymbolTable()
            });
            if(_.isFunction(dep_rs.meta && dep_rs.meta.configure)){
                yield runKRL(dep_rs.meta.configure, ctx2);
            }
            if(_.isFunction(use["with"])){
                yield runKRL(use["with"], mkCTX({
                    rid: rs.rid,//switch rid
                    scope: ctx2.scope//must share scope
                }));
            }
            if(_.isFunction(dep_rs.global)){
                yield runKRL(dep_rs.global, ctx2);
            }
            rs.modules_used[use.alias] = {
                rid: use.rid,
                scope: ctx2.scope,
                provides: dep_rs.meta.provides
            };
            if(_.has(keys_module_data, ["provided", use.rid, rs.rid])){
                _.set(keys_module_data, [
                    "used_keys",
                    rs.rid,
                ], keys_module_data.provided[use.rid][rs.rid]);
            }
        }
    });

    var registerRuleset = function(rs, loadDepRS, callback){
        cocb.run(initializeRulest(rs, loadDepRS), function(err){
            if(err) return callback(err);

            if(true
                && _.has(rs, "meta.keys")
                && _.has(rs, "meta.provides_keys")
            ){
                _.each(rs.meta.provides_keys, function(p, key){
                    _.each(p.to, function(to_rid){
                        _.set(keys_module_data, [
                            "provided",
                            rs.rid,
                            to_rid,
                            key
                        ], _.cloneDeep(rs.meta.keys[key]));
                    });
                });
            }

            if(_.has(rs, "meta.keys")){
                //"remove" keys so they don't leak out
                //don't use delete b/c it mutates the loaded rs
                rs = _.assign({}, rs, {
                    meta: _.omit(rs.meta, "keys")
                });
            }

            //now setup `salience_graph` and `rulesets`
            _.each(rs.rules, function(rule){
                rule.rid = rs.rid;
                _.each(rule.select && rule.select.graph, function(g, domain){
                    _.each(g, function(exprs, type){
                        _.set(salience_graph, [domain, type, rule.rid, rule.name], true);
                    });
                });
            });
            rulesets[rs.rid] = rs;

            callback();
        });
    };

    var getRulesetForRID = function(rid, callback){
        db.getEnabledRuleset(rid, function(err, data){
            if(err) return callback(err);
            compileAndLoadRuleset({
                rid: rid,
                src: data.src,
                hash: data.hash
            }, function(err, rs){
                if(err){
                    db.disableRuleset(rid, function(){
                        callback(err);
                    });
                    return;
                }
                callback(void 0, rs);
            });
        });
    };

    var registerRulesetSrc = function(krl_src, meta_data, callback){
        db.storeRuleset(krl_src, meta_data, function(err, hash){
            if(err) return callback(err);
            compileAndLoadRuleset({
                src: krl_src,
                hash: hash
            }, function(err, rs){
                if(err) return callback(err);
                db.enableRuleset(hash, function(err){
                    if(err) return callback(err);
                    registerRuleset(rs, function(rid){
                        return rulesets[rid];
                    }, function(err){
                        if(err){
                            db.disableRuleset(rs.rid, _.noop);//undo enable if failed
                        }
                        callback(err, {
                            rid: rs.rid,
                            hash: hash
                        });
                    });
                });
            });
        });
    };

    var picoQ = PicoQueue(function(pico_id, data, callback){
        if(data.type === "event"){
            var event = data.event;
            event.timestamp = new Date(event.timestamp);//convert from JSON string to date
            processEvent(core, mkCTX({
                event: event,
                pico_id: pico_id
            }), function(err, data){
                if(err) return callback(err);
                if(_.has(data, "event:send")){
                    _.each(data["event:send"], function(o){
                        signalEvent(o.event);
                    });
                    data = _.omit(data, "event:send");
                }
                callback(void 0, data);
            });
        }else if(data.type === "query"){
            processQuery(core, mkCTX({
                query: data.query,
                pico_id: pico_id
            }), callback);
        }else{
            callback(new Error("invalid PicoQueue type:" + data.type));
        }
    });

    var signalEvent = function(event, callback){
        if(!_.isDate(event.timestamp) || !conf.allow_event_time_override){
            event.timestamp = new Date();
        }

        var emit = mkCTX({event: event}).emit;
        emit("episode_start");
        emit("debug", "event received: " + event.domain + "/" + event.type);

        if(!_.isFunction(callback)){
            //if interal signalEvent or just no callback was given...
            callback = function(err, resp){
                if(err){
                    emit("error", err);
                }else{
                    emit("debug", resp);
                }
            };
        }

        db.getPicoIDByECI(event.eci, function(err, pico_id){
            if(err) return callback(err);
            picoQ.enqueue(pico_id, {
                type: "event",
                event: event
            }, callback);
            emit("debug", "event added to pico queue: " + pico_id);
        });
    };

    var runQuery = function(query, callback){
        var emit = mkCTX({query: query}).emit;
        emit("episode_start");
        emit("debug", "query received: " + query.rid + "/" + query.name);

        db.getPicoIDByECI(query.eci, function(err, pico_id){
            if(err) return callback(err);
            var emit = mkCTX({
                query: query,
                pico_id: pico_id,
            }).emit;
            picoQ.enqueue(pico_id, {
                type: "query",
                query: query
            }, function(err, data){
                emit("episode_stop");
                callback(err, data);
            });
            emit("debug", "query added to pico queue: " + pico_id);
        });
    };

    var registerAllEnabledRulesets = function(callback){
        db.getAllEnabledRulesets(function(err, rids){
            if(err)return callback(err);
            λ.map(rids, getRulesetForRID, function(err, rs_list){
                if(err)return callback(err);
                var rs_by_rid = {};
                _.each(rs_list, function(rs){
                    rs_by_rid[rs.rid] = rs;
                });
                var loadDepRS = function(rid){
                    return rs_by_rid[rid];
                };
                λ.each.series(rs_list, function(rs, next){
                    registerRuleset(rs, loadDepRS, next);
                }, callback);
            });
        });
    };

    registerAllEnabledRulesets(function(err){
        if(err) return callback(err);
        callback(void 0, {
            db: db,
            emitter: emitter,
            registerRuleset: registerRulesetSrc,
            signalEvent: signalEvent,
            runQuery: runQuery
        });
    });
};
<|endoftext|>


<commit_before>/**!
 *
 * Copright(c) 2015 Cisco Systems, Inc. See LICENSE file.
 * @private
 */

import {assert} from '@ciscospark/test-helper-chai';
import Team from '../..';
import MockSpark from '@ciscospark/test-helper-mock-spark';

describe(`plugin-team`, () => {
  describe(`Team`, () => {
    let spark;

    beforeEach(() => {
      spark = new MockSpark({
        children: {
          team: Team
        }
      });
    });

    describe(`#create()`, () => {
      it(`requires a displayName`, () => assert.isRejected(spark.team.create({}), /\`params.displayName\` is required/));

      it(`requires a participants attribute`, () => assert.isRejected(spark.team.create({displayName: `test`}), /\`params.participants\` is required/));

      it(`requires a participants array`, () => assert.isRejected(spark.team.create({displayName: `test`, participants: []}), /\`params.participants\` is required/));
    });

    describe(`#createConversation()`, () => {
      it(`requires a displayName`, () => assert.isRejected(spark.team.createConversation({}, {}), /\`params.displayName\` is required/));

      it(`requires a team object with a general conversation`, () => assert.isRejected(spark.team.createConversation({}, {displayName: `test`}), /\`team.generalConversationUuid\` must be present/));
    });
  });
});
<commit_msg>test(plugin-team): Add unit test coverage for teams.js<commit_after>/**!
 *
 * Copright(c) 2015 Cisco Systems, Inc. See LICENSE file.
 * @private
 */

import {assert} from '@ciscospark/test-helper-chai';
import MockSpark from '@ciscospark/test-helper-mock-spark';
import sinon from '@ciscospark/test-helper-sinon';
import Team from '../..';
import User from '@ciscospark/plugin-user';

describe(`plugin-team`, () => {
  describe(`Team`, () => {
    let spark;

    beforeEach(() => {
      spark = new MockSpark({
        children: {
          team: Team,
          user: User
        }
      });

      spark.user.recordUUID = sinon.spy();
    });

    describe(`#create()`, () => {
      it(`requires a displayName`, () => assert.isRejected(spark.team.create({}), /\`params.displayName\` is required/));

      it(`requires a participants attribute`, () => assert.isRejected(spark.team.create({displayName: `test`}), /\`params.participants\` is required/));

      it(`requires a participants array`, () => assert.isRejected(spark.team.create({displayName: `test`, participants: []}), /\`params.participants\` is required/));
    });

    describe(`#createConversation()`, () => {
      it(`requires a displayName`, () => assert.isRejected(spark.team.createConversation({}, {}), /\`params.displayName\` is required/));

      it(`requires a team object with a general conversation`, () => assert.isRejected(spark.team.createConversation({}, {displayName: `test`}), /\`team.generalConversationUuid\` must be present/));
    });

    describe(`#prepareTeamConversation()`, () => {
      it(`requires a KRO`, () => assert.isRejected(spark.team._prepareTeamConversation({}), /Error: Team general conversation must have a KRO/));
    });

    describe(`#recordUUIDs`, () => {
      it(`resolves if there are no teamMembers`, () => spark.team._recordUUIDs({})
        .then(() => assert.equal(spark.user.recordUUID.callCount, 0)));

      it(`resolves if there isn't teamMembers.items`, () => spark.team._recordUUIDs({teamMembers: {}})
        .then(() => assert.equal(spark.user.recordUUID.callCount, 0)));
    });
  });
});
<|endoftext|>