<commit_before>define(function(require, exports, module) {
    "use strict";

    var app = require("app");
    var AbstractModel = require("modules/model/AbstractModel");
    var Log = require("modules/util/Logger");

    /**
     * PCS ODataの操作を行うモデルの基底クラスを作成する。
     *
     * @class PCS ODataの操作を行うモデルの基底クラス
     * @exports AbstractODataModel
     * @constructor
     */
    var AbstractODataModel = AbstractModel.extend({
        /**
         * idとして使用するattribute中のフィールド
         * @memberOf AbstractODataModel#
         */
        idAttribute : "__id",
        /**
         * セルID
         * @memberOf AbstractODataModel#
         */
        cell : null,
        /**
         * Box名
         * @memberOf AbstractODataModel#
         */
        box : null,
        /**
         * ODataCollection名
         * @memberOf AbstractODataModel#
         */
        odata : null,
        /**
         * 操作対象のEntitySet名
         * @memberOf AbstractODataModel#
         */
        entity : null,

        /**
         * PCS ODataの取得・登録・更新・削除処理を行う。
         *
         * @param {String}
         *            method メソッド
         * @param {Object}
         *            model モデル
         * @param {Object}
         *            options オプション情報
         * @memberOf AbstractODataModel#
         */
        sync : function(method, model, options) {
            Log.info("AbstractODataModel sync");
            var def = $.Deferred();
            if (!options) {
                options = {};
            }
            // PCS設定情報読み込み
            this.cell = app.config.basic.cellId;
            this.box = app.config.basic.boxName;
            this.odata = app.config.basic.odataName;

            // dc1-clientによるODataアクセスを行う
            var odataCollection = app.accessor.cell(this.cell).box(this.box).odata(this.odata);
            this.entityset = odataCollection.entitySet(this.entity);

            var complete = function(res) {
                Log.info("AbstractODataModel search complete handler");
                // 取得したJSONオブジェクト
                var json = null;
                if (res.error) {
                    if (options.error) {
                        options.error(res);
                    }
                    def.reject(res);
                } else if (options.success) {
                    if (res.bodyAsJson) {
                        json = res.bodyAsJson();
                    }
                    if (json && json.d) {
                        json = json.d.results;
                    }
                    options.success(json);
                }

                if (options.complete) {
                    options.complete(res, model, json);
                }

                if (def.state() === "pending") {
                    def.resolve(res);
                }
            };
            Log.info("Request personium. method : " + method);
            try {
                switch (method) {
                case 'create':
                    this.create(method, model, options, complete);
                    break;
                case 'update':
                    this.update(method, model, options, complete);
                    break;
                case 'delete':
                    this.del(method, model, options, complete);
                    break;
                case 'read':
                    this.retrieve(method, model, options, complete);
                    break;
                }
            } catch (e) {
                Log.info("Personium Exception : " + e);
                app.router.go("login");
            }
            return def.promise();
        },
        /**
         * PCS ODataの登録処理を行う。
         *
         * @param {String}
         *            method メソッド
         * @param {Object}
         *            model モデル
         * @param {Object}
         *            options オプション情報
         * @param {Function}
         *            complete 検索処理が完了した際に呼び出されるコールバック関数。

         *            以下のシグネチャの関数を指定する。

         *            complete (response:Object)

         *            responseオブジェクトから、PCSが返却したレスポンス情報を取得することができる。
         * @memberOf AbstractODataModel#
         */
        create : function(method, model, options, complete) {
            Log.info("AbstractODataModel create");
            this.entityset.createAsResponse(this.getSaveData(), {
                complete : function(response) {
                    Log.info("AbstractODataModel create complete");
                    complete(response);
                }
            });
        },
        /**
         * PCS ODataの更新処理を行う。
         *
         * @param {String}
         *            method メソッド
         * @param {Object}
         *            model モデル
         * @param {Object}
         *            options オプション情報
         * @param {Function}
         *            complete 検索処理が完了した際に呼び出されるコールバック関数。

         *            以下のシグネチャの関数を指定する。

         *            complete (response:Object)

         *            responseオブジェクトから、PCSが返却したレスポンス情報を取得することができる。
         * @memberOf AbstractODataModel#
         */
        update : function(method, model, options, complete) {
            Log.info("AbstractODataModel update");
            /**
             *  * 問題点 *
             *  personium.ioではリソースのupdate時にETagをチェックしているため、
             *  ここで付与するETagが古いと、412 Precondition Failedを返されてしまう
             */
            this.entityset.update(this.get("__id"), this.getSaveData(), this.get("etag"), {
                complete : function(response) {
                    /**
                     *  * 問題点 *
                     *  並び替え後の順序をPUTした後にここに来る。
                     *  本来ならばここで`response.getHeader("ETag")`を読んで
                     *  ModelのETagを最新のものに更新したいところだが、
                     *  CORSの制約によりレスポンスヘッダのEtagを読み出すことができない。
                     *  よって、次回の並び替え時に古いEtagが送信されてしまう。
                     *
                     *  * 解決策 *
                     *  localhostなどのローカル環境からレスポンスヘッダのEtagを読み出すには
                     *  以下の2つの方法が考えられる。
                     *
                     *  1. サーバー側でAccess-Control-Expose-Headerレスポンスヘッダを付与する
                     *  2. レスポンスボディにETagを含める
                     *
                     *  * 参考 *
                     *  http://www.w3.org/TR/cors/#handling-a-response-to-a-cross-origin-request
                     */
                    Log.info("AbstractODataModel update complete");
                    complete(response);
                }
            });
        },
        /**
         * PCS ODataの削除処理を行う。
         *
         * @param {String}
         *            method メソッド
         * @param {Object}
         *            model モデル
         * @param {Object}
         *            options オプション情報
         * @param {Function}
         *            complete 検索処理が完了した際に呼び出されるコールバック関数。

         *            以下のシグネチャの関数を指定する。

         *            complete (response:Object)

         *            responseオブジェクトから、PCSが返却したレスポンス情報を取得することができる。
         * @memberOf AbstractODataModel#
         */
        del : function(method, model, options, complete) {
            Log.info("AbstractODataModel delete");
            this.entityset.del(this.get("__id"), this.get("etag"), {
                complete : function(response) {
                    Log.info("AbstractODataModel delete complete");
                    complete(response);
                }
            });
        },
        /**
         * PCS ODataの取得処理を行う。
         *
         * @param {String}
         *            method メソッド
         * @param {Object}
         *            model モデル
         * @param {Object}
         *            options オプション情報
         * @param {Function}
         *            complete 検索処理が完了した際に呼び出されるコールバック関数。

         *            以下のシグネチャの関数を指定する。

         *            complete (response:Object)

         *            responseオブジェクトから、PCSが返却したレスポンス情報を取得することができる。
         * @memberOf AbstractODataModel#
         */
        retrieve : function(method, model, options, complete) {
            Log.info("AbstractODataModel retrieve");
            this.entityset.retrieveAsResponse(this.get("__id"), {
                complete : function(response) {
                    Log.info("AbstractODataModel retrieve complete");
                    complete(response);
                }
            });
        },
        /**
         * 取得情報のparse処理を行う。
         *

         * サブクラスがparse処理を行う場合、parseODataメソッドをオーバライドして行う。
         *

         * @param {Object} response レスポンス情報
         * @param {Object} options オプション情報
         * @return {Object} パース後の情報
         * @memberOf AbstractODataModel#
         */
        parseResponse : function(response, options) {
            //Log.info("AbstractODataModel parseResponse");
            var res = this.parseOData(response, options);
            // 全ての情報で共通のパース処理を実施する
            if (response && response.__metadata) {
                res.etag = response.__metadata.etag;
            }
            return res;
        },
        /**
         * 取得したOData情報のparse処理を行う。
         *

         * サブクラスは、本メソッドをオーバライドして、取得した情報のparse処理を実装する。
         *

         * @param {Object} response レスポンス情報
         * @param {Object} options オプション情報
         * @return {Object} パース後の情報
         * @memberOf AbstractODataModel#
         */
        parseOData : function(response, options) {
            //Log.info("AbstractODataModel parseOData");
            return response;
        },
        /**
         * PCSへの永続化データを生成する。
         *
         * @return {Object} JSON オブジェクト
         * @memberOf AbstractODataModel#
         */
        getSaveData : function() {
            Log.info("AbstractODataModel getSaveData");
            var saveData = {};
            if(this.id){
                saveData.__id = this.id;
            }
            if (this.get("__id")) {
                saveData.__id = this.get("__id");
            }
            saveData.createdAt = this.get("createdAt");
            if (!saveData.createdAt) {
                saveData.createdAt = new Date().toISOString();
            }
            saveData.updatedAt = new Date().toISOString();
            if (this.get("isDelete")) {
                saveData.deletedAt = new Date().toISOString();
            } else {
                saveData.deletedAt = null;
            }

            this.makeSaveData(saveData);

            return saveData;
        },
        /**
         * モデル固有の永続化データを生成する。
         *

         * サブクラスは、本メソッドをオーバライドして、 永続化するデータを生成する処理を実装する。
         *

         * @param {Object} saveData 永続化データ
         * @memberOf AbstractODataModel#
         */
        makeSaveData : function(saveData) {

        }
    });

    module.exports = AbstractODataModel;
});
<commit_msg>NAM-889 説明用コメントを削除する<commit_after>define(function(require, exports, module) {
    "use strict";

    var app = require("app");
    var AbstractModel = require("modules/model/AbstractModel");
    var Log = require("modules/util/Logger");

    /**
     * PCS ODataの操作を行うモデルの基底クラスを作成する。
     *
     * @class PCS ODataの操作を行うモデルの基底クラス
     * @exports AbstractODataModel
     * @constructor
     */
    var AbstractODataModel = AbstractModel.extend({
        /**
         * idとして使用するattribute中のフィールド
         * @memberOf AbstractODataModel#
         */
        idAttribute : "__id",
        /**
         * セルID
         * @memberOf AbstractODataModel#
         */
        cell : null,
        /**
         * Box名
         * @memberOf AbstractODataModel#
         */
        box : null,
        /**
         * ODataCollection名
         * @memberOf AbstractODataModel#
         */
        odata : null,
        /**
         * 操作対象のEntitySet名
         * @memberOf AbstractODataModel#
         */
        entity : null,

        /**
         * PCS ODataの取得・登録・更新・削除処理を行う。
         *
         * @param {String}
         *            method メソッド
         * @param {Object}
         *            model モデル
         * @param {Object}
         *            options オプション情報
         * @memberOf AbstractODataModel#
         */
        sync : function(method, model, options) {
            Log.info("AbstractODataModel sync");
            var def = $.Deferred();
            if (!options) {
                options = {};
            }
            // PCS設定情報読み込み
            this.cell = app.config.basic.cellId;
            this.box = app.config.basic.boxName;
            this.odata = app.config.basic.odataName;

            // dc1-clientによるODataアクセスを行う
            var odataCollection = app.accessor.cell(this.cell).box(this.box).odata(this.odata);
            this.entityset = odataCollection.entitySet(this.entity);

            var complete = function(res) {
                Log.info("AbstractODataModel search complete handler");
                // 取得したJSONオブジェクト
                var json = null;
                if (res.error) {
                    if (options.error) {
                        options.error(res);
                    }
                    def.reject(res);
                } else if (options.success) {
                    if (res.bodyAsJson) {
                        json = res.bodyAsJson();
                    }
                    if (json && json.d) {
                        json = json.d.results;
                    }
                    options.success(json);
                }

                if (options.complete) {
                    options.complete(res, model, json);
                }

                if (def.state() === "pending") {
                    def.resolve(res);
                }
            };
            Log.info("Request personium. method : " + method);
            try {
                switch (method) {
                case 'create':
                    this.create(method, model, options, complete);
                    break;
                case 'update':
                    this.update(method, model, options, complete);
                    break;
                case 'delete':
                    this.del(method, model, options, complete);
                    break;
                case 'read':
                    this.retrieve(method, model, options, complete);
                    break;
                }
            } catch (e) {
                Log.info("Personium Exception : " + e);
                app.router.go("login");
            }
            return def.promise();
        },
        /**
         * PCS ODataの登録処理を行う。
         *
         * @param {String}
         *            method メソッド
         * @param {Object}
         *            model モデル
         * @param {Object}
         *            options オプション情報
         * @param {Function}
         *            complete 検索処理が完了した際に呼び出されるコールバック関数。

         *            以下のシグネチャの関数を指定する。

         *            complete (response:Object)

         *            responseオブジェクトから、PCSが返却したレスポンス情報を取得することができる。
         * @memberOf AbstractODataModel#
         */
        create : function(method, model, options, complete) {
            Log.info("AbstractODataModel create");
            this.entityset.createAsResponse(this.getSaveData(), {
                complete : function(response) {
                    Log.info("AbstractODataModel create complete");
                    complete(response);
                }
            });
        },
        /**
         * PCS ODataの更新処理を行う。
         *
         * @param {String}
         *            method メソッド
         * @param {Object}
         *            model モデル
         * @param {Object}
         *            options オプション情報
         * @param {Function}
         *            complete 検索処理が完了した際に呼び出されるコールバック関数。

         *            以下のシグネチャの関数を指定する。

         *            complete (response:Object)

         *            responseオブジェクトから、PCSが返却したレスポンス情報を取得することができる。
         * @memberOf AbstractODataModel#
         */
        update : function(method, model, options, complete) {
            Log.info("AbstractODataModel update");
            this.entityset.update(this.get("__id"), this.getSaveData(), this.get("etag"), {
                complete : function(response) {
                    Log.info("AbstractODataModel update complete");
                    complete(response);
                }
            });
        },
        /**
         * PCS ODataの削除処理を行う。
         *
         * @param {String}
         *            method メソッド
         * @param {Object}
         *            model モデル
         * @param {Object}
         *            options オプション情報
         * @param {Function}
         *            complete 検索処理が完了した際に呼び出されるコールバック関数。

         *            以下のシグネチャの関数を指定する。

         *            complete (response:Object)

         *            responseオブジェクトから、PCSが返却したレスポンス情報を取得することができる。
         * @memberOf AbstractODataModel#
         */
        del : function(method, model, options, complete) {
            Log.info("AbstractODataModel delete");
            this.entityset.del(this.get("__id"), this.get("etag"), {
                complete : function(response) {
                    Log.info("AbstractODataModel delete complete");
                    complete(response);
                }
            });
        },
        /**
         * PCS ODataの取得処理を行う。
         *
         * @param {String}
         *            method メソッド
         * @param {Object}
         *            model モデル
         * @param {Object}
         *            options オプション情報
         * @param {Function}
         *            complete 検索処理が完了した際に呼び出されるコールバック関数。

         *            以下のシグネチャの関数を指定する。

         *            complete (response:Object)

         *            responseオブジェクトから、PCSが返却したレスポンス情報を取得することができる。
         * @memberOf AbstractODataModel#
         */
        retrieve : function(method, model, options, complete) {
            Log.info("AbstractODataModel retrieve");
            this.entityset.retrieveAsResponse(this.get("__id"), {
                complete : function(response) {
                    Log.info("AbstractODataModel retrieve complete");
                    complete(response);
                }
            });
        },
        /**
         * 取得情報のparse処理を行う。
         *

         * サブクラスがparse処理を行う場合、parseODataメソッドをオーバライドして行う。
         *

         * @param {Object} response レスポンス情報
         * @param {Object} options オプション情報
         * @return {Object} パース後の情報
         * @memberOf AbstractODataModel#
         */
        parseResponse : function(response, options) {
            //Log.info("AbstractODataModel parseResponse");
            var res = this.parseOData(response, options);
            // 全ての情報で共通のパース処理を実施する
            if (response && response.__metadata) {
                res.etag = response.__metadata.etag;
            }
            return res;
        },
        /**
         * 取得したOData情報のparse処理を行う。
         *

         * サブクラスは、本メソッドをオーバライドして、取得した情報のparse処理を実装する。
         *

         * @param {Object} response レスポンス情報
         * @param {Object} options オプション情報
         * @return {Object} パース後の情報
         * @memberOf AbstractODataModel#
         */
        parseOData : function(response, options) {
            //Log.info("AbstractODataModel parseOData");
            return response;
        },
        /**
         * PCSへの永続化データを生成する。
         *
         * @return {Object} JSON オブジェクト
         * @memberOf AbstractODataModel#
         */
        getSaveData : function() {
            Log.info("AbstractODataModel getSaveData");
            var saveData = {};
            if(this.id){
                saveData.__id = this.id;
            }
            if (this.get("__id")) {
                saveData.__id = this.get("__id");
            }
            saveData.createdAt = this.get("createdAt");
            if (!saveData.createdAt) {
                saveData.createdAt = new Date().toISOString();
            }
            saveData.updatedAt = new Date().toISOString();
            if (this.get("isDelete")) {
                saveData.deletedAt = new Date().toISOString();
            } else {
                saveData.deletedAt = null;
            }

            this.makeSaveData(saveData);

            return saveData;
        },
        /**
         * モデル固有の永続化データを生成する。
         *

         * サブクラスは、本メソッドをオーバライドして、 永続化するデータを生成する処理を実装する。
         *

         * @param {Object} saveData 永続化データ
         * @memberOf AbstractODataModel#
         */
        makeSaveData : function(saveData) {

        }
    });

    module.exports = AbstractODataModel;
});
<|endoftext|>


<commit_before>import React from 'react'
import SubGrid from './SubGrid'
import {shallow} from 'enzyme'
import {context} from 'tocco-test-util'

describe('entity-browser', () => {
  describe('components', () => {
    describe('SubGrid', () => {
      it('should render', () => {
        const fakeContext = {router: {listen: () => {}}}
        const contextTypes = {router: React.PropTypes.object}

        const tableDefinition = {
          type: 'ch.tocco.nice2.model.form.components.table.Table',
          children: []
        }

        const subGridComponent = context.wrapWithContext(fakeContext, contextTypes,
          <SubGrid tableDefinition={tableDefinition} modelField={{targetEntity:'User'}} relationName="relFoo"/>
        )

        shallow(subGridComponent)
      })
    })
  })
})
<commit_msg>test(entity-browser): fix subgrid test<commit_after>import React from 'react'
import SubGrid from './SubGrid'
import {mount} from 'enzyme'
import {Provider} from 'react-redux'
import {createStore} from 'redux'
import {MemoryRouter} from 'react-router-dom'
import EntityListApp from 'entity-list/src/main'

describe('entity-browser', () => {
  describe('components', () => {
    describe('SubGrid', () => {
      it('should render', () => {
        const tableDefinition = {
          type: 'ch.tocco.nice2.model.form.components.table.Table',
          children: []
        }

        const store = createStore(() => {})

        const wrapper = mount(
          <Provider store={store}>
            <MemoryRouter>
              <SubGrid tableDefinition={tableDefinition} modelField={{targetEntity: 'User'}} relationName="relFoo"/>
            </MemoryRouter>
          </Provider>)

        expect(wrapper.find(EntityListApp)).to.have.length(1)
      })
    })
  })
})
<|endoftext|>


<commit_before>/*    Copyright 2016 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

let log = require('../net2/logger.js')(__filename);

let uuid = require("uuid");

let intercomm = require('../lib/intercomm.js');
let utils = require('../lib/utils.js');
let network = require('network');
let qrcode = require('qrcode-terminal');

let bone = require("../lib/Bone.js");

let Promise = require('bluebird');

let async = require('asyncawait/async');
let await = require('asyncawait/await');

let networkTool = require('../net2/NetworkTool')();

let license = require('../util/license.js');

let FW_SERVICE = "Firewalla";
let FW_SERVICE_TYPE = "fb";
let FW_ENDPOINT_NAME = "netbot";

let defaultCheckInterval = 2; // 2 seconds
let defaultTotalTimeout = 60*60;

class FWInvitation {

  constructor(cloud, gid, symmetrickey) {
    this.cloud = cloud;
    this.gid = gid
    this.symmetrickey = symmetrickey
    this.totalTimeout = defaultTotalTimeout;
    this.checkInterval = defaultCheckInterval;

    // in noLicenseMode, a default password will be used, a flag 'firstTime' needs to be used to tell app side to use default password
    if(symmetrickey.noLicenseMode) {
      this.firstTime = true;
    } else {
      this.firstTime = false;
    }

  }

  displayKey(key) {
      log.info("\n\n-------------------------------\n");
      log.info("If asked by APP type in this key: ", key);
      log.info("\n-------------------------------");
      log.info("\n\nOr Scan this");
      log.info("\n");

      qrcode.generate(key);
  }

  /*
   * This will enable user to scan the QR code
   * bypass the proximity encryption used
   *
   * please keep this disabled for production
   */

  displayInvite(obj) {
      log.info("\n\n-------------------------------\n");
      log.info("Please scan this to get the invite directly.\n\n");
      log.info("\n\n-------------------------------\n");
      let str = JSON.stringify(obj);
      qrcode.generate(str);
  }

  validateLicense(license) {

  }

  checkInvitation(rid) {
    return async(() => {
      log.info(`${this.leftCheckCount} Inviting ${rid} to group ${this.gid}`);
      try {
        this.leftCheckCount--;
        let rinfo = await (this.cloud.eptinviteGroupByRidAsync(this.gid, rid));
        if(!rinfo || !rinfo.value) {
          throw new Error("Invalid rinfo");
        }

        if(!rinfo.evalue) {
          // FIXME: might enforce license in the future
          log.error("License info is not provided by App, maybe app version is too old");
        }

        let eid = rinfo.value;
        let userInfoString = rinfo.evalue;
        let userInfo = null

        if(userInfoString) {
          try {
            userInfo = JSON.parse(userInfoString);
          } catch(err) {
            log.error(`Failed to parse userInfo ${userInfoString} from app side: ${err}`);
          }
        }

        if(userInfo && userInfo.license) {
          // validate license first
          await (bone.waitUntilCloudReadyAsync());
          let infs = await (networkTool.getLocalNetworkInterface())
          if(infs.length > 0) {
            let mac = infs[0].mac_address;

            try {
              let lic = await (bone.getLicenseAsync(userInfo.license, mac));
              if(lic) {
                log.info("Got a new license:", lic, {});
                await (license.writeLicense(lic));
              }
            } catch(err) {
              log.error("Invalid license");
            }
          }
        }

        let inviteResult = await (this.cloud.eptinviteGroupAsync(this.gid, eid));

        log.info(`Linked App ${eid} to this device successfully`);

        return {
          status: "success",
          payload: inviteResult
        };

      } catch(err) {
        if(err != "404") {
          log.error(err, {});
        }

        if (this.leftCheckCount <= 0) {
          log.info("Invitation is expired! No App Linked");
          return {
            status: "expired",
          };
        } else {
          return {
            status: "pending"
          };
        }
      }
    })();
  }

  broadcast(onSuccess, onTimeout) {
    onSuccess = onSuccess || function() {}
    onTimeout = onTimeout || function() {}

    this.checkCount = this.totalTimeout / this.checkInterval;
    log.info(`Check Interval: ${this.checkInterval}`);
    log.info(`Check Count: ${this.checkCount}`);

    this.leftCheckCount = this.checkCount;

    let obj = this.cloud.eptGenerateInvite(this.gid);

    let txtfield = {
        'gid': this.gid,
        'seed': this.symmetrickey.seed,
        'keyhint': 'You will find the key on the back of your device',
        'service': FW_SERVICE,
        'type': FW_SERVICE_TYPE,
        'mid': uuid.v4(),
        'exp': Date.now() / 1000 + this.totalTimeout,
        'licensemode': '1',
    };

    if (intercomm.bcapable()==false) {
      txtfield.verifymode = "qr";
    } else {
      intercomm.bpublish(this.gid, obj.r, FW_SERVICE_TYPE);
    }

    if(this.firstTime) {
      txtfield.firsttime = '1'
    }

    txtfield.ek = this.cloud.encrypt(obj.r, this.symmetrickey.key);

    this.displayKey(this.symmetrickey.userkey);
    this.displayInvite(obj);

    network.get_private_ip((err, ip) => {
        txtfield.ipaddress = ip;

        log.info("TXT:", txtfield, {});
        this.service = intercomm.publish(null, FW_ENDPOINT_NAME + utils.getCpuId(), 'devhi', 8833, 'tcp', txtfield);
    });

    if (intercomm.bcapable() != false) {
      intercomm.bpublish(gid, obj.r, config.serviceType);
    }

    let timer = setInterval(() => {
      async(() => {
        let rid = obj.r;
        let result = await (this.checkInvitation(rid));
        switch(result.status) {
          case "success":
            this.stopBroadcast();
            clearInterval(timer);
            onSuccess(result.payload)
          break;
          case "expired":
            this.stopBroadcast();
            clearInterval(timer);
            onTimeout();
          break;
          default:
            // do nothing;
          break;
        }
      })();
    }, this.checkInterval * 1000);
  }

  stopBroadcast() {
    this.service && intercomm.stop(this.service);
    intercomm.bcapable() && intercomm.bstop();
    intercomm.bye();
  }
}

module.exports = FWInvitation;
<commit_msg>fixes #328<commit_after>/*    Copyright 2016 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

let log = require('../net2/logger.js')(__filename);

let uuid = require("uuid");

let intercomm = require('../lib/intercomm.js');
let utils = require('../lib/utils.js');
let network = require('network');
let qrcode = require('qrcode-terminal');

let bone = require("../lib/Bone.js");

let Promise = require('bluebird');

let async = require('asyncawait/async');
let await = require('asyncawait/await');

let networkTool = require('../net2/NetworkTool')();

let license = require('../util/license.js');

let FW_SERVICE = "Firewalla";
let FW_SERVICE_TYPE = "fb";
let FW_ENDPOINT_NAME = "netbot";

let defaultCheckInterval = 3; // 2 seconds
let defaultTotalTimeout = 60*60;
let additionBindingTotalTimeout = 60*10;  // 10 min only

class FWInvitation {

  constructor(cloud, gid, symmetrickey) {
    this.cloud = cloud;
    this.gid = gid
    this.symmetrickey = symmetrickey
    this.totalTimeout = defaultTotalTimeout;
    this.checkInterval = defaultCheckInterval;

    // in noLicenseMode, a default password will be used, a flag 'firstTime' needs to be used to tell app side to use default password
    if(symmetrickey.noLicenseMode) {
      this.firstTime = true;
    } else {
      this.firstTime = false;
    }

    // any additional binding is only 10 min window
    if (this.firstTime == false) {
        this.totalTimeout = additionBindingTotalTimeout;
    }

  }

  displayKey(key) {
      log.info("\n\n-------------------------------\n");
      log.info("If asked by APP type in this key: ", key);
      log.info("\n-------------------------------");
      log.info("\n\nOr Scan this");
      log.info("\n");

      qrcode.generate(key);
  }

  /*
   * This will enable user to scan the QR code
   * bypass the proximity encryption used
   *
   * please keep this disabled for production
   */

  displayInvite(obj) {
      log.info("\n\n-------------------------------\n");
      log.info("Please scan this to get the invite directly.\n\n");
      log.info("\n\n-------------------------------\n");
      let str = JSON.stringify(obj);
      qrcode.generate(str);
  }

  validateLicense(license) {

  }

  checkInvitation(rid) {
    return async(() => {
      log.info(`${this.leftCheckCount} Inviting ${rid} to group ${this.gid}`);
      try {
        this.leftCheckCount--;
        let rinfo = await (this.cloud.eptinviteGroupByRidAsync(this.gid, rid));
        if(!rinfo || !rinfo.value) {
          throw new Error("Invalid rinfo");
        }

        if(!rinfo.evalue) {
          // FIXME: might enforce license in the future
          log.error("License info is not provided by App, maybe app version is too old");
        }

        let eid = rinfo.value;
        let userInfoString = rinfo.evalue;
        let userInfo = null

        if(userInfoString) {
          try {
            userInfo = JSON.parse(userInfoString);
          } catch(err) {
            log.error(`Failed to parse userInfo ${userInfoString} from app side: ${err}`);
          }
        }

        if(userInfo && userInfo.license) {
          // validate license first
          await (bone.waitUntilCloudReadyAsync());
          let infs = await (networkTool.getLocalNetworkInterface())
          if(infs.length > 0) {
            let mac = infs[0].mac_address;

            try {
              let lic = await (bone.getLicenseAsync(userInfo.license, mac));
              if(lic) {
                log.info("Got a new license:", lic, {});
                await (license.writeLicense(lic));
              }
            } catch(err) {
              log.error("Invalid license");
            }
          }
        }

        let inviteResult = await (this.cloud.eptinviteGroupAsync(this.gid, eid));

        log.info(`Linked App ${eid} to this device successfully`);

        return {
          status: "success",
          payload: inviteResult
        };

      } catch(err) {
        if(err != "404") {
          log.error(err, {});
        }

        if (this.leftCheckCount <= 0) {
          log.info("Invitation is expired! No App Linked");
          return {
            status: "expired",
          };
        } else {
          return {
            status: "pending"
          };
        }
      }
    })();
  }

  broadcast(onSuccess, onTimeout) {
    onSuccess = onSuccess || function() {}
    onTimeout = onTimeout || function() {}

    this.checkCount = this.totalTimeout / this.checkInterval;
    log.info(`Check Interval: ${this.checkInterval}`);
    log.info(`Check Count: ${this.checkCount}`);

    this.leftCheckCount = this.checkCount;

    let obj = this.cloud.eptGenerateInvite(this.gid);

    let txtfield = {
        'gid': this.gid,
        'seed': this.symmetrickey.seed,
        'keyhint': 'You will find the key on the back of your device',
        'service': FW_SERVICE,
        'type': FW_SERVICE_TYPE,
        'mid': uuid.v4(),
        'exp': Date.now() / 1000 + this.totalTimeout,
        'licensemode': '1',
    };

    if (intercomm.bcapable()==false) {
      txtfield.verifymode = "qr";
    } else {
      intercomm.bpublish(this.gid, obj.r, FW_SERVICE_TYPE);
    }

    if(this.firstTime) {
      txtfield.firsttime = '1'
    }

    txtfield.ek = this.cloud.encrypt(obj.r, this.symmetrickey.key);

    this.displayKey(this.symmetrickey.userkey);
    this.displayInvite(obj);

    network.get_private_ip((err, ip) => {
        txtfield.ipaddress = ip;

        log.info("TXT:", txtfield, {});
        this.service = intercomm.publish(null, FW_ENDPOINT_NAME + utils.getCpuId(), 'devhi', 8833, 'tcp', txtfield);
    });

    if (intercomm.bcapable() != false) {
      intercomm.bpublish(gid, obj.r, config.serviceType);
    }

    let timer = setInterval(() => {
      async(() => {
        let rid = obj.r;
        let result = await (this.checkInvitation(rid));
        switch(result.status) {
          case "success":
            this.stopBroadcast();
            clearInterval(timer);
            onSuccess(result.payload)
          break;
          case "expired":
            this.stopBroadcast();
            clearInterval(timer);
            onTimeout();
          break;
          default:
            // do nothing;
          break;
        }
      })();
    }, this.checkInterval * 1000);
  }

  stopBroadcast() {
    this.service && intercomm.stop(this.service);
    intercomm.bcapable() && intercomm.bstop();
    intercomm.bye();
  }
}

module.exports = FWInvitation;
<|endoftext|>


<commit_before>class MCProcessesWorkflowSidebarComponentController {
    /*@ngInject*/
    constructor(workflowState, templatesAPI, workflowService, $stateParams) {
        this.workflowState = workflowState;
        this.templatesAPI = templatesAPI;
        this.workflowService = workflowService;
        this.projectId = $stateParams.project_id;
        this.experimentId = $stateParams.experiment_id;
        this.selectedProcess = null;
        this.myName = 'MCProcessesWorkflowSidebar';
    }

    $onInit() {
        this.workflowState.subscribeSelectedProcess(this.myName, (process) => this.selectedProcess = process);
        this.templatesAPI.getAllTemplates().then(
            templates => this.templates = templates
        );
    }

    $onDestroy() {
        this.workflowState.leaveSelectedProcess(this.myName);
    }

    addToGraph(template) {
        this.workflowService.addProcessFromTemplateNoPopup(template.name, this.projectId, this.experimentId);
    }
}

angular.module('materialscommons').component('mcProcessesWorkflowSidebar', {
    templateUrl: 'app/project/experiments/experiment/components/workflow/mc-processes-workflow-sidebar.html',
    controller: MCProcessesWorkflowSidebarComponentController
});
<commit_msg>Templates already retrieved no need to make server side call again<commit_after>class MCProcessesWorkflowSidebarComponentController {
    /*@ngInject*/
    constructor(workflowState, templates, workflowService, $stateParams) {
        this.workflowState = workflowState;
        this.workflowService = workflowService;
        this.projectId = $stateParams.project_id;
        this.experimentId = $stateParams.experiment_id;
        this.templates = templates.get();
        this.selectedProcess = null;
        this.myName = 'MCProcessesWorkflowSidebar';
    }

    $onInit() {
        this.workflowState.subscribeSelectedProcess(this.myName, (process) => this.selectedProcess = process);
    }

    $onDestroy() {
        this.workflowState.leaveSelectedProcess(this.myName);
    }

    addToGraph(template) {
        this.workflowService.addProcessFromTemplateNoPopup(template.name, this.projectId, this.experimentId);
    }
}

angular.module('materialscommons').component('mcProcessesWorkflowSidebar', {
    templateUrl: 'app/project/experiments/experiment/components/workflow/mc-processes-workflow-sidebar.html',
    controller: MCProcessesWorkflowSidebarComponentController
});
<|endoftext|>


<commit_before>'use strict';
var url = require('url');
var config = require('config');
var request = require('request-promise');
var AWS = require('aws-sdk');
var logger = require('hoist-logger');
var _ = require('lodash');
var BBPromise = require('bluebird');
var amqp = require('amqplib');

AWS.config.update({
  accessKeyId: config.get('Hoist.aws.account'),
  secretAccessKey: config.get('Hoist.aws.secret'),
  region: config.get('Hoist.aws.region')
});

//var ec2 = BBPromise.promisifyAll(new AWS.EC2());
var autoscaling = BBPromise.promisifyAll(new AWS.AutoScaling());

function getQueues() {
  var rabbitUrl = url.parse(config.get('Hoist.rabbit.managementurl'));
  rabbitUrl.pathname = '/api/queues';
  return request.get(url.format(rabbitUrl), {
      json: true
    })
    .then(function (response) {
      return _.filter(response, function (queue) {
        return _.startsWith(queue.name, 'run_module_');
      });
    });
}

function getConsumersForQueues(queues) {
  return BBPromise.try(function () {
    return BBPromise.all(
      _.map(queues, function (queue) {
        var rabbitUrl = url.parse(config.get('Hoist.rabbit.managementurl'));
        rabbitUrl.pathname = '/api/queues/' + encodeURIComponent(queue.vhost) + '/' + encodeURIComponent(queue.name);
        return request.get(url.format(rabbitUrl), {
            json: true
          })
          .then(function (response) {
            /*jshint camelcase:false*/
            return {
              channel: _.pluck(response.consumer_details, 'channel_details'),
              queue: queue
            };
          });
      })
    );
  });

}

function setScaleMinAndMax(queueCount) {
  var autoScaleConfig = {
    MinSize: queueCount,
    MaxSize: queueCount * 10,
    AutoScalingGroupName: 'executors'
  };
  logger.info({
    config: autoScaleConfig
  }, 'updating autoscaling config');
  return autoscaling.updateAutoScalingGroupAsync(autoScaleConfig).then(function (data) {
    logger.info({
      data: data
    }, 'auto scaling group');
  });
}

function rebalanceExecutor(privateIp, queue) {
  return amqp.connect(config.get('Hoist.rabbit.url'), {
    heartbeat: 60
  }).then(function (connection) {
    return connection.createChannel()
      .then(function (channel) {
        return channel.assertExchange('executor_commands', 'topic')
          .then(function (response) {
            logger.info('publishing command');
            return channel.publish(response.exchange, 'command:' + privateIp + ':' + queue, new Buffer(JSON.stringify({
              task: 'reaquire'
            })));
          });
      }).then(function(){
        return BBPromise.delay(2000);
      }).then(function () {
        return connection.close();

      });
  });

}

function rebalance() {
  //get queue stats
  console.log('getting queues');
  return getQueues().
  then(function (queues) {
    var messageCount = _.reduce(_.pluck(queues, 'messages_ready'), function (sum, n) {
      return sum + n;
    });
    console.log(messageCount);
    console.log('parsing queues');
    //ensure minimum executors is the same as number of queues
    //setting new AutoScalingParams
    return setScaleMinAndMax(queues.length, messageCount)
      .then(function () {

        var queuesThatHaveNoConsumers = _.filter(queues, function (queue) {
          return queue.consumers < 4;
        });
        logger.info({
          queues: _.pluck(queuesThatHaveNoConsumers, 'name')
        }, 'do we have any queues that need consumers?');
        var queuesWithTooFewConsumers = _.filter(queues, function (queue) {
          /* jshint camelcase:false */
          return queue.consumers < 40 && ((queue.consumers / queue.messages_ready) < 0.001);
        });
        logger.info({
          queues: _.pluck(queuesWithTooFewConsumers, 'name')
        }, 'do we have any queues that have too few consumers?');
        var queuesWithTooManyConsumers = _.filter(queues, function (queue) {
          /* jshint camelcase:false */
          return queue.consumers > 40 || (queue.consumers > 4 && ((queue.consumers / queue.messages_ready) > 0.001));
        });
        logger.info({
          queues: _.pluck(queuesWithTooManyConsumers, 'name')
        }, 'do we have any queues that have too many consumers?');

        if (queuesThatHaveNoConsumers.length > 0 || queuesWithTooFewConsumers.length > 0) {
          logger.info('we need to add consumers');
          if (queuesWithTooManyConsumers.length > 0) {
            logger.info({
              queues: _.pluck(queuesWithTooManyConsumers, 'name')
            }, 'we should grab executors from queues');
            return getConsumersForQueues(queuesWithTooManyConsumers)
              .then(function (consumers) {
                consumers = _.flatten(consumers);
                logger.info({
                  consumers: consumers
                }, 'consumers found');
                var selectedConsumer = _.first(consumers);
                /*jshint camelcase:false*/
                var selectedChannel = selectedConsumer.channel[0];
                logger.info('telling consumer', selectedChannel.peer_host + ":" + selectedConsumer.queue.name, 'to reaquire');
                return rebalanceExecutor(selectedChannel.peer_host,selectedConsumer.queue.name);
              });
          } else {
            var scaleUpParams = {
              PolicyName: 'increase-executors',
              /* required */
              AutoScalingGroupName: 'executors',
              HonorCooldown: true
            };
            logger.info({
              params: scaleUpParams
            }, 'increasing executors');
            return autoscaling.executePolicyAsync(scaleUpParams);
          }
        } else if (queuesWithTooManyConsumers.length > 0) {
          logger.info('we can remove executors');
          var scaleDownParams = {
            PolicyName: 'decrease-executors',
            /* required */
            AutoScalingGroupName: 'executors',
            HonorCooldown: true
          };
          logger.info({
            params: scaleDownParams
          }, 'decreasing executors');
          return autoscaling.executePolicyAsync(scaleDownParams);
        } else {
          logger.info('we seem to have the right number of executors');
        }

      });

  }).catch(function (err) {
    console.log(err);
  });

  //are there any queues that need executors (have no consumers)
  //are there queues that need to be rebalanced (have too many executors per message)
  //do we need to add more executors
  //send rebalance signal?

  //scale up?
  //scale down?
}

module.exports = rebalance;
<commit_msg>fix numbers against executors<commit_after>'use strict';
var url = require('url');
var config = require('config');
var request = require('request-promise');
var AWS = require('aws-sdk');
var logger = require('hoist-logger');
var _ = require('lodash');
var BBPromise = require('bluebird');
var amqp = require('amqplib');

AWS.config.update({
  accessKeyId: config.get('Hoist.aws.account'),
  secretAccessKey: config.get('Hoist.aws.secret'),
  region: config.get('Hoist.aws.region')
});

//var ec2 = BBPromise.promisifyAll(new AWS.EC2());
var autoscaling = BBPromise.promisifyAll(new AWS.AutoScaling());

function getQueues() {
  var rabbitUrl = url.parse(config.get('Hoist.rabbit.managementurl'));
  rabbitUrl.pathname = '/api/queues';
  return request.get(url.format(rabbitUrl), {
      json: true
    })
    .then(function (response) {
      return _.filter(response, function (queue) {
        return _.startsWith(queue.name, 'run_module_');
      });
    });
}

function getConsumersForQueues(queues) {
  return BBPromise.try(function () {
    return BBPromise.all(
      _.map(queues, function (queue) {
        var rabbitUrl = url.parse(config.get('Hoist.rabbit.managementurl'));
        rabbitUrl.pathname = '/api/queues/' + encodeURIComponent(queue.vhost) + '/' + encodeURIComponent(queue.name);
        return request.get(url.format(rabbitUrl), {
            json: true
          })
          .then(function (response) {
            /*jshint camelcase:false*/
            return {
              channel: _.pluck(response.consumer_details, 'channel_details'),
              queue: queue
            };
          });
      })
    );
  });

}

function setScaleMinAndMax(queueCount) {
  var autoScaleConfig = {
    MinSize: queueCount,
    MaxSize: queueCount * 10,
    AutoScalingGroupName: 'executors'
  };
  logger.info({
    config: autoScaleConfig
  }, 'updating autoscaling config');
  return autoscaling.updateAutoScalingGroupAsync(autoScaleConfig).then(function (data) {
    logger.info({
      data: data
    }, 'auto scaling group');
  });
}

function rebalanceExecutor(privateIp, queue) {
  return amqp.connect(config.get('Hoist.rabbit.url'), {
    heartbeat: 60
  }).then(function (connection) {
    return connection.createChannel()
      .then(function (channel) {
        return channel.assertExchange('executor_commands', 'topic')
          .then(function (response) {
            logger.info('publishing command');
            return channel.publish(response.exchange, 'command:' + privateIp + ':' + queue, new Buffer(JSON.stringify({
              task: 'reaquire'
            })));
          });
      }).then(function(){
        return BBPromise.delay(2000);
      }).then(function () {
        return connection.close();

      });
  });

}

function rebalance() {
  //get queue stats
  console.log('getting queues');
  return getQueues().
  then(function (queues) {
    var messageCount = _.reduce(_.pluck(queues, 'messages_ready'), function (sum, n) {
      return sum + n;
    });
    console.log(messageCount);
    console.log('parsing queues');
    //ensure minimum executors is the same as number of queues
    //setting new AutoScalingParams
    return setScaleMinAndMax(queues.length, messageCount)
      .then(function () {

        var queuesThatHaveNoConsumers = _.filter(queues, function (queue) {
          return queue.consumers < 2;
        });
        logger.info({
          queues: _.pluck(queuesThatHaveNoConsumers, 'name')
        }, 'do we have any queues that need consumers?');
        var queuesWithTooFewConsumers = _.filter(queues, function (queue) {
          /* jshint camelcase:false */
          return queue.consumers < 20 && ((queue.consumers / queue.messages_ready) < 0.001);
        });
        logger.info({
          queues: _.pluck(queuesWithTooFewConsumers, 'name')
        }, 'do we have any queues that have too few consumers?');
        var queuesWithTooManyConsumers = _.filter(queues, function (queue) {
          /* jshint camelcase:false */
          return queue.consumers > 20 || (queue.consumers > 2 && ((queue.consumers / queue.messages_ready) > 0.001));
        });
        logger.info({
          queues: _.pluck(queuesWithTooManyConsumers, 'name')
        }, 'do we have any queues that have too many consumers?');

        if (queuesThatHaveNoConsumers.length > 0 || queuesWithTooFewConsumers.length > 0) {
          logger.info('we need to add consumers');
          if (queuesWithTooManyConsumers.length > 0) {
            logger.info({
              queues: _.pluck(queuesWithTooManyConsumers, 'name')
            }, 'we should grab executors from queues');
            return getConsumersForQueues(queuesWithTooManyConsumers)
              .then(function (consumers) {
                consumers = _.flatten(consumers);
                logger.info({
                  consumers: consumers
                }, 'consumers found');
                var selectedConsumer = _.first(consumers);
                /*jshint camelcase:false*/
                var selectedChannel = selectedConsumer.channel[0];
                logger.info('telling consumer', selectedChannel.peer_host + ":" + selectedConsumer.queue.name, 'to reaquire');
                return rebalanceExecutor(selectedChannel.peer_host,selectedConsumer.queue.name);
              });
          } else {
            var scaleUpParams = {
              PolicyName: 'increase-executors',
              /* required */
              AutoScalingGroupName: 'executors',
              HonorCooldown: true
            };
            logger.info({
              params: scaleUpParams
            }, 'increasing executors');
            return autoscaling.executePolicyAsync(scaleUpParams);
          }
        } else if (queuesWithTooManyConsumers.length > 0) {
          logger.info('we can remove executors');
          var scaleDownParams = {
            PolicyName: 'decrease-executors',
            /* required */
            AutoScalingGroupName: 'executors',
            HonorCooldown: true
          };
          logger.info({
            params: scaleDownParams
          }, 'decreasing executors');
          return autoscaling.executePolicyAsync(scaleDownParams);
        } else {
          logger.info('we seem to have the right number of executors');
        }

      });

  }).catch(function (err) {
    console.log(err);
  });

  //are there any queues that need executors (have no consumers)
  //are there queues that need to be rebalanced (have too many executors per message)
  //do we need to add more executors
  //send rebalance signal?

  //scale up?
  //scale down?
}

module.exports = rebalance;
<|endoftext|>


<commit_before>/*
 * Downloads specified version of the source code for Orchard.
 */

var fs = require('fs');
var path = require('path');
var async = require('async');
var request = require('request');
var _ = require('lodash');
var mv = require('mv');
var JSZip = require('jszip');

'use strict';

module.exports = function(grunt) {

    /**
     * Collection of URLs to Orchard source code downloads.
     */
    var orchardDownloads = [
        { version: '1.9.1', url: 'https://github.com/OrchardCMS/Orchard/archive/1.9.1.zip' },
        { version: '1.9', url: 'https://github.com/OrchardCMS/Orchard/archive/1.9.zip' },
        { version: '1.8.2', url: 'https://github.com/OrchardCMS/Orchard/archive/1.8.2.zip' },
        { version: '1.8.1', url: 'https://github.com/OrchardCMS/Orchard/archive/1.8.1.zip' },
        { version: '1.7.1', url: 'https://github.com/OrchardCMS/Orchard/archive/1.7.1.zip' }
    ];

    var helpers = {
        /**
         * Downloads single file to local computer.
         */
        curl: function (info, cb) {
            // Default to a binary request
            var options = info.src;
            var dest = info.dest;

            // Request the url
            var req = request(options);

            // On error, callback
            req.on('error', cb);

            // On response, callback for writing out the stream
            req.on('response', function handleResponse (res) {
                // Assert the statusCode was good
                var statusCode = res.statusCode;
                if (statusCode < 200 || statusCode >= 300) {
                  return cb(new Error('Fetching ' + JSON.stringify(options) + ' failed with HTTP status code ' + statusCode));
                }

                // Otherwise, write out the content
                var destdir = path.dirname(dest);
                grunt.file.mkdir(destdir);
                var writeStream = fs.createWriteStream(dest);
                // Use `req` as the source of data https://github.com/request/request/blob/v2.51.1/request.js#L1255-L1267
                // DEV: This is to pipe out gunzipped data
                var dataStream = req;
                dataStream.pipe(writeStream);

                // When the stream errors or completes, exit
                writeStream.on('error', cb);
                writeStream.on('close', cb);
            });
        },

        /**
         * Returns directories inside the directory associated to the provided path.
         */
        getDirectories: function (srcpath) {
            return fs.readdirSync(srcpath).filter(function(file) {
                return fs.statSync(path.join(srcpath, file)).isDirectory();
            });
        },

        /**
         * Returns the download URL for Orchard source code.
         */
        getOrchardDownload: function (version) {
            // return the latest version in the orchard versio.
            if (version === 'latest') {
                return _.first(orchardDownloads);
            }

            return _.findWhere(orchardDownloads, { version: version });
        }
    };

    grunt.registerTask('orchardDownload', 'Downloads Orchard source code.', function () {
        /**
         * Retrieves defined options.
         */
        var options = this.options();
        grunt.verbose.writeflags(options, 'Options');

        // directory where Orchard should be setup within.
        options.destination = './local';

        // Orchard download is extracted into this directory before being moved
        // to a directory related to the version number.
        options.tempDir = path.join(options.destination, 'temp');

        // File path where zip containing Orchard is saved.
        options.downloadDestination = path.join(options.destination, 'Orchard.Source.' + options.version + '.zip');

        // Path to the directory where Orchrd will be installed.
        options.localDir = path.join(options.destination, options.version);

        // grunt task should be marked as async.
        var done = this.async();

        /**
         * Deletes the downloaded zip & temporary directory.
         */
        var cleanUp = function () {
            fs.rmdir(options.tempDir);

            fs.exists(options.downloadDestination, function (exists) {
                if (exists) {
                    fs.unlinkSync(options.downloadDestination);
                }

                done();
            });
        };

        /**
         * Downloads a version of Orchard from the Internet.
         */
        var download = function (orchardDownload) {
            grunt.log.writeln('downloading Orchard ' + options.version + ', this may take a while...');

            helpers.curl({
                src: orchardDownload.url,
                dest: options.downloadDestination
            }, function handleCurlComplete (err) {
                // If there is an error, fail
                if (err) {
                    grunt.fail.warn(err);
                    return done();
                }

                grunt.log.writeln('download complete.');
                extractZip(orchardDownload);
            });
        };

        /**
         * Unzips the download that contains Orchard and sets up the extracted files
         * in a directory reflecting the version number.
         */
        var extractZip = function (orchardDownload) {
            var content, dest, zip;

            grunt.log.writeln('extracting downloaded zip...');

            fs.mkdirSync(options.tempDir);

            fs.readFile(options.downloadDestination, function (err, data) {
                if (err) {
                    throw err;
                }

                zip = new JSZip(data);

                Object.keys(zip.files).forEach(function(filename) {
                    content = zip.files[filename].asNodeBuffer();
                    dest = path.join(options.tempDir, filename);

                    if (filename.substr(filename.length - 1, 1) === '/') {
                        fs.mkdirSync(dest);
                    } else {
                        fs.writeFileSync(dest, content, {
                            mode: 0777
                        });
                    }
                });

                grunt.log.writeln('extraction complete.');

                // introduce a slight delay to prevent a permission error when moving
                // the Orchard source code from a temp directory to a directory
                // tied to the version number.
                setTimeout(moveFiles, 200);
            });
        };

        /**
         * Moves extract files into directory that reflects the version number.
         */
        var moveFiles = function () {
            // ensures the uncompressed Orchard code is inside the version directory
            // inside directory that contains local Orchard install. This is to
            // ensure a directory structure like `/local/1.8.1/Orchard-1.8.1`
            // doesn't occur becuase the download zip contents is within it's
            // own directory.
            var contents = helpers.getDirectories(options.tempDir),
                currentDirName = (contents.length === 1) ? path.join(options.tempDir, contents[0]) : options.tempDir;

            mv(currentDirName, options.localDir, {mkdirp: true}, function() {
                cleanUp();
            });
        };

        var orchardDownload = (options.url) ? { version: options.version, url: options.url } : helpers.getOrchardDownload(options.version);

        if (!orchardDownload) {
            grunt.fail.fatal('Unrecognised Orchard version number.');
            // TODO: Add information on how to setup custom version of Orchard.
        }

        // check if the Orchard version has already been setup.
        fs.exists(options.localDir, function (exists) {
            if (exists) {
                done();
                return;
            }

            // Orchard hasn't been setup yet, it must be downloaded first.
            download(orchardDownload);
        });
    });
};
<commit_msg>Fix issue with deleting non-empty temp directory<commit_after>/*
 * Downloads specified version of the source code for Orchard.
 */

var fs = require('fs');
var path = require('path');
var async = require('async');
var request = require('request');
var _ = require('lodash');
var mv = require('mv');
var JSZip = require('jszip');

'use strict';

module.exports = function(grunt) {

    /**
     * Collection of URLs to Orchard source code downloads.
     */
    var orchardDownloads = [
        { version: '1.9.1', url: 'https://github.com/OrchardCMS/Orchard/archive/1.9.1.zip' },
        { version: '1.9', url: 'https://github.com/OrchardCMS/Orchard/archive/1.9.zip' },
        { version: '1.8.2', url: 'https://github.com/OrchardCMS/Orchard/archive/1.8.2.zip' },
        { version: '1.8.1', url: 'https://github.com/OrchardCMS/Orchard/archive/1.8.1.zip' },
        { version: '1.7.1', url: 'https://github.com/OrchardCMS/Orchard/archive/1.7.1.zip' }
    ];

    var helpers = {
        /**
         * Downloads single file to local computer.
         */
        curl: function (info, cb) {
            // Default to a binary request
            var options = info.src;
            var dest = info.dest;

            // Request the url
            var req = request(options);

            // On error, callback
            req.on('error', cb);

            // On response, callback for writing out the stream
            req.on('response', function handleResponse (res) {
                // Assert the statusCode was good
                var statusCode = res.statusCode;
                if (statusCode < 200 || statusCode >= 300) {
                  return cb(new Error('Fetching ' + JSON.stringify(options) + ' failed with HTTP status code ' + statusCode));
                }

                // Otherwise, write out the content
                var destdir = path.dirname(dest);
                grunt.file.mkdir(destdir);
                var writeStream = fs.createWriteStream(dest);
                // Use `req` as the source of data https://github.com/request/request/blob/v2.51.1/request.js#L1255-L1267
                // DEV: This is to pipe out gunzipped data
                var dataStream = req;
                dataStream.pipe(writeStream);

                // When the stream errors or completes, exit
                writeStream.on('error', cb);
                writeStream.on('close', cb);
            });
        },

        /**
         * Returns directories inside the directory associated to the provided path.
         */
        getDirectories: function (srcpath) {
            return fs.readdirSync(srcpath).filter(function(file) {
                return fs.statSync(path.join(srcpath, file)).isDirectory();
            });
        },

        /**
         * Returns the download URL for Orchard source code.
         */
        getOrchardDownload: function (version) {
            // return the latest version in the orchard versio.
            if (version === 'latest') {
                return _.first(orchardDownloads);
            }

            return _.findWhere(orchardDownloads, { version: version });
        }
    };

    grunt.registerTask('orchardDownload', 'Downloads Orchard source code.', function () {
        /**
         * Retrieves defined options.
         */
        var options = this.options();
        grunt.verbose.writeflags(options, 'Options');

        // directory where Orchard should be setup within.
        options.destination = './local';

        // Orchard download is extracted into this directory before being moved
        // to a directory related to the version number.
        options.tempDir = path.join(options.destination, 'temp');

        // File path where zip containing Orchard is saved.
        options.downloadDestination = path.join(options.destination, 'Orchard.Source.' + options.version + '.zip');

        // Path to the directory where Orchrd will be installed.
        options.localDir = path.join(options.destination, options.version);

        // grunt task should be marked as async.
        var done = this.async();

        /**
         * Deletes the downloaded zip & temporary directory.
         */
        var cleanUp = function () {
            grunt.file.delete(options.tempDir, { force: true });

            fs.exists(options.downloadDestination, function (exists) {
                if (exists) {
                    fs.unlinkSync(options.downloadDestination);
                }

                done();
            });
        };

        /**
         * Downloads a version of Orchard from the Internet.
         */
        var download = function (orchardDownload) {
            grunt.log.writeln('downloading Orchard ' + options.version + ', this may take a while...');

            helpers.curl({
                src: orchardDownload.url,
                dest: options.downloadDestination
            }, function handleCurlComplete (err) {
                // If there is an error, fail
                if (err) {
                    grunt.fail.warn(err);
                    return done();
                }

                grunt.log.writeln('download complete.');
                extractZip(orchardDownload);
            });
        };

        /**
         * Unzips the download that contains Orchard and sets up the extracted files
         * in a directory reflecting the version number.
         */
        var extractZip = function (orchardDownload) {
            var content, dest, zip;

            grunt.log.writeln('extracting downloaded zip...');

            fs.mkdirSync(options.tempDir);

            fs.readFile(options.downloadDestination, function (err, data) {
                if (err) {
                    throw err;
                }

                zip = new JSZip(data);

                Object.keys(zip.files).forEach(function(filename) {
                    content = zip.files[filename].asNodeBuffer();
                    dest = path.join(options.tempDir, filename);

                    if (filename.substr(filename.length - 1, 1) === '/') {
                        fs.mkdirSync(dest);
                    } else {
                        fs.writeFileSync(dest, content, {
                            mode: 0777
                        });
                    }
                });

                grunt.log.writeln('extraction complete.');

                // introduce a slight delay to prevent a permission error when moving
                // the Orchard source code from a temp directory to a directory
                // tied to the version number.
                setTimeout(moveFiles, 200);
            });
        };

        /**
         * Moves extract files into directory that reflects the version number.
         */
        var moveFiles = function () {
            // ensures the uncompressed Orchard code is inside the version directory
            // inside directory that contains local Orchard install. This is to
            // ensure a directory structure like `/local/1.8.1/Orchard-1.8.1`
            // doesn't occur becuase the download zip contents is within it's
            // own directory.
            var contents = helpers.getDirectories(options.tempDir),
                currentDirName = (contents.length === 1) ? path.join(options.tempDir, contents[0]) : options.tempDir;

            mv(currentDirName, options.localDir, {mkdirp: true}, function() {
                cleanUp();
            });
        };

        var orchardDownload = (options.url) ? { version: options.version, url: options.url } : helpers.getOrchardDownload(options.version);

        if (!orchardDownload) {
            grunt.fail.fatal('Unrecognised Orchard version number.');
            // TODO: Add information on how to setup custom version of Orchard.
        }

        // check if the Orchard version has already been setup.
        fs.exists(options.localDir, function (exists) {
            if (exists) {
                done();
                return;
            }

            // Orchard hasn't been setup yet, it must be downloaded first.
            download(orchardDownload);
        });
    });
};
<|endoftext|>


<commit_before>/*
 * grunt-eb-deploy
 * https://github.com/jjongsma/grunt-eb-deploy
 *
 * Copyright (c) 2015 Jeremy Jongsma
 * Licensed under the MIT license.
 */

'use strict';

var AWS = require('aws-sdk');
var git = require('git-rev');
var fs = require('fs');

module.exports = function(grunt) {

  var compress = require('grunt-contrib-compress/tasks/lib/compress')(grunt);

  // Please see the Grunt documentation for more information regarding task
  // creation: http://gruntjs.com/creating-tasks

  grunt.registerMultiTask('ebDeploy', 'ElasticBeanstalk deployment', function() {

    // Merge task-specific and/or target-specific options with these defaults.
    var options = this.options({
      archive: '.tmp/dist.zip',
      region: 'us-east-1'
    });

    compress.options = {
      archive: options.archive,
      mode: 'zip',
      level: 1
    };

    var done = this.async();

    // First use grunt-contrib-compress to build an archive
    compress.tar(this.files, function() {

      if (options.profile) {
        AWS.config.credentials = new AWS.SharedIniFileCredentials({ profile: options.profile });
        console.log('Using credentials from profile \'' + options.profile + '\'');
      }

      var iam = new AWS.IAM();

      iam.getUser({}, function(err, data) {

        if (err) {

          console.log(err);
          done(false);

        } else {

          git.short(function(rev) {

            var date = new Date();
            var y = date.getFullYear();
            var m = date.getMonth() + 1;
            var d = date.getDate();

            var version = String(y) +
              String(m = (m < 10) ? ('0' + m) : m) +
              String(d = (d < 10) ? ('0' + d) : d) +
              '-' + rev +
              '-' + Math.floor((Math.random() * 899999) + 100000);

            var label = options.application + '-' + version;

            var account = data.User.Arn.split(/:/)[4];
            var bucket = 'elasticbeanstalk-' + options.region + '-' + account;

            var body = fs.createReadStream(options.archive);
            var s3obj = new AWS.S3({ params: { Bucket: bucket, Key: label + '.zip' } });

            console.log('Uploading application bundle');
            s3obj.upload({ Body: body }).send(function(err, data) {

              if (err) {
                console.log(err);
                done(false);
              } else {

                console.log('Creating application version \'' + label + '\'');
                var eb = new AWS.ElasticBeanstalk({ region: options.region });

                eb.createApplicationVersion({
                  ApplicationName: options.application,
                  VersionLabel: label,
                  SourceBundle: {
                    S3Bucket: bucket,
                    S3Key: label + '.zip'
                  }
                }, function (err, data) {

                  if (err) {
                    console.log(err);
                    done(false);
                  } else {

                    console.log('Updating environment \'' + options.environment + '\' to version \'' + label + '\'');
                    eb.updateEnvironment({
                      EnvironmentName: options.environment,
                      VersionLabel: label
                    }, function (err, data) {
                      if (err) {
                        console.log(err);
                        done(false);
                      } else {
                        console.log('Environment update running, please check AWS console for progress');
                        done();
                      }
                    });

                  }

                });

              }

            });

          });

        }

      });

    });

  });

};
<commit_msg>Use STS.getCallerIdentity insteadof IAM.getUser<commit_after>/*
 * grunt-eb-deploy
 * https://github.com/jjongsma/grunt-eb-deploy
 *
 * Copyright (c) 2015 Jeremy Jongsma
 * Licensed under the MIT license.
 */

'use strict';

var AWS = require('aws-sdk');
var git = require('git-rev');
var fs = require('fs');

module.exports = function(grunt) {

  var compress = require('grunt-contrib-compress/tasks/lib/compress')(grunt);

  // Please see the Grunt documentation for more information regarding task
  // creation: http://gruntjs.com/creating-tasks

  grunt.registerMultiTask('ebDeploy', 'ElasticBeanstalk deployment', function() {

    // Merge task-specific and/or target-specific options with these defaults.
    var options = this.options({
      archive: '.tmp/dist.zip',
      region: 'us-east-1'
    });

    compress.options = {
      archive: options.archive,
      mode: 'zip',
      level: 1
    };

    var done = this.async();

    // First use grunt-contrib-compress to build an archive
    compress.tar(this.files, function() {

      if (options.profile) {
        AWS.config.credentials = new AWS.SharedIniFileCredentials({ profile: options.profile });
        console.log('Using credentials from profile \'' + options.profile + '\'');
      }

      var sts = new AWS.STS();

      sts.getCallerIdentity({}, function(err, data) {

        if (err) {

          console.log(err);
          done(false);

        } else {

          git.short(function(rev) {

            var date = new Date();
            var y = date.getFullYear();
            var m = date.getMonth() + 1;
            var d = date.getDate();

            var version = String(y) +
              String(m = (m < 10) ? ('0' + m) : m) +
              String(d = (d < 10) ? ('0' + d) : d) +
              '-' + rev +
              '-' + Math.floor((Math.random() * 899999) + 100000);

            var label = options.application + '-' + version;

            var account = data.Account;
            var bucket = 'elasticbeanstalk-' + options.region + '-' + account;

            var body = fs.createReadStream(options.archive);
            var s3obj = new AWS.S3({ params: { Bucket: bucket, Key: label + '.zip' } });

            console.log('Uploading application bundle');
            s3obj.upload({ Body: body }).send(function(err, data) {

              if (err) {
                console.log(err);
                done(false);
              } else {

                console.log('Creating application version \'' + label + '\'');
                var eb = new AWS.ElasticBeanstalk({ region: options.region });

                eb.createApplicationVersion({
                  ApplicationName: options.application,
                  VersionLabel: label,
                  SourceBundle: {
                    S3Bucket: bucket,
                    S3Key: label + '.zip'
                  }
                }, function (err, data) {

                  if (err) {
                    console.log(err);
                    done(false);
                  } else {

                    console.log('Updating environment \'' + options.environment + '\' to version \'' + label + '\'');
                    eb.updateEnvironment({
                      EnvironmentName: options.environment,
                      VersionLabel: label
                    }, function (err, data) {
                      if (err) {
                        console.log(err);
                        done(false);
                      } else {
                        console.log('Environment update running, please check AWS console for progress');
                        done();
                      }
                    });

                  }

                });

              }

            });

          });

        }

      });

    });

  });

};
<|endoftext|>


<commit_before>var esprima = require('esprima');
var esmangle = require('esmangle');
var escodegen = require('escodegen');

module.exports = function(grunt) {

	var minMax = require('grunt-lib-contrib').init(grunt).minMaxInfo;

	grunt.registerMultiTask('esmangle', 'Mangle or minify JavaScript files with Esmangle.', function() {
		// Merge task-specific and/or target-specific options with these defaults:
		var options = this.options({
			'banner': '',
			// https://github.com/Constellation/escodegen/blob/master/escodegen.js:
			// → `getDefaultOptions()`
			// Some of these settings are documented here:
			// https://github.com/Constellation/escodegen/wiki/API
			'format': {
				'indent': {
					'style': '',
					'base': 0,
					'adjustMultilineComment': false
				},
				'json': false,
				'renumber': true,
				'hexadecimal': true,
				'quotes': 'single',
				'escapeless': false,
				'compact': true,
				'parentheses': false,
				'semicolons': true,
				'safeConcatenation': false
			},
			'parse': null,
			'comment': false,
			'sourceMap': undefined,
			'sourceMapRoot': undefined,
			'sourceMapWithCode': false
		});

		var banner = grunt.template.process(options.banner);

		// Iterate over all specified file groups.
		this.files.forEach(function(file) {
			// Concat specified files.
			var src = file.src.filter(function(filePath) {
				// Warn on and remove invalid source files.
				if (!grunt.file.exists(filePath)) {
					grunt.log.warn('Source file `' + filePath + '` not found.');
					return false;
				} else {
					return true;
				}
			}).map(function(filePath) {
				// Read file source.
				return grunt.file.read(filePath);
			}).join(';');

			var ast = esprima.parse(src, options.comment ? {
				'comment': true,
				'range': true,
				'tokens': true
			} : {});
			var optimizedAST = esmangle.optimize(ast, null, { 'destructive': true });
			var mangledAST = esmangle.mangle(optimizedAST, { 'destructive': true });
			if (options.comment) {
				mangledAST = escodegen.attachComments(
					mangledAST,
					mangledAST.comments,
					mangledAST.tokens
				);
			}
			var result = (banner || '') + escodegen.generate(mangledAST, options);

			minMax(result, src, 'gzip');

			// Write the destination file
			grunt.file.write(file.dest, result);

			// Print a success message
			grunt.log.writeln('File `' + file.dest + '` created.');
		});
	});

};
<commit_msg>Simplify task a bit<commit_after>var esprima = require('esprima');
var esmangle = require('esmangle');
var escodegen = require('escodegen');

module.exports = function(grunt) {

	var minMax = require('grunt-lib-contrib').init(grunt).minMaxInfo;

	grunt.registerMultiTask('esmangle', 'Mangle or minify JavaScript files with Esmangle.', function() {
		// Merge task-specific and/or target-specific options with these defaults:
		var options = this.options({
			'banner': '',
			// https://github.com/Constellation/escodegen/blob/master/escodegen.js:
			// → `getDefaultOptions()`
			// Some of these settings are documented here:
			// https://github.com/Constellation/escodegen/wiki/API
			'format': {
				'indent': {
					'style': '',
					'base': 0,
					'adjustMultilineComment': false
				},
				'json': false,
				'renumber': true,
				'hexadecimal': true,
				'quotes': 'single',
				'escapeless': false,
				'compact': true,
				'parentheses': false,
				'semicolons': true,
				'safeConcatenation': false
			},
			'parse': null,
			'comment': false,
			'sourceMap': undefined,
			'sourceMapRoot': undefined,
			'sourceMapWithCode': false
		});

		var banner = grunt.template.process(options.banner);

		// Iterate over all specified file groups.
		this.files.forEach(function(file) {
			// Concat specified files.
			var src = file.src.filter(function(filePath) {
				// Warn on and remove invalid source files.
				if (!grunt.file.exists(filePath)) {
					grunt.log.warn('Source file `' + filePath + '` not found.');
					return false;
				} else {
					return true;
				}
			}).map(function(filePath) {
				// Read file source.
				return grunt.file.read(filePath);
			}).join(';');

			var ast = esprima.parse(src, options.comment ? {
				'comment': true,
				'range': true,
				'tokens': true
			} : {});
			var optimizedAST = esmangle.optimize(ast, null, { 'destructive': true });
			var mangledAST = esmangle.mangle(optimizedAST, { 'destructive': true });
			if (options.comment) {
				mangledAST = escodegen.attachComments(
					mangledAST,
					mangledAST.comments,
					mangledAST.tokens
				);
			}
			var result = banner + escodegen.generate(mangledAST, options);

			minMax(result, src, 'gzip');

			// Write the destination file
			grunt.file.write(file.dest, result);

			// Print a success message
			grunt.log.writeln('File `' + file.dest + '` created.');
		});
	});

};
<|endoftext|>


<commit_before>import React, { useState } from "react";
import classes from "./Marketplace.module.css";
import Block from "../../components/UI/Block/Block";
import TrendingSearches from "../../components/TrendingSearches/TrendingSearches";
import RecentBuys from "../../components/RecentBuys/RecentBuysList";
import Button from "../../components/UI/Button/Button";
import ChartCard from "../../components/ChartCard/ChartCard";

const Marketplace = (props) => {
  const [searchEntry, setSearchEntry] = useState("");
  const [purchaseAmount, setPurchaseAmount] = useState(0);
  const [loadingData, setLoadingData] = useState(false);
  const [chartData, setChartData] = useState();

  const onSearchChange = (e) => {
    setSearchEntry(e.target.value);
  };

  const onSearchEntered = () => {
    if (searchEntry.trim() === "") {
      console.log("empty");
      return;
    }
    setLoadingData(true);
    console.log("setting loadingData TRUE");
    fetchContextData(searchEntry)
      .then((data) => {
        console.log("onSearchEntered: data: ");

        console.log(data);
        setChartData(data);
      })
      .then(() => {
        setLoadingData(false);
        console.log("setting loadingData FALSE");
      });
  };

  const fetchContextData = async (entry) => {
    const formattedData = await fetch(`./contextData?search_term=${entry}`)
      .then((response) => response.json())
      .then((data) => {
        const averagedData = data.map((datapoint) => datapoint / 24);
        return formatChartData(averagedData);
      })
      .catch((e) => {
        console.log(e);
        return null;
      });

    console.log("fetchContextData: formattedData: ");
    console.log(formattedData);
    return formattedData;
  };

  const formatChartData = (data) => {
    const chartData = [];
    chartData.push(["x", "Popularity"]);
    for (let i = 0; i < data.length; i++) {
      chartData.push([i, data[i]]);
    }
    return chartData;
  };

  let chartSpace = (

Search something!

  );

  if (loadingData === true) {
    chartSpace =
Loading...
;
  } else if (chartData === null) {
    chartSpace = (

        No data :(
 Try a more popular search term!

    );
  } else if (chartData !== undefined) {
    chartSpace = (
      <ChartCard
        chartInfo={{
          haxis: "Time",
          vaxis: "Net Worth",
          data: chartData,
        }}
      ></ChartCard>
    );
  }

  return (

          <input
            className={classes.SearchInput}
            placeholder="Search a trend"
            onChange={(e) => onSearchChange(e)}
          ></input>
          <Button inverted padding="3px 20px" onClick={onSearchEntered}>
            Search
          </Button>

        <Block className={classes.ChartContainer}>{chartSpace}</Block>

"search query"

            Amount to purchase:

              t$ <input></input>

            Currently available:
            t$1234

          <Button>Confirm Purchase</Button>

Trending searches

          <Block className={classes.Block}>
            <TrendingSearches
              searches={props.trendingSearches}
            ></TrendingSearches>
          </Block>

What your team is buying:

          <Block className={classes.Block}>
            <RecentBuys buys={props.recentBuys}></RecentBuys>
          </Block>

  );
};

export default Marketplace;
<commit_msg>CLean up code<commit_after>import React, { useState } from "react";
import classes from "./Marketplace.module.css";
import Block from "../../components/UI/Block/Block";
import TrendingSearches from "../../components/TrendingSearches/TrendingSearches";
import RecentBuys from "../../components/RecentBuys/RecentBuysList";
import Button from "../../components/UI/Button/Button";
import ChartCard from "../../components/ChartCard/ChartCard";

const Marketplace = (props) => {
  const [searchEntry, setSearchEntry] = useState("");
  const [purchaseAmount, setPurchaseAmount] = useState(0);
  const [loadingData, setLoadingData] = useState(false);
  const [chartData, setChartData] = useState();

  const onSearchChange = (e) => {
    setSearchEntry(e.target.value);
  };

  const onSearchEntered = () => {
    if (searchEntry.trim() === "") {
      return;
    }
    setLoadingData(true);
    fetchContextData(searchEntry)
      .then((data) => {
        setChartData(data);
      })
      .then(() => {
        setLoadingData(false);
      });
  };

  const fetchContextData = async (entry) => {
    const formattedData = await fetch(`./contextData?search_term=${entry}`)
      .then((response) => response.json())
      .then((data) => {
        const averagedData = data.map((datapoint) => datapoint / 24);
        return formatChartData(averagedData);
      })
      .catch((e) => {
        console.log(e);
        return null;
      });
    return formattedData;
  };

  const formatChartData = (data) => {
    const chartData = [];
    chartData.push(["x", "Popularity"]);
    for (let i = 0; i < data.length; i++) {
      chartData.push([i, data[i]]);
    }
    return chartData;
  };

  let chartSpace = (

Search something!

  );

  if (loadingData === true) {
    chartSpace =
Loading...
;
  } else if (chartData === null) {
    chartSpace = (

        No data :(
 Try a more popular search term!

    );
  } else if (chartData !== undefined) {
    chartSpace = (
      <ChartCard
        chartInfo={{
          haxis: "Time",
          vaxis: "Net Worth",
          data: chartData,
        }}
      ></ChartCard>
    );
  }

  return (

          <input
            className={classes.SearchInput}
            placeholder="Search a trend"
            onChange={(e) => onSearchChange(e)}
          ></input>
          <Button inverted padding="3px 20px" onClick={onSearchEntered}>
            Search
          </Button>

        <Block className={classes.ChartContainer}>{chartSpace}</Block>

"search query"

            Amount to purchase:

              t$ <input></input>

            Currently available:
            t$1234

          <Button>Confirm Purchase</Button>

Trending searches

          <Block className={classes.Block}>
            <TrendingSearches
              searches={props.trendingSearches}
            ></TrendingSearches>
          </Block>

What your team is buying:

          <Block className={classes.Block}>
            <RecentBuys buys={props.recentBuys}></RecentBuys>
          </Block>

  );
};

export default Marketplace;
<|endoftext|>


<commit_before>/* eslint-disable require-yield */

import { takeEvery } from 'redux-saga/effects';
import store from 'store';
import { isEmpty } from 'lodash';

const themeKey = 'fcc-theme';
const defaultTheme = 'default';
const nightTheme = 'night';

function setTheme(currentTheme = nightTheme, theme = nightTheme) {
  if (currentTheme !== theme) {
    store.set(themeKey, theme);
  }
  const html = document.getElementById('__fcc-html');
  html.classList.remove(theme === nightTheme ? defaultTheme : nightTheme);
  html.classList.add(theme);
}

function* updateLocalThemeSaga({ payload: { user, theme } }) {
  const currentTheme = store.get(themeKey) || nightTheme;
  if (!isEmpty(user)) {
    const { theme = nightTheme } = user;
    return setTheme(currentTheme, theme);
  }
  if (theme) {
    return setTheme(currentTheme, theme);
  }
  return setTheme(currentTheme);
}

export function createNightModeSaga(types) {
  return [
    takeEvery(types.fetchUserComplete, updateLocalThemeSaga),
    takeEvery(types.updateUserFlagComplete, updateLocalThemeSaga)
  ];
}
<commit_msg>revert: "fix: make night mode default for code blocks"<commit_after>/* eslint-disable require-yield */

import { takeEvery } from 'redux-saga/effects';
import store from 'store';

const themeKey = 'fcc-theme';
const defaultTheme = 'default';
const nightTheme = 'night';

function setTheme(currentTheme = defaultTheme, theme) {
  if (currentTheme !== theme) {
    store.set(themeKey, theme);
  }
  const html = document.getElementById('__fcc-html');
  html.classList.remove(theme === nightTheme ? defaultTheme : nightTheme);
  html.classList.add(theme);
}

function* updateLocalThemeSaga({ payload: { user, theme } }) {
  const currentTheme = store.get(themeKey) || defaultTheme;
  if (user) {
    const { theme = defaultTheme } = user;
    return setTheme(currentTheme, theme);
  }
  if (theme) {
    return setTheme(currentTheme, theme);
  }
  return setTheme(currentTheme);
}

export function createNightModeSaga(types) {
  return [
    takeEvery(types.fetchUserComplete, updateLocalThemeSaga),
    takeEvery(types.updateUserFlagComplete, updateLocalThemeSaga)
  ];
}
<|endoftext|>


<commit_before>/*
 * grunt-contrib-imagemin
 * http://gruntjs.com/
 *
 * Copyright (c) 2012 Sindre Sorhus, contributors
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function (grunt) {
    var path = require('path');
    var fs = require('fs');
    var childProcess = require('child_process');
    var filesize = require('filesize');
    var optipngPath = require('optipng-bin').path;
    var jpegtranPath = require('jpegtran-bin').path;

    grunt.registerMultiTask('imagemin', 'Minify PNG and JPEG images', function () {
        var options = this.options();
        var optipngArgs = ['-strip', 'all', '-force'];
        var jpegtranArgs = ['-copy', 'none', '-optimize'];

        if (typeof options.optimizationLevel === 'number') {
            optipngArgs.push('-o', options.optimizationLevel);
        }

        if (options.progressive === true) {
            jpegtranArgs.push('-progressive');
        }

        grunt.verbose.writeflags(options, 'Options');

        grunt.util.async.forEach(this.files, function (files, next) {
            files.src.forEach(function (src) {
                var dest = files.dest;
                if (path.extname(dest) === '') {
                    dest = path.join(dest, path.basename(src));
                }
                optimize(src, dest, next);
            });
        }.bind(this), this.async());

        function optimize(src, dest, next) {
            var cp;
            var originalSize = fs.statSync(src).size;

            function processed(err, result, code) {
                var saved, savedMsg;

                if (err) {
                    grunt.log.writeln(err);
                }

                saved = originalSize - fs.statSync(dest).size;

                if (result.stderr.indexOf('already optimized') !== -1 || saved < 10) {
                    savedMsg = 'already optimized';
                } else {
                    savedMsg = 'saved ' + filesize(saved);
                }

                grunt.log.writeln('✔ '.green + src + (' (' + savedMsg + ')').grey);
                next();
            }

            grunt.file.mkdir(path.dirname(dest));

            if (path.extname(src) === '.png') {
                cp = grunt.util.spawn({
                    cmd: optipngPath,
                    args: optipngArgs.concat(['-out', dest, src])
                }, processed);
            } else if (['.jpg', '.jpeg'].indexOf(path.extname(src)) !== -1) {
                cp = grunt.util.spawn({
                    cmd: jpegtranPath,
                    args: jpegtranArgs.concat(['-outfile', dest, src])
                }, processed);
            } else {
                next();
            }

            if (grunt.option('verbose')) {
                cp.stdout.pipe(process.stdout);
                cp.stderr.pipe(process.stderr);
            }
        }
    });
};
<commit_msg>Fix OptiPNG not being able to overwrite existing file<commit_after>/*
 * grunt-contrib-imagemin
 * http://gruntjs.com/
 *
 * Copyright (c) 2012 Sindre Sorhus, contributors
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function (grunt) {
    var path = require('path');
    var fs = require('fs');
    var childProcess = require('child_process');
    var filesize = require('filesize');
    var optipngPath = require('optipng-bin').path;
    var jpegtranPath = require('jpegtran-bin').path;

    grunt.registerMultiTask('imagemin', 'Minify PNG and JPEG images', function () {
        var options = this.options();
        var optipngArgs = ['-strip', 'all', '-force'];
        var jpegtranArgs = ['-copy', 'none', '-optimize'];

        if (typeof options.optimizationLevel === 'number') {
            optipngArgs.push('-o', options.optimizationLevel);
        }

        if (options.progressive === true) {
            jpegtranArgs.push('-progressive');
        }

        grunt.verbose.writeflags(options, 'Options');

        grunt.util.async.forEach(this.files, function (files, next) {
            files.src.forEach(function (src) {
                var dest = files.dest;
                if (path.extname(dest) === '') {
                    dest = path.join(dest, path.basename(src));
                }
                optimize(src, dest, next);
            });
        }.bind(this), this.async());

        function optimize(src, dest, next) {
            var cp;
            var originalSize = fs.statSync(src).size;

            function processed(err, result, code) {
                var saved, savedMsg;

                if (err) {
                    grunt.log.writeln(err);
                }

                saved = originalSize - fs.statSync(dest).size;

                if (result.stderr.indexOf('already optimized') !== -1 || saved < 10) {
                    savedMsg = 'already optimized';
                } else {
                    savedMsg = 'saved ' + filesize(saved);
                }

                grunt.log.writeln('✔ '.green + src + (' (' + savedMsg + ')').grey);
                next();
            }

            grunt.file.mkdir(path.dirname(dest));

            if (path.extname(src) === '.png') {
                // OptiPNG can't overwrite without creating a backup file
                // https://sourceforge.net/tracker/?func=detail&aid=3607244&group_id=151404&atid=780913
                if (grunt.file.exists(dest)) {
                    grunt.file.delete(dest);
                }

                cp = grunt.util.spawn({
                    cmd: optipngPath,
                    args: optipngArgs.concat(['-out', dest, src])
                }, processed);
            } else if (['.jpg', '.jpeg'].indexOf(path.extname(src)) !== -1) {
                cp = grunt.util.spawn({
                    cmd: jpegtranPath,
                    args: jpegtranArgs.concat(['-outfile', dest, src])
                }, processed);
            } else {
                next();
            }

            if (grunt.option('verbose')) {
                cp.stdout.pipe(process.stdout);
                cp.stderr.pipe(process.stderr);
            }
        }
    });
};
<|endoftext|>