<commit_before>import React from 'react';
import Relay from 'react-relay';
import moment from 'moment';
import classNames from 'classnames';

import Bar from './bar';
import BuildTooltip from './build-tooltip';

import { buildTime } from '../../../lib/builds';

const PASSED_COLOR = "#B0DF21";
const PASSED_COLOR_HOVER = "#669611";
const RUNNING_COLOR = "#FFBA03";
const RUNNING_COLOR_HOVER = "#DE8F0C";
const FAILED_COLOR = "#F83F23";
const FAILED_COLOR_HOVER = "#AA0A12";
const SCHEDULED_COLOR = "#BBB";
const SCHEDULED_COLOR_HOVER = "#888";
const PENDING_COLOR = "#DDD";
const PENDING_COLOR_HOVER = "#DDD";

const MAXIMUM_NUMBER_OF_BUILDS = 30;

const BAR_HEIGHT_MINIMUM = 3;
const BAR_WIDTH = 7;
const BAR_SEPERATOR_WIDTH = 1;
const BAR_WIDTH_WITH_SEPERATOR = BAR_WIDTH + BAR_SEPERATOR_WIDTH;

const GRAPH_HEIGHT = 35;
const GRAPH_WIDTH = (BAR_WIDTH * MAXIMUM_NUMBER_OF_BUILDS) + ((MAXIMUM_NUMBER_OF_BUILDS * BAR_SEPERATOR_WIDTH) - 1);

class Graph extends React.Component {
  static propTypes = {
    pipeline: React.PropTypes.shape({
      id: React.PropTypes.string.isRequired,
      builds: React.PropTypes.shape({
        edges: React.PropTypes.arrayOf(
          React.PropTypes.shape({
            node: React.PropTypes.shape({
              state: React.PropTypes.string.isRequired,
              url: React.PropTypes.string.isRequired,
              startedAt: React.PropTypes.string,
              finishedAt: React.PropTypes.string
            }).isRequired
          }).isRequired
        )
      }).isRequired
    }).isRequired
  };

  state = {
    height: 0
  }

  componentDidMount() {
    this.toggleRenderInterval(this.props.pipeline.builds.edges);

    // As soon as the graph has mounted, set the height back to 100% to animate
    // the bars growing into view.
    setTimeout(() => {
      this.setState({ height: "100%" });
    }, 0);
  }

  componentWillReceiveProps(nextProps) {
    const thisLatestBuild = this.props.pipeline.builds.edges[0];
    const nextLatestBuild = nextProps.pipeline.builds.edges[0];

    // Set `shifting` if all the builds are being moved due to a new one coming in.
    if ((thisLatestBuild && nextLatestBuild) && thisLatestBuild.node.id !== nextLatestBuild.node.id) {
      this._shifting = true;
    }
  }

  componentDidUpdate() {
    this.toggleRenderInterval(this.props.pipeline.builds.edges);

    delete this._shifting;
  }

  render() {
    const classes = classNames("animation-height align-bottom relative absolute", {
      "animation-disable": this._shifting
    });

    return (

          {this.renderBars()}

    );
  }

  renderBars() {
    const bars = [];
    let maximumDuration = 1; // 1 to avoid a `0/0` when we calculate percentages

    for (let buildIndex = 0; buildIndex < MAXIMUM_NUMBER_OF_BUILDS; buildIndex++) {
      const buildEdge = this.props.pipeline.builds.edges[buildIndex];

      if (buildEdge) {
        const { from, to } = buildTime(buildEdge.node);
        const duration = moment(to).diff(moment(from));

        if (duration > maximumDuration) {
          maximumDuration = duration;
        }

        bars[MAXIMUM_NUMBER_OF_BUILDS - buildIndex - 1] = {
          color: this.colorForBuild(buildEdge.node),
          hoverColor: this.hoverColorForBuild(buildEdge.node),
          duration,
          href: buildEdge.node.url,
          build: buildEdge.node
        };
      } else {
        bars[MAXIMUM_NUMBER_OF_BUILDS - buildIndex - 1] = {
          color: PENDING_COLOR,
          hoverColor: PENDING_COLOR_HOVER,
          duration: 0
        };
      }
    }

    return bars.map((bar, index) => {
      const left = index * BAR_WIDTH_WITH_SEPERATOR;

      // Calcualte what percentage this bar is in relation to the longest
      // running build
      let height = (bar.duration / maximumDuration);

      // See if the height is less than our minimum. If it is, set a hard pixel
      // height, otherwise make the height a percentage. We use percentages so
      // we can animate the height of the graph as it loads in.
      const heightInPixels = (height * GRAPH_HEIGHT);
      if (heightInPixels < BAR_HEIGHT_MINIMUM) {
        height = BAR_HEIGHT_MINIMUM;
      } else {
        height = `${height * 100}%`;
      }

      return (
        <Bar
          key={index}
          {...bar}
          left={left}
          width={BAR_WIDTH_WITH_SEPERATOR}
          height={height}
        />
      );
    });
  }

  colorForBuild(build) {
    if (build.state === "scheduled") {
      return SCHEDULED_COLOR;
    } else if (build.state === "running") {
      return RUNNING_COLOR;
    } else if (build.state === "passed" || build.state === "blocked") {
      return PASSED_COLOR;
    } else {
      return FAILED_COLOR;
    }
  }

  hoverColorForBuild(build) {
    if (build.state === "scheduled") {
      return SCHEDULED_COLOR_HOVER;
    } else if (build.state === "running") {
      return RUNNING_COLOR_HOVER;
    } else if (build.state === "passed" || build.state === "blocked") {
      return PASSED_COLOR_HOVER;
    } else {
      return FAILED_COLOR_HOVER;
    }
  }

  toggleRenderInterval(buildEdges) {
    // See if there is a build running
    let running = false;
    for (const edge of buildEdges) {
      if (edge.node.state === "running") {
        running = true;
        break;
      }
    }

    // If a build is running, ensure we have an interval setup that re-renders
    // the graph every second.
    if (running) {
      if (this._interval) {
        // no-op, interval already running
      } else {
        this._interval = setInterval(() => {
          this.forceUpdate();
        }, 1000);
      }
    } else {
      // Clear the interval now that nothing is running
      if (this._interval) {
        clearTimeout(this._interval);
        delete this._interval;
      }
    }
  }
}

export default Relay.createContainer(Graph, {
  fragments: {
    pipeline: () => Relay.QL`
      fragment on Pipeline {
        id
        builds(first: 30, branch: "%default", state: [ BUILD_STATE_SCHEDULED, BUILD_STATE_RUNNING, BUILD_STATE_PASSED, BUILD_STATE_FAILED, BUILD_STATE_CANCELED, BUILD_STATE_CANCELING, BUILD_STATE_BLOCKED ]) {
          edges {
            node {
              id
              state
              url
              startedAt
              finishedAt
              ${BuildTooltip.getFragment('build')}
            }
          }
        }
      }
    `
  }
});
<commit_msg>Correct GraphQL fetch for pipeline graph<commit_after>import React from 'react';
import Relay from 'react-relay';
import moment from 'moment';
import classNames from 'classnames';

import Bar from './bar';
import BuildTooltip from './build-tooltip';

import { buildTime } from '../../../lib/builds';

const PASSED_COLOR = "#B0DF21";
const PASSED_COLOR_HOVER = "#669611";
const RUNNING_COLOR = "#FFBA03";
const RUNNING_COLOR_HOVER = "#DE8F0C";
const FAILED_COLOR = "#F83F23";
const FAILED_COLOR_HOVER = "#AA0A12";
const SCHEDULED_COLOR = "#BBB";
const SCHEDULED_COLOR_HOVER = "#888";
const PENDING_COLOR = "#DDD";
const PENDING_COLOR_HOVER = "#DDD";

const MAXIMUM_NUMBER_OF_BUILDS = 30;

const BAR_HEIGHT_MINIMUM = 3;
const BAR_WIDTH = 7;
const BAR_SEPERATOR_WIDTH = 1;
const BAR_WIDTH_WITH_SEPERATOR = BAR_WIDTH + BAR_SEPERATOR_WIDTH;

const GRAPH_HEIGHT = 35;
const GRAPH_WIDTH = (BAR_WIDTH * MAXIMUM_NUMBER_OF_BUILDS) + ((MAXIMUM_NUMBER_OF_BUILDS * BAR_SEPERATOR_WIDTH) - 1);

class Graph extends React.Component {
  static propTypes = {
    pipeline: React.PropTypes.shape({
      id: React.PropTypes.string.isRequired,
      builds: React.PropTypes.shape({
        edges: React.PropTypes.arrayOf(
          React.PropTypes.shape({
            node: React.PropTypes.shape({
              state: React.PropTypes.string.isRequired,
              url: React.PropTypes.string.isRequired,
              startedAt: React.PropTypes.string,
              finishedAt: React.PropTypes.string,
              canceledAt: React.PropTypes.string,
              scheduledAt: React.PropTypes.string
            }).isRequired
          }).isRequired
        )
      }).isRequired
    }).isRequired
  };

  state = {
    height: 0
  }

  componentDidMount() {
    this.toggleRenderInterval(this.props.pipeline.builds.edges);

    // As soon as the graph has mounted, set the height back to 100% to animate
    // the bars growing into view.
    setTimeout(() => {
      this.setState({ height: "100%" });
    }, 0);
  }

  componentWillReceiveProps(nextProps) {
    const thisLatestBuild = this.props.pipeline.builds.edges[0];
    const nextLatestBuild = nextProps.pipeline.builds.edges[0];

    // Set `shifting` if all the builds are being moved due to a new one coming in.
    if ((thisLatestBuild && nextLatestBuild) && thisLatestBuild.node.id !== nextLatestBuild.node.id) {
      this._shifting = true;
    }
  }

  componentDidUpdate() {
    this.toggleRenderInterval(this.props.pipeline.builds.edges);

    delete this._shifting;
  }

  render() {
    const classes = classNames("animation-height align-bottom relative absolute", {
      "animation-disable": this._shifting
    });

    return (

          {this.renderBars()}

    );
  }

  renderBars() {
    const bars = [];
    let maximumDuration = 1; // 1 to avoid a `0/0` when we calculate percentages

    for (let buildIndex = 0; buildIndex < MAXIMUM_NUMBER_OF_BUILDS; buildIndex++) {
      const buildEdge = this.props.pipeline.builds.edges[buildIndex];

      if (buildEdge) {
        const { from, to } = buildTime(buildEdge.node);
        const duration = moment(to).diff(moment(from));

        if (duration > maximumDuration) {
          maximumDuration = duration;
        }

        bars[MAXIMUM_NUMBER_OF_BUILDS - buildIndex - 1] = {
          color: this.colorForBuild(buildEdge.node),
          hoverColor: this.hoverColorForBuild(buildEdge.node),
          duration,
          href: buildEdge.node.url,
          build: buildEdge.node
        };
      } else {
        bars[MAXIMUM_NUMBER_OF_BUILDS - buildIndex - 1] = {
          color: PENDING_COLOR,
          hoverColor: PENDING_COLOR_HOVER,
          duration: 0
        };
      }
    }

    return bars.map((bar, index) => {
      const left = index * BAR_WIDTH_WITH_SEPERATOR;

      // Calcualte what percentage this bar is in relation to the longest
      // running build
      let height = (bar.duration / maximumDuration);

      // See if the height is less than our minimum. If it is, set a hard pixel
      // height, otherwise make the height a percentage. We use percentages so
      // we can animate the height of the graph as it loads in.
      const heightInPixels = (height * GRAPH_HEIGHT);
      if (heightInPixels < BAR_HEIGHT_MINIMUM) {
        height = BAR_HEIGHT_MINIMUM;
      } else {
        height = `${height * 100}%`;
      }

      return (
        <Bar
          key={index}
          {...bar}
          left={left}
          width={BAR_WIDTH_WITH_SEPERATOR}
          height={height}
        />
      );
    });
  }

  colorForBuild(build) {
    if (build.state === "scheduled") {
      return SCHEDULED_COLOR;
    } else if (build.state === "running") {
      return RUNNING_COLOR;
    } else if (build.state === "passed" || build.state === "blocked") {
      return PASSED_COLOR;
    } else {
      return FAILED_COLOR;
    }
  }

  hoverColorForBuild(build) {
    if (build.state === "scheduled") {
      return SCHEDULED_COLOR_HOVER;
    } else if (build.state === "running") {
      return RUNNING_COLOR_HOVER;
    } else if (build.state === "passed" || build.state === "blocked") {
      return PASSED_COLOR_HOVER;
    } else {
      return FAILED_COLOR_HOVER;
    }
  }

  toggleRenderInterval(buildEdges) {
    // See if there is a build running
    let running = false;
    for (const edge of buildEdges) {
      if (edge.node.state === "running") {
        running = true;
        break;
      }
    }

    // If a build is running, ensure we have an interval setup that re-renders
    // the graph every second.
    if (running) {
      if (this._interval) {
        // no-op, interval already running
      } else {
        this._interval = setInterval(() => {
          this.forceUpdate();
        }, 1000);
      }
    } else {
      // Clear the interval now that nothing is running
      if (this._interval) {
        clearTimeout(this._interval);
        delete this._interval;
      }
    }
  }
}

export default Relay.createContainer(Graph, {
  fragments: {
    pipeline: () => Relay.QL`
      fragment on Pipeline {
        id
        builds(first: 30, branch: "%default", state: [ BUILD_STATE_SCHEDULED, BUILD_STATE_RUNNING, BUILD_STATE_PASSED, BUILD_STATE_FAILED, BUILD_STATE_CANCELED, BUILD_STATE_CANCELING, BUILD_STATE_BLOCKED ]) {
          edges {
            node {
              id
              state
              url
              startedAt
              finishedAt
              canceledAt
              scheduledAt
              ${BuildTooltip.getFragment('build')}
            }
          }
        }
      }
    `
  }
});
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
	errorHandler = require('./errors.server.controller'),
	Article = mongoose.model('Article'),
	_ = require('lodash');

/**
 * Create a article
 */
exports.create = function(req, res) {
	var article = new Article(req.body);
	article.user = req.user;

	article.save(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.json(article);
		}
	});
};

/**
 * Show the current article
 */
exports.read = function(req, res) {
	res.json(req.article);
};

/**
 * Update a article
 */
exports.update = function(req, res) {
	var article = req.article;

	article = _.extend(article, req.body);

	article.save(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.json(article);
		}
	});
};

/**
 * Delete an article
 */
exports.delete = function(req, res) {
	var article = req.article;

	article.remove(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.json(article);
		}
	});
};

/**
 * List of Articles
 */
exports.list = function(req, res) {
	Article.find().sort('-created').populate('user', 'displayName').exec(function(err, articles) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.json(articles);
		}
	});
};

/**
 * Article middleware
 */
exports.articleByID = function(req, res, next, id) {
	Article.findById(id).populate('user', 'displayName').exec(function(err, article) {
		if (err) return next(err);
		if (!article) return next(new Error('Failed to load article ' + id));
		req.article = article;
		next();
	});
};

/**
 * Article authorization middleware
 */
exports.hasAuthorization = function(req, res, next) {
	if (req.article.user.id !== req.user.id) {
		return res.status(403).send({
			message: 'User is not authorized'
		});
	}
	next();
};<commit_msg>Cambios a articulos<commit_after>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
	errorHandler = require('./errors.server.controller'),
	Article = mongoose.model('Article'),
	_ = require('lodash');

/**
 * Create a article
 */
exports.create = function(req, res) {
	var article = new Article(req.body);
	article.user = req.user;

	article.save(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			console.log("Guarda el articulo");
			res.json(article);
		}
	});
};

/**
 * Show the current article
 */
exports.read = function(req, res) {
	res.json(req.article);
};

/**
 * Update a article
 */
exports.update = function(req, res) {
	var article = req.article;

	article = _.extend(article, req.body);

	article.save(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.json(article);
		}
	});
};

/**
 * Delete an article
 */
exports.delete = function(req, res) {
	var article = req.article;

	article.remove(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.json(article);
		}
	});
};

/**
 * List of Articles
 */
exports.list = function(req, res) {
	Article.find().sort('-created').populate('user', 'displayName').exec(function(err, articles) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.json(articles);
		}
	});
};

/**
 * Article middleware
 */
exports.articleByID = function(req, res, next, id) {
	Article.findById(id).populate('user', 'displayName').exec(function(err, article) {
		if (err) return next(err);
		if (!article) return next(new Error('Failed to load article ' + id));
		req.article = article;
		next();
	});
};

/**
 * Article authorization middleware
 */
exports.hasAuthorization = function(req, res, next) {
	if (req.article.user.id !== req.user.id) {
		return res.status(403).send({
			message: 'User is not authorized'
		});
	}
	next();
};<|endoftext|>


<commit_before>function emptyTableOnError(tableId, colspan) {
    'use strict';

    var localId = tableId;
    if (tableId[0] !== '#') {
        localId = '#' + tableId;
    }

    $(localId).empty().append(
        '
' +
        '
Error loading data.

'
    );
}

$(document).ready(function() {
    'use strict';

    $('#li-home').addClass('active');
    $('body').tooltip({
        'selector': '[rel=tooltip]',
        'placement': 'auto'
    });

    $('.clickable-table tbody').on('click', 'tr', function() {
        var url = $(this).data('url');
        if (url) {
            window.location = url;
        }
    });
});

$(document).ready(function() {
    'use strict';

    var errorReason = '',
        ajaxDeferredCall = null;

    function countFailCallback() {
        $('.fail-badge').each(function() {
            $(this).empty().append('∞');
        });
    }

    function countDoneCallback(data) {
        var localData = data.result,
            len = localData.length,
            i = 0,
            batchResult = null;

        if (len > 0) {
            if (len === 1) {
                $('#fail-count0').empty().append(localData[0].count);
            } else {
                for (i; i < len; i++) {
                    batchResult = localData[i].result[0];
                    $(localData[i].operation_id).empty().append(
                        batchResult.count
                    );
                }
            }
        } else {
            countFailCallback();
        }
    }

    function countFailedDefconfigs(data) {
        var localData = data.result,
            i = 0,
            len = localData.length,
            deferredCall = null,
            batchQueries = new Array(len);

        if (len > 0) {
            if (len === 1) {
                errorReason = 'Defconfig data call failed.';
                // Peform normal GET.
                deferredCall = $.ajax({
                    'url': '/_ajax/count/defconfig',
                    'traditional': true,
                    'cache': true,
                    'dataType': 'json',
                    'data': {
                        'status': 'FAIL',
                        'job': localData[0].job,
                        'kernel': localData[0].kernel
                    },
                    'beforeSend': function(jqXHR) {
                        setXhrHeader(jqXHR);
                    },
                    'error': function() {
                        countFailCallback();
                    },
                    'timeout': 6000,
                    'statusCode': {
                        403: function() {
                            setErrorAlert('batch-403-error', 403, errorReason);
                        },
                        404: function() {
                            setErrorAlert('batch-404-error', 404, errorReason);
                        },
                        408: function() {
                            errorReason = 'Defconfing data call failed: ' +
                                'timeout.';
                            setErrorAlert('batch-408-error', 408, errorReason);
                        },
                        500: function() {
                            setErrorAlert('batch-500-error', 500, errorReason);
                        }
                    }
                });
            } else {
                // Perform POST on batch API.
                for (i; i < len; i++) {
                    batchQueries[i] = {
                        'method': 'GET',
                        'operation_id': '#fail-count' + i,
                        'collection': 'count',
                        'document_id': 'defconfig',
                        'query': 'status=FAIL&job=' + localData[i].job +
                            '&kernel=' + localData[i].kernel
                    };
                }

                errorReason = 'Batch count failed.';
                deferredCall = $.ajax({
                    'url': '/_ajax/batch',
                    'type': 'POST',
                    'traditional': true,
                    'dataType': 'json',
                    'headers': {
                        'Content-Type': 'application/json'
                    },
                    'beforeSend': function(jqXHR) {
                        setXhrHeader(jqXHR);
                    },
                    'data': JSON.stringify({
                        'batch': batchQueries
                    }),
                    'error': function() {
                        countFailCallback();
                    },
                    'timeout': 10000,
                    'statusCode': {
                        403: function() {
                            setErrorAlert('batch-403-error', 403, errorReason);
                        },
                        404: function() {
                            setErrorAlert('batch-404-error', 404, errorReason);
                        },
                        408: function() {
                            errorReason = 'Batch count failed: timeout.';
                            setErrorAlert('batch-408-error', 408, errorReason);
                        },
                        500: function() {
                            setErrorAlert('batch-500-error', 500, errorReason);
                        }
                    }
                });
            }

            $.when(deferredCall).then(countDoneCallback, countFailCallback);
        }
    }

    function populateFailedDefconfigTable(data) {
        var localData = data.result,
            row = '',
            job,
            created,
            col1,
            col2,
            col3,
            col4,
            col5,
            href,
            kernel,
            git_branch,
            i = 0,
            len = localData.length;

        if (len === 0) {
            row = '

' +
                'No failed builds.

';
            $('#failed-builds-body').empty().append(row);
        } else {
            for (i; i < len; i++) {
                job = localData[i].job;
                kernel = localData[i].kernel;
                git_branch = localData[i].git_branch;
                created = new Date(localData[i].created_on['$date']);
                href = '/build/' + job + '/kernel/' + kernel + '/';

                col1 = '' + job + ' ‐ ' +
                    git_branch + '	';
                col2 = '' + kernel + '	';
                col3 = '' +
                    '' +
                    '' +
                    '' +
                    '	';
                col4 = '' +
                    created.getCustomISODate() + '	';
                col5 = '' +
                    '' +
                    '' +
                    '' +
                    '	';
                row += '
' +
                    col1 + col2 + col3 + col4 + col5 + '
';
            }

            $('#failed-builds-body').empty().append(row);
        }
    }

    errorReason = 'Defconfig data call failed.';
    ajaxDeferredCall = $.ajax({
        'url': '/_ajax/defconf',
        'traditional': true,
        'cache': true,
        'dataType': 'json',
        'data': {
            'aggregate': 'kernel',
            'status': 'FAIL',
            'sort': 'created_on',
            'sort_order': -1,
            'limit': 25,
            'date_range': $('#date-range').val(),
            'field': ['job', 'kernel', 'created_on', 'git_branch']
        },
        'beforeSend': function(jqXHR) {
            setXhrHeader(jqXHR);
        },
        'error': function() {
            emptyTableOnError('#failed-builds-body', 5);
        },
        'timeout': 6000,
        'statusCode': {
            403: function() {
                setErrorAlert('defconfs-403-error', 403, errorReason);
            },
            404: function() {
                setErrorAlert('defconfs-404-error', 404, errorReason);
            },
            408: function() {
                errorReason = 'Defconfing data call failed: timeout.';
                setErrorAlert('defconfs-408-error', 408, errorReason);
            },
            500: function() {
                setErrorAlert('defconfs-500-error', 500, errorReason);
            }
        }
    }).done(populateFailedDefconfigTable);

    $.when(ajaxDeferredCall).then(countFailedDefconfigs, countFailCallback);
});

$(document).ready(function() {
    'use strict';

    var errorReason = 'Job data call failed.',
        ajaxCall;

    function populateJobsTalbe(data) {
        var localData = data.result,
            row = '',
            created,
            col1,
            col2,
            col3,
            href,
            job,
            git_branch,
            i = 0,
            len = localData.length;

        if (len === 0) {
            row = '

' +
                'No failed jobs.

';
            $('#failed-jobs-body').empty().append(row);
        } else {
            for (i; i < len; i++) {
                created = new Date(localData[i].created_on['$date']);
                job = localData[i].job;
                git_branch = localData[i].git_branch;
                href = '/job/' + job + '/';

                col1 = '' +
                    job + ' ‐ ' +
                    git_branch + '' + '	';
                col2 = '' +
                    created.getCustomISODate() + '	';
                col3 = '' +
                    '' +
                    '' +
                    '' +
                    '	';
                row = '
' +
                    col1 + col2 + col3 + '
';
            }

            $('#failed-jobs-body').empty().append(row);
        }
    }

    ajaxCall = $.ajax({
        'url': '/_ajax/job',
        'dataType': 'json',
        'traditional': true,
        'cache': true,
        'data': {
            'status': 'FAIL',
            'sort': 'created_on',
            'sort_order': -1,
            'limit': 25,
            'date_range': $('#date-range').val(),
            'field': ['job', 'git_branch', 'created_on']
        },
        'beforeSend': function(jqXHR) {
            setXhrHeader(jqXHR);
        },
        'error': function(jqXHR,  textStatus, errorThrown) {
            emptyTableOnError('#failed-jobs-body', 3);
        },
        'timeout': 6000,
        'statusCode': {
            403: function() {
                setErrorAlert('jobs-403-error', 403, errorReason);
            },
            404: function() {
                setErrorAlert('jobs-404-error', 404, errorReason);
            },
            408: function() {
                errorReason = 'Job data call failed: timeout.';
                setErrorAlert('jobs-408-error', 408, errorReason);
            },
            500: function() {
                setErrorAlert('jobs-500-error', 500, errorReason);
            }
        }
    });

    $.when(ajaxCall).then(populateJobsTalbe);
});

$(document).ready(function() {
    'use strict';

    var errorReason = 'Boot data call failed.',
        colSpan = 8;

    $.ajax({
        'url': '/_ajax/boot',
        'traditional': true,
        'cache': true,
        'dataType': 'json',
        'context': $('#failed-boots-body'),
        'data': {
            'status': 'FAIL',
            'sort_order': -1,
            'sort': 'created_on',
            'limit': 25,
            'date_range': $('#date-range').val(),
            'field': [
                'board', 'job', 'kernel', 'defconfig', 'created_on',
                'boot_result_description', 'lab_name', '_id'
            ]
        },
        'beforeSend': function(jqXHR) {
            setXhrHeader(jqXHR);
        },
        'error': function() {
            emptyTableOnError('#failed-boots-body', colSpan);
        },
        'timeout': 6000,
        'statusCode': {
            403: function() {
                setErrorAlert('boots-403-error', 403, errorReason);
            },
            404: function() {
                setErrorAlert('boots-404-error', 404, errorReason);
            },
            408: function() {
                errorReason = 'Boot data call failed: timeout.';
                setErrorAlert('boots-408-error', 408, errorReason);
            },
            500: function() {
                setErrorAlert('boots-500-error', 500, errorReason);
            }
        }
    }).done(function(data) {
        var localData = data.result,
            row = '',
            created,
            board,
            job,
            kernel,
            defconfig,
            labName,
            bootId,
            col1,
            col2,
            col3,
            col4,
            col5,
            col6,
            col7,
            col8,
            href,
            len = localData.length,
            col6Content,
            failureReason = null,
            i = 0;

        if (len === 0) {
            row = '

' +
                'No failed boot reports.

';
            $(this).empty().append(row);
        } else {
            for (i; i < len; i++) {
                failureReason = localData[i].boot_result_description;
                if (failureReason === null) {
                    col6Content = '' +
                        '' +
                        '' +
                        '	';
                } else {
                    col6Content = '' +
                        '' +
                        '' +
                        '	';
                }

                created = new Date(localData[i].created_on['$date']);
                job = localData[i].job;
                kernel = localData[i].kernel;
                board = localData[i].board;
                defconfig = localData[i].defconfig;
                labName = localData[i].lab_name;
                bootId = localData[i]._id;
                href = '/boot/' + board + '/job/' + job + '/kernel/' +
                    kernel + '/defconfig/' + defconfig + '/lab/' + labName +
                    '/?_id=' + bootId['$oid'];

                col1 = '' +
                    job + '	';
                col2 = '' + kernel + '	';
                col3 = '' + board + '	';
                col4 = '' + defconfig + '	';
                col5 = '' + labName + '	';
                col6 = col6Content;
                col7 = '' +
                    created.getCustomISODate() + '	';
                col8 = '' +
                    '' +
                    '' +
                    '' +
                    '	';
                row += '
' +
                    col1 + col2 + col3 + col4 + col5 + col6 + col7 + col8 +
                    '
';
            }

            $(this).empty().append(row);
        }
    });
});
<commit_msg>index: Make defconfing shorter.<commit_after>function emptyTableOnError(tableId, colspan) {
    'use strict';

    var localId = tableId;
    if (tableId[0] !== '#') {
        localId = '#' + tableId;
    }

    $(localId).empty().append(
        '
' +
        '
Error loading data.

'
    );
}

$(document).ready(function() {
    'use strict';

    $('#li-home').addClass('active');
    $('body').tooltip({
        'selector': '[rel=tooltip]',
        'placement': 'auto'
    });

    $('.clickable-table tbody').on('click', 'tr', function() {
        var url = $(this).data('url');
        if (url) {
            window.location = url;
        }
    });
});

$(document).ready(function() {
    'use strict';

    var errorReason = '',
        ajaxDeferredCall = null;

    function countFailCallback() {
        $('.fail-badge').each(function() {
            $(this).empty().append('∞');
        });
    }

    function countDoneCallback(data) {
        var localData = data.result,
            len = localData.length,
            i = 0,
            batchResult = null;

        if (len > 0) {
            if (len === 1) {
                $('#fail-count0').empty().append(localData[0].count);
            } else {
                for (i; i < len; i++) {
                    batchResult = localData[i].result[0];
                    $(localData[i].operation_id).empty().append(
                        batchResult.count
                    );
                }
            }
        } else {
            countFailCallback();
        }
    }

    function countFailedDefconfigs(data) {
        var localData = data.result,
            i = 0,
            len = localData.length,
            deferredCall = null,
            batchQueries = new Array(len);

        if (len > 0) {
            if (len === 1) {
                errorReason = 'Defconfig data call failed.';
                // Peform normal GET.
                deferredCall = $.ajax({
                    'url': '/_ajax/count/defconfig',
                    'traditional': true,
                    'cache': true,
                    'dataType': 'json',
                    'data': {
                        'status': 'FAIL',
                        'job': localData[0].job,
                        'kernel': localData[0].kernel
                    },
                    'beforeSend': function(jqXHR) {
                        setXhrHeader(jqXHR);
                    },
                    'error': function() {
                        countFailCallback();
                    },
                    'timeout': 6000,
                    'statusCode': {
                        403: function() {
                            setErrorAlert('batch-403-error', 403, errorReason);
                        },
                        404: function() {
                            setErrorAlert('batch-404-error', 404, errorReason);
                        },
                        408: function() {
                            errorReason = 'Defconfing data call failed: ' +
                                'timeout.';
                            setErrorAlert('batch-408-error', 408, errorReason);
                        },
                        500: function() {
                            setErrorAlert('batch-500-error', 500, errorReason);
                        }
                    }
                });
            } else {
                // Perform POST on batch API.
                for (i; i < len; i++) {
                    batchQueries[i] = {
                        'method': 'GET',
                        'operation_id': '#fail-count' + i,
                        'collection': 'count',
                        'document_id': 'defconfig',
                        'query': 'status=FAIL&job=' + localData[i].job +
                            '&kernel=' + localData[i].kernel
                    };
                }

                errorReason = 'Batch count failed.';
                deferredCall = $.ajax({
                    'url': '/_ajax/batch',
                    'type': 'POST',
                    'traditional': true,
                    'dataType': 'json',
                    'headers': {
                        'Content-Type': 'application/json'
                    },
                    'beforeSend': function(jqXHR) {
                        setXhrHeader(jqXHR);
                    },
                    'data': JSON.stringify({
                        'batch': batchQueries
                    }),
                    'error': function() {
                        countFailCallback();
                    },
                    'timeout': 10000,
                    'statusCode': {
                        403: function() {
                            setErrorAlert('batch-403-error', 403, errorReason);
                        },
                        404: function() {
                            setErrorAlert('batch-404-error', 404, errorReason);
                        },
                        408: function() {
                            errorReason = 'Batch count failed: timeout.';
                            setErrorAlert('batch-408-error', 408, errorReason);
                        },
                        500: function() {
                            setErrorAlert('batch-500-error', 500, errorReason);
                        }
                    }
                });
            }

            $.when(deferredCall).then(countDoneCallback, countFailCallback);
        }
    }

    function populateFailedDefconfigTable(data) {
        var localData = data.result,
            row = '',
            job,
            created,
            col1,
            col2,
            col3,
            col4,
            col5,
            href,
            kernel,
            git_branch,
            i = 0,
            len = localData.length;

        if (len === 0) {
            row = '

' +
                'No failed builds.

';
            $('#failed-builds-body').empty().append(row);
        } else {
            for (i; i < len; i++) {
                job = localData[i].job;
                kernel = localData[i].kernel;
                git_branch = localData[i].git_branch;
                created = new Date(localData[i].created_on['$date']);
                href = '/build/' + job + '/kernel/' + kernel + '/';

                col1 = '' + job + ' ‐ ' +
                    git_branch + '	';
                col2 = '' + kernel + '	';
                col3 = '' +
                    '' +
                    '' +
                    '' +
                    '	';
                col4 = '' +
                    created.getCustomISODate() + '	';
                col5 = '' +
                    '' +
                    '' +
                    '' +
                    '	';
                row += '
' +
                    col1 + col2 + col3 + col4 + col5 + '
';
            }

            $('#failed-builds-body').empty().append(row);
        }
    }

    errorReason = 'Defconfig data call failed.';
    ajaxDeferredCall = $.ajax({
        'url': '/_ajax/defconf',
        'traditional': true,
        'cache': true,
        'dataType': 'json',
        'data': {
            'aggregate': 'kernel',
            'status': 'FAIL',
            'sort': 'created_on',
            'sort_order': -1,
            'limit': 25,
            'date_range': $('#date-range').val(),
            'field': ['job', 'kernel', 'created_on', 'git_branch']
        },
        'beforeSend': function(jqXHR) {
            setXhrHeader(jqXHR);
        },
        'error': function() {
            emptyTableOnError('#failed-builds-body', 5);
        },
        'timeout': 6000,
        'statusCode': {
            403: function() {
                setErrorAlert('defconfs-403-error', 403, errorReason);
            },
            404: function() {
                setErrorAlert('defconfs-404-error', 404, errorReason);
            },
            408: function() {
                errorReason = 'Defconfing data call failed: timeout.';
                setErrorAlert('defconfs-408-error', 408, errorReason);
            },
            500: function() {
                setErrorAlert('defconfs-500-error', 500, errorReason);
            }
        }
    }).done(populateFailedDefconfigTable);

    $.when(ajaxDeferredCall).then(countFailedDefconfigs, countFailCallback);
});

$(document).ready(function() {
    'use strict';

    var errorReason = 'Job data call failed.',
        ajaxCall;

    function populateJobsTalbe(data) {
        var localData = data.result,
            row = '',
            created,
            col1,
            col2,
            col3,
            href,
            job,
            git_branch,
            i = 0,
            len = localData.length;

        if (len === 0) {
            row = '

' +
                'No failed jobs.

';
            $('#failed-jobs-body').empty().append(row);
        } else {
            for (i; i < len; i++) {
                created = new Date(localData[i].created_on['$date']);
                job = localData[i].job;
                git_branch = localData[i].git_branch;
                href = '/job/' + job + '/';

                col1 = '' +
                    job + ' ‐ ' +
                    git_branch + '' + '	';
                col2 = '' +
                    created.getCustomISODate() + '	';
                col3 = '' +
                    '' +
                    '' +
                    '' +
                    '	';
                row = '
' +
                    col1 + col2 + col3 + '
';
            }

            $('#failed-jobs-body').empty().append(row);
        }
    }

    ajaxCall = $.ajax({
        'url': '/_ajax/job',
        'dataType': 'json',
        'traditional': true,
        'cache': true,
        'data': {
            'status': 'FAIL',
            'sort': 'created_on',
            'sort_order': -1,
            'limit': 25,
            'date_range': $('#date-range').val(),
            'field': ['job', 'git_branch', 'created_on']
        },
        'beforeSend': function(jqXHR) {
            setXhrHeader(jqXHR);
        },
        'error': function(jqXHR,  textStatus, errorThrown) {
            emptyTableOnError('#failed-jobs-body', 3);
        },
        'timeout': 6000,
        'statusCode': {
            403: function() {
                setErrorAlert('jobs-403-error', 403, errorReason);
            },
            404: function() {
                setErrorAlert('jobs-404-error', 404, errorReason);
            },
            408: function() {
                errorReason = 'Job data call failed: timeout.';
                setErrorAlert('jobs-408-error', 408, errorReason);
            },
            500: function() {
                setErrorAlert('jobs-500-error', 500, errorReason);
            }
        }
    });

    $.when(ajaxCall).then(populateJobsTalbe);
});

$(document).ready(function() {
    'use strict';

    var errorReason = 'Boot data call failed.',
        colSpan = 8;

    $.ajax({
        'url': '/_ajax/boot',
        'traditional': true,
        'cache': true,
        'dataType': 'json',
        'context': $('#failed-boots-body'),
        'data': {
            'status': 'FAIL',
            'sort_order': -1,
            'sort': 'created_on',
            'limit': 25,
            'date_range': $('#date-range').val(),
            'field': [
                'board', 'job', 'kernel', 'defconfig', 'created_on',
                'boot_result_description', 'lab_name', '_id'
            ]
        },
        'beforeSend': function(jqXHR) {
            setXhrHeader(jqXHR);
        },
        'error': function() {
            emptyTableOnError('#failed-boots-body', colSpan);
        },
        'timeout': 6000,
        'statusCode': {
            403: function() {
                setErrorAlert('boots-403-error', 403, errorReason);
            },
            404: function() {
                setErrorAlert('boots-404-error', 404, errorReason);
            },
            408: function() {
                errorReason = 'Boot data call failed: timeout.';
                setErrorAlert('boots-408-error', 408, errorReason);
            },
            500: function() {
                setErrorAlert('boots-500-error', 500, errorReason);
            }
        }
    }).done(function(data) {
        var localData = data.result,
            row = '',
            created,
            board,
            job,
            kernel,
            defconfig,
            labName,
            bootId,
            col1,
            col2,
            col3,
            col4,
            col5,
            col6,
            col7,
            col8,
            href,
            len = localData.length,
            col6Content,
            failureReason = null,
            i = 0;

        if (len === 0) {
            row = '

' +
                'No failed boot reports.

';
            $(this).empty().append(row);
        } else {
            for (i; i < len; i++) {
                failureReason = localData[i].boot_result_description;
                if (failureReason === null) {
                    col6Content = '' +
                        '' +
                        '' +
                        '	';
                } else {
                    col6Content = '' +
                        '' +
                        '' +
                        '	';
                }

                created = new Date(localData[i].created_on['$date']);
                job = localData[i].job;
                kernel = localData[i].kernel;
                board = localData[i].board;
                defconfig = localData[i].defconfig;
                labName = localData[i].lab_name;
                bootId = localData[i]._id;
                href = '/boot/' + board + '/job/' + job + '/kernel/' +
                    kernel + '/defconfig/' + defconfig + '/lab/' + labName +
                    '/?_id=' + bootId['$oid'];

                if (defconfig.length > 33) {
                    defconfig = '' +
                        defconfig.slice(0, 33) + '…';
                }

                col1 = '' +
                    job + '	';
                col2 = '' + kernel + '	';
                col3 = '' + board + '	';
                col4 = '' + defconfig + '	';
                col5 = '' + labName + '	';
                col6 = col6Content;
                col7 = '' +
                    created.getCustomISODate() + '	';
                col8 = '' +
                    '' +
                    '' +
                    '' +
                    '	';
                row += '
' +
                    col1 + col2 + col3 + col4 + col5 + col6 + col7 + col8 +
                    '
';
            }

            $(this).empty().append(row);
        }
    });
});
<|endoftext|>


<commit_before>import PIXI from 'pixi.js';
import CraftyBlockSpec from './CraftyBlockSpec.js';
import * as BLOCK_CONST from '../../constants/BlockConstants.js';
import * as LINE_CONST from '../../constants/LineConstants.js';
import CraftyBlockAnimator from './CraftyBlockAnimator.js';

export default class CraftyBlock extends PIXI.Container {
    constructor(blockInfo) {
        super();
        this.id = "block";
        this.blockInfo = blockInfo;
        this.childBlocks = [];
        this.parameterBlocks = [];
        this.lines = [];

        this.initialize();

        //console.log(`DEBUG:::Created {${this.name}} block`);
    }

    /**
     * Convenient getters for block properties
     */
    get name() {
        return this.blockInfo.name;
    }
    get type() {
        return this.blockInfo.type;
    }
    get parameters() {
        return this.blockInfo.parameters;
    }

    /**
     * Convenient initializers for each block types
     */
    static constantWithValue(value) {
        let blockInfo = new CraftyBlockSpec(value, CraftyBlock.CONSTANT);
        return new CraftyBlock(blockInfo);
    }
    static functionWithName(name) {
        let blockInfo = new CraftyBlockSpec.functionWithName(name);
        return new CraftyBlock(blockInfo);
    }
    static parameterWithName(name) {
        let blockInfo = new CraftyBlockSpec(name, CraftyBlock.PARAMETER);
        return new CraftyBlock(blockInfo);
    }

    /**
     * Initializer for CraftyBlock
     *
     * Creates block text, graphics, parameter blocks, and calls makeInteractive();
     */
    initialize() {
        //  Create text and set style
        let text = new PIXI.Text(
            this.name,
            BLOCK_CONST.TEXT_STYLE
        );
        text.position.set(BLOCK_CONST.PADDING_H,BLOCK_CONST.PADDING_V);

        //  Create block graphics and set style
        let blockGraphics = new PIXI.Graphics();
        if (this.type == CraftyBlock.PARAMETER) { // if block is parameter, apply different style
            blockGraphics.beginFill(BLOCK_CONST.TYPE_PARAMETER_COLOR, BLOCK_CONST.OPACITY);
        } else if (this.type == CraftyBlock.CONSTANT) {
            blockGraphics.beginFill(BLOCK_CONST.TYPE_CONSTANT_COLOR, BLOCK_CONST.OPACITY);
        } else {
            blockGraphics.beginFill(BLOCK_CONST.TYPE_FUNCTION_COLOR, BLOCK_CONST.OPACITY);
        }
        blockGraphics.drawRoundedRect(0,0,text.width + 2 * BLOCK_CONST.PADDING_H, text.height + 2 * BLOCK_CONST.PADDING_V, BLOCK_CONST.CORNER_RADIUS);
        blockGraphics.endFill();

        //  Add PIXI Objects to parent container
        this.addChild(blockGraphics);
        this.addChild(text);

        //  Add parameter block to this(block) and make it invisible
        this.parameters.forEach((name) => {
            const newBlock = CraftyBlock.parameterWithName(name);
            this.addChild(newBlock);
            this.parameterBlocks.push(newBlock);
            newBlock.visible = false;
        });

        //  set interactivity of blocks
        this.makeInteractive();
    }

    /**
     * Returns a clone of current block
     */
    clone() {
        let blockCopy = new CraftyBlock(this.blockInfo);
        blockCopy.position = this.position.clone();;

        return blockCopy;
    }

    /**
     * Makes the block interactive
     */
    makeInteractive() {
        const events = CraftyBlockAnimator;

        this.interactive = true;
        this.hitArea = this.getChildAt(0).getBounds().clone();
        //console.log(`DEBUG:::interactivity enabled for {${this.name}}`);

        //  enable drag and drop for non-parameter blocks, enable mouse over check for parameter blocks
        if (this.type != CraftyBlock.PARAMETER) {
            this
                .on('mousedown', events.onDragStart.bind(events))
                .on('touchstart', events.onDragStart.bind(events))
                .on('mousemove', events.onDragMove.bind(events))
                .on('touchmove', events.onDragMove.bind(events))
                .on('mouseup', events.onDragEnd.bind(events))
                .on('mouseupoutside', events.onDragEnd.bind(events))
                .on('touchend', events.onDragEnd.bind(events))
                .on('touchendoutside', events.onDragEnd.bind(events));
        } else {
            this
                .on('mousedown', events.onParameterStart.bind(events))
                .on('touchstart', events.onParameterStart.bind(events))
                .on('mouseup', events.onParameterEnd.bind(events))
                .on('mouseupoutside', events.onParameterEnd.bind(events))
                .on('touchend', events.onParameterEnd.bind(events))
                .on('touchendoutside', events.onParameterEnd.bind(events))
                .on('mousemove', events.onParameterMove.bind(events))
                .on('touchmove', events.onParameterMove.bind(events));
        }
    }

    /**
     * Returns true if position is inside block's hitArea
     */
    isHit(position) {
        return this.hitArea.contains(position.x, position.y);
    }

    //** render: positions child/parameter blocks and draws lines
    render(childIndex = 0) {
        //console.log(`DEBUG::: Render {${this.name}} from index ${childIndex}`);

        const blockWidth = this.getChildAt(0).width;
        const blockHeight = this.getChildAt(0).height;
        let lineStartPosition = new PIXI.Point(blockWidth, blockHeight/2 - (LINE_CONST.STROKE_WIDTH + LINE_CONST.SPACING)*this.parameterBlocks.length/2 + childIndex*(LINE_CONST.STROKE_WIDTH + LINE_CONST.SPACING));
        let childBlockPosition = new PIXI.Point(blockWidth + BLOCK_CONST.SPACING_H, 0);

        //  recalculate starting height if render not starting from 0
        if (childIndex != 0) {
            let previousHeight = 0;
            if (this.childBlocks[childIndex-1]) {
                previousHeight = this.childBlocks[childIndex-1].y + this.childBlocks[childIndex-1].height + BLOCK_CONST.SPACING_V;
            } else {
                previousHeight = this.parameterBlocks[childIndex-1].y + this.parameterBlocks[childIndex-1].height + BLOCK_CONST.SPACING_V;
            }
            childBlockPosition.y = previousHeight;
        }

        //  remove existing lines from block
        this.lines.slice(childIndex).forEach(line => this.removeChild(line));

        //  for each parameter/branch, draw line and place block
        for (let i=childIndex;i < this.parameterBlocks.length; i++) {
            //  set line end position and draw line
            let lineEndPosition = new PIXI.Point(childBlockPosition.x, childBlockPosition.y + blockHeight/2);
            if (i==0) { // for case 0, make line straight
                lineEndPosition.y = lineStartPosition.y;
            }
            let curve = drawBezierCurve(lineStartPosition,lineEndPosition);
            this.addChild(curve);
            this.lines[i] = curve;

            //  parameter block: set position
            this.parameterBlocks[i].visible = true;
            this.parameterBlocks[i].position = childBlockPosition.clone();
            let lastChildHeight = this.parameterBlocks[i].height;

            //  child block: set position, make corresponding parameterBlock invisible
            if (this.childBlocks[i]) {
                //console.log(`DEBUG::: childBlock {${this.childBlocks[i].name}} is being added`);
                this.childBlocks[i].visible = true;
                this.childBlocks[i].position = childBlockPosition.clone();
                lastChildHeight = this.childBlocks[i].height;
                this.parameterBlocks[i].visible = false;
            }

            //  increment height of lineStartPositon and childBlockPosition
            lineStartPosition.y += LINE_CONST.STROKE_WIDTH + LINE_CONST.SPACING;
            childBlockPosition.y += BLOCK_CONST.SPACING_V + lastChildHeight;
        }

        function drawBezierCurve(startPosition,endPosition) {
            let curve = new PIXI.Graphics().lineStyle(LINE_CONST.STROKE_WIDTH,LINE_CONST.COLOR);

            let lineWidth = endPosition.x - startPosition.x;
            let lineHeight = endPosition.y - startPosition.y;
            let midPosition = new PIXI.Point(startPosition.x + lineWidth/2, startPosition.y + lineHeight/2);

            curve.moveTo(startPosition.x, startPosition.y);
            curve.bezierCurveTo(
                startPosition.x + LINE_CONST.BEZIER_SCALE_H*lineWidth,
                startPosition.y,
                midPosition.x,
                midPosition.y - LINE_CONST.BEZIER_SCALE_V*lineHeight,
                midPosition.x,
                midPosition.y);
            curve.bezierCurveTo(
                midPosition.x,
                midPosition.y + LINE_CONST.BEZIER_SCALE_V*lineHeight,
                endPosition.x - LINE_CONST.BEZIER_SCALE_H*lineWidth,
                endPosition.y,
                endPosition.x,
                endPosition.y);
            return curve;
        }
    }

    /**
     * Re-renders blocks starting from the index of current block, and repeating for its parent
     */
    update(includeSelf = false) {
        //console.log(`DEBUG::: Update called by {${this.name}}`);

        if (this.parent instanceof CraftyBlock) {
            let index = (this.type == CraftyBlock.PARAMETER ? this.parent.getParameterBlockIndex(this) : this.parent.getChildBlockIndex(this));
            this.parent.render(index + (includeSelf ? 0 : 1));
            this.parent.update();
        }
    }

    /**
     * DEPRECATED: use update() instead
     * Alternative implementation of update function
     */
    /*
    updateFrom(index) {
        //console.log(`DEBUG::: Update called by {${this.name}} from index ${index}`);

        this.render(index);
        if (this.parent instanceof CraftyBlock) {
            this.parent.updateFrom(this.parent.getChildBlockIndex(this) + 1);
        }
    }
    */

    /**
     * Returns the index position of a child CraftyBlock instance
     */
    getChildBlockIndex(block) {
        let index = this.childBlocks.indexOf(block);
        if (index === -1) {
            throw new Error('The supplied Block must be a child of the caller');
        }

        return index;
    }

    /**
     * Returns the index position of a child parameterBlock instance
     */
    getParameterBlockIndex(block) {
        let index = this.parameterBlocks.indexOf(block);
        if (index === -1) {
            throw new Error('The supplied Block must be a child of the caller');
        }

        return index;
    }

    /**
     * Removes block from original parent and adds to this instance
     */
    addChildBlock(block, index) {
        console.log(`DEBUG::: Adding {${block.name}} to {${this.name}} at index ${index}`);

        //  remove block from its parent
        if (block.parent instanceof CraftyBlock) {
            block.parent.removeChildBlock(block);
        }

        this.addChild(block);
        this.childBlocks[index] = block;

        //  update augmented block
        block.update(true);
    }

    /**
     * Removes a child block from this instance
     */
    removeChildBlock(block) {
        console.log(`DEBUG::: Removing {${block.name}} from {${this.name}}`);
        const index = this.getChildBlockIndex(block);

        this.removeChild(block);
        this.childBlocks[index] = null;

        //  set parameterBlock visible to true and update taken out block
        let parameterBlock = this.parameterBlocks[index];
        parameterBlock.visible = true;
        parameterBlock.update();
    }

    /**
     * Attach block to parameterBlock location
     *
     * @convenience of addChildBlock
     */
    attachTo(parameterBlock) {
        let parent = parameterBlock.parent;
        let index = parent.getParameterBlockIndex(parameterBlock);
        parent.addChildBlock(this,index);
    }

    //  TODO
    fold() {
    }

    //  TODO
    unfold() {
    }

    /**
     * Prints block structure onto console
     */
    print(indent = "*") {
        console.log(indent, this.name);
        this.parameterBlocks.forEach( (block,index) => {
            if (this.childBlocks[index]) {
                this.childBlocks[index].print(indent + "**");
            } else {
                block.print(indent + "**");
            }
        });
    }
}

//  Define CraftyBlock types
let blockId = 0;
CraftyBlock.FUNCTION    = blockId++;
CraftyBlock.CONSTANT    = blockId++;
CraftyBlock.PARAMETER   = blockId++;
<commit_msg>Implement absolute position calculation<commit_after>import PIXI from 'pixi.js';
import CraftyBlockSpec from './CraftyBlockSpec.js';
import * as BLOCK_CONST from '../../constants/BlockConstants.js';
import * as LINE_CONST from '../../constants/LineConstants.js';
import CraftyBlockAnimator from './CraftyBlockAnimator.js';

export default class CraftyBlock extends PIXI.Container {
    constructor(blockInfo) {
        super();
        this.id = "block";
        this.blockInfo = blockInfo;
        this.childBlocks = [];
        this.parameterBlocks = [];
        this.lines = [];

        this.initialize();

        //console.log(`DEBUG:::Created {${this.name}} block`);
    }

    /**
     * Convenient getters for block properties
     */
    get name() {
        return this.blockInfo.name;
    }
    get type() {
        return this.blockInfo.type;
    }
    get parameters() {
        return this.blockInfo.parameters;
    }
    get absolutePosition() {
        let position = this.position.clone();
        if (this.parent instanceof CraftyBlock) {
            position.x += this.parent.absolutePosition.x;
            position.y += this.parent.absolutePosition.y;
        }
        return position;
    }

    /**
     * Convenient initializers for each block types
     */
    static constantWithValue(value) {
        let blockInfo = new CraftyBlockSpec(value, CraftyBlock.CONSTANT);
        return new CraftyBlock(blockInfo);
    }
    static functionWithName(name) {
        let blockInfo = new CraftyBlockSpec.functionWithName(name);
        return new CraftyBlock(blockInfo);
    }
    static parameterWithName(name) {
        let blockInfo = new CraftyBlockSpec(name, CraftyBlock.PARAMETER);
        return new CraftyBlock(blockInfo);
    }

    /**
     * Initializer for CraftyBlock
     *
     * Creates block text, graphics, parameter blocks, and calls makeInteractive();
     */
    initialize() {
        //  Create text and set style
        let text = new PIXI.Text(
            this.name,
            BLOCK_CONST.TEXT_STYLE
        );
        text.position.set(BLOCK_CONST.PADDING_H,BLOCK_CONST.PADDING_V);

        //  Create block graphics and set style
        let blockGraphics = new PIXI.Graphics();
        if (this.type == CraftyBlock.PARAMETER) { // if block is parameter, apply different style
            blockGraphics.beginFill(BLOCK_CONST.TYPE_PARAMETER_COLOR, BLOCK_CONST.OPACITY);
        } else if (this.type == CraftyBlock.CONSTANT) {
            blockGraphics.beginFill(BLOCK_CONST.TYPE_CONSTANT_COLOR, BLOCK_CONST.OPACITY);
        } else {
            blockGraphics.beginFill(BLOCK_CONST.TYPE_FUNCTION_COLOR, BLOCK_CONST.OPACITY);
        }
        blockGraphics.drawRoundedRect(0,0,text.width + 2 * BLOCK_CONST.PADDING_H, text.height + 2 * BLOCK_CONST.PADDING_V, BLOCK_CONST.CORNER_RADIUS);
        blockGraphics.endFill();

        //  Add PIXI Objects to parent container
        this.addChild(blockGraphics);
        this.addChild(text);

        //  Add parameter block to this(block) and make it invisible
        this.parameters.forEach((name) => {
            const newBlock = CraftyBlock.parameterWithName(name);
            this.addChild(newBlock);
            this.parameterBlocks.push(newBlock);
            newBlock.visible = false;
        });

        //  set interactivity of blocks
        this.makeInteractive();
    }

    /**
     * Returns a clone of current block
     */
    clone() {
        let blockCopy = new CraftyBlock(this.blockInfo);
        blockCopy.position = this.position.clone();;

        return blockCopy;
    }

    /**
     * Makes the block interactive
     */
    makeInteractive() {
        const events = CraftyBlockAnimator;

        this.interactive = true;
        this.hitArea = this.getChildAt(0).getBounds().clone();
        //console.log(`DEBUG:::interactivity enabled for {${this.name}}`);

        //  enable drag and drop for non-parameter blocks, enable mouse over check for parameter blocks
        if (this.type != CraftyBlock.PARAMETER) {
            this
                .on('mousedown', events.onDragStart.bind(events))
                .on('touchstart', events.onDragStart.bind(events))
                .on('mousemove', events.onDragMove.bind(events))
                .on('touchmove', events.onDragMove.bind(events))
                .on('mouseup', events.onDragEnd.bind(events))
                .on('mouseupoutside', events.onDragEnd.bind(events))
                .on('touchend', events.onDragEnd.bind(events))
                .on('touchendoutside', events.onDragEnd.bind(events));
        } else {
            this
                .on('mousedown', events.onParameterStart.bind(events))
                .on('touchstart', events.onParameterStart.bind(events))
                .on('mouseup', events.onParameterEnd.bind(events))
                .on('mouseupoutside', events.onParameterEnd.bind(events))
                .on('touchend', events.onParameterEnd.bind(events))
                .on('touchendoutside', events.onParameterEnd.bind(events))
                .on('mousemove', events.onParameterMove.bind(events))
                .on('touchmove', events.onParameterMove.bind(events));
        }
    }

    /**
     * Returns true if position is inside block's hitArea
     */
    isHit(position) {
        return this.hitArea.contains(position.x, position.y);
    }

    //** render: positions child/parameter blocks and draws lines
    render(childIndex = 0) {
        //console.log(`DEBUG::: Render {${this.name}} from index ${childIndex}`);

        const blockWidth = this.getChildAt(0).width;
        const blockHeight = this.getChildAt(0).height;
        let lineStartPosition = new PIXI.Point(blockWidth, blockHeight/2 - (LINE_CONST.STROKE_WIDTH + LINE_CONST.SPACING)*this.parameterBlocks.length/2 + childIndex*(LINE_CONST.STROKE_WIDTH + LINE_CONST.SPACING));
        let childBlockPosition = new PIXI.Point(blockWidth + BLOCK_CONST.SPACING_H, 0);

        //  recalculate starting height if render not starting from 0
        if (childIndex != 0) {
            let previousHeight = 0;
            if (this.childBlocks[childIndex-1]) {
                previousHeight = this.childBlocks[childIndex-1].y + this.childBlocks[childIndex-1].height + BLOCK_CONST.SPACING_V;
            } else {
                previousHeight = this.parameterBlocks[childIndex-1].y + this.parameterBlocks[childIndex-1].height + BLOCK_CONST.SPACING_V;
            }
            childBlockPosition.y = previousHeight;
        }

        //  remove existing lines from block
        this.lines.slice(childIndex).forEach(line => this.removeChild(line));

        //  for each parameter/branch, draw line and place block
        for (let i=childIndex;i < this.parameterBlocks.length; i++) {
            //  set line end position and draw line
            let lineEndPosition = new PIXI.Point(childBlockPosition.x, childBlockPosition.y + blockHeight/2);
            if (i==0) { // for case 0, make line straight
                lineEndPosition.y = lineStartPosition.y;
            }
            let curve = drawBezierCurve(lineStartPosition,lineEndPosition);
            this.addChild(curve);
            this.lines[i] = curve;

            //  parameter block: set position
            this.parameterBlocks[i].visible = true;
            this.parameterBlocks[i].position = childBlockPosition.clone();
            let lastChildHeight = this.parameterBlocks[i].height;

            //  child block: set position, make corresponding parameterBlock invisible
            if (this.childBlocks[i]) {
                //console.log(`DEBUG::: childBlock {${this.childBlocks[i].name}} is being added`);
                this.childBlocks[i].visible = true;
                this.childBlocks[i].position = childBlockPosition.clone();
                lastChildHeight = this.childBlocks[i].height;
                this.parameterBlocks[i].visible = false;
            }

            //  increment height of lineStartPositon and childBlockPosition
            lineStartPosition.y += LINE_CONST.STROKE_WIDTH + LINE_CONST.SPACING;
            childBlockPosition.y += BLOCK_CONST.SPACING_V + lastChildHeight;
        }

        function drawBezierCurve(startPosition,endPosition) {
            let curve = new PIXI.Graphics().lineStyle(LINE_CONST.STROKE_WIDTH,LINE_CONST.COLOR);

            let lineWidth = endPosition.x - startPosition.x;
            let lineHeight = endPosition.y - startPosition.y;
            let midPosition = new PIXI.Point(startPosition.x + lineWidth/2, startPosition.y + lineHeight/2);

            curve.moveTo(startPosition.x, startPosition.y);
            curve.bezierCurveTo(
                startPosition.x + LINE_CONST.BEZIER_SCALE_H*lineWidth,
                startPosition.y,
                midPosition.x,
                midPosition.y - LINE_CONST.BEZIER_SCALE_V*lineHeight,
                midPosition.x,
                midPosition.y);
            curve.bezierCurveTo(
                midPosition.x,
                midPosition.y + LINE_CONST.BEZIER_SCALE_V*lineHeight,
                endPosition.x - LINE_CONST.BEZIER_SCALE_H*lineWidth,
                endPosition.y,
                endPosition.x,
                endPosition.y);
            return curve;
        }
    }

    /**
     * Re-renders blocks starting from the index of current block, and repeating for its parent
     */
    update(includeSelf = false) {
        //console.log(`DEBUG::: Update called by {${this.name}}`);

        if (this.parent instanceof CraftyBlock) {
            let index = (this.type == CraftyBlock.PARAMETER ? this.parent.getParameterBlockIndex(this) : this.parent.getChildBlockIndex(this));
            this.parent.render(index + (includeSelf ? 0 : 1));
            this.parent.update();
        }
    }

    /**
     * DEPRECATED: use update() instead
     * Alternative implementation of update function
     */
    /*
    updateFrom(index) {
        //console.log(`DEBUG::: Update called by {${this.name}} from index ${index}`);

        this.render(index);
        if (this.parent instanceof CraftyBlock) {
            this.parent.updateFrom(this.parent.getChildBlockIndex(this) + 1);
        }
    }
    */

    /**
     * Returns the index position of a child CraftyBlock instance
     */
    getChildBlockIndex(block) {
        let index = this.childBlocks.indexOf(block);
        if (index === -1) {
            throw new Error('The supplied Block must be a child of the caller');
        }

        return index;
    }

    /**
     * Returns the index position of a child parameterBlock instance
     */
    getParameterBlockIndex(block) {
        let index = this.parameterBlocks.indexOf(block);
        if (index === -1) {
            throw new Error('The supplied Block must be a child of the caller');
        }

        return index;
    }

    /**
     * Removes block from original parent and adds to this instance
     */
    addChildBlock(block, index) {
        console.log(`DEBUG::: Adding {${block.name}} to {${this.name}} at index ${index}`);

        //  remove block from its parent
        if (block.parent instanceof CraftyBlock) {
            block.parent.removeChildBlock(block);
        }

        this.addChild(block);
        this.childBlocks[index] = block;

        //  update augmented block
        block.update(true);
    }

    /**
     * Removes a child block from this instance
     */
    removeChildBlock(block) {
        console.log(`DEBUG::: Removing {${block.name}} from {${this.name}}`);
        const index = this.getChildBlockIndex(block);

        this.removeChild(block);
        this.childBlocks[index] = null;

        //  set parameterBlock visible to true and update taken out block
        let parameterBlock = this.parameterBlocks[index];
        parameterBlock.visible = true;
        parameterBlock.update();
    }

    /**
     * Attach block to parameterBlock location
     *
     * @convenience of addChildBlock
     */
    attachTo(parameterBlock) {
        let parent = parameterBlock.parent;
        let index = parent.getParameterBlockIndex(parameterBlock);
        parent.addChildBlock(this,index);
    }

    //  TODO
    fold() {
    }

    //  TODO
    unfold() {
    }

    /**
     * Prints block structure onto console
     */
    print(indent = "*") {
        console.log(indent, this.name);
        this.parameterBlocks.forEach( (block,index) => {
            if (this.childBlocks[index]) {
                this.childBlocks[index].print(indent + "**");
            } else {
                block.print(indent + "**");
            }
        });
    }
}

//  Define CraftyBlock types
let blockId = 0;
CraftyBlock.FUNCTION    = blockId++;
CraftyBlock.CONSTANT    = blockId++;
CraftyBlock.PARAMETER   = blockId++;
<|endoftext|>


<commit_before>import * as types from './types'
import { makeProfileZoneFile, transactions, config, network, safety } from 'blockstack'
import { uploadProfile } from '../../../account/utils'
import { IdentityActions } from '../identity'
import { signProfileForUpload, authorizationHeaderValue } from '@utils'
import { DEFAULT_PROFILE } from '@utils/profile-utils'
import { isSubdomain, getNameSuffix } from '@utils/name-utils'
import log4js from 'log4js'

const logger = log4js.getLogger(__filename)

function profileUploading() {
  return {
    type: types.PROFILE_UPLOADING
  }
}

function profileUploadError(error) {
  return {
    type: types.PROFILE_UPLOAD_ERROR,
    error
  }
}

function registrationBeforeSubmit() {
  return {
    type: types.REGISTRATION_BEFORE_SUBMIT
  }
}

function registrationSubmitting() {
  return {
    type: types.REGISTRATION_SUBMITTING
  }
}

function registrationSubmitted() {
  return {
    type: types.REGISTRATION_SUBMITTED
  }
}

function registrationError(error) {
  return {
    type: types.REGISTRATION_ERROR,
    error
  }
}

function beforeRegister() {
  logger.info('beforeRegister')
  return dispatch => {
    dispatch(registrationBeforeSubmit())
  }
}

function registerSubdomain(api, domainName, identityIndex, ownerAddress, zoneFile) {
  let nameSuffix = null
  nameSuffix = getNameSuffix(domainName)
  logger.debug(`registerName: ${domainName} is a subdomain of ${nameSuffix}`)
  const registerUrl = api.subdomains[nameSuffix].registerUrl

  const registrationRequestBody = JSON.stringify({
    name: domainName.split('.')[0],
    owner_address: ownerAddress,
    zonefile: zoneFile
  })

  const requestHeaders = {
    Accept: 'application/json',
    'Content-Type': 'application/json',
    Authorization: authorizationHeaderValue(api.coreAPIPassword)
  }

  logger.info(`Submitting registration for ${domainName} to ${registerUrl}`)

  return fetch(registerUrl, {
    method: 'POST',
    headers: requestHeaders,
    body: registrationRequestBody
  })
  .then((response) => {
    if (!response.ok) {
      logger.error(`Subdomain registrar responded with status code ${response.status}`)
      return Promise.reject(new Error('Failed to register username'))
    }
    return response.text()
  })
  .then((responseText) => JSON.parse(responseText))
}

function registerDomain(myNet, tx, domainName, identityIndex, ownerAddress, paymentKey, zoneFile) {
  if (!paymentKey) {
    logger.error('registerName: payment key not provided for non-subdomain registration')
    return Promise.reject('Missing payment key')
  }

  const compressedKey = `${paymentKey}01`
  const coercedAddress = myNet.coerceAddress(ownerAddress)

  let preorderTx = ''
  let registerTx = ''

  return safety.addressCanReceiveName(ownerAddress)
    .then((canReceive) => {
      if (!canReceive) {
        return Promise.reject(`Address ${ownerAddress} cannot receive names.`)
      }
      return safety.isNameValid(domainName)
    })
    .then((nameValid) => {
      if (!nameValid) {
        return Promise.reject(`Name ${domainName} is not valid`)
      }
      return true
    })
    .then(() => tx.makePreorder(domainName, coercedAddress, compressedKey))
    .then((rawtx) => {
      preorderTx = rawtx
      return rawtx
    })
    .then((rawtx) => {
      myNet.modifyUTXOSetFrom(preorderTx)
      return rawtx
    })
    .then(() =>
      tx.makeRegister(domainName, coercedAddress, compressedKey, zoneFile))
    .then((rawtx) => {
      registerTx = rawtx
      return rawtx
    })
    .then(() => {
      // make sure we don't double-spend the register before it is broadcasted.
      myNet.modifyUTXOSetFrom(registerTx)
    })
    .then(() => {
      logger.debug(
        `Sending registration to transaction broadcaster at ${myNet.broadcastServiceUrl}`)
      return myNet.broadcastNameRegistration(preorderTx, registerTx, zoneFile)
    })
}

function registerName(api, domainName, identity, identityIndex,
                      ownerAddress, keypair, paymentKey = null) {
  logger.info(`registerName: domainName: ${domainName}`)
  return dispatch => {
    logger.debug(`Signing a new profile for ${domainName}`)
    const profile = identity.profile || DEFAULT_PROFILE
    const signedProfileTokenData = signProfileForUpload(profile, keypair)

    dispatch(profileUploading())
    logger.info(`Uploading ${domainName} profile...`)
    return uploadProfile(api, identity, keypair, signedProfileTokenData)
    .then((profileUrl) => {
      logger.info(`Uploading ${domainName} profiled succeeded.`)
      const tokenFileUrl = profileUrl
      logger.debug(`tokenFileUrl: ${tokenFileUrl}`)

      logger.info('Making profile zonefile...')
      const zoneFile = makeProfileZoneFile(domainName, tokenFileUrl)

      const nameIsSubdomain = isSubdomain(domainName)

      dispatch(registrationSubmitting())

      if (nameIsSubdomain) {
        return registerSubdomain(api, domainName, identityIndex, ownerAddress, zoneFile)
          .then((responseJson) => {
            if (responseJson.error) {
              logger.error(responseJson.error)
              dispatch(registrationError(responseJson.error))
            } else {
              logger.debug(`Successfully submitted registration for ${domainName}`)
              dispatch(registrationSubmitted())
              dispatch(IdentityActions.addUsername(identityIndex, domainName))
            }
          })
          .catch((error) => {
            logger.error('registerName: error POSTing registration to Core', error)
            dispatch(registrationError(error))
            throw error
          })
      } else {
        if (api.regTestMode) {
          logger.info('Using regtest network')
          config.network = network.defaults.LOCAL_REGTEST
          // browser regtest environment uses 6270
          config.network.blockstackAPIUrl = 'http://localhost:6270'
        }

        const myNet = config.network

        return registerDomain(myNet, transactions, domainName, identityIndex,
          ownerAddress, paymentKey, zoneFile)
          .then((response) => {
            if (response.status) {
              logger.debug(`Successfully submitted registration for ${domainName}`)
              dispatch(registrationSubmitted())
              dispatch(IdentityActions.addUsername(identityIndex, domainName))
            } else {
              logger.error(response)
              dispatch(registrationError(response))
            }
          })
          .catch(error => {
            logger.error('registerName: error submitting name registration', error)
            dispatch(registrationError(error))
            throw new Error('Error submitting name registration')
          })
      }
    }).catch((error) => {
      logger.error('registerName: error uploading profile', error)
      dispatch(profileUploadError(error))
      throw error
    })
  }
}

const RegistrationActions = {
  profileUploading,
  profileUploadError,
  beforeRegister,
  registerName,
  registrationBeforeSubmit,
  registrationSubmitting,
  registrationSubmitted,
  registrationError,
  registerSubdomain,
  registerDomain
}

export default RegistrationActions
<commit_msg>Update name registration console error message.<commit_after>import * as types from './types'
import { makeProfileZoneFile, transactions, config, network, safety } from 'blockstack'
import { uploadProfile } from '../../../account/utils'
import { IdentityActions } from '../identity'
import { signProfileForUpload, authorizationHeaderValue } from '@utils'
import { DEFAULT_PROFILE } from '@utils/profile-utils'
import { isSubdomain, getNameSuffix } from '@utils/name-utils'
import log4js from 'log4js'

const logger = log4js.getLogger(__filename)

function profileUploading() {
  return {
    type: types.PROFILE_UPLOADING
  }
}

function profileUploadError(error) {
  return {
    type: types.PROFILE_UPLOAD_ERROR,
    error
  }
}

function registrationBeforeSubmit() {
  return {
    type: types.REGISTRATION_BEFORE_SUBMIT
  }
}

function registrationSubmitting() {
  return {
    type: types.REGISTRATION_SUBMITTING
  }
}

function registrationSubmitted() {
  return {
    type: types.REGISTRATION_SUBMITTED
  }
}

function registrationError(error) {
  return {
    type: types.REGISTRATION_ERROR,
    error
  }
}

function beforeRegister() {
  logger.info('beforeRegister')
  return dispatch => {
    dispatch(registrationBeforeSubmit())
  }
}

function registerSubdomain(api, domainName, identityIndex, ownerAddress, zoneFile) {
  let nameSuffix = null
  nameSuffix = getNameSuffix(domainName)
  logger.debug(`registerName: ${domainName} is a subdomain of ${nameSuffix}`)
  const registerUrl = api.subdomains[nameSuffix].registerUrl

  const registrationRequestBody = JSON.stringify({
    name: domainName.split('.')[0],
    owner_address: ownerAddress,
    zonefile: zoneFile
  })

  const requestHeaders = {
    Accept: 'application/json',
    'Content-Type': 'application/json',
    Authorization: authorizationHeaderValue(api.coreAPIPassword)
  }

  logger.info(`Submitting registration for ${domainName} to ${registerUrl}`)

  return fetch(registerUrl, {
    method: 'POST',
    headers: requestHeaders,
    body: registrationRequestBody
  })
  .then((response) => {
    if (!response.ok) {
      logger.error(`Subdomain registrar responded with status code ${response.status}`)
      return Promise.reject(new Error('Failed to register username'))
    }
    return response.text()
  })
  .then((responseText) => JSON.parse(responseText))
}

function registerDomain(myNet, tx, domainName, identityIndex, ownerAddress, paymentKey, zoneFile) {
  if (!paymentKey) {
    logger.error('registerName: payment key not provided for non-subdomain registration')
    return Promise.reject('Missing payment key')
  }

  const compressedKey = `${paymentKey}01`
  const coercedAddress = myNet.coerceAddress(ownerAddress)

  let preorderTx = ''
  let registerTx = ''

  return safety.addressCanReceiveName(ownerAddress)
    .then((canReceive) => {
      if (!canReceive) {
        return Promise.reject(`Address ${ownerAddress} cannot receive names.`)
      }
      return safety.isNameValid(domainName)
    })
    .then((nameValid) => {
      if (!nameValid) {
        return Promise.reject(`Name ${domainName} is not valid`)
      }
      return true
    })
    .then(() => tx.makePreorder(domainName, coercedAddress, compressedKey))
    .then((rawtx) => {
      preorderTx = rawtx
      return rawtx
    })
    .then((rawtx) => {
      myNet.modifyUTXOSetFrom(preorderTx)
      return rawtx
    })
    .then(() =>
      tx.makeRegister(domainName, coercedAddress, compressedKey, zoneFile))
    .then((rawtx) => {
      registerTx = rawtx
      return rawtx
    })
    .then(() => {
      // make sure we don't double-spend the register before it is broadcasted.
      myNet.modifyUTXOSetFrom(registerTx)
    })
    .then(() => {
      logger.debug(
        `Sending registration to transaction broadcaster at ${myNet.broadcastServiceUrl}`)
      return myNet.broadcastNameRegistration(preorderTx, registerTx, zoneFile)
    })
}

function registerName(api, domainName, identity, identityIndex,
                      ownerAddress, keypair, paymentKey = null) {
  logger.info(`registerName: domainName: ${domainName}`)
  return dispatch => {
    logger.debug(`Signing a new profile for ${domainName}`)
    const profile = identity.profile || DEFAULT_PROFILE
    const signedProfileTokenData = signProfileForUpload(profile, keypair)

    dispatch(profileUploading())
    logger.info(`Uploading ${domainName} profile...`)
    return uploadProfile(api, identity, keypair, signedProfileTokenData)
    .then((profileUrl) => {
      logger.info(`Uploading ${domainName} profiled succeeded.`)
      const tokenFileUrl = profileUrl
      logger.debug(`tokenFileUrl: ${tokenFileUrl}`)

      logger.info('Making profile zonefile...')
      const zoneFile = makeProfileZoneFile(domainName, tokenFileUrl)

      const nameIsSubdomain = isSubdomain(domainName)

      dispatch(registrationSubmitting())

      if (nameIsSubdomain) {
        return registerSubdomain(api, domainName, identityIndex, ownerAddress, zoneFile)
          .then((responseJson) => {
            if (responseJson.error) {
              logger.error(responseJson.error)
              dispatch(registrationError(responseJson.error))
            } else {
              logger.debug(`Successfully submitted registration for ${domainName}`)
              dispatch(registrationSubmitted())
              dispatch(IdentityActions.addUsername(identityIndex, domainName))
            }
          })
          .catch((error) => {
            logger.error('registerName: error POSTing registration to registrar', error)
            dispatch(registrationError(error))
            throw error
          })
      } else {
        if (api.regTestMode) {
          logger.info('Using regtest network')
          config.network = network.defaults.LOCAL_REGTEST
          // browser regtest environment uses 6270
          config.network.blockstackAPIUrl = 'http://localhost:6270'
        }

        const myNet = config.network

        return registerDomain(myNet, transactions, domainName, identityIndex,
          ownerAddress, paymentKey, zoneFile)
          .then((response) => {
            if (response.status) {
              logger.debug(`Successfully submitted registration for ${domainName}`)
              dispatch(registrationSubmitted())
              dispatch(IdentityActions.addUsername(identityIndex, domainName))
            } else {
              logger.error(response)
              dispatch(registrationError(response))
            }
          })
          .catch(error => {
            logger.error('registerName: error submitting name registration', error)
            dispatch(registrationError(error))
            throw new Error('Error submitting name registration')
          })
      }
    }).catch((error) => {
      logger.error('registerName: error uploading profile', error)
      dispatch(profileUploadError(error))
      throw error
    })
  }
}

const RegistrationActions = {
  profileUploading,
  profileUploadError,
  beforeRegister,
  registerName,
  registrationBeforeSubmit,
  registrationSubmitting,
  registrationSubmitted,
  registrationError,
  registerSubdomain,
  registerDomain
}

export default RegistrationActions
<|endoftext|>


<commit_before>import { Meteor } from 'meteor/meteor';
import { ServiceConfiguration } from 'meteor/service-configuration';
import _ from 'underscore';

import { CustomOAuth } from '../../../custom-oauth';
import { Logger } from '../../../logger';
import { settings } from '../../../settings';

const logger = new Logger('rocketchat:lib', {
	methods: {
		oauth_updated: {
			type: 'info',
		},
	},
});

function _OAuthServicesUpdate() {
	const services = settings.get(/^(Accounts_OAuth_|Accounts_OAuth_Custom-)[a-z0-9_]+$/i);
	services.forEach((service) => {
		logger.oauth_updated(service.key);
		let serviceName = service.key.replace('Accounts_OAuth_', '');
		if (serviceName === 'Meteor') {
			serviceName = 'meteor-developer';
		}
		if (/Accounts_OAuth_Custom-/.test(service.key)) {
			serviceName = service.key.replace('Accounts_OAuth_Custom-', '');
		}
		if (service.value === true) {
			const data = {
				clientId: settings.get(`${ service.key }_id`),
				secret: settings.get(`${ service.key }_secret`),
			};
			if (/Accounts_OAuth_Custom-/.test(service.key)) {
				data.custom = true;
				data.clientId = settings.get(`${ service.key }-id`);
				data.secret = settings.get(`${ service.key }-secret`);
				data.serverURL = settings.get(`${ service.key }-url`);
				data.tokenPath = settings.get(`${ service.key }-token_path`);
				data.identityPath = settings.get(`${ service.key }-identity_path`);
				data.authorizePath = settings.get(`${ service.key }-authorize_path`);
				data.scope = settings.get(`${ service.key }-scope`);
				data.accessTokenParam = settings.get(`${ service.key }-access_token_param`);
				data.buttonLabelText = settings.get(`${ service.key }-button_label_text`);
				data.buttonLabelColor = settings.get(`${ service.key }-button_label_color`);
				data.loginStyle = settings.get(`${ service.key }-login_style`);
				data.buttonColor = settings.get(`${ service.key }-button_color`);
				data.tokenSentVia = settings.get(`${ service.key }-token_sent_via`);
				data.identityTokenSentVia = settings.get(`${ service.key }-identity_token_sent_via`);
				data.usernameField = settings.get(`${ service.key }-username_field`);
				data.emailField = settings.get(`${ service.key }-email_field`);
				data.nameField = settings.get(`${ service.key }-name_field`);
				data.avatarField = settings.get(`${ service.key }-avatar_field`);
				data.rolesClaim = settings.get(`${ service.key }-roles_claim`);
				data.mergeUsers = settings.get(`${ service.key }-merge_users`);
				data.mergeRoles = settings.get(`${ service.key }-merge_roles`);
				data.showButton = settings.get(`${ service.key }-show_button`);
				new CustomOAuth(serviceName.toLowerCase(), {
					serverURL: data.serverURL,
					tokenPath: data.tokenPath,
					identityPath: data.identityPath,
					authorizePath: data.authorizePath,
					scope: data.scope,
					loginStyle: data.loginStyle,
					tokenSentVia: data.tokenSentVia,
					identityTokenSentVia: data.identityTokenSentVia,
					usernameField: data.usernameField,
					emailField: data.emailField,
					nameField: data.nameField,
					avatarField: data.avatarField,
					rolesClaim: data.rolesClaim,
					mergeUsers: data.mergeUsers,
					mergeRoles: data.mergeRoles,
					accessTokenParam: data.accessTokenParam,
					showButton: data.showButton,
				});
			}
			if (serviceName === 'Facebook') {
				data.appId = data.clientId;
				delete data.clientId;
			}
			if (serviceName === 'Twitter') {
				data.consumerKey = data.clientId;
				delete data.clientId;
			}

			if (serviceName === 'Linkedin') {
				data.clientConfig = {
					requestPermissions: ['r_liteprofile', 'r_emailaddress'],
				};
			}

			if (serviceName === 'Nextcloud') {
				data.buttonLabelText = settings.get('Accounts_OAuth_Nextcloud_button_label_text');
				data.buttonLabelColor = settings.get('Accounts_OAuth_Nextcloud_button_label_color');
				data.buttonColor = settings.get('Accounts_OAuth_Nextcloud_button_color');
			}

			// If there's no data other than the service name, then put the service name in the data object so the operation won't fail
			const keys = Object.keys(data).filter((key) => data[key] !== undefined);
			if (!keys.length) {
				data.service = serviceName.toLowerCase();
			}

			ServiceConfiguration.configurations.upsert({
				service: serviceName.toLowerCase(),
			}, {
				$set: data,
			});
		} else {
			ServiceConfiguration.configurations.remove({
				service: serviceName.toLowerCase(),
			});
		}
	});
}

const OAuthServicesUpdate = _.debounce(Meteor.bindEnvironment(_OAuthServicesUpdate), 2000);

function OAuthServicesRemove(_id) {
	const serviceName = _id.replace('Accounts_OAuth_Custom-', '');
	return ServiceConfiguration.configurations.remove({
		service: serviceName.toLowerCase(),
	});
}

settings.get(/^Accounts_OAuth_.+/, function() {
	return OAuthServicesUpdate(); // eslint-disable-line new-cap
});

settings.get(/^Accounts_OAuth_Custom-[a-z0-9_]+/, function(key, value) {
	if (!value) {
		return OAuthServicesRemove(key);// eslint-disable-line new-cap
	}
});
<commit_msg>[FIX] Create Custom OAuth services from environment variables (#17377)<commit_after>import { Meteor } from 'meteor/meteor';
import { ServiceConfiguration } from 'meteor/service-configuration';
import _ from 'underscore';

import { CustomOAuth } from '../../../custom-oauth';
import { Logger } from '../../../logger';
import { settings } from '../../../settings';

const logger = new Logger('rocketchat:lib', {
	methods: {
		oauth_updated: {
			type: 'info',
		},
	},
});

function _OAuthServicesUpdate() {
	const services = settings.get(/^(Accounts_OAuth_|Accounts_OAuth_Custom-)[a-z0-9_]+$/i);
	services.forEach((service) => {
		logger.oauth_updated(service.key);
		let serviceName = service.key.replace('Accounts_OAuth_', '');
		if (serviceName === 'Meteor') {
			serviceName = 'meteor-developer';
		}
		if (/Accounts_OAuth_Custom-/.test(service.key)) {
			serviceName = service.key.replace('Accounts_OAuth_Custom-', '');
		}
		if (service.value === true) {
			const data = {
				clientId: settings.get(`${ service.key }_id`),
				secret: settings.get(`${ service.key }_secret`),
			};
			if (/Accounts_OAuth_Custom-/.test(service.key)) {
				data.custom = true;
				data.clientId = settings.get(`${ service.key }-id`);
				data.secret = settings.get(`${ service.key }-secret`);
				data.serverURL = settings.get(`${ service.key }-url`);
				data.tokenPath = settings.get(`${ service.key }-token_path`);
				data.identityPath = settings.get(`${ service.key }-identity_path`);
				data.authorizePath = settings.get(`${ service.key }-authorize_path`);
				data.scope = settings.get(`${ service.key }-scope`);
				data.accessTokenParam = settings.get(`${ service.key }-access_token_param`);
				data.buttonLabelText = settings.get(`${ service.key }-button_label_text`);
				data.buttonLabelColor = settings.get(`${ service.key }-button_label_color`);
				data.loginStyle = settings.get(`${ service.key }-login_style`);
				data.buttonColor = settings.get(`${ service.key }-button_color`);
				data.tokenSentVia = settings.get(`${ service.key }-token_sent_via`);
				data.identityTokenSentVia = settings.get(`${ service.key }-identity_token_sent_via`);
				data.usernameField = settings.get(`${ service.key }-username_field`);
				data.emailField = settings.get(`${ service.key }-email_field`);
				data.nameField = settings.get(`${ service.key }-name_field`);
				data.avatarField = settings.get(`${ service.key }-avatar_field`);
				data.rolesClaim = settings.get(`${ service.key }-roles_claim`);
				data.mergeUsers = settings.get(`${ service.key }-merge_users`);
				data.mergeRoles = settings.get(`${ service.key }-merge_roles`);
				data.showButton = settings.get(`${ service.key }-show_button`);
				new CustomOAuth(serviceName.toLowerCase(), {
					serverURL: data.serverURL,
					tokenPath: data.tokenPath,
					identityPath: data.identityPath,
					authorizePath: data.authorizePath,
					scope: data.scope,
					loginStyle: data.loginStyle,
					tokenSentVia: data.tokenSentVia,
					identityTokenSentVia: data.identityTokenSentVia,
					usernameField: data.usernameField,
					emailField: data.emailField,
					nameField: data.nameField,
					avatarField: data.avatarField,
					rolesClaim: data.rolesClaim,
					mergeUsers: data.mergeUsers,
					mergeRoles: data.mergeRoles,
					accessTokenParam: data.accessTokenParam,
					showButton: data.showButton,
				});
			}
			if (serviceName === 'Facebook') {
				data.appId = data.clientId;
				delete data.clientId;
			}
			if (serviceName === 'Twitter') {
				data.consumerKey = data.clientId;
				delete data.clientId;
			}

			if (serviceName === 'Linkedin') {
				data.clientConfig = {
					requestPermissions: ['r_liteprofile', 'r_emailaddress'],
				};
			}

			if (serviceName === 'Nextcloud') {
				data.buttonLabelText = settings.get('Accounts_OAuth_Nextcloud_button_label_text');
				data.buttonLabelColor = settings.get('Accounts_OAuth_Nextcloud_button_label_color');
				data.buttonColor = settings.get('Accounts_OAuth_Nextcloud_button_color');
			}

			// If there's no data other than the service name, then put the service name in the data object so the operation won't fail
			const keys = Object.keys(data).filter((key) => data[key] !== undefined);
			if (!keys.length) {
				data.service = serviceName.toLowerCase();
			}

			ServiceConfiguration.configurations.upsert({
				service: serviceName.toLowerCase(),
			}, {
				$set: data,
			});
		} else {
			ServiceConfiguration.configurations.remove({
				service: serviceName.toLowerCase(),
			});
		}
	});
}

const OAuthServicesUpdate = _.debounce(Meteor.bindEnvironment(_OAuthServicesUpdate), 2000);

function OAuthServicesRemove(_id) {
	const serviceName = _id.replace('Accounts_OAuth_Custom-', '');
	return ServiceConfiguration.configurations.remove({
		service: serviceName.toLowerCase(),
	});
}

settings.get(/^Accounts_OAuth_.+/, function() {
	return OAuthServicesUpdate(); // eslint-disable-line new-cap
});

settings.get(/^Accounts_OAuth_Custom-[a-z0-9_]+/, function(key, value) {
	if (!value) {
		return OAuthServicesRemove(key);// eslint-disable-line new-cap
	}
});

function customOAuthServicesInit() {
	// Add settings for custom OAuth providers to the settings so they get
	// automatically added when they are defined in ENV variables
	Object.keys(process.env).forEach((key) => {
		if (/Accounts_OAuth_Custom-[a-zA-Z0-9_-]+$/.test(key)) {
			settings.add(key, process.env[key]);
		}
	});
}

customOAuthServicesInit();
<|endoftext|>