<commit_before>// (C) Copyright 2014-2016 Hewlett Packard Enterprise Development LP

import React, { Component } from 'react';
import stringify from 'json-stringify-pretty-compact';
import Split from 'grommet/components/Split';
import Sidebar from 'grommet/components/Sidebar';
import Header from 'grommet/components/Header';
import Footer from 'grommet/components/Footer';
import Heading from 'grommet/components/Heading';
import Form from 'grommet/components/Form';
import FormFields from 'grommet/components/FormFields';
import FormField from 'grommet/components/FormField';
import CheckBox from 'grommet/components/CheckBox';
import Box from 'grommet/components/Box';
import Value from 'grommet/components/Value';
import Label from 'grommet/components/Label';
import Button from 'grommet/components/Button';
import CloseIcon from 'grommet/components/icons/base/Close';
import LinkPreviousIcon from 'grommet/components/icons/base/LinkPrevious';
import DocsArticle from '../../../components/DocsArticle';
import NavAnchor from '../../../components/NavAnchor';
import Example from '../../Example';

const FIXED_SIZES = ['xsmall', 'small', 'medium', 'large', 'xlarge', 'xxlarge'];
const RELATIVE_SIZES = ['full', '1/2', '1/3', '2/3', '1/4', '3/4'];
const SIZES = FIXED_SIZES.concat(RELATIVE_SIZES);
const MARGIN_SIZES = ['small', 'medium', 'large', 'none'];
const PAD_SIZES = ['small', 'medium', 'large', 'none'];
const DEFAULT_BOX = {
  direction: 'row', align: 'center', justify: 'start',
  margin: 'small', pad: 'medium',
  wrap: true
};
const INITIAL_COUNT = 4;
const COLOR_INDEXES = ['light-2', 'light-1'];

export default class BoxingGymDoc extends Component {

  constructor () {
    super();

    this._onResponsive = this._onResponsive.bind(this);

    let nextId = 1;
    let boxes = {};

    let root = { ...DEFAULT_BOX,
      id: nextId++, colorIndex: COLOR_INDEXES[0], childIds: [] };
    boxes[root.id] = root;
    [...Array(INITIAL_COUNT)].forEach(() => {
      const child = { ...DEFAULT_BOX,
        id: nextId++,
        colorIndex: COLOR_INDEXES[1],
        parentId: root.id,
        childIds: []
      };
      root.childIds.push(child.id);
      boxes[child.id] = child;
    });

    this.state = {
      boxes: boxes, nextId: nextId,
      rootId: root.id, priority: 'left', activeId: root.id
    };
  }

  _onResponsive (responsive) {
    this.setState({ responsive: responsive, priority: 'left' });
  }

  _activate (id) {
    return (event) => {
      event.stopPropagation();
      this.setState({ activeId: id, priority: 'right', raw: false });
    };
  }

  _changeBoxProp (id, prop) {
    return (event) => {
      let value = event.target.value || undefined;
      if ('true' === value) {
        value = true;
      } else if ('false' === value) {
        value = false;
      }
      let boxes = { ...this.state.boxes };
      boxes[id][prop] = value;
      this.setState({ boxes: boxes });
    };
  }

  _toggleBoxProp (id, prop) {
    return () => {
      let boxes = { ...this.state.boxes };
      boxes[id][prop] = ! boxes[id][prop];
      this.setState({ boxes: boxes });
    };
  }

  _setPeers (id) {
    return () => {
      let nextBoxes = { ...this.state.boxes };
      const master = nextBoxes[id];
      // get parent and update all parent's children
      nextBoxes[master.parentId].childIds.forEach(childId => {
        let child = nextBoxes[childId];
        nextBoxes[childId] = { ...master, childIds: child.childIds };
      });
      this.setState({ boxes: nextBoxes });
    };
  }

  _removeBox (id, boxes=undefined) {
    return () => {
      let nextBoxes = boxes || { ...this.state.boxes };
      const box = nextBoxes[id];
      box.childIds.forEach(childId => this._removeBox(childId, nextBoxes)());
      const parent = nextBoxes[box.parentId];
      parent.childIds.splice(parent.childIds.indexOf(id), 1);
      delete nextBoxes[id];
      if (! boxes) { // only from the initial context
        this.setState({ boxes: nextBoxes, activeId: box.parentId });
      }
    };
  }

  _depth (id) {
    const box = this.state.boxes[id];
    return (box.parentId ? this._depth(box.parentId) : 0) + 1;
  }

  _addBox (parentId) {
    return () => {
      let nextBoxes = { ...this.state.boxes };
      let nextId = this.state.nextId;
      let colorIndex =
        COLOR_INDEXES[this._depth(parentId) % COLOR_INDEXES.length];
      const box = { ...DEFAULT_BOX, id: nextId++, colorIndex: colorIndex,
        parentId: parentId, childIds: [] };
      nextBoxes[box.id] = box;
      nextBoxes[parentId].childIds.push(box.id);
      this.setState({ boxes: nextBoxes, nextId: nextId });
    };
  }

  _renderForm () {
    const { activeId, boxes, responsive } = this.state;
    const box = boxes[activeId] || { ...DEFAULT_BOX };

    let close;
    if ('single' === responsive) {
      close = (
        <Button icon={<CloseIcon />}
          onClick={() => this.setState({ priority: 'left' })} />
      );
    }

    let basisOptions = SIZES.map(size => <option key={size}>{size}</option>);
    basisOptions.unshift(<option key={0}></option>);

    let padOptions = PAD_SIZES.map(size => <option key={size}>{size}</option>);
    padOptions.unshift(<option key={0}></option>);

    let marginOptions =
      MARGIN_SIZES.map(size => <option key={size}>{size}</option>);
    marginOptions.unshift(<option key={0}></option>);

    let containedFields;
    if (box.parentId) {
      containedFields = (
        <fieldset>
          <FormField label="basis">
            <select value={box.basis || ''}
              onChange={this._changeBoxProp(box.id, 'basis')}>
              {basisOptions}
            </select>
          </FormField>
          <FormField label="flex">
            <select value={box.flex || ''}
              onChange={this._changeBoxProp(box.id, 'flex')}>
              <option></option>
              <option>grow</option>
              <option>shrink</option>
              <option value={true}>true</option>
              <option value={false}>false</option>
            </select>
          </FormField>
        </fieldset>
      );
    }

    let containerFields;
    if (box.childIds.length > 0) {
      containerFields = (
        <fieldset>
          <FormField label="direction">
            <select value={box.direction}
              onChange={this._changeBoxProp(box.id, 'direction')}>
              <option>column</option>
              <option>row</option>
            </select>
          </FormField>
          <FormField>
            <CheckBox label="wrap" checked={box.wrap || false}
              onChange={this._toggleBoxProp(box.id, 'direction')} />
          </FormField>
          <FormField>
            <CheckBox label="reverse" checked={box.reverse || false}
              onChange={this._toggleBoxProp(box.id, 'reverse')} />
          </FormField>
        </fieldset>
      );
    }

    let buttons = [];
    if (box.parentId) {
      buttons.push(
        <Button key="peers" label="Set peers" secondary={true}
          onClick={this._setPeers(box.id)} />
      );
      buttons.push(
        <Button key="remove" label="Remove" secondary={true}
          onClick={this._removeBox(box.id)} />
      );
    }

    return (
      <Form>

          <Heading tag="h3">{`Box ${box.id}`}</Heading>
          {close}

        <FormFields>
          {containerFields}
          {containedFields}
          <fieldset>
            <FormField label="justify">
              <select value={box.justify}
                onChange={this._changeBoxProp(box.id, 'justify')}>
                <option>start</option>
                <option>center</option>
                <option>between</option>
                <option>end</option>
              </select>
            </FormField>
            <FormField label="align">
              <select value={box.align}
                onChange={this._changeBoxProp(box.id, 'align')}>
                <option>stretch</option>
                <option>start</option>
                <option>center</option>
                <option>baseline</option>
                <option>end</option>
              </select>
            </FormField>
            <FormField label="pad">
              <select value={box.pad || ''}
                onChange={this._changeBoxProp(box.id, 'pad')}>
                {padOptions}
              </select>
            </FormField>
            <FormField label="margin">
              <select value={box.margin || ''}
                onChange={this._changeBoxProp(box.id, 'margin')}>
                {marginOptions}
              </select>
            </FormField>
          </fieldset>
        </FormFields>

          {buttons}
          <Button label="Add" secondary={true}
            onClick={this._addBox(box.id)} />

      </Form>
    );
  }

  _renderRaw () {
    const { boxes, rawBoxes } = this.state;
    return (
      <Form>

          <Heading tag="h3">Raw</Heading>
          <Button icon={<CloseIcon />}
            onClick={() => {
              this.setState({ raw: false, rawBoxes: undefined });
            }} />

        <FormFields>
          <FormField>
            <textarea rows="20" value={rawBoxes || stringify(boxes)}
              onChange={(event) => {
                this.setState({ rawBoxes: event.target.value });
              }} />
          </FormField>
        </FormFields>

          <Button label="Submit" primary={true}
            onClick={() => {
              const nextBoxes = JSON.parse(this.state.rawBoxes);
              this.setState({
                boxes: nextBoxes, raw: false, rawBoxes: undefined,
                activeId: this.state.rootId
              });
            }} />

      </Form>
    );
  }

  _renderBox (id) {
    const box = this.state.boxes[id];
    const {
      colorIndex, direction, justify, align, pad, margin, reverse, wrap
    } = box;
    let contents = box.childIds.map(childId => this._renderBox(childId));
    if (! contents.length) {
      contents = <Value value={`Box ${id}`} />;
    } else {
      contents.unshift(
        {`Box ${id}`}
      );
    }
    return (
      <Box key={id} direction={direction} justify={justify} align={align}
        wrap={wrap} reverse={reverse}
        pad={pad} margin={margin} colorIndex={colorIndex}
        onClick={this._activate(id)} onFocus={this._activate(id)}>
        {contents}
      </Box>
    );
  }

  render () {
    const { activeId, priority, raw, rootId } = this.state;

    const form = raw ? this._renderRaw() : this._renderForm();
    const box = this._renderBox(rootId);

    let rawControl;
    if (! box.parentId && ! raw && activeId === rootId) {
      rawControl = (
        <Box pad="medium">
          <Button label="Raw" plain={true}
            onClick={() => this.setState({ raw: true })} />
        </Box>
      );
    }

    return (
      <Split flex="left" priority={priority} onResponsive={this._onResponsive}>
        <DocsArticle title="Boxing Gym" colorIndex="neutral-3">

This is a boxing gym where you can spar
            with <NavAnchor path="/docs/box">Boxes</NavAnchor>.

          <Example code={box}/>

            <NavAnchor path="/docs/box" icon={<LinkPreviousIcon />}
              label="Box" primary={true} />

        </DocsArticle>
        <Sidebar separator="left" colorIndex="light-2">
          {form}
          {rawControl}
        </Sidebar>
      </Split>
    );
  }
};
<commit_msg>Fixed Boxing Gym wrap property setting issue.<commit_after>// (C) Copyright 2014-2016 Hewlett Packard Enterprise Development LP

import React, { Component } from 'react';
import stringify from 'json-stringify-pretty-compact';
import Split from 'grommet/components/Split';
import Sidebar from 'grommet/components/Sidebar';
import Header from 'grommet/components/Header';
import Footer from 'grommet/components/Footer';
import Heading from 'grommet/components/Heading';
import Form from 'grommet/components/Form';
import FormFields from 'grommet/components/FormFields';
import FormField from 'grommet/components/FormField';
import CheckBox from 'grommet/components/CheckBox';
import Box from 'grommet/components/Box';
import Value from 'grommet/components/Value';
import Label from 'grommet/components/Label';
import Button from 'grommet/components/Button';
import CloseIcon from 'grommet/components/icons/base/Close';
import LinkPreviousIcon from 'grommet/components/icons/base/LinkPrevious';
import DocsArticle from '../../../components/DocsArticle';
import NavAnchor from '../../../components/NavAnchor';
import Example from '../../Example';

const FIXED_SIZES = ['xsmall', 'small', 'medium', 'large', 'xlarge', 'xxlarge'];
const RELATIVE_SIZES = ['full', '1/2', '1/3', '2/3', '1/4', '3/4'];
const SIZES = FIXED_SIZES.concat(RELATIVE_SIZES);
const MARGIN_SIZES = ['small', 'medium', 'large', 'none'];
const PAD_SIZES = ['small', 'medium', 'large', 'none'];
const DEFAULT_BOX = {
  direction: 'row', align: 'center', justify: 'start',
  margin: 'small', pad: 'medium',
  wrap: true
};
const INITIAL_COUNT = 4;
const COLOR_INDEXES = ['light-2', 'light-1'];

export default class BoxingGymDoc extends Component {

  constructor () {
    super();

    this._onResponsive = this._onResponsive.bind(this);

    let nextId = 1;
    let boxes = {};

    let root = { ...DEFAULT_BOX,
      id: nextId++, colorIndex: COLOR_INDEXES[0], childIds: [] };
    boxes[root.id] = root;
    [...Array(INITIAL_COUNT)].forEach(() => {
      const child = { ...DEFAULT_BOX,
        id: nextId++,
        colorIndex: COLOR_INDEXES[1],
        parentId: root.id,
        childIds: []
      };
      root.childIds.push(child.id);
      boxes[child.id] = child;
    });

    this.state = {
      boxes: boxes, nextId: nextId,
      rootId: root.id, priority: 'left', activeId: root.id
    };
  }

  _onResponsive (responsive) {
    this.setState({ responsive: responsive, priority: 'left' });
  }

  _activate (id) {
    return (event) => {
      event.stopPropagation();
      this.setState({ activeId: id, priority: 'right', raw: false });
    };
  }

  _changeBoxProp (id, prop) {
    return (event) => {
      let value = event.target.value || undefined;
      if ('true' === value) {
        value = true;
      } else if ('false' === value) {
        value = false;
      }
      let boxes = { ...this.state.boxes };
      boxes[id][prop] = value;
      this.setState({ boxes: boxes });
    };
  }

  _toggleBoxProp (id, prop) {
    return () => {
      let boxes = { ...this.state.boxes };
      boxes[id][prop] = ! boxes[id][prop];
      this.setState({ boxes: boxes });
    };
  }

  _setPeers (id) {
    return () => {
      let nextBoxes = { ...this.state.boxes };
      const master = nextBoxes[id];
      // get parent and update all parent's children
      nextBoxes[master.parentId].childIds.forEach(childId => {
        let child = nextBoxes[childId];
        nextBoxes[childId] = { ...master, childIds: child.childIds };
      });
      this.setState({ boxes: nextBoxes });
    };
  }

  _removeBox (id, boxes=undefined) {
    return () => {
      let nextBoxes = boxes || { ...this.state.boxes };
      const box = nextBoxes[id];
      box.childIds.forEach(childId => this._removeBox(childId, nextBoxes)());
      const parent = nextBoxes[box.parentId];
      parent.childIds.splice(parent.childIds.indexOf(id), 1);
      delete nextBoxes[id];
      if (! boxes) { // only from the initial context
        this.setState({ boxes: nextBoxes, activeId: box.parentId });
      }
    };
  }

  _depth (id) {
    const box = this.state.boxes[id];
    return (box.parentId ? this._depth(box.parentId) : 0) + 1;
  }

  _addBox (parentId) {
    return () => {
      let nextBoxes = { ...this.state.boxes };
      let nextId = this.state.nextId;
      let colorIndex =
        COLOR_INDEXES[this._depth(parentId) % COLOR_INDEXES.length];
      const box = { ...DEFAULT_BOX, id: nextId++, colorIndex: colorIndex,
        parentId: parentId, childIds: [] };
      nextBoxes[box.id] = box;
      nextBoxes[parentId].childIds.push(box.id);
      this.setState({ boxes: nextBoxes, nextId: nextId });
    };
  }

  _renderForm () {
    const { activeId, boxes, responsive } = this.state;
    const box = boxes[activeId] || { ...DEFAULT_BOX };

    let close;
    if ('single' === responsive) {
      close = (
        <Button icon={<CloseIcon />}
          onClick={() => this.setState({ priority: 'left' })} />
      );
    }

    let basisOptions = SIZES.map(size => <option key={size}>{size}</option>);
    basisOptions.unshift(<option key={0} />);

    let padOptions = PAD_SIZES.map(size => <option key={size}>{size}</option>);
    padOptions.unshift(<option key={0} />);

    let marginOptions =
      MARGIN_SIZES.map(size => <option key={size}>{size}</option>);
    marginOptions.unshift(<option key={0} />);

    let containedFields;
    if (box.parentId) {
      containedFields = (
        <fieldset>
          <FormField label="basis">
            <select value={box.basis || ''}
              onChange={this._changeBoxProp(box.id, 'basis')}>
              {basisOptions}
            </select>
          </FormField>
          <FormField label="flex">
            <select value={box.flex || ''}
              onChange={this._changeBoxProp(box.id, 'flex')}>
              <option />
              <option>grow</option>
              <option>shrink</option>
              <option value={true}>true</option>
              <option value={false}>false</option>
            </select>
          </FormField>
        </fieldset>
      );
    }

    let containerFields;
    if (box.childIds.length > 0) {
      containerFields = (
        <fieldset>
          <FormField label="direction">
            <select value={box.direction}
              onChange={this._changeBoxProp(box.id, 'direction')}>
              <option>column</option>
              <option>row</option>
            </select>
          </FormField>
          <FormField>
            <CheckBox label="wrap" checked={box.wrap || false}
              onChange={this._toggleBoxProp(box.id, 'wrap')} />
          </FormField>
          <FormField>
            <CheckBox label="reverse" checked={box.reverse || false}
              onChange={this._toggleBoxProp(box.id, 'reverse')} />
          </FormField>
        </fieldset>
      );
    }

    let buttons = [];
    if (box.parentId) {
      buttons.push(
        <Button key="peers" label="Set peers" secondary={true}
          onClick={this._setPeers(box.id)} />
      );
      buttons.push(
        <Button key="remove" label="Remove" secondary={true}
          onClick={this._removeBox(box.id)} />
      );
    }

    return (
      <Form>

          <Heading tag="h3">{`Box ${box.id}`}</Heading>
          {close}

        <FormFields>
          {containerFields}
          {containedFields}
          <fieldset>
            <FormField label="justify">
              <select value={box.justify}
                onChange={this._changeBoxProp(box.id, 'justify')}>
                <option>start</option>
                <option>center</option>
                <option>between</option>
                <option>end</option>
              </select>
            </FormField>
            <FormField label="align">
              <select value={box.align}
                onChange={this._changeBoxProp(box.id, 'align')}>
                <option>stretch</option>
                <option>start</option>
                <option>center</option>
                <option>baseline</option>
                <option>end</option>
              </select>
            </FormField>
            <FormField label="pad">
              <select value={box.pad || ''}
                onChange={this._changeBoxProp(box.id, 'pad')}>
                {padOptions}
              </select>
            </FormField>
            <FormField label="margin">
              <select value={box.margin || ''}
                onChange={this._changeBoxProp(box.id, 'margin')}>
                {marginOptions}
              </select>
            </FormField>
          </fieldset>
        </FormFields>

          {buttons}
          <Button label="Add" secondary={true}
            onClick={this._addBox(box.id)} />

      </Form>
    );
  }

  _renderRaw () {
    const { boxes, rawBoxes } = this.state;
    return (
      <Form>

          <Heading tag="h3">Raw</Heading>
          <Button icon={<CloseIcon />}
            onClick={() => {
              this.setState({ raw: false, rawBoxes: undefined });
            }} />

        <FormFields>
          <FormField>
            <textarea rows="20" value={rawBoxes || stringify(boxes)}
              onChange={(event) => {
                this.setState({ rawBoxes: event.target.value });
              }} />
          </FormField>
        </FormFields>

          <Button label="Submit" primary={true}
            onClick={() => {
              const nextBoxes = JSON.parse(this.state.rawBoxes);
              this.setState({
                boxes: nextBoxes, raw: false, rawBoxes: undefined,
                activeId: this.state.rootId
              });
            }} />

      </Form>
    );
  }

  _renderBox (id) {
    const box = this.state.boxes[id];
    const {
      colorIndex, direction, justify, align, pad, margin, reverse, wrap
    } = box;
    let contents = box.childIds.map(childId => this._renderBox(childId));
    if (! contents.length) {
      contents = <Value value={`Box ${id}`} />;
    } else {
      contents.unshift(
        {`Box ${id}`}
      );
    }
    return (
      <Box key={id} direction={direction} justify={justify} align={align}
        wrap={wrap} reverse={reverse}
        pad={pad} margin={margin} colorIndex={colorIndex}
        onClick={this._activate(id)} onFocus={this._activate(id)}>
        {contents}
      </Box>
    );
  }

  render () {
    const { activeId, priority, raw, rootId } = this.state;

    const form = raw ? this._renderRaw() : this._renderForm();
    const box = this._renderBox(rootId);

    let rawControl;
    if (! box.parentId && ! raw && activeId === rootId) {
      rawControl = (
        <Box pad="medium">
          <Button label="Raw" plain={true}
            onClick={() => this.setState({ raw: true })} />
        </Box>
      );
    }

    return (
      <Split flex="left" priority={priority} onResponsive={this._onResponsive}>
        <DocsArticle title="Boxing Gym" colorIndex="neutral-3">

This is a boxing gym where you can spar
            with <NavAnchor path="/docs/box">Boxes</NavAnchor>.

          <Example code={box}/>

            <NavAnchor path="/docs/box" icon={<LinkPreviousIcon />}
              label="Box" primary={true} />

        </DocsArticle>
        <Sidebar separator="left" colorIndex="light-2">
          {form}
          {rawControl}
        </Sidebar>
      </Split>
    );
  }
};
<|endoftext|>


<commit_before>'use strict';

var express = require('express');
var router = express.Router();
var extend = require('node.extend');
var redis = require("redis");
var client = redis.createClient();
var request = require('request');
var lxc = require('../lxc');

var timeoutEvents = {};
var ip2name = {};

var lxcTimeout = function(ip, time){
	var name = ip2name[ip];
	console.log(name)
	time = time || 100000;
	var keys = Object.keys(timeoutEvents)
	if(keys.indexOf(name) !== -1){
		clearTimeout(timeoutEvents[name])
	}
	timeoutEvents[name] = setTimeout(function(){
		console.log('killing:', name)
		lxc.stop(name);
	}, time);
}

var runner = function(req, res, ip){
	lxcTimeout(ip, 30000);
	return request.post({url:'http://'+ip, form: req.body}, function(error, response, body){
		body = JSON.parse(body);
		body['ip'] = ip.replace('10.0.', '');
		return res.json(body);
	});
};

var addToRedis = function(){
	lxc.info(req.params.name, null, function(data){
			var domain = req.query.domain || 'vm42.us';
			domain = req.params.name+'.'+domain;
			client.SADD("hosts", domain, function(){});

			var ip = data.ip + ':5000';
			client.HSET(domain, "ip", ip, redis.print);
			client.HSET(domain, "updated", (new Date).getTime(), redis.print);
			client.hset(domain, "include", "proxy.include");
			return res.json({status: 200, info: data});
	 });
};

router.get('/start/:name', function(req, res, next){
	return lxc.start(req.params.name, function(data){
		console.log('start', arguments);
		if(!data){
			return res.json({status: 500, name: req.params.name, message: data});
		}else{
			res.json({});
		}
	});
});

router.get('/live/:template/:name', function(req, res, next){
	return lxc.startEphemeral(req.params.name, req.params.template, function (data) {
		console.log('live', arguments);
		return res.json(data);
	});
});

router.get('/stop/:name', function(req, res, next){
	return lxc.stop(req.params.name, function(data){
		console.log('stop', arguments);
		if(data){
			return res.json({status: 500, name: req.params.name, message: data});
		}else{
			return res.json({status: 200});
		}
	});
});

router.get('/clone/:template/:name', function(req, res, next){
	return lxc.clone(req.params.name, req.params.template, function(data){
		console.log('clone', arguments);
		if( data.match(/Created container/) ){
			return res.json({status: 200});
		}else{
			return res.json({status: 500, message: data});
		}
	});
});

router.get('/destroy/:name', function(req, res, next){
	return lxc.destroy(req.params.name, function(data){
		console.log('destroy', arguments);
		if(data){
			return res.json({status: 500, message: data});
		}else{
			return res.json({status: 200});
		}
	});
});

router.get('/info/:name', function(req, res, next){
	return lxc.info(req.params.name, function(data){
		return res.json(data);
	});
});

router.get('/list', function(req, res, next) {
	return lxc.list(function(data){
		return res.json(data);
	});
});

router.post('/run/:ip?', function doRun(req, res, next){
	// check if server is

	return lxc.list(function(data){
		if(!req.params.ip) data = [];
		var ip = '10.0.'+ req.params.ip;
		var found = false;

		for(var idx=data.length; idx--;){
			if( data[idx]['ipv4'] === ip ){
				found = true;
				break;
			}
		}

		if(found){
			return runner(req, res, ip)
		}else{
			var name = 'u1-'+(Math.random()*100).toString().replace('.','');
			return lxc.startEphemeral(name, 'u1', function(data){
				ip2name[data.ip] = name;
				return runner(req, res, data.ip);
			});
		}
	});

});

module.exports = router;
<commit_msg>crunner<commit_after>'use strict';

var express = require('express');
var router = express.Router();
var extend = require('node.extend');
var redis = require("redis");
var client = redis.createClient();
var request = require('request');
var lxc = require('../lxc');

var timeoutEvents = {};
var ip2name = {};

var lxcTimeout = function(ip, time){
	var name = ip2name[ip];
	console.log(name)
	time = time || 900000; // 15 minutes
	var keys = Object.keys(timeoutEvents)
	if(keys.indexOf(name) !== -1){
		clearTimeout(timeoutEvents[name])
	}
	timeoutEvents[name] = setTimeout(function(){
		console.log('killing:', name)
		lxc.stop(name);
	}, time);
}

var runner = function(req, res, ip){
	lxcTimeout(ip);
	return request.post({url:'http://'+ip, form: req.body}, function(error, response, body){
		body = JSON.parse(body);
		body['ip'] = ip.replace('10.0.', '');
		return res.json(body);
	});
};

var addToRedis = function(){
	lxc.info(req.params.name, null, function(data){
			var domain = req.query.domain || 'vm42.us';
			domain = req.params.name+'.'+domain;
			client.SADD("hosts", domain, function(){});

			var ip = data.ip + ':5000';
			client.HSET(domain, "ip", ip, redis.print);
			client.HSET(domain, "updated", (new Date).getTime(), redis.print);
			client.hset(domain, "include", "proxy.include");
			return res.json({status: 200, info: data});
	 });
};

router.get('/start/:name', function(req, res, next){
	return lxc.start(req.params.name, function(data){
		console.log('start', arguments);
		if(!data){
			return res.json({status: 500, name: req.params.name, message: data});
		}else{
			res.json({});
		}
	});
});

router.get('/live/:template/:name', function(req, res, next){
	return lxc.startEphemeral(req.params.name, req.params.template, function (data) {
		console.log('live', arguments);
		return res.json(data);
	});
});

router.get('/stop/:name', function(req, res, next){
	return lxc.stop(req.params.name, function(data){
		console.log('stop', arguments);
		if(data){
			return res.json({status: 500, name: req.params.name, message: data});
		}else{
			return res.json({status: 200});
		}
	});
});

router.get('/clone/:template/:name', function(req, res, next){
	return lxc.clone(req.params.name, req.params.template, function(data){
		console.log('clone', arguments);
		if( data.match(/Created container/) ){
			return res.json({status: 200});
		}else{
			return res.json({status: 500, message: data});
		}
	});
});

router.get('/destroy/:name', function(req, res, next){
	return lxc.destroy(req.params.name, function(data){
		console.log('destroy', arguments);
		if(data){
			return res.json({status: 500, message: data});
		}else{
			return res.json({status: 200});
		}
	});
});

router.get('/info/:name', function(req, res, next){
	return lxc.info(req.params.name, function(data){
		return res.json(data);
	});
});

router.get('/list', function(req, res, next) {
	return lxc.list(function(data){
		return res.json(data);
	});
});

router.post('/run/:ip?', function doRun(req, res, next){
	// check if server is

	return lxc.list(function(data){
		if(!req.params.ip) data = [];
		var ip = '10.0.'+ req.params.ip;
		var found = false;

		for(var idx=data.length; idx--;){
			if( data[idx]['ipv4'] === ip ){
				found = true;
				break;
			}
		}

		if(found){
			return runner(req, res, ip)
		}else{
			var name = 'crunner-'+(Math.random()*100).toString().replace('.','');
			return lxc.startEphemeral(name, 'crunner', function(data){
				ip2name[data.ip] = name;
				return runner(req, res, data.ip);
			});
		}
	});

});

module.exports = router;
<|endoftext|>


<commit_before>var express = require('express');
var _ = require('lodash');
var router = express.Router();
var request = require('request');
var moment = require('moment');
var configuration = require('../config.json');

router.get('/flights/:from/:to/:start/:end/:limit', function(req, res, next) {
	var leaveFrom = req.params.from;
	var goTo = req.params.to;
	var start = req.params.start;
	var end = req.params.end;
	var limit = req.params.limit;

	var parsedBody;
	var jsonUrl = 'http://static.letsgo.woooooo.science/flights/outbound.json';
	var endpointUrl = 'https://www.priceline.com/pws/v0/air/search/'+leaveFrom+'-'+goTo+'-'+start+'/'+goTo+'-'+leaveFrom+'-'+end+'/1';

	function parseBody(err, resp, body) {
		if(!err && resp.statusCode == 200) {
			parsedBody = JSON.parse(body)["airSearchRsp"];
			var sliceMap = parsedBody["sliceMap"];
			var segmentMap = parsedBody["segmentMap"];

			var segments = _.map(segmentMap, function(segment) {
				return {
					'flightID': segment.uniqueSegId,
					'arrival': segment.arrivalDateTime,
					'depart': segment.departDateTime,
					'origin': segment.origAirport,
					'dest': segment.destAirport,
					'airline': segment.marketingAirline,
					'fltno': segment.flightNumber,
					'aircraft': segment.equipmentCode,
					'duration': segment.duration
				}
			});

			var flights = _.map(sliceMap, function(slice) {

				var fullSegment = [];
				var flightduration = 0;
				for (var i = 0; i < slice.segment.length; i++) {
					var applicableSegments = _.filter(segments, {'flightID': slice.segment[i].uniqueSegId});
					fullSegment.push(applicableSegments);
					applicableSegments.forEach(function(element) {
						flightduration += element.duration;
					}, this);
				}
				return {
					'layoverTime': slice.duration - flightduration,
					'tripSegment': fullSegment
				}
			});

			//console.log(flights);

			// sliceMap.forEach(function(entry) {
			// 	var fullSegment = [];
			// 	entry["segment"].forEach(function(flight) {
			// 		fullSegment.push(segmentMap[flight.uniqueSegId]);
			// 	});
			// 	flights.push(fullSegment);
			// });
			if(limit > 0) {
				res.json(_.slice(flights, 0, limit));
			} else {
				res.json(flights);
			}
		}
	}

	request(jsonUrl, parseBody);
});

//Query http link
//  receive JSON
//    parse usable data into js array
router.get('/hotels/:coordinates/:start/:end', function(req, res, next) {
	var coordinates = req.params.coordinates;
	var start = req.params.start;
	var end = req.params.end;

	request('http://priceline.com/api/hotelretail/listing/v3/'+coordinates+'/'+start+'/'+end+'/1/50?minStars=3&minGuestRating=6&sort=2', function(error, response, body){
		var hotelResp = JSON.parse(body);

		var sortedPrice = hotelResp["priceSorted"];
		var availableHotels = [];
		for(var i = 0; i < sortedPrice.length; i++) {
			if(hotelResp["hotels"][sortedPrice[i]].remainingRooms > 0) {
				availableHotels.push(sortedPrice[i]);
			}
		}
		var firstElement = hotelResp["hotels"][availableHotels[0]];
		res.send({
			'name': firstElement.hotelName,
			'price': firstElement.merchPrice,
			'overallRating': firstElement.overallRatingScore,
			'address': firstElement.address,
			'image': firstElement.thumbnailURL,
			'amenities': firstElement.amenities
		});
	});

});

router.get('/places/:loc/:query', function(req, res, next) {
	var loc = req.params.loc;
	var query = req.params.query;
	var rand;

	request('https://maps.googleapis.com/maps/api/place/textsearch/json?query='+query+' in '+loc+"&key="+configuration.gmaps.API_KEY, function(error, response, body){
			if(!error && response.statusCode == 200 && response.status!="ZERO") {
				var stuff = JSON.parse(body)["results"];
				rand = Math.floor(Math.random() * stuff.length);
				// console.log(rand + " length of array is " + stuff.length);
				res.send({
					'name': stuff[rand].name,
					'address': stuff[rand].formatted_address,
					'icon': stuff[rand].icon
				});
			} else {
				res.send({
					'status': 'ERR_NORESULT'
				});
			}
	});

});

router.get('/uber/:startLat/:startLon/:endLat/:endLon', function(req, res, next){
	var startLat = req.params.startLat;
	var startLon = req.params.startLon;
	var endLat = req.params.endLat;
	var endLon = req.params.endLon;
	var rideInfo;

	request('https://api.uber.com/v1/estimates/price?start_latitude='+startLat+'&start_longitude='+startLon+'&end_latitude='+endLat+'&end_longitude='+endLon+'&server_token='+configuration.uber.API_KEY, function(error, response, body){
			rideInfo = JSON.parse(body)["prices"][0];
			res.send({
				'name': rideInfo.display_name,
				'costEstimate': rideInfo.estimate,
				'duration': rideInfo.duration
			});
	});
});

module.exports = router;
<commit_msg>checkstuff<commit_after>var express = require('express');
var _ = require('lodash');
var router = express.Router();
var request = require('request');
var moment = require('moment');
var configuration = require('../config.json');

router.get('/flights/:from/:to/:start/:end/:limit', function(req, res, next) {
	var leaveFrom = req.params.from;
	var goTo = req.params.to;
	var start = req.params.start;
	var end = req.params.end;
	var limit = req.params.limit;

	var parsedBody;
	var jsonUrl = 'http://static.letsgo.woooooo.science/flights/outbound.json';
	var endpointUrl = 'https://www.priceline.com/pws/v0/air/search/'+leaveFrom+'-'+goTo+'-'+start+'/'+goTo+'-'+leaveFrom+'-'+end+'/1';

	function parseBody(err, resp, body) {
		if(!err && resp.statusCode == 200) {
			parsedBody = JSON.parse(body)["airSearchRsp"];
			var sliceMap = parsedBody["sliceMap"];
			var segmentMap = parsedBody["segmentMap"];

			var segments = _.map(segmentMap, function(segment) {
				return {
					'flightID': segment.uniqueSegId,
					'arrival': segment.arrivalDateTime,
					'depart': segment.departDateTime,
					'origin': segment.origAirport,
					'dest': segment.destAirport,
					'airline': segment.marketingAirline,
					'fltno': segment.flightNumber,
					'aircraft': segment.equipmentCode,
					'duration': segment.duration
				}
			});

			var flights = _.map(sliceMap, function(slice) {

				var fullSegment = [];
				var flightduration = 0;
				for (var i = 0; i < slice.segment.length; i++) {
					var applicableSegments = _.filter(segments, {'flightID': slice.segment[i].uniqueSegId});
					fullSegment.push(applicableSegments);
					applicableSegments.forEach(function(element) {
						flightduration += element.duration;
					}, this);
				}
				return {
					'layoverTime': slice.duration - flightduration,
					'tripSegment': fullSegment
				}
			});

			//console.log(flights);

			// sliceMap.forEach(function(entry) {
			// 	var fullSegment = [];
			// 	entry["segment"].forEach(function(flight) {
			// 		fullSegment.push(segmentMap[flight.uniqueSegId]);
			// 	});
			// 	flights.push(fullSegment);
			// });
			if(limit > 0) {
				res.json(_.slice(flights, 0, limit));
			} else {
				res.json(flights);
			}
		}
	}

	request(jsonUrl, parseBody);
});

//Query http link
//  receive JSON
//    parse usable data into js array
router.get('/hotels/:coordinates/:start/:end', function(req, res, next) {
	var coordinates = req.params.coordinates;
	var start = req.params.start;
	var end = req.params.end;

	request('http://priceline.com/api/hotelretail/listing/v3/'+coordinates+'/'+start+'/'+end+'/1/50?minStars=3&minGuestRating=6&sort=2', function(error, response, body){
		var hotelResp = JSON.parse(body);

		var sortedPrice = hotelResp["priceSorted"];
		var availableHotels = [];
		for(var i = 0; i < sortedPrice.length; i++) {
			if(hotelResp["hotels"][sortedPrice[i]].remainingRooms > 0) {
				availableHotels.push(sortedPrice[i]);
			}
		}
		var firstElement = hotelResp["hotels"][availableHotels[0]];
		res.send({
			'name': firstElement.hotelName,
			'price': firstElement.merchPrice,
			'overallRating': firstElement.overallRatingScore,
			'address': firstElement.address,
			'image': firstElement.thumbnailURL,
			'amenities': firstElement.amenities
		});
	});

});

router.get('/places/:loc/:query', function(req, res, next) {
	var loc = req.params.loc;
	var query = req.params.query;
	var rand;

	request('https://maps.googleapis.com/maps/api/place/textsearch/json?query='+query+' in '+loc+"&key="+configuration.gmaps.API_KEY, function(error, response, body){
			if(!error && response.statusCode == 200 && response.status!="ZERO") {
				var stuff = JSON.parse(body)["results"];
				rand = Math.floor(Math.random() * stuff.length);
				// console.log(rand + " length of array is " + stuff.length);
				if(stuff[rand]){
					res.send({
						'name': stuff[rand].name,
						'address': stuff[rand].formatted_address,
						'icon': stuff[rand].icon
					});
				}
			} else {
				res.send({
					'status': 'ERR_NORESULT'
				});
			}
	});

});

router.get('/uber/:startLat/:startLon/:endLat/:endLon', function(req, res, next){
	var startLat = req.params.startLat;
	var startLon = req.params.startLon;
	var endLat = req.params.endLat;
	var endLon = req.params.endLon;
	var rideInfo;

	request('https://api.uber.com/v1/estimates/price?start_latitude='+startLat+'&start_longitude='+startLon+'&end_latitude='+endLat+'&end_longitude='+endLon+'&server_token='+configuration.uber.API_KEY, function(error, response, body){
			rideInfo = JSON.parse(body)["prices"][0];
			res.send({
				'name': rideInfo.display_name,
				'costEstimate': rideInfo.estimate,
				'duration': rideInfo.duration
			});
	});
});

module.exports = router;
<|endoftext|>


<commit_before>'use strict';

var express = require('express');
var router = express.Router();
var util = require('util');
var request = require('request');
var lxc = require('../lxc');
var doapi = require('../doapi')();

var workerSnapID = 'V1'

// console.log = function(){};

var label2runner = {};
var workers = [];
var isCheckingWorkers = false;

var dopletNewID = 0;

var workers = (function(){
	var workers = [];

	workers.checkDroplet = function(id, time){
		time = time || 5000;
		doapi.dropletInfo(id, function(data){
			var worker = JSON.parse(data)['droplet'];
			if(worker.status == 'active'){
				console.log('Droplet is now active, starting runners in 20 seconds')
				setTimeout(function(worker){
					console.log('Ready to start runners!')
					workers.startRunners(workers[workers.push(workers.makeWorkerObj(worker))-1])
					isCheckingWorkers = false;
				}, 20000, worker);
				return true;
			}else{
				console.log('Worker not ready, check again in ', time, 'MS');
				setTimeout(function(){
					workers.checkDroplet(id);
				}, time);
			}
		});
	};

	workers.create = function(){
		doapi.dropletCreate({
			name: 'clw'+workerSnapID+'-'+(Math.random()*100).toString().replace('.',''),
			image: '17575764'
		}, function(data){
			data = JSON.parse(data);
			setTimeout(function(dopletNewID){
				workers.checkDroplet(dopletNewID);
			}, 60000, data.droplet.id);
			doapi.dropletSetTag('clworker', data.droplet.id, function(){});
		});

	};

	workers.destroy = function(worker){
		worker = worker || workers.pop();
		doapi.dropletDestroy(worker.id, function(){});
		workers.checkBalance();
	};

	workers.makeWorkerObj = function(worker){
		worker.networks.v4.forEach(function(value){
			worker[value.type+'IP'] = value.ip_address;
		});
		worker.availrunners = [];
		worker.ip = worker.privateIP;
		worker.usedrunner = 0;
		worker.index = workers.length,
		worker.getRunner = function(){
			if(this.availrunners.length === 0) return false;
			console.log('geting runner from ', worker.name, ' aval length ', this.availrunners.length);
			var runner = this.availrunners.pop();
			this.usedrunner++;
			label2runner[runner.label] = runner;

			return runner;
		}
		return worker;
	};

	workers.destroyOld = function(){
		doapi.dropletsByTag('clworker', function(data){
			data = JSON.parse(data);
			console.log('dropletsByTag data:',data);
			data['droplets'].forEach(function(worker){
				console.log('found old droplet, killing it');
				doapi.dropletDestroy(worker.id, function(){});
			});
			workers.checkBalance();
		});
	};

	workers.startRunners = function(worker, stopPercent){
		console.log('starting runners on', worker.name)
		stopPercent = stopPercent || 80;
		ramPercentUsed(worker.ip, function(usedMemPercent){
			if(usedMemPercent < stopPercent ){
				var name = 'crunner-'+(Math.random()*100).toString().replace('.','');
				return lxc.startEphemeral(name, 'crunner0', worker.ip, function(data){
					if( !data.ip ) return setTimeout(workers.startRunners(worker),0);
					console.log('started runner')

					worker.availrunners.push({
						ip: data.ip,
						name: name,
						worker: worker,
						label: worker.name + ':' + name
					});
					return setTimeout(workers.startRunners(worker, stopPercent), 0);
				});
			}else{
				setTimeout(workers.checkBalance, 10000);
				console.log('using', usedMemPercent, 'percent memory, stopping runner creation!', worker.availrunners.length, 'created on ', worker.name);
			}
		});
	};

	workers.checkBalance = function(){
		if(isCheckingWorkers) return false;
		isCheckingWorkers = true;
		var changed = false;
		console.log('checking balance');

		if(workers.length < 3){
			console.log('less then 3 workers, starting a droplet');
			for(var i=2; i--;) workers.create();
			return ;
		}
		if(workers[workers.length-1].usedrunner !== 0){
			console.log('last droplet has no free runners, starting droplet');
			return workers.create();
		}
		if(workers.length > 3 && workers[workers.length-1].usedrunner === 0 && workers[workers.length-2].usedrunner === 0){
			console.log('Last 2 runners not used, killing last runner');
			workers.destroy();
		}

		for(let worker of workers){
			if(worker.availrunners.length === 0 && worker.usedrunner === 0){
				console.log('found zombie worker, destroying')
				workers.destroy(worker);
				changed = true;
			}
		}

		console.log('stopping workers balancing check');
		isCheckingWorkers = false;
		if(changed) setTimeout(function(){
			workers.checkBalance();
		}, 3000);
	};

	return workers;

})();

var ramPercentUsed = function(ip, callback){

	return lxc.exec(
		"python3 -c \"a=`head /proc/meminfo|grep MemAvail|grep -Po '\\d+'`;t=`head /proc/meminfo|grep MemTotal|grep -Po '\\d+'`;print(round(((t-a)/t)*100, 2))\"",
		ip,
		callback
	);
};

var runnerFree = function(runner){
	lxc.stop(runner.name, runner.worker.ip);
	runner.worker.usedrunner--;
	if(runner.hasOwnProperty('timeout')){
		clearTimeout(runner.timeout);
	}
	delete label2runner[runner.label];

	workers.startRunners(runner.worker);
};

var lxcTimeout = function(runner, time){
	time = time || 60000 // 900000; // 15 minutes

	if(runner.hasOwnProperty('timeout')){
		clearTimeout(runner.timeout);
	}

	return runner.timeout = setTimeout(function(){
		runnerFree(runner);
	}, time);
};

var run = function(req, res, runner){

	var httpOptions = {
		url: 'http://' + runner.worker.ip,
		headers: {
			Host: runner.name
		},
		body: JSON.stringify({
			code: req.body.code
		})
	};

	return request.post(httpOptions, function(error, response, body){
		// console.log('runner response:', arguments)
		if(response.statusCode !== 200 || error) return run(req, res, getAvailrunner());
		body = JSON.parse(body);

		body['ip'] = getAvailrunner(runner).label;
		lxcTimeout(runner);
		return res.json(body);

	});
};

var getAvailrunner = function(runner){
	for(let worker of workers){
		console.log('checking ', worker.name, ' with ', worker.availrunners.length, ' free workers');
		if(worker.availrunners.length === 0) continue;
		if(runner && runner.worker.index <= worker.index) break;
		if(runner) runnerFree(runner);
		workers.checkBalance();
		return worker.getRunner();
	}
	if(runner) return runner;
	return false;
};

setInterval(workers.checkBalance, 30000);
workers.destroyOld();

// router.get('/start/:name', function(req, res, next){
// 	return lxc.start(req.params.name, function(data){
// 		if(!data){
// 			return res.json({
// 				status: 500,
// 				name: req.params.name,
// 				message: data
// 			});
// 		}else{
// 			res.json({});
// 		}
// 	});
// });

// router.get('/live/:template/:name', function(req, res, next){
// 	return lxc.startEphemeral(req.params.name, req.params.template, function (data) {
// 		console.log('live', arguments);
// 		return res.json(data);
// 	});
// });

// router.get('/clone/:template/:name', function(req, res, next){
// 	return lxc.clone(req.params.name, req.params.template, function(data){
// 		console.log('clone', arguments);
// 		if( data.match(/Created runner/) ){
// 			return res.json({status: 200});
// 		}else{
// 			return res.json({status: 500, message: data});
// 		}
// 	});
// });

// router.get('/destroy/:name', function(req, res, next){
// 	return lxc.destroy(req.params.name, function(data){
// 		console.log('destroy', arguments);
// 		if(data){
// 			return res.json({status: 500, message: data});
// 		}else{
// 			return res.json({status: 200});
// 		}
// 	});
// });

// router.get('/info/:name', function(req, res, next){
// 	return lxc.info(req.params.name, function(data){
// 		return res.json(data);
// 	});
// });

// router.get('/list', function(req, res, next) {
// 	return lxc.list(workers.clworker0.ip, function(data){
// 		return res.json(data);
// 	});
// });

router.get('/stop/:name', function(req, res, next){
	return lxc.stop(req.params.name, function(data){
		console.log('stop', arguments);
		if(data){
			return res.json({status: 500, name: req.params.name, message: data});
		}else{
			return res.json({status: 200});
		}
	});
});

router.get('/liststuff', function(req, res, next){
	var obj = util.inspect(workers, {depth: 4});
	res.send("
Workers

"+obj+"

label2runner

"+util.inspect(label2runner)+'

DO calls
'+doapi.calls);
});

router.post('/run/:ip?', function doRun(req, res, next){
	console.log('hit runner route')
	var runner = label2runner[req.params.ip] || getAvailrunner();
	console.log('')
	return run(req, res, runner);
});

module.exports = router;
<commit_msg>stuff<commit_after>'use strict';

var express = require('express');
var router = express.Router();
var util = require('util');
var request = require('request');
var lxc = require('../lxc');
var doapi = require('../doapi')();

var workerSnapID = 'V1'

// console.log = function(){};

var label2runner = {};
var workers = [];
var isCheckingWorkers = false;

var dopletNewID = 0;

var workers = (function(){
	var workers = [];

	workers.checkDroplet = function(id, time){
		time = time || 5000;
		doapi.dropletInfo(id, function(data){
			var worker = JSON.parse(data)['droplet'];
			if(worker.status == 'active'){
				console.log('Droplet is now active, starting runners in 20 seconds')
				setTimeout(function(worker){
					console.log('Ready to start runners!')
					workers.startRunners(workers[workers.push(workers.makeWorkerObj(worker))-1])
					isCheckingWorkers = false;
				}, 20000, worker);
				return true;
			}else{
				console.log('Worker not ready, check again in ', time, 'MS');
				setTimeout(function(){
					workers.checkDroplet(id);
				}, time);
			}
		});
	};

	workers.create = function(){
		doapi.dropletCreate({
			name: 'clw'+workerSnapID+'-'+(Math.random()*100).toString().replace('.',''),
			image: '17575764'
		}, function(data){
			data = JSON.parse(data);
			setTimeout(function(dopletNewID){
				workers.checkDroplet(dopletNewID);
			}, 60000, data.droplet.id);
			doapi.dropletSetTag('clworker', data.droplet.id, function(){});
		});

	};

	workers.destroy = function(worker){
		worker = worker || workers.pop();
		doapi.dropletDestroy(worker.id, function(){});
		workers.checkBalance();
	};

	workers.makeWorkerObj = function(worker){
		worker.networks.v4.forEach(function(value){
			worker[value.type+'IP'] = value.ip_address;
		});
		worker.availrunners = [];
		worker.ip = worker.privateIP;
		worker.usedrunner = 0;
		worker.index = workers.length,
		worker.getRunner = function(){
			if(this.availrunners.length === 0) return false;
			console.log('geting runner from ', worker.name, ' aval length ', this.availrunners.length);
			var runner = this.availrunners.pop();
			this.usedrunner++;
			label2runner[runner.label] = runner;

			return runner;
		}
		return worker;
	};

	workers.destroyOld = function(){
		doapi.dropletsByTag('clworker', function(data){
			data = JSON.parse(data);
			console.log('dropletsByTag data:',data);
			data['droplets'].forEach(function(worker){
				console.log('found old droplet, killing it');
				doapi.dropletDestroy(worker.id, function(){});
			});
			// workers.checkBalance();
		});
	};

	workers.startRunners = function(worker, stopPercent){
		console.log('starting runners on', worker.name)
		stopPercent = stopPercent || 80;
		ramPercentUsed(worker.ip, function(usedMemPercent){
			if(usedMemPercent < stopPercent ){
				var name = 'crunner-'+(Math.random()*100).toString().replace('.','');
				return lxc.startEphemeral(name, 'crunner0', worker.ip, function(data){
					if( !data.ip ) return setTimeout(workers.startRunners(worker),0);
					console.log('started runner')

					worker.availrunners.push({
						ip: data.ip,
						name: name,
						worker: worker,
						label: worker.name + ':' + name
					});
					return setTimeout(workers.startRunners(worker, stopPercent), 0);
				});
			}else{
				setTimeout(workers.checkBalance, 10000);
				console.log('using', usedMemPercent, 'percent memory, stopping runner creation!', worker.availrunners.length, 'created on ', worker.name);
			}
		});
	};

	workers.checkBalance = function(){
		if(isCheckingWorkers) return false;
		isCheckingWorkers = true;
		var changed = false;
		var minWorkers = 3;
		console.log('checking balance');

		if(workers.length < minWorkers){
			console.log('less then 3 workers, starting a droplet');
			for(var i=minWorkers; i--;) workers.create();
			return ;
		}
		if(workers[workers.length-1].usedrunner !== 0){
			console.log('last droplet has no free runners, starting droplet');
			return workers.create();
		}
		if(workers.length > minWorkers && workers[workers.length-1].usedrunner === 0 && workers[workers.length-2].usedrunner === 0){
			console.log('Last 2 runners not used, killing last runner');
			workers.destroy();
			changed = true;
		}

		for(let worker of workers){
			if(worker.availrunners.length === 0 && worker.usedrunner === 0){
				console.log('found zombie worker, destroying')
				workers.destroy(worker);
				changed = true;
			}
		}

		console.log('stopping workers balancing check');
		isCheckingWorkers = false;
		if(changed) setTimeout(function(){
			workers.checkBalance();
		}, 5000);
	};

	return workers;

})();

var ramPercentUsed = function(ip, callback){

	return lxc.exec(
		"python3 -c \"a=`head /proc/meminfo|grep MemAvail|grep -Po '\\d+'`;t=`head /proc/meminfo|grep MemTotal|grep -Po '\\d+'`;print(round(((t-a)/t)*100, 2))\"",
		ip,
		callback
	);
};

var runnerFree = function(runner){
	lxc.stop(runner.name, runner.worker.ip);
	runner.worker.usedrunner--;
	if(runner.hasOwnProperty('timeout')){
		clearTimeout(runner.timeout);
	}
	delete label2runner[runner.label];

	workers.startRunners(runner.worker);
};

var lxcTimeout = function(runner, time){
	time = time || 60000 // 900000; // 15 minutes

	if(runner.hasOwnProperty('timeout')){
		clearTimeout(runner.timeout);
	}

	return runner.timeout = setTimeout(function(){
		runnerFree(runner);
	}, time);
};

var run = function(req, res, runner){

	var httpOptions = {
		url: 'http://' + runner.worker.ip,
		headers: {
			Host: runner.name
		},
		body: JSON.stringify({
			code: req.body.code
		})
	};

	return request.post(httpOptions, function(error, response, body){
		// console.log('runner response:', arguments)
		if(response.statusCode !== 200 || error) return run(req, res, getAvailrunner());
		body = JSON.parse(body);

		body['ip'] = getAvailrunner(runner).label;
		lxcTimeout(runner);
		return res.json(body);

	});
};

var getAvailrunner = function(runner){
	for(let worker of workers){
		console.log('checking ', worker.name, ' with ', worker.availrunners.length, ' free workers');
		if(worker.availrunners.length === 0) continue;
		if(runner && runner.worker.index <= worker.index) break;
		if(runner) runnerFree(runner);
		workers.checkBalance();
		return worker.getRunner();
	}
	if(runner) return runner;
	return false;
};

setInterval(workers.checkBalance, 30000);
workers.destroyOld();

// router.get('/start/:name', function(req, res, next){
// 	return lxc.start(req.params.name, function(data){
// 		if(!data){
// 			return res.json({
// 				status: 500,
// 				name: req.params.name,
// 				message: data
// 			});
// 		}else{
// 			res.json({});
// 		}
// 	});
// });

// router.get('/live/:template/:name', function(req, res, next){
// 	return lxc.startEphemeral(req.params.name, req.params.template, function (data) {
// 		console.log('live', arguments);
// 		return res.json(data);
// 	});
// });

// router.get('/clone/:template/:name', function(req, res, next){
// 	return lxc.clone(req.params.name, req.params.template, function(data){
// 		console.log('clone', arguments);
// 		if( data.match(/Created runner/) ){
// 			return res.json({status: 200});
// 		}else{
// 			return res.json({status: 500, message: data});
// 		}
// 	});
// });

// router.get('/destroy/:name', function(req, res, next){
// 	return lxc.destroy(req.params.name, function(data){
// 		console.log('destroy', arguments);
// 		if(data){
// 			return res.json({status: 500, message: data});
// 		}else{
// 			return res.json({status: 200});
// 		}
// 	});
// });

// router.get('/info/:name', function(req, res, next){
// 	return lxc.info(req.params.name, function(data){
// 		return res.json(data);
// 	});
// });

// router.get('/list', function(req, res, next) {
// 	return lxc.list(workers.clworker0.ip, function(data){
// 		return res.json(data);
// 	});
// });

router.get('/stop/:name', function(req, res, next){
	return lxc.stop(req.params.name, function(data){
		console.log('stop', arguments);
		if(data){
			return res.json({status: 500, name: req.params.name, message: data});
		}else{
			return res.json({status: 200});
		}
	});
});

router.get('/liststuff', function(req, res, next){
	var obj = util.inspect(workers, {depth: 4});
	res.send("
Workers

"+obj+"

label2runner

"+util.inspect(label2runner)+'

DO calls
'+doapi.calls);
});

router.post('/run/:ip?', function doRun(req, res, next){
	console.log('hit runner route')
	var runner = label2runner[req.params.ip] || getAvailrunner();
	console.log('')
	return run(req, res, runner);
});

module.exports = router;
<|endoftext|>


<commit_before>var express = require('express');
var session = require('express-session');
var redis = require('redis');

var router = express.Router();
var authentication = require('../authentication');
var request = require('request-promise');
var url = require('url');

var client;
if (process.env.REDISTOGO_URL) {
  var rtg   = require("url").parse(process.env.REDISTOGO_URL);
  client = require("redis").createClient(rtg.port, rtg.hostname);

  client.auth(rtg.auth.split(":")[1]);
} else {
  client = redis.createClient();
}

// Simple route middleware to ensure user is authenticated.
//   Use this route middleware on any resource that needs to be protected.  If
//   the request is authenticated (typically via a persistent login session),
//   the request will proceed.  Otherwise, the user will be redirected to the
//   login page.
function ensureAuthenticated(req, res, next) {
  if (req.isAuthenticated()) {
    return next()
  }
  res.status(401).send({
    authUri: authentication.getAuthUri(),
    msg: 'Authentication required.'
  });
}

router.sendNotify = function(req, res) {
  console.log("** SERVER EVENT - Notify ");
  console.log("    received notification of event - " + req.body.event);

  if (req.body.event == 'onshape.model.lifecycle.changed') {
    var state = {
      elementId : req.body.elementId,
      change : true
    };

    var stateString = JSON.stringify(state);
    var uniqueID = "change" + req.body.elementId;

    console.log("   uniqueID - " + client + ", " + uniqueID);

    client.set(uniqueID, stateString);
  }

  res.send("ok");
}

router.post('/logout', function(req, res) {
  req.session.destroy();
  return res.send({});
});

var getSession = function(req, res) {
  request.get({
    uri: 'https://partner.dev.onshape.com/api/users/session',
    headers: {
      'Authorization': 'Bearer ' + req.user.accessToken
    }
  }).then(function(data) {
    res.send(data);
  }).catch(function(data) {
    console.log('****** getSession - CATCH ' + data.statusCode);
    if (data.statusCode === 401) {
      authentication.refreshOAuthToken(req, res).then(function() {
        getSession(req, res);
      }).catch(function(err) {
        console.log('Error refreshing token or getting session: ', err);
      });
    } else {
      console.log('GET /api/users/session error: ', data);
    }
  });
};

var getDocuments = function(req, res) {
  request.get({
    uri: 'https://partner.dev.onshape.com/api/documents',
    headers: {
      'Authorization': 'Bearer ' + req.user.accessToken
    }
  }).then(function(data) {
    res.send(data);
  }).catch(function(data) {
    if (data.statusCode === 401) {
      authentication.refreshOAuthToken(req, res).then(function() {
        getDocuments(req, res);
      }).catch(function(err) {
        console.log('Error refreshing token or getting documents: ', err);
      });
    } else {
      console.log('GET /api/documents error: ', data);
    }
  });
};

var getElementList = function(req, res) {
  var url = 'https://partner.dev.onshape.com/api/documents/d/' + req.query.documentId + '/w/' + req.query.workspaceId + '/elements';
  if (req.query.elementId) {
    url += '/?elementId=' + req.query.elementId;
  }

  request.get({
    uri: url,
    headers: {
      'Authorization': 'Bearer ' + req.user.accessToken
    }
  }).then(function(data) {
    res.send(data);
  }).catch(function(data) {
    console.log('****** getElementList - CATCH ' + data.statusCode);
    if (data.statusCode === 401) {
      authentication.refreshOAuthToken(req, res).then(function() {
        getElementList(req, res);
      }).catch(function(err) {
        console.log('Error refreshing token or getting elements: ', err);
      });
    } else {
      console.log('GET /api/documents/elements error: ', data);
    }
  });
};

var getAssemblyList = function(req, res) {
  var url = 'https://partner.dev.onshape.com/api/documents/d/' + req.query.documentId + '/w/' + req.query.workspaceId + '/elements?elementType=assembly';
  if (req.query.elementId) {
    url += '/?elementId=' + req.query.elementId;
  }

  request.get({
    uri: url,
    headers: {
           'Authorization': 'Bearer ' + req.user.accessToken
         }
  }).then(function(data) {
    res.send(data);
  }).catch(function(data) {
    console.log('****** getAssemblyList - CATCH ' + data.statusCode);
    if (data.statusCode === 401) {
      authentication.refreshOAuthToken(req, res).then(function() {
        getElementList(req, res);
      }).catch(function(err) {
        console.log('Error refreshing token or getting elements: ', err);
      });
    } else {
      console.log('GET /api/documents/elements ?type=assembly error: ', data);
    }
  });
};

var getShadedView = function(req, res) {
  request.get({
    uri: 'https://partner.dev.onshape.com/api/assemblies/d/' + req.query.documentId +
    '/w/' + req.query.workspaceId + '/e/' + req.query.elementId + '/shadedviews?' +
    '&outputHeight=' + req.query.outputHeight + '&outputWidth=' + req.query.outputWidth + '&pixelSize=' + req.query.pixelSize +
    '&viewMatrix=' + req.query.viewMatrix1 + ',' + req.query.viewMatrix2 + ',' + req.query.viewMatrix3 + ',' + req.query.viewMatrix4 +
    ',' + req.query.viewMatrix5 + ',' + req.query.viewMatrix6 + ',' + req.query.viewMatrix7 + ',' + req.query.viewMatrix8 +
    ',' + req.query.viewMatrix9 + ',' + req.query.viewMatrix10 + ',' + req.query.viewMatrix11 + ',' + req.query.viewMatrix12 +
    '&perspective=false',
    headers: {
      'Authorization': 'Bearer ' + req.user.accessToken
    }
  }).then(function(data) {

    res.send(data);
  }).catch(function(data) {
    if (data.statusCode === 401) {
      authentication.refreshOAuthToken(req, res).then(function() {
        getShadedView(req, res);
      }).catch(function(err) {
        console.log('Error refreshing token or getting shaded view: ', err);
      });
    } else {
      console.log('GET /api/assemblies/shadedviews error: ', data);
    }
  });
};

var getBoundingBox = function(req, res) {
  request.get({
    uri: 'https://partner.dev.onshape.com/api/assemblies/d/' + req.query.documentId +
          '/w/' + req.query.workspaceId + '/e/' + req.query.elementId + '/boundingboxes/',
    headers: {
      'Authorization': 'Bearer ' + req.user.accessToken
    }
  }).then(function(data) {
    res.send(data);
  }).catch(function(data) {
    if (data.statusCode === 401) {
      authentication.refreshOAuthToken(req, res).then(function() {
        getBoundingBox(req, res);
      }).catch(function(err) {
        console.log('Error refreshing token or getting bounding box: ', err);
      });
    } else {
      console.log('GET /api/assemblies/boundingbox error: ', data);
    }
  });
};

var getPartsList = function(req, res) {
  request.get({
    uri: 'https://partner.dev.onshape.com/api/parts/d/' + req.query.documentId + '/w/' + req.query.workspaceId,
    headers: {
      'Authorization': 'Bearer ' + req.user.accessToken
    }
  }).then(function(data) {
    res.send(data);
  }).catch(function(data) {
    if (data.statusCode === 401) {
      authentication.refreshOAuthToken(req, res).then(function() {
        getPartsList(req, res);
      }).catch(function(err) {
        console.log('Error refreshing token or getting parts: ', err);
      });
    } else {
      console.log('GET /api/parts/workspace error: ', data);
    }
  });
};

var getAssemblyDefinition = function(req, res) {
  request.get({
    uri: 'https://partner.dev.onshape.com/api/assemblies/d/' + req.query.documentId + '/w/' + req.query.workspaceId + '/e/' + req.query.nextElement + '?includeMateFeatures=false',
    headers: {
      'Authorization': 'Bearer ' + req.user.accessToken
    }
  }).then(function(data) {
    res.send(data);
  }).catch(function(data) {
    if (data.statusCode === 401) {
      authentication.refreshOAuthToken(req, res).then(function() {
        getAssemblyDefinition(req, res);
      }).catch(function(err) {
        console.log('Error refreshing token or getting assembly definition: ', err);
      });
    } else {
      console.log('GET /api/models/assembly/definition error: ', data);
    }
  });
};

var getMetadata = function(req, res) {
   request.get({
    uri: 'https://partner.dev.onshape.com/api/parts/d/' + req.query.documentId + '/w/' + req.query.workspaceId + '/e/' + req.query.elementId + '/partid/' + req.query.partId + '/metadata',
    headers: {
      'Authorization': 'Bearer ' + req.user.accessToken
    }
  }).then(function(data) {
    res.send(data);
  }).catch(function(data) {
    if (data.statusCode === 401) {
      authentication.refreshOAuthToken(req, res).then(function() {
        getMetadata(req, res);
      }).catch(function(err) {
        console.log('Error refreshing token or getting part metadata: ', err);
      });
    } else {
      console.log('GET /api/parts/metadata error: ', data);
    }
  });
};

var setWebhooks = function(req, res) {
  var eventList = [ "onshape.model.lifecycle.changed" ];
  var options = { collapseEvents : true };
  var urlNotify = "https://onshape-appstore-bom.herokuapp.com/notify";
  var filter = "{$DocumentId} = '" + req.query.documentId + "' && " +
               "{$WorkspaceId} = '" + req.query.workspaceId + "' && " +
               "{$ElementId} = '" + req.query.elementId + "'";

  request.post({
    uri: 'https://partner.dev.onshape.com/api/webhooks/',
    body: {
      url : urlNotify,
      events : eventList,
      filter : filter,
      options : options
    },
    headers: {
      'Authorization': 'Bearer ' + req.user.accessToken
    },
    json : true
  }).then(function(data) {
    res.send(data);
  }).catch(function(data) {
    if (data.statusCode === 401) {
      authentication.refreshOAuthToken(req, res).then(function() {
        setWebhooks(req, res);
      }).catch(function(err) {
        console.log('*** Error refreshing token or setting webhooks: ', err);
      });
    } else {
      console.log('*** POST /api/webhooks error: ', data);
    }
  });
};

var checkModelChange = function(req, res) {
  var data = {
    statusCode : 200,
    change : false
  };

  // Get the current setting from Redis (if there is one)
  var uniqueID = "change" + req.query.elementId;

  console.log("** CHECK State - " + uniqueID + ", " + client);

  client.get(uniqueID, function(err, reply) {
    // reply is null when the key is missing
    if (reply != null) {
      var newParams = JSON.parse(reply);
      data.change = newParams.change;

      console.log("** CLIENT State Check - " + newParams.change);

      // Now that we have the value, clear it in Redis
      var state = {
        elementId : req.query.elementId,
        change : false
      };

      console.log("** RESET State - " + uniqueID);

      var stateString = JSON.stringify(state);
      client.set(uniqueID, stateString);
    }

    res.send(data);
  });
}

router.get('/documents', getDocuments);
router.get('/session', getSession);
router.get('/elements', getElementList);
router.get('/assemblies', getAssemblyList);
router.get('/parts', getPartsList);
router.get('/boundingBox', getBoundingBox);
router.get('/definition', getAssemblyDefinition);
router.get('/shadedView', getShadedView);
router.get('/metadata', getMetadata);
router.get('/webhooks', setWebhooks);
router.get('/modelchange', checkModelChange);

module.exports = router;
<commit_msg>Webhook<commit_after>var express = require('express');
var session = require('express-session');
var redis = require('redis');

var router = express.Router();
var authentication = require('../authentication');
var request = require('request-promise');
var url = require('url');

var client;
if (process.env.REDISTOGO_URL) {
  var rtg   = require("url").parse(process.env.REDISTOGO_URL);
  client = require("redis").createClient(rtg.port, rtg.hostname);

  client.auth(rtg.auth.split(":")[1]);
} else {
  client = redis.createClient();
}

// Simple route middleware to ensure user is authenticated.
//   Use this route middleware on any resource that needs to be protected.  If
//   the request is authenticated (typically via a persistent login session),
//   the request will proceed.  Otherwise, the user will be redirected to the
//   login page.
function ensureAuthenticated(req, res, next) {
  if (req.isAuthenticated()) {
    return next()
  }
  res.status(401).send({
    authUri: authentication.getAuthUri(),
    msg: 'Authentication required.'
  });
}

router.sendNotify = function(req, res) {
  console.log("** SERVER EVENT - Notify ");
  console.log("    received notification of event - " + req.body.event);

  if (req.body.event == 'onshape.model.lifecycle.changed') {
    var state = {
      elementId : req.body.elementId,
      change : true
    };

    var stateString = JSON.stringify(state);
    var uniqueID = "change" + req.body.elementId;

    console.log("   uniqueID - " + client + ", " + uniqueID);

    client.set(uniqueID, stateString);
  }

  res.send("ok");
}

router.post('/logout', function(req, res) {
  req.session.destroy();
  return res.send({});
});

var getSession = function(req, res) {
  request.get({
    uri: 'https://partner.dev.onshape.com/api/users/session',
    headers: {
      'Authorization': 'Bearer ' + req.user.accessToken
    }
  }).then(function(data) {
    res.send(data);
  }).catch(function(data) {
    console.log('****** getSession - CATCH ' + data.statusCode);
    if (data.statusCode === 401) {
      authentication.refreshOAuthToken(req, res).then(function() {
        getSession(req, res);
      }).catch(function(err) {
        console.log('Error refreshing token or getting session: ', err);
      });
    } else {
      console.log('GET /api/users/session error: ', data);
    }
  });
};

var getDocuments = function(req, res) {
  request.get({
    uri: 'https://partner.dev.onshape.com/api/documents',
    headers: {
      'Authorization': 'Bearer ' + req.user.accessToken
    }
  }).then(function(data) {
    res.send(data);
  }).catch(function(data) {
    if (data.statusCode === 401) {
      authentication.refreshOAuthToken(req, res).then(function() {
        getDocuments(req, res);
      }).catch(function(err) {
        console.log('Error refreshing token or getting documents: ', err);
      });
    } else {
      console.log('GET /api/documents error: ', data);
    }
  });
};

var getElementList = function(req, res) {
  var url = 'https://partner.dev.onshape.com/api/documents/d/' + req.query.documentId + '/w/' + req.query.workspaceId + '/elements';
  if (req.query.elementId) {
    url += '/?elementId=' + req.query.elementId;
  }

  request.get({
    uri: url,
    headers: {
      'Authorization': 'Bearer ' + req.user.accessToken
    }
  }).then(function(data) {
    res.send(data);
  }).catch(function(data) {
    console.log('****** getElementList - CATCH ' + data.statusCode);
    if (data.statusCode === 401) {
      authentication.refreshOAuthToken(req, res).then(function() {
        getElementList(req, res);
      }).catch(function(err) {
        console.log('Error refreshing token or getting elements: ', err);
      });
    } else {
      console.log('GET /api/documents/elements error: ', data);
    }
  });
};

var getAssemblyList = function(req, res) {
  var url = 'https://partner.dev.onshape.com/api/documents/d/' + req.query.documentId + '/w/' + req.query.workspaceId + '/elements?elementType=assembly';
  if (req.query.elementId) {
    url += '/?elementId=' + req.query.elementId;
  }

  request.get({
    uri: url,
    headers: {
           'Authorization': 'Bearer ' + req.user.accessToken
         }
  }).then(function(data) {
    res.send(data);
  }).catch(function(data) {
    console.log('****** getAssemblyList - CATCH ' + data.statusCode);
    if (data.statusCode === 401) {
      authentication.refreshOAuthToken(req, res).then(function() {
        getElementList(req, res);
      }).catch(function(err) {
        console.log('Error refreshing token or getting elements: ', err);
      });
    } else {
      console.log('GET /api/documents/elements ?type=assembly error: ', data);
    }
  });
};

var getShadedView = function(req, res) {
  request.get({
    uri: 'https://partner.dev.onshape.com/api/assemblies/d/' + req.query.documentId +
    '/w/' + req.query.workspaceId + '/e/' + req.query.elementId + '/shadedviews?' +
    '&outputHeight=' + req.query.outputHeight + '&outputWidth=' + req.query.outputWidth + '&pixelSize=' + req.query.pixelSize +
    '&viewMatrix=' + req.query.viewMatrix1 + ',' + req.query.viewMatrix2 + ',' + req.query.viewMatrix3 + ',' + req.query.viewMatrix4 +
    ',' + req.query.viewMatrix5 + ',' + req.query.viewMatrix6 + ',' + req.query.viewMatrix7 + ',' + req.query.viewMatrix8 +
    ',' + req.query.viewMatrix9 + ',' + req.query.viewMatrix10 + ',' + req.query.viewMatrix11 + ',' + req.query.viewMatrix12 +
    '&perspective=false',
    headers: {
      'Authorization': 'Bearer ' + req.user.accessToken
    }
  }).then(function(data) {

    res.send(data);
  }).catch(function(data) {
    if (data.statusCode === 401) {
      authentication.refreshOAuthToken(req, res).then(function() {
        getShadedView(req, res);
      }).catch(function(err) {
        console.log('Error refreshing token or getting shaded view: ', err);
      });
    } else {
      console.log('GET /api/assemblies/shadedviews error: ', data);
    }
  });
};

var getBoundingBox = function(req, res) {
  request.get({
    uri: 'https://partner.dev.onshape.com/api/assemblies/d/' + req.query.documentId +
          '/w/' + req.query.workspaceId + '/e/' + req.query.elementId + '/boundingboxes/',
    headers: {
      'Authorization': 'Bearer ' + req.user.accessToken
    }
  }).then(function(data) {
    res.send(data);
  }).catch(function(data) {
    if (data.statusCode === 401) {
      authentication.refreshOAuthToken(req, res).then(function() {
        getBoundingBox(req, res);
      }).catch(function(err) {
        console.log('Error refreshing token or getting bounding box: ', err);
      });
    } else {
      console.log('GET /api/assemblies/boundingbox error: ', data);
    }
  });
};

var getPartsList = function(req, res) {
  request.get({
    uri: 'https://partner.dev.onshape.com/api/parts/d/' + req.query.documentId + '/w/' + req.query.workspaceId,
    headers: {
      'Authorization': 'Bearer ' + req.user.accessToken
    }
  }).then(function(data) {
    res.send(data);
  }).catch(function(data) {
    if (data.statusCode === 401) {
      authentication.refreshOAuthToken(req, res).then(function() {
        getPartsList(req, res);
      }).catch(function(err) {
        console.log('Error refreshing token or getting parts: ', err);
      });
    } else {
      console.log('GET /api/parts/workspace error: ', data);
    }
  });
};

var getAssemblyDefinition = function(req, res) {
  request.get({
    uri: 'https://partner.dev.onshape.com/api/assemblies/d/' + req.query.documentId + '/w/' + req.query.workspaceId + '/e/' + req.query.nextElement + '?includeMateFeatures=false',
    headers: {
      'Authorization': 'Bearer ' + req.user.accessToken
    }
  }).then(function(data) {
    res.send(data);
  }).catch(function(data) {
    if (data.statusCode === 401) {
      authentication.refreshOAuthToken(req, res).then(function() {
        getAssemblyDefinition(req, res);
      }).catch(function(err) {
        console.log('Error refreshing token or getting assembly definition: ', err);
      });
    } else {
      console.log('GET /api/models/assembly/definition error: ', data);
    }
  });
};

var getMetadata = function(req, res) {
   request.get({
    uri: 'https://partner.dev.onshape.com/api/parts/d/' + req.query.documentId + '/w/' + req.query.workspaceId + '/e/' + req.query.elementId + '/partid/' + req.query.partId + '/metadata',
    headers: {
      'Authorization': 'Bearer ' + req.user.accessToken
    }
  }).then(function(data) {
    res.send(data);
  }).catch(function(data) {
    if (data.statusCode === 401) {
      authentication.refreshOAuthToken(req, res).then(function() {
        getMetadata(req, res);
      }).catch(function(err) {
        console.log('Error refreshing token or getting part metadata: ', err);
      });
    } else {
      console.log('GET /api/parts/metadata error: ', data);
    }
  });
};

var setWebhooks = function(req, res) {
  var eventList = [ "onshape.model.lifecycle.changed" ];
  var options = { collapseEvents : true };
  var urlNotify = "https://onshape-app-bom.herokuapp.com/notify";
  var filter = "{$DocumentId} = '" + req.query.documentId + "' && " +
               "{$WorkspaceId} = '" + req.query.workspaceId + "' && " +
               "{$ElementId} = '" + req.query.elementId + "'";

  request.post({
    uri: 'https://partner.dev.onshape.com/api/webhooks/',
    body: {
      url : urlNotify,
      events : eventList,
      filter : filter,
      options : options
    },
    headers: {
      'Authorization': 'Bearer ' + req.user.accessToken
    },
    json : true
  }).then(function(data) {
    res.send(data);
  }).catch(function(data) {
    if (data.statusCode === 401) {
      authentication.refreshOAuthToken(req, res).then(function() {
        setWebhooks(req, res);
      }).catch(function(err) {
        console.log('*** Error refreshing token or setting webhooks: ', err);
      });
    } else {
      console.log('*** POST /api/webhooks error: ', data);
    }
  });
};

var checkModelChange = function(req, res) {
  var data = {
    statusCode : 200,
    change : false
  };

  // Get the current setting from Redis (if there is one)
  var uniqueID = "change" + req.query.elementId;

  console.log("** CHECK State - " + uniqueID + ", " + client);

  client.get(uniqueID, function(err, reply) {
    // reply is null when the key is missing
    if (reply != null) {
      var newParams = JSON.parse(reply);
      data.change = newParams.change;

      console.log("** CLIENT State Check - " + newParams.change);

      // Now that we have the value, clear it in Redis
      var state = {
        elementId : req.query.elementId,
        change : false
      };

      console.log("** RESET State - " + uniqueID);

      var stateString = JSON.stringify(state);
      client.set(uniqueID, stateString);
    }

    res.send(data);
  });
}

router.get('/documents', getDocuments);
router.get('/session', getSession);
router.get('/elements', getElementList);
router.get('/assemblies', getAssemblyList);
router.get('/parts', getPartsList);
router.get('/boundingBox', getBoundingBox);
router.get('/definition', getAssemblyDefinition);
router.get('/shadedView', getShadedView);
router.get('/metadata', getMetadata);
router.get('/webhooks', setWebhooks);
router.get('/modelchange', checkModelChange);

module.exports = router;
<|endoftext|>