<commit_before>/**
 * Copyright 2015 CANAL+ Group
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import Segment from "../segment.js";
import {
  normalizeRange,
  getTimelineRangeEnd,
  getInitSegment,
  setTimescale,
  scale,
} from "./helpers.js";

/**
 * Get index of the segment containing the given timescaled timestamp.
 * @param {Object} index
 * @param {Number} ts
 * @returns {Number}
 */
const getSegmentIndex = (index, ts) => {
  const { timeline } = index;

  let low = 0;
  let high = timeline.length;

  while (low < high) {
    const mid = (low + high) >>> 1;
    if (timeline[mid].ts < ts) {
      low = mid + 1;
    } else {
      high = mid;
    }
  }

  return (low > 0)
    ? low - 1
    : low;
};

/**
 * @param {Number} ts
 * @param {Number} up
 * @param {Number} duration
 * @returns {Number}
 */
const getSegmentNumber = (ts, up, duration) => {
  const diff = up - ts;
  if (diff > 0) {
    return Math.floor(diff / duration);
  } else {
    return 0;
  }
};

/**
 * Calculate the number of times a segment repeat based on the next segment.
 * @param {Object} seg
 * @param {Number} seg.ts - beginning timescaled timestamp
 * @param {Number} seg.d - timescaled duration of the segment
 * @param {Object} nextSeg
 * @param {Number} nextSeg.t - TODO check that one
 * @returns {Number}
 */
const calculateRepeat = (seg, nextSeg) => {
  let rep = seg.r || 0;

  // A negative value of the @r attribute of the S element indicates
  // that the duration indicated in @d attribute repeats until the
  // start of the next S element, the end of the Period or until the
  // next MPD update.
  if (rep < 0) {
    const repEnd = nextSeg
      ? nextSeg.t
      : Infinity;
    rep = Math.ceil((repEnd - seg.ts) / seg.d) - 1;
  }

  return rep;
};

const SegmentTimelineHelpers = {
  getInitSegment,
  setTimescale,
  scale,

  /**
   * @param {string|Number} repId
   * @param {Object} index
   * @param {Number} _up
   * @param {Number} _to
   * @returns {Array.<Segment>}
   */
  getSegments(repId, index, _up, _to) {
    const { up, to } = normalizeRange(index, _up, _to);
    const { timeline, timescale, media, startNumber } = index;

    const hasNumbering = startNumber != null;
    let currentNumber = hasNumbering ? startNumber : undefined;

    const segments = [];

    const timelineLength = timeline.length;

    // if we need to add a number, we have to parse the timeline from the
    // beginning to know which segment we're talking of.
    // If not, we can optimize to know where the first interesting timeline
    // index might be
    const initialIndex = hasNumbering ? 0 : getSegmentIndex(index, up) - 1;

    // TODO(pierre): use @maxSegmentDuration if possible
    let maxEncounteredDuration = (timeline.length && timeline[0].d) || 0;

    for (let i = initialIndex; i < timelineLength; i++) {
      const segmentRange = timeline[i];
      const { d, ts, range } = segmentRange;

      maxEncounteredDuration = Math.max(maxEncounteredDuration, d);

      // live-added segments have @d attribute equals to -1
      if (d < 0) {
        if (ts + maxEncounteredDuration < to) { // TODO what?
          const segment = new Segment({
            id: "" + repId + "_" + ts,
            time: ts,
            init: false,
            range: range,
            duration: undefined,
            indexRange: null,
            timescale,
            media,
            number: hasNumbering ? currentNumber : undefined,
          });
          segments.push(segment);
        }
        return segments;
      }

      const repeat = calculateRepeat(segmentRange, timeline[i + 1]);
      let segmentNumberInCurrentRange = getSegmentNumber(ts, up, d);
      let segmentTime = ts + segmentNumberInCurrentRange * d;
      while (segmentTime < to && segmentNumberInCurrentRange <= repeat) {
        const segment = new Segment({
          id: "" + repId + "_" + segmentTime,
          time: segmentTime,
          init: false,
          range: range,
          duration: d,
          indexRange: null,
          timescale,
          media,
          number: hasNumbering ?
            currentNumber + segmentNumberInCurrentRange : undefined,
        });
        segments.push(segment);

        // update segment number and segment time for the next segment
        segmentNumberInCurrentRange++;
        segmentTime = ts + segmentNumberInCurrentRange * d;
      }

      if (segmentTime >= to) {
        // we reached ``to``, we're done
        return segments;
      }

      if (hasNumbering) {
        currentNumber += repeat + 1;
      }
    }

    return segments;
  },

  /**
   * Returns true if, based on the arguments, the index should be refreshed.
   * @param {Object} index
   * @param {Number} time
   * @param {Number} up
   * @param {Number} to
   * @returns {Boolean}
   */
  shouldRefresh(index, time, up, to) {
    const {
      timeline,
      timescale,
      presentationTimeOffset = 0,
    } = index;

    const scaledTo = to * timescale - presentationTimeOffset;

    let last = timeline[timeline.length - 1];
    if (!last) {
      return false;
    }

    if (last.d < 0) {
      last = { ts: last.ts, d: 0, r: last.r };
    }

    return !(scaledTo <= getTimelineRangeEnd(last));
  },

  /**
   * Returns first position in index.
   * @param {Object} index
   * @returns {Number}
   */
  getFirstPosition(index) {
    if (!index.timeline.length) {
      return undefined;
    }
    return index.timeline[0].ts / index.timescale;
  },

  /**
   * Returns last position in index.
   * @param {Object} index
   * @returns {Number}
   */
  getLastPosition(index) {
    if (!index.timeline.length) {
      return undefined;
    }
    const lastTimelineElement = index.timeline[index.timeline.length - 1];
    return (getTimelineRangeEnd(lastTimelineElement) / index.timescale);
  },

  /**
   * Checks if the time given is in a discontinuity. That is:
   *   - We're on the upper bound of the current range (end of the range - time
   *     is inferior to the timescale)
   *   - The next range starts after the end of the current range.
   * @param {Object} index
   * @param {Number} _time
   * @returns {Number} - If a discontinuity is present, this is the Starting ts
   * for the next (discontinuited) range. If not this is equal to -1.
   */
  checkDiscontinuity(index, _time) {
    const { timeline, timescale = 1 } = index;
    const time = _time * timescale;

    if (time <= 0) {
      return -1;
    }

    const segmentIndex = getSegmentIndex(index, time);
    if (segmentIndex < 0 || segmentIndex >= timeline.length - 1) {
      return -1;
    }

    const range = timeline[segmentIndex];
    if (range.d === -1) {
      return -1;
    }

    const rangeUp = range.ts;
    const rangeTo = getTimelineRangeEnd(range);
    const nextRange = timeline[segmentIndex + 1];

    // when we are actually inside the found range and this range has
    // an explicit discontinuity with the next one
    if (rangeTo !== nextRange.ts &&
        time >= rangeUp &&
        time <= rangeTo &&
        (rangeTo - time) < timescale) {
      return nextRange.ts / timescale;
    }

    return -1;
  },

  /**
   * Add a new segment to the index.
   *
   * /!\ Mutate the given index
   * @param {Object} index
   * @param {Object} newSegment
   * @param {Number} newSegment.timescale
   * @param {Number} newSegment.time
   * @param {Number} newSegment.duration
   * @param {Object} currentSegment
   * @param {Number} currentSegment.timescale
   * @param {Number} currentSegment.time
   * @returns {Boolean} - true if the segment has been added
   */
  _addSegmentInfos(index, newSegment, currentSegment) {
    const { timeline, timescale } = index;
    const timelineLength = timeline.length;
    const last = timeline[timelineLength - 1];

    const scaledNewSegment = newSegment.timescale === timescale ? {
      time: newSegment.time,
      duration: newSegment.duration,
    } : {
      time: (newSegment.time / newSegment.timescale) * timescale,
      duration: (newSegment.duration / newSegment.timescale) * timescale,
    };

    let scaledCurrentTime;

    if (currentSegment) {
      scaledCurrentTime = currentSegment.timescale === timescale ?
        currentSegment.time :
        (currentSegment.time / currentSegment.timescale) * timescale;
    }

    // in some circumstances, the new segment informations are only
    // duration informations that we can use to deduct the ts of the
    // next segment. this is the case where the new segment are
    // associated to a current segment and have the same ts
    const shouldDeductNextSegment = scaledCurrentTime != null &&
      (scaledNewSegment.time === scaledCurrentTime);
    if (shouldDeductNextSegment) {
      const newSegmentTs = scaledNewSegment.time + scaledNewSegment.duration;
      const lastSegmentTs = (last.ts + last.d * last.r);
      const tsDiff = newSegmentTs - lastSegmentTs;

      if (tsDiff <= 0) { // same segment / behind the last
        return false;
      }

      // try to use the compact notation with @r attribute on the last
      // to elements of the timeline if we find out they have the same
      // duration
      if (last.d === -1) {
        const prev = timeline[timelineLength - 2];
        if (prev && prev.d === tsDiff) {
          prev.r++;
          timeline.pop();
        } else {
          last.d = tsDiff;
        }
      }

      index.timeline.push({
        d: -1,
        ts: newSegmentTs,
        r: 0,
      });
      return true;
    }

    // if the given timing has a timestamp after the timeline end we
    // just need to push a new element in the timeline, or increase
    // the @r attribute of the last element.
    else if (scaledNewSegment.time >= getTimelineRangeEnd(last)) {
      if (last.d === scaledNewSegment.duration) {
        last.r++;
      } else {
        index.timeline.push({
          d: scaledNewSegment.duration,
          ts: scaledNewSegment.time,
          r: 0,
        });
      }
      return true;
    }

    return false;
  },
};

export default SegmentTimelineHelpers;
<commit_msg>dash: simplify code for SegmentTimeline indexes<commit_after>/**
 * Copyright 2015 CANAL+ Group
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import Segment from "../segment.js";
import {
  normalizeRange,
  getTimelineRangeEnd,
  getInitSegment,
  setTimescale,
  scale,
} from "./helpers.js";

/**
 * Get index of the segment containing the given timescaled timestamp.
 * @param {Object} index
 * @param {Number} ts
 * @returns {Number}
 */
const getSegmentIndex = (index, ts) => {
  const { timeline } = index;

  let low = 0;
  let high = timeline.length;

  while (low < high) {
    const mid = (low + high) >>> 1;
    if (timeline[mid].ts < ts) {
      low = mid + 1;
    } else {
      high = mid;
    }
  }

  return (low > 0)
    ? low - 1
    : low;
};

/**
 * @param {Number} ts
 * @param {Number} up
 * @param {Number} duration
 * @returns {Number}
 */
const getSegmentNumber = (ts, up, duration) => {
  const diff = up - ts;
  if (diff > 0) {
    return Math.floor(diff / duration);
  } else {
    return 0;
  }
};

/**
 * Calculate the number of times a segment repeat based on the next segment.
 * @param {Object} seg
 * @param {Number} seg.ts - beginning timescaled timestamp
 * @param {Number} seg.d - timescaled duration of the segment
 * @param {Object} nextSeg
 * @param {Number} nextSeg.t - TODO check that one
 * @returns {Number}
 */
const calculateRepeat = (seg, nextSeg) => {
  let rep = seg.r || 0;

  // A negative value of the @r attribute of the S element indicates
  // that the duration indicated in @d attribute repeats until the
  // start of the next S element, the end of the Period or until the
  // next MPD update.
  if (rep < 0) {
    const repEnd = nextSeg
      ? nextSeg.t
      : Infinity;
    rep = Math.ceil((repEnd - seg.ts) / seg.d) - 1;
  }

  return rep;
};

const SegmentTimelineHelpers = {
  getInitSegment,
  setTimescale,
  scale,

  /**
   * @param {string|Number} repId
   * @param {Object} index
   * @param {Number} _up
   * @param {Number} _to
   * @returns {Array.<Segment>}
   */
  getSegments(repId, index, _up, _to) {
    const { up, to } = normalizeRange(index, _up, _to);
    const { timeline, timescale, media, startNumber } = index;

    const hasNumbering = startNumber != null;
    let currentNumber = hasNumbering ? startNumber : undefined;

    const segments = [];

    const timelineLength = timeline.length;

    // TODO(pierre): use @maxSegmentDuration if possible
    let maxEncounteredDuration = (timeline.length && timeline[0].d) || 0;

    for (let i = 0; i < timelineLength; i++) {
      const segmentRange = timeline[i];
      const { d, ts, range } = segmentRange;

      maxEncounteredDuration = Math.max(maxEncounteredDuration, d);

      // live-added segments have @d attribute equals to -1
      if (d < 0) {
        // TODO what? May be to play it safe and avoid adding segments which are
        // not completely generated
        if (ts + maxEncounteredDuration < to) {
          const segment = new Segment({
            id: "" + repId + "_" + ts,
            time: ts,
            init: false,
            range: range,
            duration: undefined,
            indexRange: null,
            timescale,
            media,
            number: hasNumbering ? currentNumber : undefined,
          });
          segments.push(segment);
        }
        return segments;
      }

      const repeat = calculateRepeat(segmentRange, timeline[i + 1]);
      let segmentNumberInCurrentRange = getSegmentNumber(ts, up, d);
      let segmentTime = ts + segmentNumberInCurrentRange * d;
      while (segmentTime < to && segmentNumberInCurrentRange <= repeat) {
        const segment = new Segment({
          id: "" + repId + "_" + segmentTime,
          time: segmentTime,
          init: false,
          range: range,
          duration: d,
          indexRange: null,
          timescale,
          media,
          number: hasNumbering ?
            currentNumber + segmentNumberInCurrentRange : undefined,
        });
        segments.push(segment);

        // update segment number and segment time for the next segment
        segmentNumberInCurrentRange++;
        segmentTime = ts + segmentNumberInCurrentRange * d;
      }

      if (segmentTime >= to) {
        // we reached ``to``, we're done
        return segments;
      }

      if (hasNumbering) {
        currentNumber += repeat + 1;
      }
    }

    return segments;
  },

  /**
   * Returns true if, based on the arguments, the index should be refreshed.
   * @param {Object} index
   * @param {Number} time
   * @param {Number} up
   * @param {Number} to
   * @returns {Boolean}
   */
  shouldRefresh(index, time, up, to) {
    const {
      timeline,
      timescale,
      presentationTimeOffset = 0,
    } = index;

    const scaledTo = to * timescale - presentationTimeOffset;

    let last = timeline[timeline.length - 1];
    if (!last) {
      return false;
    }

    if (last.d < 0) {
      last = { ts: last.ts, d: 0, r: last.r };
    }

    return !(scaledTo <= getTimelineRangeEnd(last));
  },

  /**
   * Returns first position in index.
   * @param {Object} index
   * @returns {Number}
   */
  getFirstPosition(index) {
    if (!index.timeline.length) {
      return undefined;
    }
    return index.timeline[0].ts / index.timescale;
  },

  /**
   * Returns last position in index.
   * @param {Object} index
   * @returns {Number}
   */
  getLastPosition(index) {
    if (!index.timeline.length) {
      return undefined;
    }
    const lastTimelineElement = index.timeline[index.timeline.length - 1];
    return (getTimelineRangeEnd(lastTimelineElement) / index.timescale);
  },

  /**
   * Checks if the time given is in a discontinuity. That is:
   *   - We're on the upper bound of the current range (end of the range - time
   *     is inferior to the timescale)
   *   - The next range starts after the end of the current range.
   * @param {Object} index
   * @param {Number} _time
   * @returns {Number} - If a discontinuity is present, this is the Starting ts
   * for the next (discontinuited) range. If not this is equal to -1.
   */
  checkDiscontinuity(index, _time) {
    const { timeline, timescale = 1 } = index;
    const time = _time * timescale;

    if (time <= 0) {
      return -1;
    }

    const segmentIndex = getSegmentIndex(index, time);
    if (segmentIndex < 0 || segmentIndex >= timeline.length - 1) {
      return -1;
    }

    const range = timeline[segmentIndex];
    if (range.d === -1) {
      return -1;
    }

    const rangeUp = range.ts;
    const rangeTo = getTimelineRangeEnd(range);
    const nextRange = timeline[segmentIndex + 1];

    // when we are actually inside the found range and this range has
    // an explicit discontinuity with the next one
    if (rangeTo !== nextRange.ts &&
        time >= rangeUp &&
        time <= rangeTo &&
        (rangeTo - time) < timescale) {
      return nextRange.ts / timescale;
    }

    return -1;
  },

  /**
   * Add a new segment to the index.
   *
   * /!\ Mutate the given index
   * @param {Object} index
   * @param {Object} newSegment
   * @param {Number} newSegment.timescale
   * @param {Number} newSegment.time
   * @param {Number} newSegment.duration
   * @param {Object} currentSegment
   * @param {Number} currentSegment.timescale
   * @param {Number} currentSegment.time
   * @returns {Boolean} - true if the segment has been added
   */
  _addSegmentInfos(index, newSegment, currentSegment) {
    const { timeline, timescale } = index;
    const timelineLength = timeline.length;
    const last = timeline[timelineLength - 1];

    const scaledNewSegment = newSegment.timescale === timescale ? {
      time: newSegment.time,
      duration: newSegment.duration,
    } : {
      time: (newSegment.time / newSegment.timescale) * timescale,
      duration: (newSegment.duration / newSegment.timescale) * timescale,
    };

    let scaledCurrentTime;

    if (currentSegment) {
      scaledCurrentTime = currentSegment.timescale === timescale ?
        currentSegment.time :
        (currentSegment.time / currentSegment.timescale) * timescale;
    }

    // in some circumstances, the new segment informations are only
    // duration informations that we can use to deduct the ts of the
    // next segment. this is the case where the new segment are
    // associated to a current segment and have the same ts
    const shouldDeductNextSegment = scaledCurrentTime != null &&
      (scaledNewSegment.time === scaledCurrentTime);
    if (shouldDeductNextSegment) {
      const newSegmentTs = scaledNewSegment.time + scaledNewSegment.duration;
      const lastSegmentTs = (last.ts + last.d * last.r);
      const tsDiff = newSegmentTs - lastSegmentTs;

      if (tsDiff <= 0) { // same segment / behind the last
        return false;
      }

      // try to use the compact notation with @r attribute on the last
      // to elements of the timeline if we find out they have the same
      // duration
      if (last.d === -1) {
        const prev = timeline[timelineLength - 2];
        if (prev && prev.d === tsDiff) {
          prev.r++;
          timeline.pop();
        } else {
          last.d = tsDiff;
        }
      }

      index.timeline.push({
        d: -1,
        ts: newSegmentTs,
        r: 0,
      });
      return true;
    }

    // if the given timing has a timestamp after the timeline end we
    // just need to push a new element in the timeline, or increase
    // the @r attribute of the last element.
    else if (scaledNewSegment.time >= getTimelineRangeEnd(last)) {
      if (last.d === scaledNewSegment.duration) {
        last.r++;
      } else {
        index.timeline.push({
          d: scaledNewSegment.duration,
          ts: scaledNewSegment.time,
          r: 0,
        });
      }
      return true;
    }

    return false;
  },
};

export default SegmentTimelineHelpers;
<|endoftext|>


<commit_before>import { matcherHint, printExpected, printReceived } from 'jest-matcher-utils';

import predicate from './predicate';

const passMessage = received => () =>
  matcherHint('.not.toBeFinite', 'received', '') +
  '\n\n' +
  'Expected value to not be finite received:\n' +
  `  ${printReceived(received)}`;

const failMessage = received => () =>
  matcherHint('.toBeFinite', 'received', '') +
  '\n\n' +
  'Expected value to be finite received:\n' +
  `  ${printReceived(received)}`;

export default {
  toBeFinite: expected => {
    const pass = predicate(expected);
    if (pass) {
      return { pass: true, message: passMessage(expected) };
    }

    return { pass: false, message: failMessage(expected) };
  }
};
<commit_msg>Fix lint warning<commit_after>import { matcherHint, printReceived } from 'jest-matcher-utils';

import predicate from './predicate';

const passMessage = received => () =>
  matcherHint('.not.toBeFinite', 'received', '') +
  '\n\n' +
  'Expected value to not be finite received:\n' +
  `  ${printReceived(received)}`;

const failMessage = received => () =>
  matcherHint('.toBeFinite', 'received', '') +
  '\n\n' +
  'Expected value to be finite received:\n' +
  `  ${printReceived(received)}`;

export default {
  toBeFinite: expected => {
    const pass = predicate(expected);
    if (pass) {
      return { pass: true, message: passMessage(expected) };
    }

    return { pass: false, message: failMessage(expected) };
  }
};
<|endoftext|>


<commit_before>#! /usr/bin/env node
/* eslint-disable camelcase */
const github = require('octonode');
const normalizeUrl = require('normalize-url');
const spawn = require('cross-spawn');
const travisAfterAll = require('travis-after-all');
const urlRegex = require('url-regex');
const argv = require('yargs').argv;

const nowCli = require.resolve('now/download/dist/now');

if (!process.env.CI || !process.env.TRAVIS) {
  throw new Error('Could not detect Travis CI environment');
}

const githubToken = process.env.GH_TOKEN;
const nowToken = process.env.NOW_TOKEN;

if (!githubToken) {
  throw new Error('Missing required environment variable GH_TOKEN');
}

if (!nowToken) {
  throw new Error('Missing required environment variable NOW_TOKEN');
}

const client = github.client(githubToken);
const ghRepo = client.repo(process.env.TRAVIS_REPO_SLUG);
let ghPR;

if (process.env.TRAVIS_PULL_REQUEST) {
  ghPR = client.pr(
    process.env.TRAVIS_REPO_SLUG,
    process.env.TRAVIS_PULL_REQUEST,
  );
}

function noop() {}

function getUrl(content) {
  const urls = content.match(urlRegex()) || [];

  return urls.map(url => normalizeUrl(url.trim().replace(/\.+$/, '')))[0];
}

function deploy(context, sha) {
  ghRepo.status(
    sha,
    {
      context,
      state: 'pending',
      description: `Δ Now ${context} deployment pending`,
    },
    noop,
  );

  const args = [
    '--token',
    nowToken,
    '--team',
    'coderplex',
    '--no-clipboard',
    '-n',
    'coderplex-app',
  ];
  const alias = context === 'production' && process.env.NOW_ALIAS;
  let stdout = '';

  if (alias) {
    args.push(...['--alias', alias]);
  }

  if (argv.p || argv.public) {
    args.push(...['-p']);
  }

  if (argv.folder) {
    args.push(argv.folder);
  }

  const child = spawn(nowCli, args);

  if (!alias) {
    child.stdout.on('data', data => {
      stdout += data;
    });
  }

  child.on('error', err => {
    console.error(err);
    ghRepo.status(
      sha,
      {
        context,
        state: 'error',
        description: `Δ Now ${context} deployment failed. See Travis logs for details.`,
      },
      noop,
    );
  });

  child.on('close', () => {
    const target_url = alias || getUrl(stdout);

    ghRepo.status(
      sha,
      {
        context,
        target_url,
        state: 'success',
        description: `Δ Now ${context} deployment complete`,
      },
      noop,
    );
    if (ghPR) {
      const comment = `
      ### Δ Now ${context} deployment complete
      - ✅ **Build Passed**
      - 🚀 **Staging URL** : ${target_url}
      ---
      Note: **This is autogenerated through Travis build**
    `;
      ghPR.createComment(
        {
          body: comment,
          commit_id: process.env.TRAVIS_COMMIT,
        },
        noop,
      );
    }
  });
}

travisAfterAll((code, err) => {
  // Don't do anything if there was an error of if the build returned a failing code
  if (err || code) {
    return;
  }

  switch (process.env.TRAVIS_EVENT_TYPE) {
    case 'pull_request':
      return deploy('staging', process.env.TRAVIS_PULL_REQUEST_SHA);
    case 'push':
      return deploy('production', process.env.TRAVIS_COMMIT);
    default:
      return '';
  }
});
<commit_msg>remove commitId to check if it creates comment<commit_after>#! /usr/bin/env node
/* eslint-disable camelcase */
const github = require('octonode');
const normalizeUrl = require('normalize-url');
const spawn = require('cross-spawn');
const travisAfterAll = require('travis-after-all');
const urlRegex = require('url-regex');
const argv = require('yargs').argv;

const nowCli = require.resolve('now/download/dist/now');

if (!process.env.CI || !process.env.TRAVIS) {
  throw new Error('Could not detect Travis CI environment');
}

const githubToken = process.env.GH_TOKEN;
const nowToken = process.env.NOW_TOKEN;

if (!githubToken) {
  throw new Error('Missing required environment variable GH_TOKEN');
}

if (!nowToken) {
  throw new Error('Missing required environment variable NOW_TOKEN');
}

const client = github.client(githubToken);
const ghRepo = client.repo(process.env.TRAVIS_REPO_SLUG);
let ghPR;

if (process.env.TRAVIS_PULL_REQUEST) {
  ghPR = client.pr(
    process.env.TRAVIS_REPO_SLUG,
    process.env.TRAVIS_PULL_REQUEST,
  );
}

function noop() {}

function getUrl(content) {
  const urls = content.match(urlRegex()) || [];

  return urls.map(url => normalizeUrl(url.trim().replace(/\.+$/, '')))[0];
}

function deploy(context, sha) {
  ghRepo.status(
    sha,
    {
      context,
      state: 'pending',
      description: `Δ Now ${context} deployment pending`,
    },
    noop,
  );

  const args = [
    '--token',
    nowToken,
    '--team',
    'coderplex',
    '--no-clipboard',
    '-n',
    'coderplex-app',
  ];
  const alias = context === 'production' && process.env.NOW_ALIAS;
  let stdout = '';

  if (alias) {
    args.push(...['--alias', alias]);
  }

  if (argv.p || argv.public) {
    args.push(...['-p']);
  }

  if (argv.folder) {
    args.push(argv.folder);
  }

  const child = spawn(nowCli, args);

  if (!alias) {
    child.stdout.on('data', data => {
      stdout += data;
    });
  }

  child.on('error', err => {
    console.error(err);
    ghRepo.status(
      sha,
      {
        context,
        state: 'error',
        description: `Δ Now ${context} deployment failed. See Travis logs for details.`,
      },
      noop,
    );
  });

  child.on('close', () => {
    const target_url = alias || getUrl(stdout);

    ghRepo.status(
      sha,
      {
        context,
        target_url,
        state: 'success',
        description: `Δ Now ${context} deployment complete`,
      },
      noop,
    );
    if (ghPR) {
      const comment = `
      ### Δ Now ${context} deployment complete
      - ✅ **Build Passed**
      - 🚀 **Staging URL** : ${target_url}
      ---
      Note: **This is autogenerated through Travis build**
    `;
      ghPR.createComment(
        {
          body: comment,
        },
        noop,
      );
    } else {
      console.log('No PR found');
    }
  });
}

travisAfterAll((code, err) => {
  // Don't do anything if there was an error of if the build returned a failing code
  if (err || code) {
    return;
  }

  switch (process.env.TRAVIS_EVENT_TYPE) {
    case 'pull_request':
      return deploy('staging', process.env.TRAVIS_PULL_REQUEST_SHA);
    case 'push':
      return deploy('production', process.env.TRAVIS_COMMIT);
    default:
      return '';
  }
});
<|endoftext|>


<commit_before>export default class MetadataProvider {

    /**
     * Returns a cloned schema which is canonical, meaning that every 'entities', 'fields', 'layouts' and 'groups' are
     * arrays and not objects.
     * @param schema
     * @returns {*}
     */
    static canonizeSchema(schema) {
        if (!schema) throw Error('\'schema\' should be truthy');

        if(!schema.entities) {
            // when no entities are specified in the schema, the schema is considered to be in the SIMPLEST form, example:
            // {
            //     name: {
            //         type: 'string'
            //     },
            //     dateOfBirth: {
            //         type: 'string'
            //     }
            // }
            // OR...
            // [
            //     { name: 'name', type: 'string' },
            //     { name: 'dateOfBirth', type: 'date' },
            // ]
            // In this case, I'm just creating a 'default' entity
            return MetadataProvider.canonizeSchema({
                entities: [
                    {
                        name: 'default',
                        fields: schema
                    }
                ]
            });
        }

        schema = {...schema};
        this._canonizeArrays(schema, ["entities", "layouts", "groups"])

        //schema.entities = MetadataProvider.canonizeArray(schema.entities);
        //_.each(schema.entities, entity => {
        //    entity.fields = MetadataProvider.canonizeArray(entity.fields);
        //    entity.layouts = MetadataProvider.canonizeArray(entity.layouts);
        //    _.each(entity.layouts, layout => {
        //        layout.fields = MetadataProvider.canonizeArray(layout.fields);
        //        layout.groups = MetadataProvider.canonizeArray(layout.groups);
        //        _.each(layout.groups, group => {
        //            group.fields = MetadataProvider.canonizeArray(group.fields);
        //        })
        //    });
        //});

        return schema;
    }

    static _canonizeArrays(dataArray, keys, id = 0) {
        dataArray[keys[id]] = MetadataProvider.canonizeArray(dataArray[keys[id]]);
        if(dataArray[keys[id]]) {
            dataArray[keys[id]].forEach(elem => {
                elem.fields = MetadataProvider.canonizeArray(elem.fields);
                if(id + 1 < keys.length)
                    this._canonizeArrays(elem, keys, id + 1);
            })
        }

    }
    /**
     * Ensures the object passed in is an array. If it is, it is returned, otherwise, this function
     * converts the target object into an array.
     * This is important to convert this:
     *     { fields: { dateOfBirth: { type: 'string' } } // this should be acceptable as a fields definition
     * into this:
     *     { fields: [{ name: 'dateOfBirth', type: 'string' }] } // this is the canonical field definition
     * @param obj
     */
    static canonizeArray(obj) {
        if (!obj) return obj; // this is so the canonizeSchema method doesn't have to check every property for undefined

        if (Array.isArray(obj))
            return obj;

        // let's create an array
        return Object.keys(obj).map((property) => {
            let isObject = obj[property] && typeof obj[property] === "object";
            if (!isObject)
                throw Error('cannot generate canonical array. Every field should be an object');

            return {name: property, ...obj[property]};
        });
    }

    /**
     * Validates a field metadata
     * @param metadata
     * @private
     */
    static validateFieldMetadata(metadata) {
        if (!metadata) throw Error('metadata should not be null or undefined');
        if (!metadata.name) throw Error('metadata\'s "name" property is required');
        if (!metadata.type) throw Error('metadata\'s "type" property is required');
    }

    /**
     * Gets a raw entity from the given schema. No processing.
     * @param schema
     * @param entityName
     * @returns {*}
     */
    static getEntity(schema, entityName) {
        if (!schema) throw Error('\'schema\' should be truthy');
        if (schema.entities === undefined || schema.entities === null || !schema.entities.length)
            throw Error('schema should have entities');

        let entity;

        if (entityName) {
            entity = schema.entities.find(e => e.name === entityName);
        } else {
            if(schema.entities.length != 1)
                throw Error('When an entityName is not specified, there must be one and only one entity');
            entity = schema.entities[0];
        }

        if (!entity) throw Error(`Could not find entity. Entity name: ${entityName}`);

        return entity;
    }

    /**
     * Gets a raw layout from the given entity. No processing.
     * @param entity
     * @param layoutName
     * @returns {*}
     */
    static getLayout(entity, layoutName) {
        if (!entity) throw Error('\'entity\' should be truthy');
        let layout;

        if (layoutName) {
            layout = entity.layouts ? entity.layouts.find(l => l.name === layoutName) : layout;
        } else {
            if(entity.layouts.length != 1)
                throw Error('When the layoutName is not specified, there must be one and only one layout');
            layout = entity.layouts[0];
        }

        if (!layout) throw Error(`Could not find layout. Layout name: ${layoutName}`);

        return layout;
    }

    /**
     * Gets a raw entity and a raw layout from the given schema.
     * @param schema
     * @param entityName
     * @param layoutName
     * @returns {{entity: *, layout: *}}
     */
    static getEntityAndLayout(schema, entityName, layoutName) {
        let entity = this.getEntity(schema, entityName);
        let layout = layoutName ? this.getLayout(entity, layoutName) : this.generateDefaultLayout(schema, entity);

        return {
            entity: entity,
            layout: layout
        }
    }

    /**
     * Internal method for merging entity and layout fields
     * @param schema
     * @param entity
     * @param layout
     * @param partialResult
     * @param callback
     * @return {*[]}
     */
    static getFieldsInternal(schema, entity, layout, partialResult, callback) {

        if (!schema) throw Error('\'schema\' should be truthy');
        if (!entity) throw Error('\'entity\' should be truthy');
        if (!layout.fields && !layout.groups) throw Error('A layout should have fields or groups');

        partialResult = partialResult || [];
        let thisGroupFields = [];

        if (layout.groups) {
            layout.groups.forEach(g => {
                thisGroupFields = [... new Set(thisGroupFields.concat(this.getFieldsInternal(schema, entity, g, partialResult, callback)))];
            });
        }

        if (layout.fields) {

            for (let i = 0; i < layout.fields.length; i++) {

                let groupField = layout.fields[i];
                let existingEntityProperty = entity.fields.find(field => field.name == groupField.name);

                let field = {...existingEntityProperty || {}, ...groupField};
                this.validateFieldMetadata(field);

                thisGroupFields.push(field);

                if (field.type == 'entity') {

                    if (!field.entityName) {
                        throw Error('when a field is of type \'entity\', it needs to specify an \'entityName\'')
                    }

                    let entityAndLayout = this.getEntityAndLayout(schema, field.entityName, field.layoutName);
                    field.layout = this.processLayout(schema, entityAndLayout.entity, entityAndLayout.layout);
                    field.fields = this.getFieldsInternal(schema, entityAndLayout.entity, entityAndLayout.layout, partialResult, callback);
                }

                if (field.type == 'array') {
                    if (!field.arrayType) {
                        throw Error('when a field is of type \'array\', it needs to specify an \'arrayType\'');
                    }

                    if (field.arrayType != 'entity') {
                        throw Error('only entity arrays are currently supported');
                    }

                    if (!field.entityType) {
                        throw Error('when a field is of type \'array\' and arrayType is \'entity\', it needs to specify an \'entityType\'');
                    }

                    let entityAndLayout = this.getEntityAndLayout(schema, field.entityType, field.layoutName);
                    field.layout = this.processLayout(schema, entityAndLayout.entity, entityAndLayout.layout);
                    field.fields = this.getFieldsInternal(schema, entityAndLayout.entity, entityAndLayout.layout, partialResult, callback);
                }

                if (callback) {
                    callback(field);
                }
            }
        }

        return [...new Set(partialResult.concat(thisGroupFields))];
    }

    /**
     * Merges the given field collection
     * @param schema
     * @param entity
     * @param layout
     * @param callback
     * @external https://github.com/gearz-lab/react-metaform/blob/master/docs-md/MetadataProvider.md
     */
    static getFields(schema, entity, layout, callback) {
        entity = typeof entity === 'string' ? this.getEntity(schema, entity) : entity;

        if (!layout) {
            layout = this.generateDefaultLayout(schema, entity);
        } else {
            layout = typeof layout === 'string' ? this.getLayout(entity, layout) : layout;
        }

        return this.getFieldsInternal(schema, entity, layout, undefined, callback);
    }

    /**
     * Creates a clone of the given layout-group that maintains only the hierarchy and the 'name' property
     * for fields
     * @param layoutGroup
     * @returns {Object}
     */
    static processLayoutGroup(layoutGroup) {
        if (!layoutGroup) throw Error('\'layoutGroup\' should be truthy');

        let layoutGroupClone = {};
        if (layoutGroup.fields) {
            layoutGroupClone.fields = [];

            for (let i = 0; i < layoutGroup.fields.length; i++) {
                layoutGroupClone.fields.push({name: layoutGroup.fields[i].name});
            }

        } else if (layoutGroup.groups) {
            layoutGroupClone.groups = [];

            for (let i = 0; i < layoutGroup.groups.length; i++) {
                layoutGroupClone.groups.push(this.processLayoutGroup(layoutGroup.groups[i]));
            }
        }

        // copying useful properties
        layoutGroupClone.orientation = layoutGroup.orientation;
        layoutGroupClone.component = layoutGroup.component;
        layoutGroupClone.title = layoutGroup.title;

        return layoutGroupClone;
    }

    static processLayout(schema, entity, layout) {
        entity = typeof entity === 'string' ? this.getEntity(schema, entity) : entity;
        layout = typeof layout === 'string' ? this.getLayout(entity, layout) : layout;

        return this.processLayoutGroup(layout);
    }

    /**
     * Generates a default layout for the given entity. Useful so it's not obligatory to implement layouts.
     * @param schema application schema
     * @param entity
     */
    static generateDefaultLayout(schema, entity) {
        entity = typeof entity === 'string' ? this.getEntity(schema, entity) : entity;
        return {
            name: `${entity.name}-default`,
            fields: entity.fields.map(f => {
                return {
                    name: f.name
                };
            })
        };
    }

    /**
     * Gets the field-list for Redux-Form
     * @param fieldMetadata
     * @param prefix
     */
    static getReduxFormFields(fieldMetadata, prefix) {
        if (!fieldMetadata) throw Error('fieldMetadata should be truthy');
        let result = [];

        fieldMetadata.forEach(f => {
            if (f.fields) {
                // if a field has fields, it's either an array or a complex object
                let fieldPrefix = f.type == 'array' ? `${f.name}[]` : f.name;
                let totalPrefix = prefix ? `${prefix}.${fieldPrefix}` : fieldPrefix;
                this.getReduxFormFields(f.fields, totalPrefix).map(f2 => result.push(f2));
            } else {
                result.push(prefix ? `${prefix}.${f.name}` : f.name)
            }
        });

        return result;
    }
};<commit_msg>Add semicolon<commit_after>export default class MetadataProvider {

    /**
     * Returns a cloned schema which is canonical, meaning that every 'entities', 'fields', 'layouts' and 'groups' are
     * arrays and not objects.
     * @param schema
     * @returns {*}
     */
    static canonizeSchema(schema) {
        if (!schema) throw Error('\'schema\' should be truthy');

        if(!schema.entities) {
            // when no entities are specified in the schema, the schema is considered to be in the SIMPLEST form, example:
            // {
            //     name: {
            //         type: 'string'
            //     },
            //     dateOfBirth: {
            //         type: 'string'
            //     }
            // }
            // OR...
            // [
            //     { name: 'name', type: 'string' },
            //     { name: 'dateOfBirth', type: 'date' },
            // ]
            // In this case, I'm just creating a 'default' entity
            return MetadataProvider.canonizeSchema({
                entities: [
                    {
                        name: 'default',
                        fields: schema
                    }
                ]
            });
        }

        schema = {...schema};
        this._canonizeArrays(schema, ["entities", "layouts", "groups"]);

        //schema.entities = MetadataProvider.canonizeArray(schema.entities);
        //_.each(schema.entities, entity => {
        //    entity.fields = MetadataProvider.canonizeArray(entity.fields);
        //    entity.layouts = MetadataProvider.canonizeArray(entity.layouts);
        //    _.each(entity.layouts, layout => {
        //        layout.fields = MetadataProvider.canonizeArray(layout.fields);
        //        layout.groups = MetadataProvider.canonizeArray(layout.groups);
        //        _.each(layout.groups, group => {
        //            group.fields = MetadataProvider.canonizeArray(group.fields);
        //        })
        //    });
        //});

        return schema;
    }

    static _canonizeArrays(dataArray, keys, id = 0) {
        dataArray[keys[id]] = MetadataProvider.canonizeArray(dataArray[keys[id]]);
        if(dataArray[keys[id]]) {
            dataArray[keys[id]].forEach(elem => {
                elem.fields = MetadataProvider.canonizeArray(elem.fields);
                if(id + 1 < keys.length)
                    this._canonizeArrays(elem, keys, id + 1);
            })
        }

    }
    /**
     * Ensures the object passed in is an array. If it is, it is returned, otherwise, this function
     * converts the target object into an array.
     * This is important to convert this:
     *     { fields: { dateOfBirth: { type: 'string' } } // this should be acceptable as a fields definition
     * into this:
     *     { fields: [{ name: 'dateOfBirth', type: 'string' }] } // this is the canonical field definition
     * @param obj
     */
    static canonizeArray(obj) {
        if (!obj) return obj; // this is so the canonizeSchema method doesn't have to check every property for undefined

        if (Array.isArray(obj))
            return obj;

        // let's create an array
        return Object.keys(obj).map((property) => {
            let isObject = obj[property] && typeof obj[property] === "object";
            if (!isObject)
                throw Error('cannot generate canonical array. Every field should be an object');

            return {name: property, ...obj[property]};
        });
    }

    /**
     * Validates a field metadata
     * @param metadata
     * @private
     */
    static validateFieldMetadata(metadata) {
        if (!metadata) throw Error('metadata should not be null or undefined');
        if (!metadata.name) throw Error('metadata\'s "name" property is required');
        if (!metadata.type) throw Error('metadata\'s "type" property is required');
    }

    /**
     * Gets a raw entity from the given schema. No processing.
     * @param schema
     * @param entityName
     * @returns {*}
     */
    static getEntity(schema, entityName) {
        if (!schema) throw Error('\'schema\' should be truthy');
        if (schema.entities === undefined || schema.entities === null || !schema.entities.length)
            throw Error('schema should have entities');

        let entity;

        if (entityName) {
            entity = schema.entities.find(e => e.name === entityName);
        } else {
            if(schema.entities.length != 1)
                throw Error('When an entityName is not specified, there must be one and only one entity');
            entity = schema.entities[0];
        }

        if (!entity) throw Error(`Could not find entity. Entity name: ${entityName}`);

        return entity;
    }

    /**
     * Gets a raw layout from the given entity. No processing.
     * @param entity
     * @param layoutName
     * @returns {*}
     */
    static getLayout(entity, layoutName) {
        if (!entity) throw Error('\'entity\' should be truthy');
        let layout;

        if (layoutName) {
            layout = entity.layouts ? entity.layouts.find(l => l.name === layoutName) : layout;
        } else {
            if(entity.layouts.length != 1)
                throw Error('When the layoutName is not specified, there must be one and only one layout');
            layout = entity.layouts[0];
        }

        if (!layout) throw Error(`Could not find layout. Layout name: ${layoutName}`);

        return layout;
    }

    /**
     * Gets a raw entity and a raw layout from the given schema.
     * @param schema
     * @param entityName
     * @param layoutName
     * @returns {{entity: *, layout: *}}
     */
    static getEntityAndLayout(schema, entityName, layoutName) {
        let entity = this.getEntity(schema, entityName);
        let layout = layoutName ? this.getLayout(entity, layoutName) : this.generateDefaultLayout(schema, entity);

        return {
            entity: entity,
            layout: layout
        }
    }

    /**
     * Internal method for merging entity and layout fields
     * @param schema
     * @param entity
     * @param layout
     * @param partialResult
     * @param callback
     * @return {*[]}
     */
    static getFieldsInternal(schema, entity, layout, partialResult, callback) {

        if (!schema) throw Error('\'schema\' should be truthy');
        if (!entity) throw Error('\'entity\' should be truthy');
        if (!layout.fields && !layout.groups) throw Error('A layout should have fields or groups');

        partialResult = partialResult || [];
        let thisGroupFields = [];

        if (layout.groups) {
            layout.groups.forEach(g => {
                thisGroupFields = [... new Set(thisGroupFields.concat(this.getFieldsInternal(schema, entity, g, partialResult, callback)))];
            });
        }

        if (layout.fields) {

            for (let i = 0; i < layout.fields.length; i++) {

                let groupField = layout.fields[i];
                let existingEntityProperty = entity.fields.find(field => field.name == groupField.name);

                let field = {...existingEntityProperty || {}, ...groupField};
                this.validateFieldMetadata(field);

                thisGroupFields.push(field);

                if (field.type == 'entity') {

                    if (!field.entityName) {
                        throw Error('when a field is of type \'entity\', it needs to specify an \'entityName\'')
                    }

                    let entityAndLayout = this.getEntityAndLayout(schema, field.entityName, field.layoutName);
                    field.layout = this.processLayout(schema, entityAndLayout.entity, entityAndLayout.layout);
                    field.fields = this.getFieldsInternal(schema, entityAndLayout.entity, entityAndLayout.layout, partialResult, callback);
                }

                if (field.type == 'array') {
                    if (!field.arrayType) {
                        throw Error('when a field is of type \'array\', it needs to specify an \'arrayType\'');
                    }

                    if (field.arrayType != 'entity') {
                        throw Error('only entity arrays are currently supported');
                    }

                    if (!field.entityType) {
                        throw Error('when a field is of type \'array\' and arrayType is \'entity\', it needs to specify an \'entityType\'');
                    }

                    let entityAndLayout = this.getEntityAndLayout(schema, field.entityType, field.layoutName);
                    field.layout = this.processLayout(schema, entityAndLayout.entity, entityAndLayout.layout);
                    field.fields = this.getFieldsInternal(schema, entityAndLayout.entity, entityAndLayout.layout, partialResult, callback);
                }

                if (callback) {
                    callback(field);
                }
            }
        }

        return [...new Set(partialResult.concat(thisGroupFields))];
    }

    /**
     * Merges the given field collection
     * @param schema
     * @param entity
     * @param layout
     * @param callback
     * @external https://github.com/gearz-lab/react-metaform/blob/master/docs-md/MetadataProvider.md
     */
    static getFields(schema, entity, layout, callback) {
        entity = typeof entity === 'string' ? this.getEntity(schema, entity) : entity;

        if (!layout) {
            layout = this.generateDefaultLayout(schema, entity);
        } else {
            layout = typeof layout === 'string' ? this.getLayout(entity, layout) : layout;
        }

        return this.getFieldsInternal(schema, entity, layout, undefined, callback);
    }

    /**
     * Creates a clone of the given layout-group that maintains only the hierarchy and the 'name' property
     * for fields
     * @param layoutGroup
     * @returns {Object}
     */
    static processLayoutGroup(layoutGroup) {
        if (!layoutGroup) throw Error('\'layoutGroup\' should be truthy');

        let layoutGroupClone = {};
        if (layoutGroup.fields) {
            layoutGroupClone.fields = [];

            for (let i = 0; i < layoutGroup.fields.length; i++) {
                layoutGroupClone.fields.push({name: layoutGroup.fields[i].name});
            }

        } else if (layoutGroup.groups) {
            layoutGroupClone.groups = [];

            for (let i = 0; i < layoutGroup.groups.length; i++) {
                layoutGroupClone.groups.push(this.processLayoutGroup(layoutGroup.groups[i]));
            }
        }

        // copying useful properties
        layoutGroupClone.orientation = layoutGroup.orientation;
        layoutGroupClone.component = layoutGroup.component;
        layoutGroupClone.title = layoutGroup.title;

        return layoutGroupClone;
    }

    static processLayout(schema, entity, layout) {
        entity = typeof entity === 'string' ? this.getEntity(schema, entity) : entity;
        layout = typeof layout === 'string' ? this.getLayout(entity, layout) : layout;

        return this.processLayoutGroup(layout);
    }

    /**
     * Generates a default layout for the given entity. Useful so it's not obligatory to implement layouts.
     * @param schema application schema
     * @param entity
     */
    static generateDefaultLayout(schema, entity) {
        entity = typeof entity === 'string' ? this.getEntity(schema, entity) : entity;
        return {
            name: `${entity.name}-default`,
            fields: entity.fields.map(f => {
                return {
                    name: f.name
                };
            })
        };
    }

    /**
     * Gets the field-list for Redux-Form
     * @param fieldMetadata
     * @param prefix
     */
    static getReduxFormFields(fieldMetadata, prefix) {
        if (!fieldMetadata) throw Error('fieldMetadata should be truthy');
        let result = [];

        fieldMetadata.forEach(f => {
            if (f.fields) {
                // if a field has fields, it's either an array or a complex object
                let fieldPrefix = f.type == 'array' ? `${f.name}[]` : f.name;
                let totalPrefix = prefix ? `${prefix}.${fieldPrefix}` : fieldPrefix;
                this.getReduxFormFields(f.fields, totalPrefix).map(f2 => result.push(f2));
            } else {
                result.push(prefix ? `${prefix}.${f.name}` : f.name)
            }
        });

        return result;
    }
};<|endoftext|>


<commit_before>var url = require('url'),
	util = require('util'),
	amqp = require('amqp'),
	redis = require('redis'),
	assert = require('assert'),
	events = require('events')
	uuid = require('node-uuid');

var createMessage = require('./protocol').createMessage;

debug = process.env.NODE_CELERY_DEBUG === '1' ? util.debug : function() {};

function Configuration(options) {
	var self = this;

	for (var o in options) {
		if (options.hasOwnProperty(o)) {
			self[o.replace(/^CELERY_/, '')] = options[o];
		}
	}

	self.BROKER_URL = self.BROKER_URL || 'amqp://';
	self.DEFAULT_QUEUE = self.DEFAULT_QUEUE || 'celery';
	self.DEFAULT_EXCHANGE = self.DEFAULT_EXCHANGE || '';
	self.DEFAULT_EXCHANGE_TYPE = self.DEFAULT_EXCHANGE_TYPE || 'direct';
	self.DEFAULT_ROUTING_KEY = self.DEFAULT_ROUTING_KEY || 'celery';
	self.RESULT_EXCHANGE = self.RESULT_EXCHANGE || 'celeryresults';
	self.TASK_RESULT_EXPIRES = self.TASK_RESULT_EXPIRES || 86400000; // 1 day
	self.ROUTES = self.ROUTES || {};

	if (self.RESULT_BACKEND && self.RESULT_BACKEND.toLowerCase() === 'amqp') {
		self.backend_type = 'amqp';
	} else if (self.RESULT_BACKEND && url.parse(self.RESULT_BACKEND)
		.protocol === 'redis:') {
		self.backend_type = 'redis';
	}
}

function Client(conf, callback) {
	var self = this;

	self.conf = new Configuration(conf);

	self.ready = false;
	self.broker_connected = false;
	self.backend_connected = false;

	self.broker = amqp.createConnection({
		url: self.conf.BROKER_URL
	}, {
		defaultExchangeName: self.conf.DEFAULT_EXCHANGE
	}, callback);

	if (self.conf.backend_type === 'amqp') {
		self.backend = self.broker;
		self.backend_connected = true;
	} else if (self.conf.backend_type === 'redis') {
		var purl = url.parse(self.conf.RESULT_BACKEND);
		self.backend = redis.createClient(purl.port, purl.hostname);

		var on_ready = function() {
			self.backend_connected = true;
			if (self.broker_connected) {
				self.ready = true;
				debug('Emiting connect event...');
				self.emit('connect');
			}
		};

		self.backend.on('connect', function() {
			debug('Backend connected...');
			if (purl.auth) {
				self.backend.auth(purl.auth.split(':')[1], on_ready);
			} else {
				on_ready();
			}
		});

		self.backend.on('error', function(err) {
			self.emit('error', err);
		});
	} else {
		self.backend_connected = true;
	}

	self.broker.on('ready', function() {
		debug('Broker connected...');
		self.broker_connected = true;
		if (self.backend_connected) {
			self.ready = true;
			self.emit('connect');
		}
	});

	self.broker.on('error', function(err) {
		self.emit('error', err);
	});

	self.broker.on('end', function() {
		self.emit('end');
	});
}

util.inherits(Client, events.EventEmitter);

Client.prototype.createTask = function(name, options) {
	return new Task(this, name, options);
};

Client.prototype.end = function() {
	this.broker.end();
	if (this.broker !== this.backend) {
		this.backend.end();
	}
};

Client.prototype.call = function(name /*[args], [kwargs], [options], [callback]*/ ) {
	var args, kwargs, options, callback;
	for (var i = arguments.length - 1; i > 0; i--) {
		if (typeof arguments[i] === 'function') {
			callback = arguments[i];
		} else if (Object.prototype.toString.call(arguments[i]) === '[object Array]') {
			args = arguments[i];
		} else if (typeof arguments[i] === 'object') {
			if (kwargs) {
				options = arguments[i];
			} else {
				kwargs = arguments[i];
			}
		}
	}

	var task = this.createTask(name),
		result = task.call(args, kwargs, options);

	if (callback) {
		result.on('ready', callback);
	}
	return result;
};

function Task(client, name, options) {
	var self = this;

	self.client = client;
	self.name = name;
	self.options = options || {};

	var route = self.client.conf.ROUTES[name],
		queue = route && route.queue;

	self.publish = function(args, kwargs, options, callback) {
		var id = uuid.v4();
		self.client.broker.publish(
		self.options.queue || queue || self.client.conf.DEFAULT_QUEUE,
		createMessage(self.name, args, kwargs, options, id), {
			'contentType': 'application/json'
		},
		callback);
		return new Result(id, self.client);
	};
}

Task.prototype.call = function(args, kwargs, options, callback) {
	var self = this;

	args = args || [];
	kwargs = kwargs || {};
	options = options || self.options || {};

	assert(self.client.ready);
	return self.publish(args, kwargs, options, callback);
};

function Result(taskid, client) {
	var self = this;

	events.EventEmitter.call(self);
	self.taskid = taskid;
	self.client = client;
	self.result = null;

	if (self.client.conf.backend_type === 'amqp') {
		debug('Subscribing to result queue...');
		self.client.backend.queue(
		self.taskid.replace(/-/g, ''), {
			"arguments": {
				'x-expires': self.client.conf.TASK_RESULT_EXPIRES
			}
		},

		function(q) {
			q.bind(self.client.conf.RESULT_EXCHANGE, '#');
			q.subscribe(function(message) {
				self.result = message;
				//q.unbind('#');
				debug('Emiting ready event...');
				self.emit('ready', message);
				self.emit(message.status.toLowerCase(), message);
			});
		});
	}
}

util.inherits(Result, events.EventEmitter);

Result.prototype.get = function(callback) {
	var self = this;
	if (callback && self.result === null) {
		self.client.backend.get('celery-task-meta-' + self.taskid, function(err, reply) {
			self.result = JSON.parse(reply);
			callback(self.result);
		});
	} else {
		return self.result;
	}
};

exports.createClient = function(config, callback) {
	return new Client(config, callback);
};
<commit_msg>Fixes keyword argument handling<commit_after>var url = require('url'),
	util = require('util'),
	amqp = require('amqp'),
	redis = require('redis'),
	assert = require('assert'),
	events = require('events')
	uuid = require('node-uuid');

var createMessage = require('./protocol').createMessage;

debug = process.env.NODE_CELERY_DEBUG === '1' ? util.debug : function() {};

function Configuration(options) {
	var self = this;

	for (var o in options) {
		if (options.hasOwnProperty(o)) {
			self[o.replace(/^CELERY_/, '')] = options[o];
		}
	}

	self.BROKER_URL = self.BROKER_URL || 'amqp://';
	self.DEFAULT_QUEUE = self.DEFAULT_QUEUE || 'celery';
	self.DEFAULT_EXCHANGE = self.DEFAULT_EXCHANGE || '';
	self.DEFAULT_EXCHANGE_TYPE = self.DEFAULT_EXCHANGE_TYPE || 'direct';
	self.DEFAULT_ROUTING_KEY = self.DEFAULT_ROUTING_KEY || 'celery';
	self.RESULT_EXCHANGE = self.RESULT_EXCHANGE || 'celeryresults';
	self.TASK_RESULT_EXPIRES = self.TASK_RESULT_EXPIRES || 86400000; // 1 day
	self.ROUTES = self.ROUTES || {};

	if (self.RESULT_BACKEND && self.RESULT_BACKEND.toLowerCase() === 'amqp') {
		self.backend_type = 'amqp';
	} else if (self.RESULT_BACKEND && url.parse(self.RESULT_BACKEND)
		.protocol === 'redis:') {
		self.backend_type = 'redis';
	}
}

function Client(conf, callback) {
	var self = this;

	self.conf = new Configuration(conf);

	self.ready = false;
	self.broker_connected = false;
	self.backend_connected = false;

	self.broker = amqp.createConnection({
		url: self.conf.BROKER_URL
	}, {
		defaultExchangeName: self.conf.DEFAULT_EXCHANGE
	}, callback);

	if (self.conf.backend_type === 'amqp') {
		self.backend = self.broker;
		self.backend_connected = true;
	} else if (self.conf.backend_type === 'redis') {
		var purl = url.parse(self.conf.RESULT_BACKEND);
		self.backend = redis.createClient(purl.port, purl.hostname);

		var on_ready = function() {
			self.backend_connected = true;
			if (self.broker_connected) {
				self.ready = true;
				debug('Emiting connect event...');
				self.emit('connect');
			}
		};

		self.backend.on('connect', function() {
			debug('Backend connected...');
			if (purl.auth) {
				self.backend.auth(purl.auth.split(':')[1], on_ready);
			} else {
				on_ready();
			}
		});

		self.backend.on('error', function(err) {
			self.emit('error', err);
		});
	} else {
		self.backend_connected = true;
	}

	self.broker.on('ready', function() {
		debug('Broker connected...');
		self.broker_connected = true;
		if (self.backend_connected) {
			self.ready = true;
			self.emit('connect');
		}
	});

	self.broker.on('error', function(err) {
		self.emit('error', err);
	});

	self.broker.on('end', function() {
		self.emit('end');
	});
}

util.inherits(Client, events.EventEmitter);

Client.prototype.createTask = function(name, options) {
	return new Task(this, name, options);
};

Client.prototype.end = function() {
	this.broker.end();
	if (this.broker !== this.backend) {
		this.backend.end();
	}
};

Client.prototype.call = function(name /*[args], [kwargs], [options], [callback]*/ ) {
	var args, kwargs, options, callback;
	for (var i = arguments.length - 1; i > 0; i--) {
		if (typeof arguments[i] === 'function') {
			callback = arguments[i];
		} else if (Object.prototype.toString.call(arguments[i]) === '[object Array]') {
			args = arguments[i];
		} else if (typeof arguments[i] === 'object') {
			if (options) {
				kwargs = arguments[i];
			} else {
				options = arguments[i];
			}
		}
	}

	var task = this.createTask(name),
		result = task.call(args, kwargs, options);

	if (callback) {
		result.on('ready', callback);
	}
	return result;
};

function Task(client, name, options) {
	var self = this;

	self.client = client;
	self.name = name;
	self.options = options || {};

	var route = self.client.conf.ROUTES[name],
		queue = route && route.queue;

	self.publish = function(args, kwargs, options, callback) {
		var id = uuid.v4();
		self.client.broker.publish(
		self.options.queue || queue || self.client.conf.DEFAULT_QUEUE,
		createMessage(self.name, args, kwargs, options, id), {
			'contentType': 'application/json'
		},
		callback);
		return new Result(id, self.client);
	};
}

Task.prototype.call = function(args, kwargs, options, callback) {
	var self = this;

	args = args || [];
	kwargs = kwargs || {};
	options = options || self.options || {};

	assert(self.client.ready);
	return self.publish(args, kwargs, options, callback);
};

function Result(taskid, client) {
	var self = this;

	events.EventEmitter.call(self);
	self.taskid = taskid;
	self.client = client;
	self.result = null;

	if (self.client.conf.backend_type === 'amqp') {
		debug('Subscribing to result queue...');
		self.client.backend.queue(
		self.taskid.replace(/-/g, ''), {
			"arguments": {
				'x-expires': self.client.conf.TASK_RESULT_EXPIRES
			}
		},

		function(q) {
			q.bind(self.client.conf.RESULT_EXCHANGE, '#');
			q.subscribe(function(message) {
				self.result = message;
				//q.unbind('#');
				debug('Emiting ready event...');
				self.emit('ready', message);
				self.emit(message.status.toLowerCase(), message);
			});
		});
	}
}

util.inherits(Result, events.EventEmitter);

Result.prototype.get = function(callback) {
	var self = this;
	if (callback && self.result === null) {
		self.client.backend.get('celery-task-meta-' + self.taskid, function(err, reply) {
			self.result = JSON.parse(reply);
			callback(self.result);
		});
	} else {
		return self.result;
	}
};

exports.createClient = function(config, callback) {
	return new Client(config, callback);
};
<|endoftext|>


<commit_before>app.config(function ($routeProvider) {

    /** This checks if the user is authenticated for a route and what the isRequired is set to.
        Depending on whether isRequired = true, it first check if the user is authenticated and will resolve successfully
        otherwise the route will fail and the $routeChangeError event will execute, in that handler we will redirect to the rejected
        path that is resolved from this method and prevent default (prevent the route from executing) */
    var canRoute = function(isRequired) {

        return {
            /** Checks that the user is authenticated, then ensures that are requires assets are loaded */
            isAuthenticatedAndReady: function ($q, userService, $route, assetsService, appState) {
                var deferred = $q.defer();

                //don't need to check if we've redirected to login and we've already checked auth
                if (!$route.current.params.section
                    && ($route.current.params.check === false || $route.current.params.check === "false")) {
                    deferred.resolve(true);
                    return deferred.promise;
                }

                userService.isAuthenticated()
                    .then(function () {

                        assetsService._loadInitAssets().then(function() {

                            //This could be the first time has loaded after the user has logged in, in this case
                            // we need to broadcast the authenticated event - this will be handled by the startup (init)
                            // handler to set/broadcast the ready state
                            var broadcast = appState.getGlobalState("isReady") !== true;

                            userService.getCurrentUser({ broadcastEvent: broadcast }).then(function (user) {
                                //is auth, check if we allow or reject
                                if (isRequired) {
                                    // U4-5430, Benjamin Howarth
                                    // We need to change the current route params if the user only has access to a single section
                                    // To do this we need to grab the current user's allowed sections, then reject the promise with the correct path.
                                    if (user.allowedSections.indexOf($route.current.params.section) > -1) {
                                        //this will resolve successfully so the route will continue
                                        deferred.resolve(true);
                                    } else {
                                        deferred.reject({ path: "/" + user.allowedSections[0] });
                                    }
                                }
                                else {
                                    deferred.reject({ path: "/" });
                                }
                            });

                        });

                    }, function () {
                        //not auth, check if we allow or reject
                        if (isRequired) {
                            //the check=false is checked above so that we don't have to make another http call to check
                            //if they are logged in since we already know they are not.
                            deferred.reject({ path: "/login/false" });
                        }
                        else {
                            //this will resolve successfully so the route will continue
                            deferred.resolve(true);
                        }
                    });
                return deferred.promise;
            }
        };
    };

    /** When this is used to resolve it will attempt to log the current user out */
    var doLogout = function() {
        return {
            isLoggedOut: function ($q, userService) {
                var deferred = $q.defer();
                userService.logout().then(function () {
                    //success so continue
                    deferred.resolve(true);
                }, function() {
                    //logout failed somehow ? we'll reject with the login page i suppose
                    deferred.reject({ path: "/login/false" });
                });
                return deferred.promise;
            }
        }
    }

    $routeProvider
        .when('/login', {
            templateUrl: 'views/common/login.html',
            //ensure auth is *not* required so it will redirect to /
            resolve: canRoute(false)
        })
        .when('/login/:check', {
            templateUrl: 'views/common/login.html',
            //ensure auth is *not* required so it will redirect to /
            resolve: canRoute(false)
        })
         .when('/logout', {
             redirectTo: '/login/false',
             resolve: doLogout()
         })
        .when('/:section', {
            templateUrl: function (rp) {
                if (rp.section.toLowerCase() === "default" || rp.section.toLowerCase() === "umbraco" || rp.section === "")
                {
                    rp.section = "content";
                }

                rp.url = "dashboard.aspx?app=" + rp.section;
                return 'views/common/dashboard.html';
            },
            resolve: canRoute(true)
        })
        .when('/:section/framed/:url', {
            //This occurs when we need to launch some content in an iframe
            templateUrl: function (rp) {
                if (!rp.url)
                    throw "A framed resource must have a url route parameter";

                return 'views/common/legacy.html';
            },
            resolve: canRoute(true)
        })
        .when('/:section/:tree/:method', {
            templateUrl: function (rp) {
                if (!rp.method)
                    return "views/common/dashboard.html";

                //NOTE: This current isn't utilized by anything but does open up some cool opportunities for
                // us since we'll be able to have specialized views for individual sections which is something
                // we've never had before. So could utilize this for a new dashboard model when we get native
                // angular dashboards working. Perhaps a normal section dashboard would list out the registered
                // dashboards (as tabs if we wanted) and each tab could actually be a route link to one of these views?

                return 'views/' + rp.tree + '/' + rp.method + '.html';
            },
            resolve: canRoute(true)
        })
        .when('/:section/:tree/:method/:id', {
            //This allows us to dynamically change the template for this route since you cannot inject services into the templateUrl method.
            template: "

",
            //This controller will execute for this route, then we replace the template dynamnically based on the current tree.
            controller: function ($scope, $route, $routeParams, treeService) {

                if (!$routeParams.tree || !$routeParams.method) {
                    $scope.templateUrl = "views/common/dashboard.html";
                }

                // Here we need to figure out if this route is for a package tree and if so then we need
                // to change it's convention view path to:
                // /App_Plugins/{mypackage}/backoffice/{treetype}/{method}.html

                // otherwise if it is a core tree we use the core paths:
                // views/{treetype}/{method}.html

                var packageTreeFolder = treeService.getTreePackageFolder($routeParams.tree);

                if (packageTreeFolder) {
                    $scope.templateUrl = Umbraco.Sys.ServerVariables.umbracoSettings.appPluginsPath +
                        "/" + packageTreeFolder +
                        "/backoffice/" + $routeParams.tree + "/" + $routeParams.method + ".html";
                }
                else {
                    $scope.templateUrl = 'views/' + $routeParams.tree + '/' + $routeParams.method + '.html';
                }

            },
            resolve: canRoute(true)
        })
        .otherwise({ redirectTo: '/login' });
    }).config(function ($locationProvider) {

        //$locationProvider.html5Mode(false).hashPrefix('!'); //turn html5 mode off
        // $locationProvider.html5Mode(true);         //turn html5 mode on
});<commit_msg>Ensures routing only loads from all-lower case templates<commit_after>app.config(function ($routeProvider) {

    /** This checks if the user is authenticated for a route and what the isRequired is set to.
        Depending on whether isRequired = true, it first check if the user is authenticated and will resolve successfully
        otherwise the route will fail and the $routeChangeError event will execute, in that handler we will redirect to the rejected
        path that is resolved from this method and prevent default (prevent the route from executing) */
    var canRoute = function(isRequired) {

        return {
            /** Checks that the user is authenticated, then ensures that are requires assets are loaded */
            isAuthenticatedAndReady: function ($q, userService, $route, assetsService, appState) {
                var deferred = $q.defer();

                //don't need to check if we've redirected to login and we've already checked auth
                if (!$route.current.params.section
                    && ($route.current.params.check === false || $route.current.params.check === "false")) {
                    deferred.resolve(true);
                    return deferred.promise;
                }

                userService.isAuthenticated()
                    .then(function () {

                        assetsService._loadInitAssets().then(function() {

                            //This could be the first time has loaded after the user has logged in, in this case
                            // we need to broadcast the authenticated event - this will be handled by the startup (init)
                            // handler to set/broadcast the ready state
                            var broadcast = appState.getGlobalState("isReady") !== true;

                            userService.getCurrentUser({ broadcastEvent: broadcast }).then(function (user) {
                                //is auth, check if we allow or reject
                                if (isRequired) {
                                    // U4-5430, Benjamin Howarth
                                    // We need to change the current route params if the user only has access to a single section
                                    // To do this we need to grab the current user's allowed sections, then reject the promise with the correct path.
                                    if (user.allowedSections.indexOf($route.current.params.section) > -1) {
                                        //this will resolve successfully so the route will continue
                                        deferred.resolve(true);
                                    } else {
                                        deferred.reject({ path: "/" + user.allowedSections[0] });
                                    }
                                }
                                else {
                                    deferred.reject({ path: "/" });
                                }
                            });

                        });

                    }, function () {
                        //not auth, check if we allow or reject
                        if (isRequired) {
                            //the check=false is checked above so that we don't have to make another http call to check
                            //if they are logged in since we already know they are not.
                            deferred.reject({ path: "/login/false" });
                        }
                        else {
                            //this will resolve successfully so the route will continue
                            deferred.resolve(true);
                        }
                    });
                return deferred.promise;
            }
        };
    };

    /** When this is used to resolve it will attempt to log the current user out */
    var doLogout = function() {
        return {
            isLoggedOut: function ($q, userService) {
                var deferred = $q.defer();
                userService.logout().then(function () {
                    //success so continue
                    deferred.resolve(true);
                }, function() {
                    //logout failed somehow ? we'll reject with the login page i suppose
                    deferred.reject({ path: "/login/false" });
                });
                return deferred.promise;
            }
        }
    }

    $routeProvider
        .when('/login', {
            templateUrl: 'views/common/login.html',
            //ensure auth is *not* required so it will redirect to /
            resolve: canRoute(false)
        })
        .when('/login/:check', {
            templateUrl: 'views/common/login.html',
            //ensure auth is *not* required so it will redirect to /
            resolve: canRoute(false)
        })
         .when('/logout', {
             redirectTo: '/login/false',
             resolve: doLogout()
         })
        .when('/:section', {
            templateUrl: function (rp) {
                if (rp.section.toLowerCase() === "default" || rp.section.toLowerCase() === "umbraco" || rp.section === "")
                {
                    rp.section = "content";
                }

                rp.url = "dashboard.aspx?app=" + rp.section;
                return 'views/common/dashboard.html';
            },
            resolve: canRoute(true)
        })
        .when('/:section/framed/:url', {
            //This occurs when we need to launch some content in an iframe
            templateUrl: function (rp) {
                if (!rp.url)
                    throw "A framed resource must have a url route parameter";

                return 'views/common/legacy.html';
            },
            resolve: canRoute(true)
        })
        .when('/:section/:tree/:method', {
            templateUrl: function (rp) {
                if (!rp.method)
                    return "views/common/dashboard.html";

                //NOTE: This current isn't utilized by anything but does open up some cool opportunities for
                // us since we'll be able to have specialized views for individual sections which is something
                // we've never had before. So could utilize this for a new dashboard model when we get native
                // angular dashboards working. Perhaps a normal section dashboard would list out the registered
                // dashboards (as tabs if we wanted) and each tab could actually be a route link to one of these views?

                return ('views/' + rp.tree + '/' + rp.method + '.html').toLowerCase();
            },
            resolve: canRoute(true)
        })
        .when('/:section/:tree/:method/:id', {
            //This allows us to dynamically change the template for this route since you cannot inject services into the templateUrl method.
            template: "

",
            //This controller will execute for this route, then we replace the template dynamnically based on the current tree.
            controller: function ($scope, $route, $routeParams, treeService) {

                if (!$routeParams.tree || !$routeParams.method) {
                    $scope.templateUrl = "views/common/dashboard.html";
                }

                // Here we need to figure out if this route is for a package tree and if so then we need
                // to change it's convention view path to:
                // /App_Plugins/{mypackage}/backoffice/{treetype}/{method}.html

                // otherwise if it is a core tree we use the core paths:
                // views/{treetype}/{method}.html

                var packageTreeFolder = treeService.getTreePackageFolder($routeParams.tree);

                if (packageTreeFolder) {
                    $scope.templateUrl = (Umbraco.Sys.ServerVariables.umbracoSettings.appPluginsPath +
                        "/" + packageTreeFolder +
                        "/backoffice/" + $routeParams.tree + "/" + $routeParams.method + ".html").toLowerCase();
                }
                else {
                    $scope.templateUrl = ('views/' + $routeParams.tree + '/' + $routeParams.method + '.html').toLowerCase();
                }

            },
            resolve: canRoute(true)
        })
        .otherwise({ redirectTo: '/login' });
    }).config(function ($locationProvider) {

        //$locationProvider.html5Mode(false).hashPrefix('!'); //turn html5 mode off
        // $locationProvider.html5Mode(true);         //turn html5 mode on
    });
<|endoftext|>


<commit_before>/**
 * @file ScrollableTable component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component, createRef} from 'react';
import PropTypes from 'prop-types';

class ScrollableTable extends Component {

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.wrapper = createRef();

        this.state = {
            wrapperEl: null
        };

    }

    getStyle = () => {

        const {style, scroll, overflowHidden, horizontalOverflowScroll} = this.props;

        if (overflowHidden) {
            return style;
        }

        return {
            ...style,
            overflowX: horizontalOverflowScroll ? 'scroll' : scroll.width ? 'auto' : null,
            overflowY: scroll.height || scroll.maxHeight ? 'scroll' : null
        };

    };

    getChildren = () => {

        const {children} = this.props,
            {wrapperEl} = this.state;

        return (typeof children === 'function' ? children(wrapperEl) : children) || null;

    };

    componentDidMount() {
        this.setState({
            wrapperEl: this.wrapper && this.wrapper.current
        });
    }

    render() {

        const {

                style, scroll,

                // not passing down these props
                children: c, overflowHidden, horizontalOverflowScroll,

                ...restProps

            } = this.props,
            children = this.getChildren();

        return scroll ?
            <div {...restProps}
                 ref={this.wrapper}
                 style={this.getStyle()}>
                {children}
            </div>
            :
            children;

    }
}

ScrollableTable.propTypes = {

    children: PropTypes.any,

    style: PropTypes.object,

    /**
     * scroll
     */
    scroll: PropTypes.shape({
        width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
        height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
        maxHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
    }),

    overflowHidden: PropTypes.bool,
    horizontalOverflowScroll: PropTypes.bool

};

ScrollableTable.defaultProps = {
    overflowHidden: false,
    horizontalOverflowScroll: false
};

export default ScrollableTable;
<commit_msg>update ScrollableTable<commit_after>/**
 * @file ScrollableTable component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component, createRef} from 'react';
import PropTypes from 'prop-types';

// Statics
import TableFragment from '../_statics/TableFragment';

// Vendors
import Util from '../_vendors/Util';

class ScrollableTable extends Component {

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.wrapper = createRef();

        this.state = {
            wrapperEl: null
        };

    }

    getStyle = () => {

        const {
                style, scroll, fragment, overflowHidden, horizontalOverflowScroll,
                useDynamicRender, tableHeight
            } = this.props,
            result = {
                ...style
            };

        if ((!fragment || fragment === TableFragment.BODY) && useDynamicRender) {
            result.height = tableHeight;
        }

        if (overflowHidden) {
            return result;
        }

        return {
            ...result,
            overflowX: horizontalOverflowScroll ? 'scroll' : scroll.width ? 'auto' : null,
            overflowY: scroll.height || scroll.maxHeight ? 'scroll' : null
        };

    };

    getChildren = () => {

        const {children} = this.props,
            {wrapperEl} = this.state;

        return (typeof children === 'function' ? children(wrapperEl) : children) || null;

    };

    componentDidMount() {
        this.setState({
            wrapperEl: this.wrapper && this.wrapper.current
        });
    }

    render() {

        const {

                style, scroll,

                // not passing down these props
                children: c, overflowHidden, horizontalOverflowScroll,
                useDynamicRender, tableHeight, rowHeight, scrollBuffer,

                ...restProps

            } = this.props,
            children = this.getChildren();

        return scroll ?
            <div {...restProps}
                 ref={this.wrapper}
                 style={this.getStyle()}>
                {children}
            </div>
            :
            children;

    }
}

ScrollableTable.propTypes = {

    children: PropTypes.any,

    style: PropTypes.object,

    /**
     * scroll
     */
    scroll: PropTypes.shape({
        width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
        height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
        maxHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
    }),

    fragment: PropTypes.oneOf(Util.enumerateValue(TableFragment)),
    overflowHidden: PropTypes.bool,
    horizontalOverflowScroll: PropTypes.bool,

    /**
     * Dynamic Render
     */
    useDynamicRender: PropTypes.bool,
    tableHeight: PropTypes.number,
    rowHeight: PropTypes.number,
    scrollBuffer: PropTypes.number

};

ScrollableTable.defaultProps = {

    overflowHidden: false,
    horizontalOverflowScroll: false,

    /**
     * Dynamic Render
     */
    useDynamicRender: false,
    tableHeight: 200,
    rowHeight: 40,
    scrollBuffer: 6

};

export default ScrollableTable;
<|endoftext|>


<commit_before>'use strict';

const { throwError } = require('../../error');
const { uniq, includes } = require('../../utilities');

const { getModel } = require('./get_model');
const { mergeActions } = require('./merge');

// Parse `args.cascade` into a set of delete nested `actions`
const parseCascade = function ({
  actions,
  top,
  top: { args: { cascade } },
  schema: { shortcuts: { modelsMap } },
}) {
  if (cascade === undefined) { return { actions }; }

  const newActions = getCascadeActions({ cascade, top, modelsMap });

  const actionsA = mergeActions({ actions, newActions });

  return { actions: actionsA };
};

const getCascadeActions = function ({ cascade, top, modelsMap }) {
  const actions = cascade.split(',');
  const actionsA = uniq(actions);
  const actionsB = actionsA.map(attrName => attrName.split('.'));
  const actionsC = actionsB.map((attrName, index, attrs) =>
    normalizeCascade({ attrName, attrs, top, modelsMap }));
  return actionsC;
};

// From `attr.child_attr` to:
//   commandPath: ['commandName', 'attr', 'child_attr']
//   modelName
//   args: {}
const normalizeCascade = function ({ attrName, attrs, top, modelsMap }) {
  validateMiddleAction({ attrName, attrs });

  const commandPath = [...top.commandPath, ...attrName];
  const model = getModel({ modelsMap, top, commandPath });

  validateCascade({ model, commandPath });

  const { modelName } = model;
  return { commandPath, modelName, args: {}, isWrite: true };
};

// Cannot specify `args.cascade` `parent.child` but not `parent`.
// Otherwise, this would require create an intermediate `find` action.
const validateMiddleAction = function ({ attrName, attrs }) {
  if (attrName.length <= 1) { return; }

  const parentAttr = attrName.slice(0, -1);
  const hasParentAttr = includes(attrs, parentAttr);
  if (hasParentAttr) { return; }

  const message = `In 'cascade' argument, must not specify '${attrName.join('.')}' unless '${parentAttr.join('.')}' is also specified`;
  throwError(message, { reason: 'INPUT_VALIDATION' });
};

const validateCascade = function ({ model, commandPath }) {
  if (model !== undefined) { return; }

  const attrName = commandPath.slice(1).join('.');
  const message = attrName === ''
    ? '\'cascade\' argument cannot contain empty attributes'
    : `In 'cascade' argument, attribute '${attrName}' is unknown`;
  throwError(message, { reason: 'INPUT_VALIDATION' });
};

module.exports = {
  parseCascade,
};
<commit_msg>Refactor args.cascade parsing<commit_after>'use strict';

const { throwError } = require('../../error');
const { uniq, includes } = require('../../utilities');

const { getModel } = require('./get_model');
const { mergeActions } = require('./merge');

// Parse `args.cascade` into a set of delete nested `actions`
const parseCascade = function ({
  actions,
  top,
  top: { args: { cascade } },
  schema: { shortcuts: { modelsMap } },
}) {
  if (cascade === undefined) { return; }

  const newActions = getCascadeActions({ cascade, top, modelsMap });

  const actionsA = mergeActions({ actions, newActions });

  return { actions: actionsA };
};

const getCascadeActions = function ({ cascade, top, modelsMap }) {
  const attrs = cascade.split(',');
  const attrsA = uniq(attrs);
  const attrsB = attrsA.map(attrName => attrName.split('.'));
  const actions = attrsB.map(attrName =>
    getCascadeAction({ attrName, attrs: attrsB, top, modelsMap }));
  return actions;
};

// From `attr.child_attr` to:
//   commandPath: ['commandName', 'attr', 'child_attr']
//   modelName
//   args: {}
const getCascadeAction = function ({ attrName, attrs, top, modelsMap }) {
  validateMiddleAction({ attrName, attrs });

  const commandPath = [...top.commandPath, ...attrName];
  const model = getModel({ modelsMap, top, commandPath });

  validateCascade({ model, commandPath });

  const { modelName } = model;
  return { commandPath, modelName, args: {}, isWrite: true };
};

// Cannot specify `args.cascade` `parent.child` but not `parent`.
// Otherwise, this would require create an intermediate `find` action.
const validateMiddleAction = function ({ attrName, attrs }) {
  // Not for top-level attributes
  if (attrName.length <= 1) { return; }

  const parentAttr = attrName.slice(0, -1);
  const hasParentAttr = includes(attrs, parentAttr);
  if (hasParentAttr) { return; }

  const message = `In 'cascade' argument, must not specify '${attrName.join('.')}' unless '${parentAttr.join('.')}' is also specified`;
  throwError(message, { reason: 'INPUT_VALIDATION' });
};

const validateCascade = function ({ model, commandPath }) {
  if (model !== undefined) { return; }

  const attrName = commandPath.slice(1).join('.');
  const message = attrName === ''
    ? '\'cascade\' argument cannot contain empty attributes'
    : `In 'cascade' argument, attribute '${attrName}' is unknown`;
  throwError(message, { reason: 'INPUT_VALIDATION' });
};

module.exports = {
  parseCascade,
};
<|endoftext|>


<commit_before>import React, {PureComponent} from 'react'
import {Redirect} from 'react-router-dom'
import {fetchQuest, fetchSessions} from '../api/apiAdmin'
import {calculateTime} from '../utils/timeUtils'

export default class extends PureComponent {
    constructor(props) {
        super(props)
        this.state = {
            questName: '',
            questId: '',
            sessions: [],
            shouldRedirectBack: false
        }
    }

    componentDidMount() {
        fetchQuest(this.props.match.params.questId)
            .then(({name, id}) => this.setState({questName: name, questId: id}))

        fetchSessions(this.props.match.params.questId)
            .then(sessions => this.setState({sessions: sessions}))
    }

    goBack() {
        this.setState({shouldRedirectBack: true});
    }

    render() {
        const {shouldRedirectBack, questName, questId} = this.state;

        return (

                {shouldRedirectBack && <Redirect to='/admin'/>}

{questName}

Quest id: {questId}

                {this.state.sessions.map(session => {
                    const {name, wrongAnswers, hintRetrievals, createdDate, finishedDate, time} = session
                    const {hours, minutes, seconds} = calculateTime(time)
                    return

--------------------------------

Team name: {name}

Created: {createdDate}

Finished: {finishedDate || 'not finished yet'}

Wrong answers: {wrongAnswers}

Hint retrievals: {hintRetrievals}

                        {finishedDate && (
Total time: {`${hours}:${minutes}:${seconds}`}
)}

                })}
                < button className="admin-button" onClick={this.goBack.bind(this)}>{'<'}</button>

        )
    }
}<commit_msg>Admin: * handling cases when team name is missing * added question index to statistics<commit_after>import React, {PureComponent} from 'react'
import {Redirect} from 'react-router-dom'
import {fetchQuest, fetchSessions} from '../api/apiAdmin'
import {calculateTime} from '../utils/timeUtils'

export default class extends PureComponent {
    constructor(props) {
        super(props)
        this.state = {
            questName: '',
            questId: '',
            sessions: [],
            shouldRedirectBack: false
        }
    }

    componentDidMount() {
        fetchQuest(this.props.match.params.questId)
            .then(({name, id}) => this.setState({questName: name, questId: id}))

        fetchSessions(this.props.match.params.questId)
            .then(sessions => this.setState({sessions: sessions}))
    }

    goBack() {
        this.setState({shouldRedirectBack: true});
    }

    render() {
        const {shouldRedirectBack, questName, questId} = this.state;

        return (

                {shouldRedirectBack && <Redirect to='/admin'/>}

{questName}

Quest id: {questId}

                {this.state.sessions.map(session => {
                    const {name, wrongAnswers, hintRetrievals, createdDate, finishedDate, time, questionIndex} = session
                    const {hours, minutes, seconds} = calculateTime(time)
                    return

--------------------------------

Team name: {name ? {name} : unnamed}

Created: {createdDate}

Finished: {finishedDate ? {finishedDate} : not finished yet}

Steps completed: {questionIndex}

Wrong answers: {wrongAnswers}

Hint retrievals: {hintRetrievals}

                        {finishedDate && (
Total time: {`${hours}:${minutes}:${seconds}`}
)}

                })}
                < button className="admin-button" onClick={this.goBack.bind(this)}>{'<'}</button>

        )
    }
}<|endoftext|>