diff options
author | Dan Willemsen <dwillemsen@google.com> | 2023-01-04 12:40:16 -0800 |
---|---|---|
committer | Dan Willemsen <dwillemsen@google.com> | 2023-01-04 12:44:38 -0800 |
commit | 865438d45d5e2b37f89522fe6a17b91ac182d88e (patch) | |
tree | 4fc217bb24aa0780c67f70b2bf2c814009badf64 | |
parent | f94c87ca2242c1c1b875be8f9625ad3c0510613d (diff) | |
download | test_serving-865438d45d5e2b37f89522fe6a17b91ac182d88e.tar.gz |
This project was removed from the manifest in Dec'21 with aosp/1920522
This change will ensure that automated code scanners aren't reporting
issues on it.
Bug: 253296312
Change-Id: If2722726f5c90fb84602f2e89ceb171ed1e4e255
187 files changed, 0 insertions, 25817 deletions
diff --git a/METADATA b/METADATA deleted file mode 100644 index d97975c..0000000 --- a/METADATA +++ /dev/null @@ -1,3 +0,0 @@ -third_party { - license_type: NOTICE -} @@ -1,5 +0,0 @@ -yim@google.com -dchon@google.com -hyunwooko@google.com -jongmok@google.com -younggyu@google.com diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg deleted file mode 100644 index 7049c8c..0000000 --- a/PREUPLOAD.cfg +++ /dev/null @@ -1,9 +0,0 @@ -[Builtin Hooks] -clang_format = true - -[Builtin Hooks Options] -clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp,java - -[Hook Scripts] -test_serving_unittests = ${REPO_ROOT}/test/framework/script/run-unittest.sh - diff --git a/__init__.py b/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/__init__.py +++ /dev/null diff --git a/configs/global/Android.mk b/configs/global/Android.mk deleted file mode 100644 index aba3244..0000000 --- a/configs/global/Android.mk +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (C) 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH := $(call my-dir) - -vti-global-config-prod-zip := -vti-global-config-test-zip := - -dirs := $(strip \ - $(wildcard test/vti/test_serving/configs/global/prod) \ - $(wildcard vendor/google_vts/configs/global/prod)) -ifdef dirs -vti-global-config-prod-zip := $(HOST_OUT)/vti-global-config/vti-global-config-prod.zip -$(vti-global-config-prod-zip): $(SOONG_ZIP) $(call find-files-in-subdirs,.,"*.*_config", $(dirs)) - @echo "build vti config package: $@" - $(hide) mkdir -p $(dir $@) - $(hide) rm -f $@ - $(hide) find test/vti/test_serving/configs/global/prod/ -name '*.*_config' | sort > $@.list - $(hide) find vendor/google_vts/configs/global/prod/ -name '*.*_config' | sort > $@.list.vendor - $(hide) $(SOONG_ZIP) -d -o $@ -C test/vti/test_serving/configs/global -l $@.list \ - -C vendor/google_vts/configs/global -l $@.list.vendor - $(hide) rm -f $@.list $@.list.vendor -endif - -dirs := $(strip \ - $(wildcard test/vti/test_serving/configs/global/test) \ - $(wildcard vendor/google_vts/configs/global/test)) -ifdef dirs -vti-global-config-test-zip := $(HOST_OUT)/vti-global-config/vti-global-config-test.zip -$(vti-global-config-test-zip): $(SOONG_ZIP) $(call find-files-in-subdirs,.,"*.*_config", $(dirs)) - @echo "build vti config package: $@" - $(hide) mkdir -p $(dir $@) - $(hide) rm -f $@ - $(hide) find test/vti/test_serving/configs/global/test/ -name '*.*_config' | sort > $@.list - $(hide) find vendor/google_vts/configs/global/test/ -name '*.*_config' | sort > $@.list.vendor - $(hide) $(SOONG_ZIP) -d -o $@ -C test/vti/test_serving/configs/global -l $@.list \ - -C vendor/google_vts/configs/global -l $@.list.vendor - $(hide) rm -f $@.list $@.list.vendor -endif - -.PHONY: vti-global-config -vti-global-config: $(vti-global-config-prod-zip) $(vti-global-config-test-zip) -$(call dist-for-goals, vti-global-config, $(vti-global-config-prod-zip) $(vti-global-config-test-zip)) - -.PHONY: vti-config -vti-config: vti-global-config - -.PHONY: vti -vti: vti-config - -.PHONY: vts -vts: vti-config diff --git a/configs/infra/prod/androidtestcenter/bots.cfg b/configs/infra/prod/androidtestcenter/bots.cfg deleted file mode 100644 index d48675f..0000000 --- a/configs/infra/prod/androidtestcenter/bots.cfg +++ /dev/null @@ -1,17 +0,0 @@ -# Instance: androidtestcenter (swarming-prod) -# -# Schema: BotsCfg message in -# https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/swarming/proto/bots.proto - -trusted_dimensions: "pool" -bot_group { - bot_id_prefix: "homedemo" - owners: "yim@google.com" - auth { - require_luci_machine_token: false - ip_whitelist: "vtslab-swarming-bots" - } - dimensions: "pool:home-demo" - bot_config_script: "home_bot_config.py" -} - diff --git a/configs/infra/prod/androidtestcenter/scripts/home_bot_config.py b/configs/infra/prod/androidtestcenter/scripts/home_bot_config.py deleted file mode 100644 index fe078fa..0000000 --- a/configs/infra/prod/androidtestcenter/scripts/home_bot_config.py +++ /dev/null @@ -1,340 +0,0 @@ -# coding: utf-8 -# Copyright 2013 The LUCI Authors. All rights reserved. -# Use of this source code is governed under the Apache License, Version 2.0 -# that can be found in the LICENSE file. -"""bot_config for swarming bots running in Home CI. - -A copy of this file is downloaded to every swarming bot automatically. -Updates to this file are propogated to bots within ~10 minutes. - -Prerequisites: - - bot has eurtest_libs installed via: cipd install home_internal/eurtest_libs - - eurtest_libs directory is in PYTHONPATH - -There's 3 types of functions in this file: - - get_*() to return properties to describe this bot. - - on_*() as hooks based on events happening on the bot. - - setup_*() to setup global state on the host. -""" - -import collections -import json -import logging -import os -import re -import sys -import time - -from api import os_utilities -from api import platforms - -# Hack to de-prioritize sys.path variables that bot_code imports which are -# incompatible with the libraries we're importing below. -# see https://crbug.com/758609 -sys.path.sort(key=lambda x: 'swarm_env' not in x) -# each bot must be run in a virtualenv that has eurtest_libs in PYTHONPATH -try: - from eurtest.device import device as device_lib - from utilities import ssh_helper -except ImportError as e: - sys.stderr.write('Exception: %s\n' % e) - sys.stderr.write('sys.path:\n%s' % str(sys.path)) - # Temporary while debugging issue - import google - print 'google.__path__: %s' % str(google.__path__) - raise - -# Unused argument 'bot' - pylint: disable=W0613 -DeviceBot = collections.namedtuple('DeviceBot', 'dimensions state') - - -def device(device_type, ip, groups=None, nic=None, serial=None, extra_state=None): - if not hasattr(ip, '__iter__'): - ip = [ip] - dimensions = { - u'device_ip': ip, - u'device_type': [device_type], - u'group': groups or ['COMMON'], - u'nic': [nic or 'ethernet'], - } - # You can't advertise a key that cannot be selected, - # so only set a dimension key if the value is not None - if serial: - dimensions[u'serial'] = [serial] - if len(ip) > 1: - dimensions[u'group'].append('multiconfig_box') - return DeviceBot(dimensions, extra_state) - -_BOT_DEVICE_MAP = { - ### home-demo pool ### - enforced via bot name prefix - 'homedemo--1': device('assistantdefault', '1.2.3.4', ['abc_test']), -} - -# global for caching device properties gathered dynamically during runtime -_DEVICE = None - -### Helper Methods - - -class SSHError(Exception): - pass - - -class Device(object): - """Retrieves device property information for a device over SSH.""" - # amount of time (secs) until state is due for a refresh - _STALE_STATE_DELTA = 60 - - def __init__(self, device_ip, bot_id): - self.device_ip = device_ip - self.bot_id = bot_id - self._device_client = device_lib.Device( - ethernet_ip=self.device_ip) - self._device_client.UseSsh() - self._last_connect_succeeded = False - self._properties = {} - self._stale_state_ts = 0 - - @property - def build_fingerprint(self): - return self.properties.get('ro.build.fingerprint', None) - - @property - def hardware(self): - return self.properties.get('ro.hardware', None) - - @property - def is_reachable(self): - """Checks if device is reachable over ssh.""" - if self._properties is None or time.time() > self._stale_state_ts: - self._properties = self._fetch_properties() - return self._last_connect_succeeded - - @property - def product(self): - return self.properties.get('ro.build.product', None) - - @property - def properties(self): - """Returns all of the last-known device properties as a dict.""" - if self._properties is None or time.time() > self._stale_state_ts: - try: - # Keep last-known properties when SSHError is raised - self._properties = self._fetch_properties() - except SSHError as e: - logging.exception(e) - return self._properties - - def _exec_ssh_cmd(self, cmd): - """Executes the given cmd against the device over SSH. - Arguments: - - cmd: the command to be run on the device over ssh. - Returns: - stdout of cmd output - Raises: - SSHError: upon ssh cmd exec failure - """ - # Temporary: fetch device properties from static local file when present. - # This let's us demo what our swarm bots will look like, even though the - # demo host ('voyager') is on a lab network in WAT that can't reach the - # MTV devices over ssh. The real device properties are fetched over ssh - # by a script on Corp and copied to the swarm host before starting bots. - # TODO(jonesmi): remove this temporary workaround when bots run in MTV lab - bot_common_dir = os.getenv('BOT_COMMON_DIR') - if bot_common_dir: - props_file = os.path.join(bot_common_dir, '%s.props' % self.bot_id) - if os.path.isfile(props_file): - with open(props_file, 'r') as f: - # pretend that we read this from SSH connection - self._last_connect_succeeded = True - return f.read() - try: - if self._device_client.Cmd(cmd, timeout_secs=30): - self._last_connect_succeeded = True - return self._device_client.GetCmdOutput() - else: - self._last_connect_succeeded = False - raise SSHError('failed to execute cmd over ssh: %s' % cmd) - except ssh_helper.CommandThreadTimeoutError: - self._last_connect_succeeded = False - raise SSHError('timeout when waiting for ssh connect: %s' % cmd) - - def _fetch_properties(self): - """Fetches device properties over ssh using getprop command.""" - self._stale_state_ts = time.time() + self._STALE_STATE_DELTA - properties = {} - stdout = self._exec_ssh_cmd('getprop') - for line in stdout.strip().split('\n'): - match = re.match(r'\[(.*)\]: \[(.*)\]', line) - if match: - properties[match.group(1)] = match.group(2) - return properties - - -### _get* Hooks -def get_dimensions(bot): - # pylint: disable=line-too-long - """Returns dict with the bot's dimensions. - The dimensions are what are used to select the bot that can run each task. - The bot id will be automatically selected based on the hostname with - os_utilities.get_dimensions(). If you want something more special, specify it - in your bot_config.py and override the item 'id'. - The dimensions returned here will be joined with server defined dimensions - (extracted from bots.cfg config file based on the bot id). Server defined - dimensions override the ones provided by the bot. See bot.Bot.dimensions for - more information. - See https://github.com/luci/luci-py/tree/master/appengine/swarming/doc/Magic-Values.md. - Arguments: - - bot: bot.Bot instance or None. See ../api/bot.py. - """ - dimensions = os_utilities.get_dimensions() - # The bot base directory is formatted like <HOME>/bots/<Id> - id = '%s--%s' % ( - os_utilities.get_hostname_short(), - os.path.basename(bot.base_dir)) - dimensions[u'id'] = [id] - if id in _BOT_DEVICE_MAP: - dimensions.update(_BOT_DEVICE_MAP[id].dimensions) - global _DEVICE - if not _DEVICE: - device_ip = dimensions[u'device_ip'] - # use first device_ip in list of device IPs for pulling device attributes - if device_ip: - assert isinstance(device_ip, (list, tuple)), repr(device_ip) - _DEVICE = Device(device_ip[0], id) - if _DEVICE: - dimensions[u'build'] = _DEVICE.build_fingerprint, - dimensions[u'hardware'] = _DEVICE.hardware, - dimensions[u'product'] = _DEVICE.product, - # TODO(jonesmi): don't strip these anymore after swarming fix goes in for limit on # dimensions - del dimensions[u'cores'] - del dimensions[u'cpu'] - del dimensions[u'gpu'] - del dimensions[u'machine_type'] - return dimensions - - -def get_state(bot): - # pylint: disable=line-too-long - """Returns dict with a state of the bot reported to the server with each poll. - It is only for dynamic state that changes while bot is running for information - for the sysadmins. - The server can not use this state for immediate scheduling purposes (use - 'dimensions' for that), but it can use it for maintenance and bookkeeping - tasks. - See https://github.com/luci/luci-py/tree/master/appengine/swarming/doc/Magic-Values.md. - Arguments: - - bot: bot.Bot instance or None. See ../api/bot.py. - """ - state = os_utilities.get_state() - if _DEVICE: - state[u'device'] = _DEVICE.properties - if _DEVICE.bot_id in _BOT_DEVICE_MAP: - extra_state = _BOT_DEVICE_MAP[_DEVICE.bot_id].state - if extra_state: - state[u'device'].update(extra_state) - if not _DEVICE.is_reachable: - state[u'quarantined'] = 'device is not reachable' - return state - - -def get_authentication_headers(bot): - """Returns authentication headers and their expiration time. - The returned headers will be passed with each HTTP request to the Swarming - server (and only Swarming server). The bot will use the returned headers until - they are close to expiration (usually 6 min, see AUTH_HEADERS_EXPIRATION_SEC - in remote_client.py), and then it'll attempt to refresh them by calling - get_authentication_headers again. - Can be used to implement per-bot authentication. If no headers are returned, - the server will use only IP whitelist for bot authentication. - On GCE will use OAuth token of the default GCE service account. It should have - "User info" API scope enabled (this can be set when starting an instance). The - server should be configured (via bots.cfg) to trust this account (see - 'require_service_account' in bots.proto). - May be called by different threads, but never concurrently. - Arguments: - - bot: bot.Bot instance. See ../api/bot.py. - Returns: - Tuple (dict with headers or None, unix timestamp of when they expire). - """ - if platforms.is_gce(): - tok = platforms.gce.oauth2_access_token() - return {'Authorization': 'Bearer %s' % tok}, time.time() + 5*60 - return (None, None) - - -### on_* Hooks -def on_bot_shutdown(bot): - """Hook function called when the bot shuts down, usually rebooting. - It's a good time to do other kinds of cleanup. - Arguments: - - bot: bot.Bot instance. See ../api/bot.py. - """ - pass - - -def on_handshake(bot): - """Hook function called when the bot starts, after handshake with the server. - Here the bot already knows server enforced dimensions (defined in server side - bots.cfg file). - This is called right before starting to poll for tasks. It's a good time to - do some final initialization or cleanup that may depend on server provided - configuration. - Arguments: - - bot: bot.Bot instance. See ../api/bot.py. - """ - pass - - -def on_before_task(bot, bot_file): - """Hook function called before running a task. - It shouldn't do much, since it can't cancel the task so it shouldn't do - anything too fancy. - Arguments: - - bot: bot.Bot instance. See ../api/bot.py. - - bot_file: Path to file to write information about the state of the bot. - This file can be used to pass certain info about the bot - to tasks, such as which connected android devices to run on. See - https://github.com/luci/luci-py/tree/master/appengine/swarming/doc/Magic-Values.md#run_isolated - """ - with open(bot_file, 'wb') as f: - json.dump(bot.dimensions, f) - - -def on_after_task(bot, failure, internal_failure, dimensions, summary): - """Hook function called after running a task. - It is an excellent place to do post-task cleanup of temporary files. - The default implementation restarts after a task failure or an internal - failure. - Arguments: - - bot: bot.Bot instance. See ../api/bot.py. - - failure: bool, True if the task failed. - - internal_failure: bool, True if an internal failure happened. - - dimensions: dict, Dimensions requested as part of the task. - - summary: dict, Summary of the task execution. - """ - # Example code: - #if failure: - # bot.restart('Task failure') - #elif internal_failure: - # bot.restart('Internal failure') - - -def on_bot_idle(bot, since_last_action): - """Hook function called once when the bot has been idle; when it has no - command to execute. - This is an excellent place to put device in 'cool down' mode or any - "pre-warming" kind of stuff that could take several seconds to do, that would - not be appropriate to do in on_after_task(). It could be worth waiting for - `since_last_action` to be several seconds before doing a more lengthy - operation. - This function is called repeatedly until an action is taken (a task, updating, - etc). - This is a good place to do "auto reboot" for hardware based bots that are - rebooted periodically. - Arguments: - - bot: bot.Bot instance. See ../api/bot.py. - - since_last_action: time in second since last action; e.g. amount of time the - bot has been idle. - """ - pass diff --git a/configs/infra/prod/androidtestcenter/settings.cfg b/configs/infra/prod/androidtestcenter/settings.cfg deleted file mode 100644 index 1f6777b..0000000 --- a/configs/infra/prod/androidtestcenter/settings.cfg +++ /dev/null @@ -1,27 +0,0 @@ -# Instance: androidtestcenter (swarming-prod) -# Browse online: https://androidtestcenter.appspot.com/restricted/config -# -# Schema: SettingsCfg message in -# https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/swarming/proto/config.proto - -google_analytics: "UA-107546859-1" -reusable_task_age_secs: 604800 -bot_death_timeout_secs: 600 -enable_ts_monitoring: true -ui_client_id: "849130811101-pk615h6979i256cb243t2cacjohamkq7.apps.googleusercontent.com" -display_server_url_template: "https://androidtestcenter.appspot.com/swarming/task/%s" -isolate { - default_server: "https://vtslab-isolate-prod.appspot.com" - default_namespace: "default-gzip" -} -mp { - enabled: true -} -auth { - admins_group: "vtslab-swarming-admins" - bot_bootstrap_group: "vtslab-swarming-bot-bootstrap" - privileged_users_group: "vtslab-swarming-privileged-users" - users_group: "vtslab-swarming-users" - view_all_bots_group: "vtslab-swarming-view-all-bots" - view_all_tasks_group: "vtslab-swarming-view-all-tasks" -} diff --git a/configs/infra/prod/vtslab-config-prod/acl.cfg b/configs/infra/prod/vtslab-config-prod/acl.cfg deleted file mode 100644 index 0da0ca1..0000000 --- a/configs/infra/prod/vtslab-config-prod/acl.cfg +++ /dev/null @@ -1,9 +0,0 @@ -# Schema for this file: -# https://vtslab-config-prod.appspot.com/schemas/services/vtslab-config-prod:acl.cfg -# *** WARNING *** -# This file is sensitive. project_access_group specifies a super group -# that has access to all projects, including internal ones. -# This file is cached in process memory of instances for 10min -# because it ~never changes. -admin_group: "vtslab-config-admins" -project_access_group: "vtslab-config-project-access" diff --git a/configs/infra/prod/vtslab-config-prod/import.cfg b/configs/infra/prod/vtslab-config-prod/import.cfg deleted file mode 100644 index d956889..0000000 --- a/configs/infra/prod/vtslab-config-prod/import.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Schema for this file: -# https://vtslab-config-prod.appspot.com/schemas/services/vtslab-config-prod:import.cfg -gitiles { - project_config_default_ref: "refs/heads/infra/config" - ref_config_default_path: "infra/config" -} diff --git a/configs/infra/prod/vtslab-config-prod/projects.cfg b/configs/infra/prod/vtslab-config-prod/projects.cfg deleted file mode 100644 index 3f00a7d..0000000 --- a/configs/infra/prod/vtslab-config-prod/projects.cfg +++ /dev/null @@ -1,19 +0,0 @@ -# Schema for this file: -# https://vtslab-config-prod.appspot.com/schemas/services/vtslab-config-prod:projects.cfg -# -# Please keep the list sorted by project id! -projects { - id: "aosp-master-vts" - config_location { - url: "https://android.googlesource.com/platform/test/vts/" - storage_type: GITILES - } -} - -projects { - id: "aosp-master-goldfish" - config_location { - url: "https://android.googlesource.com/device/generic/goldfish/" - storage_type: GITILES - } -} diff --git a/configs/infra/prod/vtslab-config-prod/schemas.cfg b/configs/infra/prod/vtslab-config-prod/schemas.cfg deleted file mode 100644 index 4ba2ae9..0000000 --- a/configs/infra/prod/vtslab-config-prod/schemas.cfg +++ /dev/null @@ -1,79 +0,0 @@ -# Schema for this file: -# https://vtslab-config-prod.appspot.com/schemas/services/vtslab-config-prod:schemas.cfg -# -# Please keep this list sorted by schema name. -# -################################################################################ -# WARNING: names and urls in this file will be exposed publicly. Do not put -# internal stuff here!!! -################################################################################ -################################################################################ -# ref configs -schemas { - name: "projects/refs:cq.cfg" - url: "https://chromium.googlesource.com/chromium/tools/depot_tools/+/master/third_party/cq_client/cq.proto" -} -################################################################################ -# project configs -schemas { - name: "projects:buildbucket.cfg" - url: "https://chromium.googlesource.com/infra/infra/+/master/appengine/cr-buildbucket/proto/project_config.proto" -} -schemas { - name: "projects:refs.cfg" - url: "https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/components/components/config/proto/project_config.proto" -} -schemas { - name: "projects:project.cfg" - url: "https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/components/components/config/proto/project_config.proto" -} -################################################################################ -# luci-config service schemas -schemas { - name: "services/luci-config:acl.cfg" - url: "https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/components/components/config/proto/service_config.proto" -} -schemas { - name: "services/luci-config:import.cfg" - url: "https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/components/components/config/proto/service_config.proto" -} -schemas { - name: "services/luci-config:projects.cfg" - url: "https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/components/components/config/proto/service_config.proto" -} -schemas { - name: "services/luci-config:services.cfg" - url: "https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/components/components/config/proto/service_config.proto" -} -schemas { - name: "services/luci-config:schemas.cfg" - url: "https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/components/components/config/proto/service_config.proto" -} -################################################################################ -# chrome-infra-auth service schemas. -schemas { - name: "services/chrome-infra-auth:delegation.cfg" - url: "https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/auth_service/proto/config.proto" -} -schemas { - name: "services/chrome-infra-auth:imports.cfg" - url: "https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/auth_service/proto/config.proto" -} -schemas { - name: "services/chrome-infra-auth:ip_whitelist.cfg" - url: "https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/auth_service/proto/config.proto" -} -schemas { - name: "services/chrome-infra-auth:oauth.cfg" - url: "https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/auth_service/proto/config.proto" -} -################################################################################ -# LUCI LogDog service schemas. -schemas { - name: "services/luci-logdog:services.cfg" - url: "https://chromium.googlesource.com/infra/luci/luci-go/+/master/logdog/api/config/svcconfig/config.proto" -} -schemas { - name: "services/luci-logdog:logdog.cfg" - url: "https://chromium.googlesource.com/infra/luci/luci-go/+/master/logdog/api/config/svcconfig/project.proto" -} diff --git a/configs/infra/prod/vtslab-config-prod/services.cfg b/configs/infra/prod/vtslab-config-prod/services.cfg deleted file mode 100644 index a9b22d6..0000000 --- a/configs/infra/prod/vtslab-config-prod/services.cfg +++ /dev/null @@ -1,60 +0,0 @@ -# Schema for this file: ServicesCfg message in -# https://vtslab-config-prod.appspot.com/schemas/services/vtslab-config-prod:services.cfg -# -# Please keep the list sorted by id. -# -# *** WARNING -# This file is sensitive. These services may receive contents of any config -# files for validation purposes, including configs of internal projects. -# *** -################################################################################ -# Swarming -services { - id: "androidtestcenter" - owners: "yim@google.com" - metadata_url: "https://androidtestcenter.appspot.com/_ah/api/config/v1/metadata" - access: "androidtestcenter@appspot.gserviceaccount.com" -} -################################################################################ -# Auth -services { - id: "vtslab-auth-prod" - owners: "yim@google.com" - metadata_url: "https://vtslab-auth-prod.appspot.com/_ah/api/config/v1/metadata" - access: "vtslab-auth-prod@appspot.gserviceaccount.com" -} -################################################################################ -# Commit Queue -# services { -# id: "commit-queue" -# owners: "yim@google.com" -# metadata_url: "https://vtslab-cq-prod.appspot.com/api/metadata" -# } -################################################################################ -# GCE Backend -services { - id: "vtslab-backend-prod" - owners: "yim@google.com" - access: "vtslab-backend-prod@appspot.gserviceaccount.com" -} -################################################################################ -# LUCI / Isolate Server -services { - id: "vtslab-isolate-prod" - owners: "yim@google.com" - metadata_url: "https://vtslab-isolate-prod.appspot.com/_ah/api/config/v1/metadata" - access: "vtslab-isolate-prod@appspot.gserviceaccount.com" -} -################################################################################ -# Machine Provider -services { - id: "vtslab-machine-prod" - owners: "yim@google.com" - access: "vtslab-machine-prod@appspot.gserviceaccount.com" -} -################################################################################ -# Doc Indexer -services { - id: "vtslab-doc" - owners: "yim@google.com" -} diff --git a/configs/infra/test/androidtestcenter-test/bots.cfg b/configs/infra/test/androidtestcenter-test/bots.cfg deleted file mode 100644 index d48675f..0000000 --- a/configs/infra/test/androidtestcenter-test/bots.cfg +++ /dev/null @@ -1,17 +0,0 @@ -# Instance: androidtestcenter (swarming-prod) -# -# Schema: BotsCfg message in -# https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/swarming/proto/bots.proto - -trusted_dimensions: "pool" -bot_group { - bot_id_prefix: "homedemo" - owners: "yim@google.com" - auth { - require_luci_machine_token: false - ip_whitelist: "vtslab-swarming-bots" - } - dimensions: "pool:home-demo" - bot_config_script: "home_bot_config.py" -} - diff --git a/configs/infra/test/androidtestcenter-test/scripts/home_bot_config.py b/configs/infra/test/androidtestcenter-test/scripts/home_bot_config.py deleted file mode 100644 index fe078fa..0000000 --- a/configs/infra/test/androidtestcenter-test/scripts/home_bot_config.py +++ /dev/null @@ -1,340 +0,0 @@ -# coding: utf-8 -# Copyright 2013 The LUCI Authors. All rights reserved. -# Use of this source code is governed under the Apache License, Version 2.0 -# that can be found in the LICENSE file. -"""bot_config for swarming bots running in Home CI. - -A copy of this file is downloaded to every swarming bot automatically. -Updates to this file are propogated to bots within ~10 minutes. - -Prerequisites: - - bot has eurtest_libs installed via: cipd install home_internal/eurtest_libs - - eurtest_libs directory is in PYTHONPATH - -There's 3 types of functions in this file: - - get_*() to return properties to describe this bot. - - on_*() as hooks based on events happening on the bot. - - setup_*() to setup global state on the host. -""" - -import collections -import json -import logging -import os -import re -import sys -import time - -from api import os_utilities -from api import platforms - -# Hack to de-prioritize sys.path variables that bot_code imports which are -# incompatible with the libraries we're importing below. -# see https://crbug.com/758609 -sys.path.sort(key=lambda x: 'swarm_env' not in x) -# each bot must be run in a virtualenv that has eurtest_libs in PYTHONPATH -try: - from eurtest.device import device as device_lib - from utilities import ssh_helper -except ImportError as e: - sys.stderr.write('Exception: %s\n' % e) - sys.stderr.write('sys.path:\n%s' % str(sys.path)) - # Temporary while debugging issue - import google - print 'google.__path__: %s' % str(google.__path__) - raise - -# Unused argument 'bot' - pylint: disable=W0613 -DeviceBot = collections.namedtuple('DeviceBot', 'dimensions state') - - -def device(device_type, ip, groups=None, nic=None, serial=None, extra_state=None): - if not hasattr(ip, '__iter__'): - ip = [ip] - dimensions = { - u'device_ip': ip, - u'device_type': [device_type], - u'group': groups or ['COMMON'], - u'nic': [nic or 'ethernet'], - } - # You can't advertise a key that cannot be selected, - # so only set a dimension key if the value is not None - if serial: - dimensions[u'serial'] = [serial] - if len(ip) > 1: - dimensions[u'group'].append('multiconfig_box') - return DeviceBot(dimensions, extra_state) - -_BOT_DEVICE_MAP = { - ### home-demo pool ### - enforced via bot name prefix - 'homedemo--1': device('assistantdefault', '1.2.3.4', ['abc_test']), -} - -# global for caching device properties gathered dynamically during runtime -_DEVICE = None - -### Helper Methods - - -class SSHError(Exception): - pass - - -class Device(object): - """Retrieves device property information for a device over SSH.""" - # amount of time (secs) until state is due for a refresh - _STALE_STATE_DELTA = 60 - - def __init__(self, device_ip, bot_id): - self.device_ip = device_ip - self.bot_id = bot_id - self._device_client = device_lib.Device( - ethernet_ip=self.device_ip) - self._device_client.UseSsh() - self._last_connect_succeeded = False - self._properties = {} - self._stale_state_ts = 0 - - @property - def build_fingerprint(self): - return self.properties.get('ro.build.fingerprint', None) - - @property - def hardware(self): - return self.properties.get('ro.hardware', None) - - @property - def is_reachable(self): - """Checks if device is reachable over ssh.""" - if self._properties is None or time.time() > self._stale_state_ts: - self._properties = self._fetch_properties() - return self._last_connect_succeeded - - @property - def product(self): - return self.properties.get('ro.build.product', None) - - @property - def properties(self): - """Returns all of the last-known device properties as a dict.""" - if self._properties is None or time.time() > self._stale_state_ts: - try: - # Keep last-known properties when SSHError is raised - self._properties = self._fetch_properties() - except SSHError as e: - logging.exception(e) - return self._properties - - def _exec_ssh_cmd(self, cmd): - """Executes the given cmd against the device over SSH. - Arguments: - - cmd: the command to be run on the device over ssh. - Returns: - stdout of cmd output - Raises: - SSHError: upon ssh cmd exec failure - """ - # Temporary: fetch device properties from static local file when present. - # This let's us demo what our swarm bots will look like, even though the - # demo host ('voyager') is on a lab network in WAT that can't reach the - # MTV devices over ssh. The real device properties are fetched over ssh - # by a script on Corp and copied to the swarm host before starting bots. - # TODO(jonesmi): remove this temporary workaround when bots run in MTV lab - bot_common_dir = os.getenv('BOT_COMMON_DIR') - if bot_common_dir: - props_file = os.path.join(bot_common_dir, '%s.props' % self.bot_id) - if os.path.isfile(props_file): - with open(props_file, 'r') as f: - # pretend that we read this from SSH connection - self._last_connect_succeeded = True - return f.read() - try: - if self._device_client.Cmd(cmd, timeout_secs=30): - self._last_connect_succeeded = True - return self._device_client.GetCmdOutput() - else: - self._last_connect_succeeded = False - raise SSHError('failed to execute cmd over ssh: %s' % cmd) - except ssh_helper.CommandThreadTimeoutError: - self._last_connect_succeeded = False - raise SSHError('timeout when waiting for ssh connect: %s' % cmd) - - def _fetch_properties(self): - """Fetches device properties over ssh using getprop command.""" - self._stale_state_ts = time.time() + self._STALE_STATE_DELTA - properties = {} - stdout = self._exec_ssh_cmd('getprop') - for line in stdout.strip().split('\n'): - match = re.match(r'\[(.*)\]: \[(.*)\]', line) - if match: - properties[match.group(1)] = match.group(2) - return properties - - -### _get* Hooks -def get_dimensions(bot): - # pylint: disable=line-too-long - """Returns dict with the bot's dimensions. - The dimensions are what are used to select the bot that can run each task. - The bot id will be automatically selected based on the hostname with - os_utilities.get_dimensions(). If you want something more special, specify it - in your bot_config.py and override the item 'id'. - The dimensions returned here will be joined with server defined dimensions - (extracted from bots.cfg config file based on the bot id). Server defined - dimensions override the ones provided by the bot. See bot.Bot.dimensions for - more information. - See https://github.com/luci/luci-py/tree/master/appengine/swarming/doc/Magic-Values.md. - Arguments: - - bot: bot.Bot instance or None. See ../api/bot.py. - """ - dimensions = os_utilities.get_dimensions() - # The bot base directory is formatted like <HOME>/bots/<Id> - id = '%s--%s' % ( - os_utilities.get_hostname_short(), - os.path.basename(bot.base_dir)) - dimensions[u'id'] = [id] - if id in _BOT_DEVICE_MAP: - dimensions.update(_BOT_DEVICE_MAP[id].dimensions) - global _DEVICE - if not _DEVICE: - device_ip = dimensions[u'device_ip'] - # use first device_ip in list of device IPs for pulling device attributes - if device_ip: - assert isinstance(device_ip, (list, tuple)), repr(device_ip) - _DEVICE = Device(device_ip[0], id) - if _DEVICE: - dimensions[u'build'] = _DEVICE.build_fingerprint, - dimensions[u'hardware'] = _DEVICE.hardware, - dimensions[u'product'] = _DEVICE.product, - # TODO(jonesmi): don't strip these anymore after swarming fix goes in for limit on # dimensions - del dimensions[u'cores'] - del dimensions[u'cpu'] - del dimensions[u'gpu'] - del dimensions[u'machine_type'] - return dimensions - - -def get_state(bot): - # pylint: disable=line-too-long - """Returns dict with a state of the bot reported to the server with each poll. - It is only for dynamic state that changes while bot is running for information - for the sysadmins. - The server can not use this state for immediate scheduling purposes (use - 'dimensions' for that), but it can use it for maintenance and bookkeeping - tasks. - See https://github.com/luci/luci-py/tree/master/appengine/swarming/doc/Magic-Values.md. - Arguments: - - bot: bot.Bot instance or None. See ../api/bot.py. - """ - state = os_utilities.get_state() - if _DEVICE: - state[u'device'] = _DEVICE.properties - if _DEVICE.bot_id in _BOT_DEVICE_MAP: - extra_state = _BOT_DEVICE_MAP[_DEVICE.bot_id].state - if extra_state: - state[u'device'].update(extra_state) - if not _DEVICE.is_reachable: - state[u'quarantined'] = 'device is not reachable' - return state - - -def get_authentication_headers(bot): - """Returns authentication headers and their expiration time. - The returned headers will be passed with each HTTP request to the Swarming - server (and only Swarming server). The bot will use the returned headers until - they are close to expiration (usually 6 min, see AUTH_HEADERS_EXPIRATION_SEC - in remote_client.py), and then it'll attempt to refresh them by calling - get_authentication_headers again. - Can be used to implement per-bot authentication. If no headers are returned, - the server will use only IP whitelist for bot authentication. - On GCE will use OAuth token of the default GCE service account. It should have - "User info" API scope enabled (this can be set when starting an instance). The - server should be configured (via bots.cfg) to trust this account (see - 'require_service_account' in bots.proto). - May be called by different threads, but never concurrently. - Arguments: - - bot: bot.Bot instance. See ../api/bot.py. - Returns: - Tuple (dict with headers or None, unix timestamp of when they expire). - """ - if platforms.is_gce(): - tok = platforms.gce.oauth2_access_token() - return {'Authorization': 'Bearer %s' % tok}, time.time() + 5*60 - return (None, None) - - -### on_* Hooks -def on_bot_shutdown(bot): - """Hook function called when the bot shuts down, usually rebooting. - It's a good time to do other kinds of cleanup. - Arguments: - - bot: bot.Bot instance. See ../api/bot.py. - """ - pass - - -def on_handshake(bot): - """Hook function called when the bot starts, after handshake with the server. - Here the bot already knows server enforced dimensions (defined in server side - bots.cfg file). - This is called right before starting to poll for tasks. It's a good time to - do some final initialization or cleanup that may depend on server provided - configuration. - Arguments: - - bot: bot.Bot instance. See ../api/bot.py. - """ - pass - - -def on_before_task(bot, bot_file): - """Hook function called before running a task. - It shouldn't do much, since it can't cancel the task so it shouldn't do - anything too fancy. - Arguments: - - bot: bot.Bot instance. See ../api/bot.py. - - bot_file: Path to file to write information about the state of the bot. - This file can be used to pass certain info about the bot - to tasks, such as which connected android devices to run on. See - https://github.com/luci/luci-py/tree/master/appengine/swarming/doc/Magic-Values.md#run_isolated - """ - with open(bot_file, 'wb') as f: - json.dump(bot.dimensions, f) - - -def on_after_task(bot, failure, internal_failure, dimensions, summary): - """Hook function called after running a task. - It is an excellent place to do post-task cleanup of temporary files. - The default implementation restarts after a task failure or an internal - failure. - Arguments: - - bot: bot.Bot instance. See ../api/bot.py. - - failure: bool, True if the task failed. - - internal_failure: bool, True if an internal failure happened. - - dimensions: dict, Dimensions requested as part of the task. - - summary: dict, Summary of the task execution. - """ - # Example code: - #if failure: - # bot.restart('Task failure') - #elif internal_failure: - # bot.restart('Internal failure') - - -def on_bot_idle(bot, since_last_action): - """Hook function called once when the bot has been idle; when it has no - command to execute. - This is an excellent place to put device in 'cool down' mode or any - "pre-warming" kind of stuff that could take several seconds to do, that would - not be appropriate to do in on_after_task(). It could be worth waiting for - `since_last_action` to be several seconds before doing a more lengthy - operation. - This function is called repeatedly until an action is taken (a task, updating, - etc). - This is a good place to do "auto reboot" for hardware based bots that are - rebooted periodically. - Arguments: - - bot: bot.Bot instance. See ../api/bot.py. - - since_last_action: time in second since last action; e.g. amount of time the - bot has been idle. - """ - pass diff --git a/configs/infra/test/androidtestcenter-test/settings.cfg b/configs/infra/test/androidtestcenter-test/settings.cfg deleted file mode 100644 index 412349f..0000000 --- a/configs/infra/test/androidtestcenter-test/settings.cfg +++ /dev/null @@ -1,27 +0,0 @@ -# Instance: androidtestcenter-test (swarming-test) -# Browse online: https://androidtestcenter-test.appspot.com/restricted/config -# -# Schema: SettingsCfg message in -# https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/swarming/proto/config.proto - -google_analytics: "UA-107546859-1" -reusable_task_age_secs: 604800 -bot_death_timeout_secs: 600 -enable_ts_monitoring: true -ui_client_id: "819908144382-mktl5tlg4cq9jkqfrqi0lat873itcfpi.apps.googleusercontent.com" -display_server_url_template: "https://androidtestcenter-test.appspot.com/swarming/task/%s" -isolate { - default_server: "https://vtslab-isolate-test.appspot.com" - default_namespace: "default-gzip" -} -mp { - enabled: true -} -auth { - admins_group: "vtslab-test-swarming-admins" - bot_bootstrap_group: "vtslab-test-swarming-bot-bootstrap" - privileged_users_group: "vtslab-test-swarming-privileged-users" - users_group: "vtslab-test-swarming-users" - view_all_bots_group: "vtslab-test-swarming-view-all-bots" - view_all_tasks_group: "vtslab-test-swarming-view-all-tasks" -} diff --git a/configs/infra/test/vtslab-config-test/acl.cfg b/configs/infra/test/vtslab-config-test/acl.cfg deleted file mode 100644 index 0da0ca1..0000000 --- a/configs/infra/test/vtslab-config-test/acl.cfg +++ /dev/null @@ -1,9 +0,0 @@ -# Schema for this file: -# https://vtslab-config-prod.appspot.com/schemas/services/vtslab-config-prod:acl.cfg -# *** WARNING *** -# This file is sensitive. project_access_group specifies a super group -# that has access to all projects, including internal ones. -# This file is cached in process memory of instances for 10min -# because it ~never changes. -admin_group: "vtslab-config-admins" -project_access_group: "vtslab-config-project-access" diff --git a/configs/infra/test/vtslab-config-test/import.cfg b/configs/infra/test/vtslab-config-test/import.cfg deleted file mode 100644 index 6bc8bdc..0000000 --- a/configs/infra/test/vtslab-config-test/import.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Schema for this file: -# https://vtslab-config-test.appspot.com/schemas/services/vtslab-config-test:import.cfg -gitiles { - project_config_default_ref: "refs/heads/infra/config" - ref_config_default_path: "infra/config" -} diff --git a/configs/infra/test/vtslab-config-test/projects.cfg b/configs/infra/test/vtslab-config-test/projects.cfg deleted file mode 100644 index b8043de..0000000 --- a/configs/infra/test/vtslab-config-test/projects.cfg +++ /dev/null @@ -1,19 +0,0 @@ -# Schema for this file: -# https://vtslab-config-test.appspot.com/schemas/services/vtslab-config-test:projects.cfg -# -# Please keep the list sorted by project id! -projects { - id: "aosp-master-vts" - config_location { - url: "https://android.googlesource.com/platform/test/vts/" - storage_type: GITILES - } -} - -projects { - id: "aosp-master-goldfish" - config_location { - url: "https://android.googlesource.com/device/generic/goldfish/" - storage_type: GITILES - } -} diff --git a/configs/infra/test/vtslab-config-test/schemas.cfg b/configs/infra/test/vtslab-config-test/schemas.cfg deleted file mode 100644 index 91d34c7..0000000 --- a/configs/infra/test/vtslab-config-test/schemas.cfg +++ /dev/null @@ -1,79 +0,0 @@ -# Schema for this file: -# https://vtslab-config-test.appspot.com/schemas/services/vtslab-config-test:schemas.cfg -# -# Please keep this list sorted by schema name. -# -################################################################################ -# WARNING: names and urls in this file will be exposed publicly. Do not put -# internal stuff here!!! -################################################################################ -################################################################################ -# ref configs -schemas { - name: "projects/refs:cq.cfg" - url: "https://chromium.googlesource.com/chromium/tools/depot_tools/+/master/third_party/cq_client/cq.proto" -} -################################################################################ -# project configs -schemas { - name: "projects:buildbucket.cfg" - url: "https://chromium.googlesource.com/infra/infra/+/master/appengine/cr-buildbucket/proto/project_config.proto" -} -schemas { - name: "projects:refs.cfg" - url: "https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/components/components/config/proto/project_config.proto" -} -schemas { - name: "projects:project.cfg" - url: "https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/components/components/config/proto/project_config.proto" -} -################################################################################ -# luci-config service schemas -schemas { - name: "services/luci-config:acl.cfg" - url: "https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/components/components/config/proto/service_config.proto" -} -schemas { - name: "services/luci-config:import.cfg" - url: "https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/components/components/config/proto/service_config.proto" -} -schemas { - name: "services/luci-config:projects.cfg" - url: "https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/components/components/config/proto/service_config.proto" -} -schemas { - name: "services/luci-config:services.cfg" - url: "https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/components/components/config/proto/service_config.proto" -} -schemas { - name: "services/luci-config:schemas.cfg" - url: "https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/components/components/config/proto/service_config.proto" -} -################################################################################ -# chrome-infra-auth service schemas. -schemas { - name: "services/chrome-infra-auth:delegation.cfg" - url: "https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/auth_service/proto/config.proto" -} -schemas { - name: "services/chrome-infra-auth:imports.cfg" - url: "https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/auth_service/proto/config.proto" -} -schemas { - name: "services/chrome-infra-auth:ip_whitelist.cfg" - url: "https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/auth_service/proto/config.proto" -} -schemas { - name: "services/chrome-infra-auth:oauth.cfg" - url: "https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/auth_service/proto/config.proto" -} -################################################################################ -# LUCI LogDog service schemas. -schemas { - name: "services/luci-logdog:services.cfg" - url: "https://chromium.googlesource.com/infra/luci/luci-go/+/master/logdog/api/config/svcconfig/config.proto" -} -schemas { - name: "services/luci-logdog:logdog.cfg" - url: "https://chromium.googlesource.com/infra/luci/luci-go/+/master/logdog/api/config/svcconfig/project.proto" -} diff --git a/configs/infra/test/vtslab-config-test/services.cfg b/configs/infra/test/vtslab-config-test/services.cfg deleted file mode 100644 index 3377123..0000000 --- a/configs/infra/test/vtslab-config-test/services.cfg +++ /dev/null @@ -1,60 +0,0 @@ -# Schema for this file: ServicesCfg message in -# https://vtslab-config-test.appspot.com/schemas/services/vtslab-config-test:services.cfg -# -# Please keep the list sorted by id. -# -# *** WARNING -# This file is sensitive. These services may receive contents of any config -# files for validation purposes, including configs of internal projects. -# *** -################################################################################ -# Swarming -services { - id: "androidtestcenter-test" - owners: "yim@google.com" - metadata_url: "https://androidtestcenter-test.appspot.com/_ah/api/config/v1/metadata" - access: "androidtestcenter-test@appspot.gserviceaccount.com" -} -################################################################################ -# Auth -services { - id: "vtslab-auth-test" - owners: "yim@google.com" - metadata_url: "https://vtslab-auth-test.appspot.com/_ah/api/config/v1/metadata" - access: "vtslab-auth-test@appspot.gserviceaccount.com" -} -################################################################################ -# Commit Queue -# services { -# id: "commit-queue" -# owners: "yim@google.com" -# metadata_url: "https://vtslab-cq-test.appspot.com/api/metadata" -# } -################################################################################ -# GCE Backend -services { - id: "vtslab-backend-test" - owners: "yim@google.com" - access: "vtslab-backend-test@appspot.gserviceaccount.com" -} -################################################################################ -# LUCI / Isolate Server -services { - id: "vtslab-isolate-test" - owners: "yim@google.com" - metadata_url: "https://vtslab-isolate-test.appspot.com/_ah/api/config/v1/metadata" - access: "vtslab-isolate-test@appspot.gserviceaccount.com" -} -################################################################################ -# Machine Provider -services { - id: "vtslab-machine-test" - owners: "yim@google.com" - access: "vtslab-machine-test@appspot.gserviceaccount.com" -} -################################################################################ -# Doc Indexer -services { - id: "vtslab-doc" - owners: "yim@google.com" -} diff --git a/gae/.gitignore b/gae/.gitignore deleted file mode 100644 index 9d1699c..0000000 --- a/gae/.gitignore +++ /dev/null @@ -1 +0,0 @@ -webapp/static/** diff --git a/gae/app.yaml b/gae/app.yaml deleted file mode 100644 index 374c07c..0000000 --- a/gae/app.yaml +++ /dev/null @@ -1,57 +0,0 @@ -runtime: python27 -api_version: 1 -threadsafe: true - -# [START env_vars] -env_variables: - ENDPOINTS_SERVICE_NAME: vtslab-schedule-prod.appspot.com - ENDPOINTS_SERVICE_VERSION: 2018-09-03r0 - SESSION_SECRET_KEY: '' -# [END env_vars] - -# [START builtins] -builtins: -# [END builtins] - -# [START handlers] -handlers: -# The endpoints handler must be mapped to /_ah/api. -- url: /_ah/api/.* - script: webapp.src.endpoint_main.api - -- url: /(.*\.(html|js|css|txt|ico)) - static_files: webapp/static/\1 - upload: webapp/static/(.*\.(html|js|css|txt|ico)) - -- url: /((build|device|job|lab|schedule)([?&/].*)?)? - static_files: webapp/static/index.html - upload: webapp/static/index.html - -- url: /.* - script: webapp.src.webapp_main.app -# [END handlers] - -# [START libraries] -libraries: -- name: webapp2 - version: latest -- name: jinja2 - version: latest -- name: pycrypto - version: 2.6 -- name: ssl - version: 2.7.11 -# [END libraries] - -# [START exclude] -skip_files: -- ^(.*/)?#.*#$ -- ^(.*/)?.*~$ -- ^(.*/)?.*\.py[co]$ -- ^(.*/)?.*/RCS/.*$ -- ^(.*/)?\..*$ -- ^script/*$ -- testrunner.py -- .*_test.py$ -- ^(.*/)?frontend/(.*) -# [END exclude] diff --git a/gae/appengine_config.py b/gae/appengine_config.py deleted file mode 100644 index 1003e5e..0000000 --- a/gae/appengine_config.py +++ /dev/null @@ -1,5 +0,0 @@ -# appengine_config.py -from google.appengine.ext import vendor - -# Add any libraries install in the "lib" folder. -vendor.add('lib') diff --git a/gae/buildv1openapi.json b/gae/buildv1openapi.json deleted file mode 100644 index a59a137..0000000 --- a/gae/buildv1openapi.json +++ /dev/null @@ -1,194 +0,0 @@ -{ - "basePath": "/_ah/api", - "consumes": [ - "application/json" - ], - "definitions": { - "WebappSrcProtoModelBuildInfoMessage": { - "properties": { - "artifact_type": { - "type": "string" - }, - "artifacts": { - "items": { - "type": "string" - }, - "type": "array" - }, - "build_id": { - "type": "string" - }, - "build_target": { - "type": "string" - }, - "build_type": { - "type": "string" - }, - "manifest_branch": { - "type": "string" - }, - "signed": { - "type": "boolean" - } - }, - "type": "object" - }, - "WebappSrcProtoModelBuildResponseMessage": { - "properties": { - "builds": { - "description": "A message for representing an individual build entry.", - "items": { - "$ref": "#/definitions/WebappSrcProtoModelBuildInfoMessage" - }, - "type": "array" - }, - "has_next": { - "type": "boolean" - } - }, - "type": "object" - }, - "WebappSrcProtoModelCountRequestMessage": { - "properties": { - "filter": { - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelCountResponseMessage": { - "properties": { - "count": { - "format": "int64", - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelDefaultResponse": { - "properties": { - "return_code": { - "enum": [ - "SUCCESS", - "FAIL" - ], - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelGetRequestMessage": { - "properties": { - "direction": { - "type": "string" - }, - "filter": { - "type": "string" - }, - "offset": { - "format": "int64", - "type": "string" - }, - "size": { - "format": "int64", - "type": "string" - }, - "sort": { - "type": "string" - } - }, - "type": "object" - } - }, - "host": "vtslab-schedule-prod.appspot.com", - "info": { - "description": "Endpoint API for build_info.", - "title": "build", - "version": "v1" - }, - "paths": { - "/build/v1/count": { - "post": { - "operationId": "BuildInfoApi_count", - "parameters": [ - { - "in": "body", - "name": "body", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelCountRequestMessage" - } - } - ], - "responses": { - "200": { - "description": "A successful response", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelCountResponseMessage" - } - } - } - } - }, - "/build/v1/get": { - "post": { - "operationId": "BuildInfoApi_get", - "parameters": [ - { - "in": "body", - "name": "body", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelGetRequestMessage" - } - } - ], - "responses": { - "200": { - "description": "A successful response", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelBuildResponseMessage" - } - } - } - } - }, - "/build/v1/set": { - "post": { - "operationId": "BuildInfoApi_set", - "parameters": [ - { - "in": "body", - "name": "body", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelBuildInfoMessage" - } - } - ], - "responses": { - "200": { - "description": "A successful response", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelDefaultResponse" - } - } - } - } - } - }, - "produces": [ - "application/json" - ], - "schemes": [ - "https" - ], - "securityDefinitions": { - "google_id_token": { - "authorizationUrl": "", - "flow": "implicit", - "type": "oauth2", - "x-google-issuer": "https://accounts.google.com", - "x-google-jwks_uri": "https://www.googleapis.com/oauth2/v3/certs" - } - }, - "swagger": "2.0", - "x-google-api-name": "build" -}
\ No newline at end of file diff --git a/gae/cron.yaml b/gae/cron.yaml deleted file mode 100644 index e4d4a71..0000000 --- a/gae/cron.yaml +++ /dev/null @@ -1,13 +0,0 @@ -cron: -- description: "test scheduler job" - url: /tasks/schedule - schedule: every 5 minutes -- description: "device_heartbeat" - url: /tasks/device_heartbeat - schedule: every 1 minutes -- description: "job_heartbeat" - url: /tasks/job_heartbeat - schedule: every 1 minutes -- description: "remove_outdated_devices" - url: /tasks/remove_outdated_devices - schedule: every 1 hours diff --git a/gae/favicon.ico b/gae/favicon.ico Binary files differdeleted file mode 100644 index 42506c0..0000000 --- a/gae/favicon.ico +++ /dev/null diff --git a/gae/frontend/.editorconfig b/gae/frontend/.editorconfig deleted file mode 100644 index 6e87a00..0000000 --- a/gae/frontend/.editorconfig +++ /dev/null @@ -1,13 +0,0 @@ -# Editor configuration, see http://editorconfig.org -root = true - -[*] -charset = utf-8 -indent_style = space -indent_size = 2 -insert_final_newline = true -trim_trailing_whitespace = true - -[*.md] -max_line_length = off -trim_trailing_whitespace = false diff --git a/gae/frontend/.gitignore b/gae/frontend/.gitignore deleted file mode 100644 index ee5c9d8..0000000 --- a/gae/frontend/.gitignore +++ /dev/null @@ -1,39 +0,0 @@ -# See http://help.github.com/ignore-files/ for more about ignoring files. - -# compiled output -/dist -/tmp -/out-tsc - -# dependencies -/node_modules - -# IDEs and editors -/.idea -.project -.classpath -.c9/ -*.launch -.settings/ -*.sublime-workspace - -# IDE - VSCode -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json - -# misc -/.sass-cache -/connect.lock -/coverage -/libpeerconnection.log -npm-debug.log -yarn-error.log -testem.log -/typings - -# System Files -.DS_Store -Thumbs.db diff --git a/gae/frontend/MODULE_LICENSE_MIT b/gae/frontend/MODULE_LICENSE_MIT deleted file mode 100644 index e69de29..0000000 --- a/gae/frontend/MODULE_LICENSE_MIT +++ /dev/null diff --git a/gae/frontend/NOTICE b/gae/frontend/NOTICE deleted file mode 100644 index 40040b9..0000000 --- a/gae/frontend/NOTICE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License - -Copyright (c) 2014-2018 Google, Inc. http://angular.io - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - diff --git a/gae/frontend/angular.json b/gae/frontend/angular.json deleted file mode 100644 index ca84a43..0000000 --- a/gae/frontend/angular.json +++ /dev/null @@ -1,127 +0,0 @@ -{ - "$schema": "./node_modules/@angular/cli/lib/config/schema.json", - "version": 1, - "newProjectRoot": "projects", - "projects": { - "frontend": { - "root": "", - "sourceRoot": "src", - "projectType": "application", - "prefix": "app", - "schematics": {}, - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:browser", - "options": { - "outputPath": "dist", - "index": "src/index.html", - "main": "src/main.ts", - "polyfills": "src/polyfills.ts", - "tsConfig": "src/tsconfig.app.json", - "assets": [ - "src/favicon.ico", - "src/assets" - ], - "styles": [ - "src/styles.scss" - ], - "scripts": [] - }, - "configurations": { - "production": { - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.prod.ts" - } - ], - "optimization": true, - "outputHashing": "all", - "sourceMap": false, - "extractCss": true, - "namedChunks": false, - "aot": true, - "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": true - } - } - }, - "serve": { - "builder": "@angular-devkit/build-angular:dev-server", - "options": { - "browserTarget": "frontend:build" - }, - "configurations": { - "production": { - "browserTarget": "frontend:build:production" - } - } - }, - "extract-i18n": { - "builder": "@angular-devkit/build-angular:extract-i18n", - "options": { - "browserTarget": "frontend:build" - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "src/test.ts", - "polyfills": "src/polyfills.ts", - "tsConfig": "src/tsconfig.spec.json", - "karmaConfig": "src/karma.conf.js", - "styles": [ - "src/styles.scss" - ], - "scripts": [], - "assets": [ - "src/favicon.ico", - "src/assets" - ] - } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": [ - "src/tsconfig.app.json", - "src/tsconfig.spec.json" - ], - "exclude": [ - "**/node_modules/**" - ] - } - } - } - }, - "frontend-e2e": { - "root": "e2e/", - "projectType": "application", - "architect": { - "e2e": { - "builder": "@angular-devkit/build-angular:protractor", - "options": { - "protractorConfig": "e2e/protractor.conf.js", - "devServerTarget": "frontend:serve" - }, - "configurations": { - "production": { - "devServerTarget": "frontend:serve:production" - } - } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": "e2e/tsconfig.e2e.json", - "exclude": [ - "**/node_modules/**" - ] - } - } - } - } - }, - "defaultProject": "frontend" -} diff --git a/gae/frontend/e2e/protractor.conf.js b/gae/frontend/e2e/protractor.conf.js deleted file mode 100644 index 86776a3..0000000 --- a/gae/frontend/e2e/protractor.conf.js +++ /dev/null @@ -1,28 +0,0 @@ -// Protractor configuration file, see link for more information -// https://github.com/angular/protractor/blob/master/lib/config.ts - -const { SpecReporter } = require('jasmine-spec-reporter'); - -exports.config = { - allScriptsTimeout: 11000, - specs: [ - './src/**/*.e2e-spec.ts' - ], - capabilities: { - 'browserName': 'chrome' - }, - directConnect: true, - baseUrl: 'http://localhost:4200/', - framework: 'jasmine', - jasmineNodeOpts: { - showColors: true, - defaultTimeoutInterval: 30000, - print: function() {} - }, - onPrepare() { - require('ts-node').register({ - project: require('path').join(__dirname, './tsconfig.e2e.json') - }); - jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); - } -};
\ No newline at end of file diff --git a/gae/frontend/e2e/src/app.e2e-spec.ts b/gae/frontend/e2e/src/app.e2e-spec.ts deleted file mode 100644 index 87525cf..0000000 --- a/gae/frontend/e2e/src/app.e2e-spec.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { AppPage } from './app.po'; - -describe('workspace-project App', () => { - let page: AppPage; - - beforeEach(() => { - page = new AppPage(); - }); - - it('should display welcome message', () => { - page.navigateTo(); - expect(page.getParagraphText()).toEqual('Welcome to frontend!'); - }); -}); diff --git a/gae/frontend/e2e/src/app.po.ts b/gae/frontend/e2e/src/app.po.ts deleted file mode 100644 index 82ea75b..0000000 --- a/gae/frontend/e2e/src/app.po.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { browser, by, element } from 'protractor'; - -export class AppPage { - navigateTo() { - return browser.get('/'); - } - - getParagraphText() { - return element(by.css('app-root h1')).getText(); - } -} diff --git a/gae/frontend/e2e/tsconfig.e2e.json b/gae/frontend/e2e/tsconfig.e2e.json deleted file mode 100644 index a6dd622..0000000 --- a/gae/frontend/e2e/tsconfig.e2e.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "outDir": "../out-tsc/app", - "module": "commonjs", - "target": "es5", - "types": [ - "jasmine", - "jasminewd2", - "node" - ] - } -}
\ No newline at end of file diff --git a/gae/frontend/package-lock.json b/gae/frontend/package-lock.json deleted file mode 100644 index f697726..0000000 --- a/gae/frontend/package-lock.json +++ /dev/null @@ -1,10746 +0,0 @@ -{ - "name": "frontend", - "version": "0.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@angular-devkit/architect": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.6.8.tgz", - "integrity": "sha512-ZKTm/zC61iY9IBHOEAKoMSzZpvhkmv+1O/HHzpHEuR551jCzu6vSyCmMY9Z7GBcccscCV+hjeSMwgFrFRcqlkw==", - "dev": true, - "requires": { - "@angular-devkit/core": "0.6.8", - "rxjs": "6.2.1" - } - }, - "@angular-devkit/build-angular": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.6.8.tgz", - "integrity": "sha512-VGqYAk8jpISraz2UHfsDre270NOUmV0CTSZw2p9sm5g/XIr5m+IHetFZz3gpoAr9+If2aFTs8Rt3sGdCRzwBqA==", - "dev": true, - "requires": { - "@angular-devkit/architect": "0.6.8", - "@angular-devkit/build-optimizer": "0.6.8", - "@angular-devkit/core": "0.6.8", - "@ngtools/webpack": "6.0.8", - "ajv": "6.4.0", - "autoprefixer": "8.6.2", - "cache-loader": "1.2.2", - "chalk": "2.2.2", - "circular-dependency-plugin": "5.0.2", - "clean-css": "4.1.11", - "copy-webpack-plugin": "4.5.1", - "file-loader": "1.1.11", - "glob": "7.1.2", - "html-webpack-plugin": "3.2.0", - "istanbul": "0.4.5", - "istanbul-instrumenter-loader": "3.0.1", - "karma-source-map-support": "1.3.0", - "less": "3.0.4", - "less-loader": "4.1.0", - "license-webpack-plugin": "1.3.1", - "lodash": "4.17.10", - "memory-fs": "0.4.1", - "mini-css-extract-plugin": "0.4.0", - "minimatch": "3.0.4", - "node-sass": "4.9.0", - "opn": "5.3.0", - "parse5": "4.0.0", - "portfinder": "1.0.13", - "postcss": "6.0.22", - "postcss-import": "11.1.0", - "postcss-loader": "2.1.5", - "postcss-url": "7.3.2", - "raw-loader": "0.5.1", - "resolve": "1.7.1", - "rxjs": "6.2.1", - "sass-loader": "7.0.3", - "silent-error": "1.1.0", - "source-map-support": "0.5.6", - "stats-webpack-plugin": "0.6.2", - "style-loader": "0.21.0", - "stylus": "0.54.5", - "stylus-loader": "3.0.2", - "tree-kill": "1.2.0", - "uglifyjs-webpack-plugin": "1.2.5", - "url-loader": "1.0.1", - "webpack": "4.8.3", - "webpack-dev-middleware": "3.1.3", - "webpack-dev-server": "3.1.4", - "webpack-merge": "4.1.3", - "webpack-sources": "1.1.0", - "webpack-subresource-integrity": "1.1.0-rc.4" - } - }, - "@angular-devkit/build-optimizer": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.6.8.tgz", - "integrity": "sha512-of5syQbv3uNPp4AQkfRecfnp8AE8kvffbfYi+FFPZ6OGr7e59T1fGwk6+Zgb2qQFQg8HO2tzWI/uygtLIqmbmw==", - "dev": true, - "requires": { - "loader-utils": "1.1.0", - "source-map": "0.5.7", - "typescript": "2.9.2", - "webpack-sources": "1.1.0" - }, - "dependencies": { - "typescript": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", - "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", - "dev": true - } - } - }, - "@angular-devkit/core": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.6.8.tgz", - "integrity": "sha512-rkIa1OSVWTt4g9leLSK/PsqOj3HZbDKHbZjqlslyfVa3AyCeiumFoOgViOVXlYgPX3HHDbE5uH24nyUWSD8uww==", - "dev": true, - "requires": { - "ajv": "6.4.0", - "chokidar": "2.0.3", - "rxjs": "6.2.1", - "source-map": "0.5.7" - } - }, - "@angular-devkit/schematics": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-0.6.8.tgz", - "integrity": "sha512-R4YqAUdo62wtrhX/5HSRGSKXNTWqfQb66ZE6m8jj6GEJNFKdNXMdxOchxr07LCiKTxfh1w6G3nGzxIsu/+D4KA==", - "dev": true, - "requires": { - "@angular-devkit/core": "0.6.8", - "rxjs": "6.2.1" - } - }, - "@angular/animations": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-6.0.6.tgz", - "integrity": "sha512-mJvWn0GuYARJfV9/KNUn5qUc5iNJKMSSNm//pRtUB8n829KnJHLnGpNsr95dzARH5wI3Om/t6hG3M0XCLbIfNQ==", - "requires": { - "tslib": "1.9.2" - } - }, - "@angular/cdk": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-6.2.1.tgz", - "integrity": "sha512-uwW4eIGJKqOkR+ew6YcEAh1J4SP98jdyDpsZ4IEMkV9+jXcKfcwcxGFpZvs9wJsAvAr8EgNmZ8h+iuZLwJsvmA==", - "requires": { - "tslib": "1.9.2" - } - }, - "@angular/cli": { - "version": "6.0.8", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-6.0.8.tgz", - "integrity": "sha512-DhH1Zq5Yonthw6zh6W07fhf+9XrAZbD1fcQ0MrmbxlieCfLlTAdBqyK2LavFCKwSZkUMLF6UHM3+jiNRVZSSIg==", - "dev": true, - "requires": { - "@angular-devkit/architect": "0.6.8", - "@angular-devkit/core": "0.6.8", - "@angular-devkit/schematics": "0.6.8", - "@schematics/angular": "0.6.8", - "@schematics/update": "0.6.8", - "opn": "5.3.0", - "resolve": "1.7.1", - "rxjs": "6.2.1", - "semver": "5.5.0", - "silent-error": "1.1.0", - "symbol-observable": "1.2.0", - "yargs-parser": "10.0.0" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "yargs-parser": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.0.0.tgz", - "integrity": "sha512-+DHejWujTVYeMHLff8U96rLc4uE4Emncoftvn5AjhB1Jw1pWxLzgBUT/WYbPrHmy6YPEBTZQx5myHhVcuuu64g==", - "dev": true, - "requires": { - "camelcase": "4.1.0" - } - } - } - }, - "@angular/common": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-6.0.6.tgz", - "integrity": "sha512-SjCrrGNJSeRMtNLv/ug5HpyRUexdNl11TrWCWMeu3ye3ss4k6EnuM9jGB196B0PIm0IbjO0KrpQ8bqBx0/2vqw==", - "requires": { - "tslib": "1.9.2" - } - }, - "@angular/compiler": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-6.0.6.tgz", - "integrity": "sha512-lcDNfkYLOWzOOqdD2Kspxwjk3xGs8kVLbq/8uk/aJ96ty8aA9j8Nbf3h53SCY9LuGoJMjOaaUpgwZCszFzqQyA==", - "requires": { - "tslib": "1.9.2" - } - }, - "@angular/compiler-cli": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-6.0.6.tgz", - "integrity": "sha512-vWJK+X6B63+kdAN2s7Az1NHF4gAbECf1fkB+zkO6pP706teW4VlN2xdXeHLXgvK39iDJbhbctTnDfhqIaPmyjw==", - "dev": true, - "requires": { - "chokidar": "1.7.0", - "minimist": "1.2.0", - "reflect-metadata": "0.1.12", - "tsickle": "0.29.0" - }, - "dependencies": { - "anymatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", - "dev": true, - "requires": { - "micromatch": "2.3.11", - "normalize-path": "2.1.1" - } - }, - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "requires": { - "arr-flatten": "1.1.0" - } - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true - }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "dev": true, - "requires": { - "expand-range": "1.8.2", - "preserve": "0.2.0", - "repeat-element": "1.1.2" - } - }, - "chokidar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", - "dev": true, - "requires": { - "anymatch": "1.3.2", - "async-each": "1.0.1", - "fsevents": "1.2.4", - "glob-parent": "2.0.0", - "inherits": "2.0.3", - "is-binary-path": "1.0.1", - "is-glob": "2.0.1", - "path-is-absolute": "1.0.1", - "readdirp": "2.1.0" - } - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "dev": true, - "requires": { - "is-posix-bracket": "0.1.1" - } - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - }, - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "requires": { - "is-glob": "2.0.1" - } - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "2.0.0", - "array-unique": "0.2.1", - "braces": "1.8.5", - "expand-brackets": "0.1.5", - "extglob": "0.3.2", - "filename-regex": "2.0.1", - "is-extglob": "1.0.0", - "is-glob": "2.0.1", - "kind-of": "3.2.2", - "normalize-path": "2.1.1", - "object.omit": "2.0.1", - "parse-glob": "3.0.4", - "regex-cache": "0.4.4" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "@angular/core": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-6.0.6.tgz", - "integrity": "sha512-7J4wuQ5Bss2GmCptyXSfmgWk/IbCFK/MJwaXOpADLB9iWOkOIvKRSTntb4l6j3OVd9boCbs6Z/xW/HT964iMvw==", - "requires": { - "tslib": "1.9.2" - } - }, - "@angular/flex-layout": { - "version": "6.0.0-beta.16", - "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-6.0.0-beta.16.tgz", - "integrity": "sha512-0AYtIBGrEJshdFMc6TXGloCkD19YTCRKVJl6xZHX4H5dLnUn+daqXcbh4UsWhayevnLp85HEf2ViHLmTa6jv3g==", - "requires": { - "tslib": "1.9.2" - } - }, - "@angular/forms": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-6.0.6.tgz", - "integrity": "sha512-uVcvUz8JzO/R6HtxIUtefjK55nf4gJt9WjVdnjmA66pQe1+aQYscyQu9QFykGfGqta/0luhVSU7J+5g0rIRr/g==", - "requires": { - "tslib": "1.9.2" - } - }, - "@angular/http": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/@angular/http/-/http-6.0.6.tgz", - "integrity": "sha512-ZyY7JS3lQM0HnKfoCJl+S9ZHeQVdG+FefjYE2s7pBKUufaoMo9DTIfQe5ZgSQeXRAFKjuUyJDf1EZlPVVvQzIw==", - "requires": { - "tslib": "1.9.2" - } - }, - "@angular/language-service": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-6.0.6.tgz", - "integrity": "sha512-6zRuKreMPlLQkLGS7KaJ4xehwirPbst+S6tQZltcSHjgIKrZBu3acL7/tUo5G5jQW6OnPXWK9UYs2kCffPS3AQ==", - "dev": true - }, - "@angular/material": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-6.2.1.tgz", - "integrity": "sha512-SBoUXxHknkgwzp5pNDHW0jyrTM0d0Tk4lVyDbtEX8VEPtXqG5nL3BSgyjpJbTvqlmy2kOooUu3qgAmt87VH9lw==", - "requires": { - "tslib": "1.9.2" - } - }, - "@angular/platform-browser": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-6.0.6.tgz", - "integrity": "sha512-c+2c4Ba8IeIt9CnF1RmJVf/0xwljT9GSIJUC61SLrX01NMwRxDq/LC+tatcBGLzZ6rc1eYmsd1exTHOGfENOxw==", - "requires": { - "tslib": "1.9.2" - } - }, - "@angular/platform-browser-dynamic": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-6.0.6.tgz", - "integrity": "sha512-t5+dvfcwVaDa5H8qsVnPAvmNJa0rDwJMu1T6kfz8sAxzgiw6tOvIQShJX0Ka94+nPpd4mg7gv43VV705z6ryMA==", - "requires": { - "tslib": "1.9.2" - } - }, - "@angular/router": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-6.0.6.tgz", - "integrity": "sha512-R49Gh/ate//AloPGjtQ2Nl3HNMT21pumcUoWZEZtYw8UyTbxSKLMc40yzdsldGrKZ/G/CafFTaS1hpZD7MF5/w==", - "requires": { - "tslib": "1.9.2" - } - }, - "@ngtools/webpack": { - "version": "6.0.8", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-6.0.8.tgz", - "integrity": "sha512-jorGpTd82ILbyUwg4JQekovHFaYwSMlZan4f7x+sd3+2WgyL3Z1+ZbVSGKvXZWKS/mAVx7eLkRikzJkuC4FgHw==", - "dev": true, - "requires": { - "@angular-devkit/core": "0.6.8", - "tree-kill": "1.2.0", - "webpack-sources": "1.1.0" - } - }, - "@schematics/angular": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-0.6.8.tgz", - "integrity": "sha512-9kRphqTYG5Df/I8fvnT1zMsw0YNDPO9tl18tQZXj4am4raT7l9UCr+WkwJdlBoA5pwG6baWE9sL0iGWV/bzF/g==", - "dev": true, - "requires": { - "@angular-devkit/core": "0.6.8", - "@angular-devkit/schematics": "0.6.8", - "typescript": "2.7.2" - } - }, - "@schematics/update": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.6.8.tgz", - "integrity": "sha512-1Uq7LYnwL2wBwGVCgNz76QAR13ghAk+2vDDHOi+VX5+usHManxydrpoMGeX66OBPd+y5D3D2MFb+8mYHE7mygg==", - "dev": true, - "requires": { - "@angular-devkit/core": "0.6.8", - "@angular-devkit/schematics": "0.6.8", - "npm-registry-client": "8.5.1", - "rxjs": "6.2.1", - "semver": "5.5.0", - "semver-intersect": "1.3.1" - } - }, - "@types/jasmine": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.8.tgz", - "integrity": "sha512-OJSUxLaxXsjjhob2DBzqzgrkLmukM3+JMpRp0r0E4HTdT1nwDCWhaswjYxazPij6uOdzHCJfNbDjmQ1/rnNbCg==", - "dev": true - }, - "@types/jasminewd2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.3.tgz", - "integrity": "sha512-hYDVmQZT5VA2kigd4H4bv7vl/OhlympwREUemqBdOqtrYTo5Ytm12a5W5/nGgGYdanGVxj0x/VhZ7J3hOg/YKg==", - "dev": true, - "requires": { - "@types/jasmine": "2.8.8" - } - }, - "@types/node": { - "version": "8.9.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.9.5.tgz", - "integrity": "sha512-jRHfWsvyMtXdbhnz5CVHxaBgnV6duZnPlQuRSo/dm/GnmikNcmZhxIES4E9OZjUmQ8C+HCl4KJux+cXN/ErGDQ==", - "dev": true - }, - "@types/q": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", - "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=", - "dev": true - }, - "@types/selenium-webdriver": { - "version": "2.53.43", - "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-2.53.43.tgz", - "integrity": "sha512-UBYHWph6P3tutkbXpW6XYg9ZPbTKjw/YC2hGG1/GEvWwTbvezBUv3h+mmUFw79T3RFPnmedpiXdOBbXX+4l0jg==", - "dev": true - }, - "@webassemblyjs/ast": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.4.3.tgz", - "integrity": "sha512-S6npYhPcTHDYe9nlsKa9CyWByFi8Vj8HovcAgtmMAQZUOczOZbQ8CnwMYKYC5HEZzxEE+oY0jfQk4cVlI3J59Q==", - "dev": true, - "requires": { - "@webassemblyjs/helper-wasm-bytecode": "1.4.3", - "@webassemblyjs/wast-parser": "1.4.3", - "debug": "3.1.0", - "webassemblyjs": "1.4.3" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.4.3.tgz", - "integrity": "sha512-3zTkSFswwZOPNHnzkP9ONq4bjJSeKVMcuahGXubrlLmZP8fmTIJ58dW7h/zOVWiFSuG2em3/HH3BlCN7wyu9Rw==", - "dev": true - }, - "@webassemblyjs/helper-buffer": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.4.3.tgz", - "integrity": "sha512-e8+KZHh+RV8MUvoSRtuT1sFXskFnWG9vbDy47Oa166xX+l0dD5sERJ21g5/tcH8Yo95e9IN3u7Jc3NbhnUcSkw==", - "dev": true, - "requires": { - "debug": "3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "@webassemblyjs/helper-code-frame": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.4.3.tgz", - "integrity": "sha512-9FgHEtNsZQYaKrGCtsjswBil48Qp1agrzRcPzCbQloCoaTbOXLJ9IRmqT+uEZbenpULLRNFugz3I4uw18hJM8w==", - "dev": true, - "requires": { - "@webassemblyjs/wast-printer": "1.4.3" - } - }, - "@webassemblyjs/helper-fsm": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.4.3.tgz", - "integrity": "sha512-JINY76U+702IRf7ePukOt037RwmtH59JHvcdWbTTyHi18ixmQ+uOuNhcdCcQHTquDAH35/QgFlp3Y9KqtyJsCQ==", - "dev": true - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.4.3.tgz", - "integrity": "sha512-I7bS+HaO0K07Io89qhJv+z1QipTpuramGwUSDkwEaficbSvCcL92CUZEtgykfNtk5wb0CoLQwWlmXTwGbNZUeQ==", - "dev": true - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.4.3.tgz", - "integrity": "sha512-p0yeeO/h2r30PyjnJX9xXSR6EDcvJd/jC6xa/Pxg4lpfcNi7JUswOpqDToZQ55HMMVhXDih/yqkaywHWGLxqyQ==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.4.3", - "@webassemblyjs/helper-buffer": "1.4.3", - "@webassemblyjs/helper-wasm-bytecode": "1.4.3", - "@webassemblyjs/wasm-gen": "1.4.3", - "debug": "3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "@webassemblyjs/leb128": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.4.3.tgz", - "integrity": "sha512-4u0LJLSPzuRDWHwdqsrThYn+WqMFVqbI2ltNrHvZZkzFPO8XOZ0HFQ5eVc4jY/TNHgXcnwrHjONhPGYuuf//KQ==", - "dev": true, - "requires": { - "leb": "0.3.0" - } - }, - "@webassemblyjs/validation": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/validation/-/validation-1.4.3.tgz", - "integrity": "sha512-R+rRMKfhd9mq0rj2mhU9A9NKI2l/Rw65vIYzz4lui7eTKPcCu1l7iZNi4b9Gen8D42Sqh/KGiaQNk/x5Tn/iBQ==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.4.3" - } - }, - "@webassemblyjs/wasm-edit": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.4.3.tgz", - "integrity": "sha512-qzuwUn771PV6/LilqkXcS0ozJYAeY/OKbXIWU3a8gexuqb6De2p4ya/baBeH5JQ2WJdfhWhSvSbu86Vienttpw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.4.3", - "@webassemblyjs/helper-buffer": "1.4.3", - "@webassemblyjs/helper-wasm-bytecode": "1.4.3", - "@webassemblyjs/helper-wasm-section": "1.4.3", - "@webassemblyjs/wasm-gen": "1.4.3", - "@webassemblyjs/wasm-opt": "1.4.3", - "@webassemblyjs/wasm-parser": "1.4.3", - "@webassemblyjs/wast-printer": "1.4.3", - "debug": "3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.4.3.tgz", - "integrity": "sha512-eR394T8dHZfpLJ7U/Z5pFSvxl1L63JdREebpv9gYc55zLhzzdJPAuxjBYT4XqevUdW67qU2s0nNA3kBuNJHbaQ==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.4.3", - "@webassemblyjs/helper-wasm-bytecode": "1.4.3", - "@webassemblyjs/leb128": "1.4.3" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.4.3.tgz", - "integrity": "sha512-7Gp+nschuKiDuAL1xmp4Xz0rgEbxioFXw4nCFYEmy+ytynhBnTeGc9W9cB1XRu1w8pqRU2lbj2VBBA4cL5Z2Kw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.4.3", - "@webassemblyjs/helper-buffer": "1.4.3", - "@webassemblyjs/wasm-gen": "1.4.3", - "@webassemblyjs/wasm-parser": "1.4.3", - "debug": "3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.4.3.tgz", - "integrity": "sha512-KXBjtlwA3BVukR/yWHC9GF+SCzBcgj0a7lm92kTOaa4cbjaTaa47bCjXw6cX4SGQpkncB9PU2hHGYVyyI7wFRg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.4.3", - "@webassemblyjs/helper-wasm-bytecode": "1.4.3", - "@webassemblyjs/leb128": "1.4.3", - "@webassemblyjs/wasm-parser": "1.4.3", - "webassemblyjs": "1.4.3" - } - }, - "@webassemblyjs/wast-parser": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.4.3.tgz", - "integrity": "sha512-QhCsQzqV0CpsEkRYyTzQDilCNUZ+5j92f+g35bHHNqS22FppNTywNFfHPq8ZWZfYCgbectc+PoghD+xfzVFh1Q==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.4.3", - "@webassemblyjs/floating-point-hex-parser": "1.4.3", - "@webassemblyjs/helper-code-frame": "1.4.3", - "@webassemblyjs/helper-fsm": "1.4.3", - "long": "3.2.0", - "webassemblyjs": "1.4.3" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.4.3.tgz", - "integrity": "sha512-EgXk4anf8jKmuZJsqD8qy5bz2frEQhBvZruv+bqwNoLWUItjNSFygk8ywL3JTEz9KtxTlAmqTXNrdD1d9gNDtg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.4.3", - "@webassemblyjs/wast-parser": "1.4.3", - "long": "3.2.0" - } - }, - "abbrev": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", - "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", - "dev": true - }, - "accepts": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", - "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", - "dev": true, - "requires": { - "mime-types": "2.1.18", - "negotiator": "0.6.1" - } - }, - "acorn": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.6.2.tgz", - "integrity": "sha512-zUzo1E5dI2Ey8+82egfnttyMlMZ2y0D8xOCO3PNPPlYXpl8NZvF6Qk9L9BEtJs+43FqEmfBViDqc5d1ckRDguw==", - "dev": true - }, - "acorn-dynamic-import": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz", - "integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==", - "dev": true, - "requires": { - "acorn": "5.6.2" - } - }, - "adm-zip": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.4.tgz", - "integrity": "sha1-ph7VrmkFw66lizplfSUDMJEFJzY=", - "dev": true - }, - "after": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", - "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", - "dev": true - }, - "agent-base": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.0.tgz", - "integrity": "sha512-c+R/U5X+2zz2+UCrCFv6odQzJdoqI+YecuhnAJLa1zYaMc13zPfwMwZrr91Pd1DYNo/yPRbiM4WVf9whgwFsIg==", - "dev": true, - "requires": { - "es6-promisify": "5.0.0" - } - }, - "ajv": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.4.0.tgz", - "integrity": "sha1-06/3jpJ3VJdx2vAWTP9ISCt1T8Y=", - "dev": true, - "requires": { - "fast-deep-equal": "1.1.0", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1", - "uri-js": "3.0.2" - } - }, - "ajv-keywords": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", - "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", - "dev": true - }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "dev": true, - "requires": { - "kind-of": "3.2.2", - "longest": "1.0.1", - "repeat-string": "1.6.1" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true - }, - "ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", - "dev": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "1.9.2" - } - }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "3.1.10", - "normalize-path": "2.1.1" - } - }, - "app-root-path": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.0.1.tgz", - "integrity": "sha1-zWLc+OT9WkF+/GZNLlsQZTxlG0Y=", - "dev": true - }, - "append-transform": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", - "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", - "dev": true, - "requires": { - "default-require-extensions": "2.0.0" - } - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "dev": true, - "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.6" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "1.0.3" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", - "dev": true - }, - "array-flatten": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.1.tgz", - "integrity": "sha1-Qmu52oQJDBg42BLIFQryCoMx4pY=", - "dev": true - }, - "array-includes": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", - "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", - "dev": true, - "requires": { - "define-properties": "1.1.2", - "es-abstract": "1.12.0" - } - }, - "array-slice": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", - "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", - "dev": true - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "1.0.3" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "arraybuffer.slice": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz", - "integrity": "sha1-8zshWfBTKj8xB6JywMz70a0peco=", - "dev": true - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true - }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", - "dev": true, - "optional": true - }, - "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", - "dev": true - }, - "asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "inherits": "2.0.3", - "minimalistic-assert": "1.0.1" - } - }, - "assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", - "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", - "dev": true, - "requires": { - "util": "0.10.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "requires": { - "inherits": "2.0.1" - } - } - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true - }, - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true - }, - "async-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", - "dev": true - }, - "async-foreach": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", - "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", - "dev": true, - "optional": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "atob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.1.tgz", - "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=", - "dev": true - }, - "autoprefixer": { - "version": "8.6.2", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-8.6.2.tgz", - "integrity": "sha512-cv9v1mYYBcAnZq4MHseJ9AIdjQmNahnpCpPO46oTkQJS2GggsBp2azHjNpAuQ95Epvsg+AIsyjYhfI9YwFxGSA==", - "dev": true, - "requires": { - "browserslist": "3.2.8", - "caniuse-lite": "1.0.30000855", - "normalize-range": "0.1.2", - "num2fraction": "1.2.2", - "postcss": "6.0.22", - "postcss-value-parser": "3.3.0" - } - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", - "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==", - "dev": true - }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "1.1.3", - "esutils": "2.0.2", - "js-tokens": "3.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "babel-generator": { - "version": "6.26.1", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", - "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", - "dev": true, - "requires": { - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "detect-indent": "4.0.0", - "jsesc": "1.3.0", - "lodash": "4.17.10", - "source-map": "0.5.7", - "trim-right": "1.0.1" - } - }, - "babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "dev": true, - "requires": { - "core-js": "2.5.7", - "regenerator-runtime": "0.11.1" - } - }, - "babel-template": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "lodash": "4.17.10" - } - }, - "babel-traverse": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", - "dev": true, - "requires": { - "babel-code-frame": "6.26.0", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "debug": "2.6.9", - "globals": "9.18.0", - "invariant": "2.2.4", - "lodash": "4.17.10" - } - }, - "babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "esutils": "2.0.2", - "lodash": "4.17.10", - "to-fast-properties": "1.0.3" - } - }, - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true - }, - "backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "cache-base": "1.0.1", - "class-utils": "0.3.6", - "component-emitter": "1.2.1", - "define-property": "1.0.0", - "isobject": "3.0.1", - "mixin-deep": "1.3.1", - "pascalcase": "0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "1.0.2" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" - } - } - } - }, - "base64-arraybuffer": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", - "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", - "dev": true - }, - "base64-js": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", - "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", - "dev": true - }, - "base64id": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", - "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=", - "dev": true - }, - "batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", - "dev": true, - "optional": true, - "requires": { - "tweetnacl": "0.14.5" - } - }, - "better-assert": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", - "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", - "dev": true, - "requires": { - "callsite": "1.0.0" - } - }, - "big.js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", - "dev": true - }, - "binary-extensions": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", - "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", - "dev": true - }, - "blob": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz", - "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE=", - "dev": true - }, - "block-stream": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", - "dev": true, - "optional": true, - "requires": { - "inherits": "2.0.3" - } - }, - "blocking-proxy": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz", - "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==", - "dev": true, - "requires": { - "minimist": "1.2.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "bluebird": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", - "dev": true - }, - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true - }, - "body-parser": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", - "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", - "dev": true, - "requires": { - "bytes": "3.0.0", - "content-type": "1.0.4", - "debug": "2.6.9", - "depd": "1.1.2", - "http-errors": "1.6.3", - "iconv-lite": "0.4.19", - "on-finished": "2.3.0", - "qs": "6.5.1", - "raw-body": "2.3.2", - "type-is": "1.6.16" - }, - "dependencies": { - "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", - "dev": true - } - } - }, - "bonjour": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", - "dev": true, - "requires": { - "array-flatten": "2.1.1", - "deep-equal": "1.0.1", - "dns-equal": "1.0.0", - "dns-txt": "2.0.2", - "multicast-dns": "6.2.3", - "multicast-dns-service-types": "1.1.0" - } - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", - "dev": true - }, - "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "1.1.0", - "array-unique": "0.3.2", - "extend-shallow": "2.0.1", - "fill-range": "4.0.0", - "isobject": "3.0.1", - "repeat-element": "1.1.2", - "snapdragon": "0.8.2", - "snapdragon-node": "2.1.1", - "split-string": "3.1.0", - "to-regex": "3.0.2" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - } - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, - "requires": { - "buffer-xor": "1.0.3", - "cipher-base": "1.0.4", - "create-hash": "1.2.0", - "evp_bytestokey": "1.0.3", - "inherits": "2.0.3", - "safe-buffer": "5.1.2" - } - }, - "browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "dev": true, - "requires": { - "browserify-aes": "1.2.0", - "browserify-des": "1.0.1", - "evp_bytestokey": "1.0.3" - } - }, - "browserify-des": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.1.tgz", - "integrity": "sha512-zy0Cobe3hhgpiOM32Tj7KQ3Vl91m0njwsjzZQK1L+JDf11dzP9qIvjreVinsvXrgfjhStXwUWAEpB9D7Gwmayw==", - "dev": true, - "requires": { - "cipher-base": "1.0.4", - "des.js": "1.0.0", - "inherits": "2.0.3" - } - }, - "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "randombytes": "2.0.6" - } - }, - "browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "browserify-rsa": "4.0.1", - "create-hash": "1.2.0", - "create-hmac": "1.1.7", - "elliptic": "6.4.0", - "inherits": "2.0.3", - "parse-asn1": "5.1.1" - } - }, - "browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "dev": true, - "requires": { - "pako": "1.0.6" - } - }, - "browserslist": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz", - "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", - "dev": true, - "requires": { - "caniuse-lite": "1.0.30000855", - "electron-to-chromium": "1.3.48" - } - }, - "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", - "dev": true, - "requires": { - "base64-js": "1.3.0", - "ieee754": "1.1.12", - "isarray": "1.0.0" - } - }, - "buffer-from": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz", - "integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ==", - "dev": true - }, - "buffer-indexof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", - "dev": true - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true - }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true - }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", - "dev": true - }, - "builtins": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", - "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=", - "dev": true - }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "dev": true - }, - "cacache": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", - "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", - "dev": true, - "requires": { - "bluebird": "3.5.1", - "chownr": "1.0.1", - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "lru-cache": "4.1.3", - "mississippi": "2.0.0", - "mkdirp": "0.5.1", - "move-concurrently": "1.0.1", - "promise-inflight": "1.0.1", - "rimraf": "2.6.2", - "ssri": "5.3.0", - "unique-filename": "1.1.0", - "y18n": "4.0.0" - } - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "1.0.0", - "component-emitter": "1.2.1", - "get-value": "2.0.6", - "has-value": "1.0.0", - "isobject": "3.0.1", - "set-value": "2.0.0", - "to-object-path": "0.3.0", - "union-value": "1.0.0", - "unset-value": "1.0.0" - } - }, - "cache-loader": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cache-loader/-/cache-loader-1.2.2.tgz", - "integrity": "sha512-rsGh4SIYyB9glU+d0OcHwiXHXBoUgDhHZaQ1KAbiXqfz1CDPxtTboh1gPbJ0q2qdO8a9lfcjgC5CJ2Ms32y5bw==", - "dev": true, - "requires": { - "loader-utils": "1.1.0", - "mkdirp": "0.5.1", - "neo-async": "2.5.1", - "schema-utils": "0.4.5" - } - }, - "callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", - "dev": true - }, - "camel-case": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", - "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", - "dev": true, - "requires": { - "no-case": "2.3.2", - "upper-case": "1.1.3" - } - }, - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true, - "optional": true - }, - "camelcase-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", - "dev": true, - "requires": { - "camelcase": "2.1.1", - "map-obj": "1.0.1" - }, - "dependencies": { - "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", - "dev": true - } - } - }, - "caniuse-lite": { - "version": "1.0.30000855", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000855.tgz", - "integrity": "sha512-ajORrkXa5UYk62P5PK6ZmBraYOAOr9HWy+XxLwjDg8Ys/5KiSyarg8tIA32ZVqbFhtz67wyySXnU9imkh2ZT2w==", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", - "dev": true, - "optional": true, - "requires": { - "align-text": "0.1.4", - "lazy-cache": "1.0.4" - } - }, - "chalk": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.2.2.tgz", - "integrity": "sha512-LvixLAQ4MYhbf7hgL4o5PeK32gJKvVzDRiSNIApDofQvyhl8adgG2lJVXn4+ekQoK7HL9RF8lqxwerpe0x2pCw==", - "dev": true, - "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" - }, - "dependencies": { - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "dev": true, - "requires": { - "has-flag": "2.0.0" - } - } - } - }, - "chokidar": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.3.tgz", - "integrity": "sha512-zW8iXYZtXMx4kux/nuZVXjkLP+CyIK5Al5FHnj1OgTKGZfp4Oy6/ymtMSKFv3GD8DviEmUPmJg9eFdJ/JzudMg==", - "dev": true, - "requires": { - "anymatch": "2.0.0", - "async-each": "1.0.1", - "braces": "2.3.2", - "fsevents": "1.2.4", - "glob-parent": "3.1.0", - "inherits": "2.0.3", - "is-binary-path": "1.0.1", - "is-glob": "4.0.0", - "normalize-path": "2.1.1", - "path-is-absolute": "1.0.1", - "readdirp": "2.1.0", - "upath": "1.1.0" - } - }, - "chownr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", - "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", - "dev": true - }, - "chrome-trace-event": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-0.1.3.tgz", - "integrity": "sha512-sjndyZHrrWiu4RY7AkHgjn80GfAM2ZSzUkZLV/Js59Ldmh6JDThf0SUmOHU53rFu2rVxxfCzJ30Ukcfch3Gb/A==", - "dev": true - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.2" - } - }, - "circular-dependency-plugin": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.0.2.tgz", - "integrity": "sha512-oC7/DVAyfcY3UWKm0sN/oVoDedQDQiw/vIiAnuTWTpE5s0zWf7l3WY417Xw/Fbi/QbAjctAkxgMiS9P0s3zkmA==", - "dev": true - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "3.1.0", - "define-property": "0.2.5", - "isobject": "3.0.1", - "static-extend": "0.1.2" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - } - } - }, - "clean-css": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.1.11.tgz", - "integrity": "sha1-Ls3xRaujj1R0DybO/Q/z4D4SXWo=", - "dev": true, - "requires": { - "source-map": "0.5.7" - } - }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "dev": true, - "optional": true, - "requires": { - "center-align": "0.1.3", - "right-align": "0.1.3", - "wordwrap": "0.0.2" - }, - "dependencies": { - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true, - "optional": true - } - } - }, - "clone": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", - "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=", - "dev": true - }, - "clone-deep": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-2.0.2.tgz", - "integrity": "sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==", - "dev": true, - "requires": { - "for-own": "1.0.0", - "is-plain-object": "2.0.4", - "kind-of": "6.0.2", - "shallow-clone": "1.0.0" - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "codelyzer": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-4.2.1.tgz", - "integrity": "sha512-CKwfgpfkqi9dyzy4s6ELaxJ54QgJ6A8iTSsM4bzHbLuTpbKncvNc3DUlCvpnkHBhK47gEf4qFsWoYqLrJPhy6g==", - "dev": true, - "requires": { - "app-root-path": "2.0.1", - "css-selector-tokenizer": "0.7.0", - "cssauron": "1.4.0", - "semver-dsl": "1.0.1", - "source-map": "0.5.7", - "sprintf-js": "1.0.3" - } - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "requires": { - "map-visit": "1.0.0", - "object-visit": "1.0.1" - } - }, - "color-convert": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz", - "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==", - "dev": true, - "requires": { - "color-name": "1.1.1" - } - }, - "color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=", - "dev": true - }, - "colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", - "dev": true - }, - "combine-lists": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/combine-lists/-/combine-lists-1.0.1.tgz", - "integrity": "sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=", - "dev": true, - "requires": { - "lodash": "4.17.10" - } - }, - "combined-stream": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", - "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", - "dev": true, - "requires": { - "delayed-stream": "1.0.0" - } - }, - "commander": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", - "dev": true - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "compare-versions": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.3.0.tgz", - "integrity": "sha512-MAAAIOdi2s4Gl6rZ76PNcUa9IOYB+5ICdT41o5uMRf09aEu/F9RK+qhe8RjXNPwcTjGV7KU7h2P/fljThFVqyQ==", - "dev": true - }, - "component-bind": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", - "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", - "dev": true - }, - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "component-inherit": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", - "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", - "dev": true - }, - "compressible": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.14.tgz", - "integrity": "sha1-MmxfUH+7BV9UEWeCuWmoG2einac=", - "dev": true, - "requires": { - "mime-db": "1.34.0" - }, - "dependencies": { - "mime-db": { - "version": "1.34.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.34.0.tgz", - "integrity": "sha1-RS0Oz/XDA0am3B5kseruDTcZ/5o=", - "dev": true - } - } - }, - "compression": { - "version": "1.7.2", - "resolved": "http://registry.npmjs.org/compression/-/compression-1.7.2.tgz", - "integrity": "sha1-qv+81qr4VLROuygDU9WtFlH1mmk=", - "dev": true, - "requires": { - "accepts": "1.3.5", - "bytes": "3.0.0", - "compressible": "2.0.14", - "debug": "2.6.9", - "on-headers": "1.0.1", - "safe-buffer": "5.1.1", - "vary": "1.1.2" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true - } - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "1.1.0", - "inherits": "2.0.3", - "readable-stream": "2.3.6", - "typedarray": "0.0.6" - } - }, - "connect": { - "version": "3.6.6", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz", - "integrity": "sha1-Ce/2xVr3I24TcTWnJXSFi2eG9SQ=", - "dev": true, - "requires": { - "debug": "2.6.9", - "finalhandler": "1.1.0", - "parseurl": "1.3.2", - "utils-merge": "1.0.1" - }, - "dependencies": { - "finalhandler": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", - "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "on-finished": "2.3.0", - "parseurl": "1.3.2", - "statuses": "1.3.1", - "unpipe": "1.0.0" - } - }, - "statuses": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", - "dev": true - } - } - }, - "connect-history-api-fallback": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", - "integrity": "sha1-sGhzk0vF40T+9hGhlqb6rgruAVo=", - "dev": true - }, - "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", - "dev": true, - "requires": { - "date-now": "0.1.4" - } - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true - }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true - }, - "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", - "dev": true - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true - }, - "convert-source-map": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", - "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=", - "dev": true - }, - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", - "dev": true - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", - "dev": true - }, - "copy-concurrently": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", - "dev": true, - "requires": { - "aproba": "1.2.0", - "fs-write-stream-atomic": "1.0.10", - "iferr": "0.1.5", - "mkdirp": "0.5.1", - "rimraf": "2.6.2", - "run-queue": "1.0.3" - } - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, - "copy-webpack-plugin": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.5.1.tgz", - "integrity": "sha512-OlTo6DYg0XfTKOF8eLf79wcHm4Ut10xU2cRBRPMW/NA5F9VMjZGTfRHWDIYC3s+1kObGYrBLshXWU1K0hILkNQ==", - "dev": true, - "requires": { - "cacache": "10.0.4", - "find-cache-dir": "1.0.0", - "globby": "7.1.1", - "is-glob": "4.0.0", - "loader-utils": "1.1.0", - "minimatch": "3.0.4", - "p-limit": "1.3.0", - "serialize-javascript": "1.5.0" - } - }, - "core-js": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", - "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "cosmiconfig": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", - "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", - "dev": true, - "requires": { - "is-directory": "0.3.1", - "js-yaml": "3.12.0", - "minimist": "1.2.0", - "object-assign": "4.1.1", - "os-homedir": "1.0.2", - "parse-json": "2.2.0", - "require-from-string": "1.2.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "create-ecdh": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", - "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "elliptic": "6.4.0" - } - }, - "create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, - "requires": { - "cipher-base": "1.0.4", - "inherits": "2.0.3", - "md5.js": "1.3.4", - "ripemd160": "2.0.2", - "sha.js": "2.4.11" - } - }, - "create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, - "requires": { - "cipher-base": "1.0.4", - "create-hash": "1.2.0", - "inherits": "2.0.3", - "ripemd160": "2.0.2", - "safe-buffer": "5.1.2", - "sha.js": "2.4.11" - } - }, - "cross-spawn": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", - "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", - "dev": true, - "optional": true, - "requires": { - "lru-cache": "4.1.3", - "which": "1.3.1" - } - }, - "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", - "dev": true, - "requires": { - "boom": "2.10.1" - } - }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dev": true, - "requires": { - "browserify-cipher": "1.0.1", - "browserify-sign": "4.0.4", - "create-ecdh": "4.0.3", - "create-hash": "1.2.0", - "create-hmac": "1.1.7", - "diffie-hellman": "5.0.3", - "inherits": "2.0.3", - "pbkdf2": "3.0.16", - "public-encrypt": "4.0.2", - "randombytes": "2.0.6", - "randomfill": "1.0.4" - } - }, - "css-parse": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.7.0.tgz", - "integrity": "sha1-Mh9s9zeCpv91ERE5D8BeLGV9jJs=", - "dev": true - }, - "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", - "dev": true, - "requires": { - "boolbase": "1.0.0", - "css-what": "2.1.0", - "domutils": "1.5.1", - "nth-check": "1.0.1" - } - }, - "css-selector-tokenizer": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", - "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=", - "dev": true, - "requires": { - "cssesc": "0.1.0", - "fastparse": "1.1.1", - "regexpu-core": "1.0.0" - } - }, - "css-what": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", - "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0=", - "dev": true - }, - "cssauron": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz", - "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=", - "dev": true, - "requires": { - "through": "2.3.8" - } - }, - "cssesc": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", - "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", - "dev": true - }, - "cuint": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz", - "integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs=", - "dev": true - }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "dev": true, - "requires": { - "array-find-index": "1.0.2" - } - }, - "custom-event": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", - "dev": true - }, - "cyclist": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", - "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", - "dev": true - }, - "d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", - "dev": true, - "requires": { - "es5-ext": "0.10.45" - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "1.0.0" - } - }, - "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true - }, - "deep-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", - "dev": true - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "default-require-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", - "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", - "dev": true, - "requires": { - "strip-bom": "3.0.0" - }, - "dependencies": { - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } - } - }, - "define-properties": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", - "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", - "dev": true, - "requires": { - "foreach": "2.0.5", - "object-keys": "1.0.11" - } - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "requires": { - "is-descriptor": "1.0.2", - "isobject": "3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" - } - } - } - }, - "del": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", - "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", - "dev": true, - "requires": { - "globby": "6.1.0", - "is-path-cwd": "1.0.0", - "is-path-in-cwd": "1.0.1", - "p-map": "1.2.0", - "pify": "3.0.0", - "rimraf": "2.6.2" - }, - "dependencies": { - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "dev": true, - "requires": { - "array-union": "1.0.2", - "glob": "7.1.2", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - } - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true - }, - "des.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", - "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "minimalistic-assert": "1.0.1" - } - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", - "dev": true - }, - "detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", - "dev": true, - "requires": { - "repeating": "2.0.1" - } - }, - "detect-node": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.3.tgz", - "integrity": "sha1-ogM8CcyOFY03dI+951B4Mr1s4Sc=", - "dev": true - }, - "di": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", - "dev": true - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "miller-rabin": "4.0.1", - "randombytes": "2.0.6" - } - }, - "dir-glob": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz", - "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", - "dev": true, - "requires": { - "arrify": "1.0.1", - "path-type": "3.0.0" - } - }, - "dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", - "dev": true - }, - "dns-packet": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", - "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", - "dev": true, - "requires": { - "ip": "1.1.5", - "safe-buffer": "5.1.2" - } - }, - "dns-txt": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", - "dev": true, - "requires": { - "buffer-indexof": "1.1.1" - } - }, - "dom-converter": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.1.4.tgz", - "integrity": "sha1-pF71cnuJDJv/5tfIduexnLDhfzs=", - "dev": true, - "requires": { - "utila": "0.3.3" - }, - "dependencies": { - "utila": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", - "integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY=", - "dev": true - } - } - }, - "dom-serialize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", - "dev": true, - "requires": { - "custom-event": "1.0.1", - "ent": "2.2.0", - "extend": "3.0.1", - "void-elements": "2.0.1" - } - }, - "dom-serializer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", - "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", - "dev": true, - "requires": { - "domelementtype": "1.1.3", - "entities": "1.1.1" - }, - "dependencies": { - "domelementtype": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", - "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", - "dev": true - } - } - }, - "domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", - "dev": true - }, - "domelementtype": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", - "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=", - "dev": true - }, - "domhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz", - "integrity": "sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ=", - "dev": true, - "requires": { - "domelementtype": "1.3.0" - } - }, - "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "dev": true, - "requires": { - "dom-serializer": "0.1.0", - "domelementtype": "1.3.0" - } - }, - "duplexify": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz", - "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", - "dev": true, - "requires": { - "end-of-stream": "1.4.1", - "inherits": "2.0.3", - "readable-stream": "2.3.6", - "stream-shift": "1.0.0" - } - }, - "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "dev": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", - "dev": true - }, - "ejs": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz", - "integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==", - "dev": true - }, - "electron-to-chromium": { - "version": "1.3.48", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.48.tgz", - "integrity": "sha1-07DYWTgUBE4JLs4hCPw6ya6kuQA=", - "dev": true - }, - "elliptic": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", - "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "brorand": "1.1.0", - "hash.js": "1.1.4", - "hmac-drbg": "1.0.1", - "inherits": "2.0.3", - "minimalistic-assert": "1.0.1", - "minimalistic-crypto-utils": "1.0.1" - } - }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "dev": true - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true - }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dev": true, - "requires": { - "once": "1.4.0" - } - }, - "engine.io": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-1.8.3.tgz", - "integrity": "sha1-jef5eJXSDTm4X4ju7nd7K9QrE9Q=", - "dev": true, - "requires": { - "accepts": "1.3.3", - "base64id": "1.0.0", - "cookie": "0.3.1", - "debug": "2.3.3", - "engine.io-parser": "1.3.2", - "ws": "1.1.2" - }, - "dependencies": { - "accepts": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", - "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", - "dev": true, - "requires": { - "mime-types": "2.1.18", - "negotiator": "0.6.1" - } - }, - "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", - "dev": true, - "requires": { - "ms": "0.7.2" - } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - } - } - }, - "engine.io-client": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-1.8.3.tgz", - "integrity": "sha1-F5jtk0USRkU9TG9jXXogH+lA1as=", - "dev": true, - "requires": { - "component-emitter": "1.2.1", - "component-inherit": "0.0.3", - "debug": "2.3.3", - "engine.io-parser": "1.3.2", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "parsejson": "0.0.3", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "ws": "1.1.2", - "xmlhttprequest-ssl": "1.5.3", - "yeast": "0.1.2" - }, - "dependencies": { - "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", - "dev": true, - "requires": { - "ms": "0.7.2" - } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - } - } - }, - "engine.io-parser": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-1.3.2.tgz", - "integrity": "sha1-k3sHnwAH0Ik+xW1GyyILjLQ1Igo=", - "dev": true, - "requires": { - "after": "0.8.2", - "arraybuffer.slice": "0.0.6", - "base64-arraybuffer": "0.1.5", - "blob": "0.0.4", - "has-binary": "0.1.7", - "wtf-8": "1.0.0" - } - }, - "enhanced-resolve": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.0.0.tgz", - "integrity": "sha512-jox/62b2GofV1qTUQTMPEJSDIGycS43evqYzD/KVtEb9OCoki9cnacUPxCrZa7JfPzZSYOCZhu9O9luaMxAX8g==", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "memory-fs": "0.4.1", - "tapable": "1.0.0" - } - }, - "ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", - "dev": true - }, - "entities": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", - "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", - "dev": true - }, - "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "dev": true, - "requires": { - "prr": "1.0.1" - } - }, - "error-ex": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", - "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", - "dev": true, - "requires": { - "is-arrayish": "0.2.1" - } - }, - "es-abstract": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz", - "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", - "dev": true, - "requires": { - "es-to-primitive": "1.1.1", - "function-bind": "1.1.1", - "has": "1.0.3", - "is-callable": "1.1.3", - "is-regex": "1.0.4" - } - }, - "es-to-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", - "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", - "dev": true, - "requires": { - "is-callable": "1.1.3", - "is-date-object": "1.0.1", - "is-symbol": "1.0.1" - } - }, - "es5-ext": { - "version": "0.10.45", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.45.tgz", - "integrity": "sha512-FkfM6Vxxfmztilbxxz5UKSD4ICMf5tSpRFtDNtkAhOxZ0EKtX6qwmXNyH/sFyIbX2P/nU5AMiA9jilWsUGJzCQ==", - "dev": true, - "requires": { - "es6-iterator": "2.0.3", - "es6-symbol": "3.1.1", - "next-tick": "1.0.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.45", - "es6-symbol": "3.1.1" - } - }, - "es6-promise": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz", - "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==", - "dev": true - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", - "dev": true, - "requires": { - "es6-promise": "4.2.4" - } - }, - "es6-symbol": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", - "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.45" - } - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "escodegen": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", - "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", - "dev": true, - "requires": { - "esprima": "2.7.3", - "estraverse": "1.9.3", - "esutils": "2.0.2", - "optionator": "0.8.2", - "source-map": "0.2.0" - }, - "dependencies": { - "source-map": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", - "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", - "dev": true, - "optional": true, - "requires": { - "amdefine": "1.0.1" - } - } - } - }, - "eslint-scope": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", - "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", - "dev": true, - "requires": { - "esrecurse": "4.2.1", - "estraverse": "4.2.0" - }, - "dependencies": { - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true - } - } - }, - "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", - "dev": true - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "requires": { - "estraverse": "4.2.0" - }, - "dependencies": { - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true - } - } - }, - "estraverse": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", - "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", - "dev": true - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "dev": true - }, - "eventemitter3": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", - "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", - "dev": true - }, - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", - "dev": true - }, - "eventsource": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-0.1.6.tgz", - "integrity": "sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=", - "dev": true, - "requires": { - "original": "1.0.1" - } - }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "requires": { - "md5.js": "1.3.4", - "safe-buffer": "5.1.2" - } - }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "requires": { - "cross-spawn": "5.1.0", - "get-stream": "3.0.0", - "is-stream": "1.1.0", - "npm-run-path": "2.0.2", - "p-finally": "1.0.0", - "signal-exit": "3.0.2", - "strip-eof": "1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "4.1.3", - "shebang-command": "1.2.0", - "which": "1.3.1" - } - } - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "expand-braces": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/expand-braces/-/expand-braces-0.1.2.tgz", - "integrity": "sha1-SIsdHSRRyz06axks/AMPRMWFX+o=", - "dev": true, - "requires": { - "array-slice": "0.2.3", - "array-unique": "0.2.1", - "braces": "0.1.5" - }, - "dependencies": { - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true - }, - "braces": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-0.1.5.tgz", - "integrity": "sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=", - "dev": true, - "requires": { - "expand-range": "0.1.1" - } - }, - "expand-range": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-0.1.1.tgz", - "integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=", - "dev": true, - "requires": { - "is-number": "0.1.1", - "repeat-string": "0.2.2" - } - }, - "is-number": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-0.1.1.tgz", - "integrity": "sha1-aaevEWlj1HIG7JvZtIoUIW8eOAY=", - "dev": true - }, - "repeat-string": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-0.2.2.tgz", - "integrity": "sha1-x6jTI2BoNiBZp+RlH8aITosftK4=", - "dev": true - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "posix-character-classes": "0.1.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - } - } - }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "dev": true, - "requires": { - "fill-range": "2.2.4" - }, - "dependencies": { - "fill-range": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", - "dev": true, - "requires": { - "is-number": "2.1.0", - "isobject": "2.1.0", - "randomatic": "3.0.0", - "repeat-element": "1.1.2", - "repeat-string": "1.6.1" - } - }, - "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - }, - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "express": { - "version": "4.16.3", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", - "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", - "dev": true, - "requires": { - "accepts": "1.3.5", - "array-flatten": "1.1.1", - "body-parser": "1.18.2", - "content-disposition": "0.5.2", - "content-type": "1.0.4", - "cookie": "0.3.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "1.1.2", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "etag": "1.8.1", - "finalhandler": "1.1.1", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "1.1.2", - "on-finished": "2.3.0", - "parseurl": "1.3.2", - "path-to-regexp": "0.1.7", - "proxy-addr": "2.0.3", - "qs": "6.5.1", - "range-parser": "1.2.0", - "safe-buffer": "5.1.1", - "send": "0.16.2", - "serve-static": "1.13.2", - "setprototypeof": "1.1.0", - "statuses": "1.4.0", - "type-is": "1.6.16", - "utils-merge": "1.0.1", - "vary": "1.1.2" - }, - "dependencies": { - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", - "dev": true - }, - "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", - "dev": true - }, - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true - } - } - }, - "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", - "dev": true - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "1.0.0", - "is-extendable": "1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "2.0.4" - } - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "0.3.2", - "define-property": "1.0.0", - "expand-brackets": "2.1.4", - "extend-shallow": "2.0.1", - "fragment-cache": "0.2.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "1.0.2" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" - } - } - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "fastparse": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", - "integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=", - "dev": true - }, - "faye-websocket": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", - "dev": true, - "requires": { - "websocket-driver": "0.7.0" - } - }, - "file-loader": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", - "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", - "dev": true, - "requires": { - "loader-utils": "1.1.0", - "schema-utils": "0.4.5" - } - }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", - "dev": true - }, - "fileset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", - "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", - "dev": true, - "requires": { - "glob": "7.1.2", - "minimatch": "3.0.4" - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "2.0.1", - "is-number": "3.0.0", - "repeat-string": "1.6.1", - "to-regex-range": "2.1.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - } - } - }, - "finalhandler": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", - "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "on-finished": "2.3.0", - "parseurl": "1.3.2", - "statuses": "1.4.0", - "unpipe": "1.0.0" - } - }, - "find-cache-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", - "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", - "dev": true, - "requires": { - "commondir": "1.0.1", - "make-dir": "1.3.0", - "pkg-dir": "2.0.0" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "2.0.0" - } - }, - "flush-write-stream": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz", - "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.6" - } - }, - "follow-redirects": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.0.tgz", - "integrity": "sha512-fdrt472/9qQ6Kgjvb935ig6vJCuofpBUD14f9Vb+SLlm7xIe4Qva5gey8EKtv8lp7ahE1wilg3xL1znpVGtZIA==", - "dev": true, - "requires": { - "debug": "3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", - "dev": true, - "requires": { - "for-in": "1.0.2" - } - }, - "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", - "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", - "dev": true, - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.6", - "mime-types": "2.1.18" - } - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", - "dev": true - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "0.2.2" - } - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "dev": true - }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.6" - } - }, - "fs-access": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", - "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", - "dev": true, - "requires": { - "null-check": "1.0.0" - } - }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "iferr": "0.1.5", - "imurmurhash": "0.1.4", - "readable-stream": "2.3.6" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", - "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", - "dev": true, - "optional": true, - "requires": { - "nan": "2.10.0", - "node-pre-gyp": "0.10.0" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "requires": { - "balanced-match": "1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "debug": { - "version": "2.6.9", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "2.2.4" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" - } - }, - "glob": { - "version": "7.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.21", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safer-buffer": "2.1.2" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimatch": "3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "requires": { - "brace-expansion": "1.1.11" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true - }, - "minipass": { - "version": "2.2.4", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "5.1.1", - "yallist": "3.0.2" - } - }, - "minizlib": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "2.2.4" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "needle": { - "version": "2.2.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "2.6.9", - "iconv-lite": "0.4.21", - "sax": "1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.10.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "detect-libc": "1.0.3", - "mkdirp": "0.5.1", - "needle": "2.2.0", - "nopt": "4.0.1", - "npm-packlist": "1.1.10", - "npmlog": "4.1.2", - "rc": "1.2.7", - "rimraf": "2.6.2", - "semver": "5.5.0", - "tar": "4.4.1" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "abbrev": "1.1.1", - "osenv": "0.1.5" - } - }, - "npm-bundled": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "npm-packlist": { - "version": "1.1.10", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ignore-walk": "3.0.1", - "npm-bundled": "1.0.3" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "requires": { - "wrappy": "1.0.2" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.7", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "0.5.1", - "ini": "1.3.5", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.1", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" - } - }, - "rimraf": { - "version": "2.6.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "glob": "7.1.2" - } - }, - "safe-buffer": { - "version": "5.1.1", - "bundled": true, - "dev": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, - "dev": true, - "optional": true - }, - "semver": { - "version": "5.5.0", - "bundled": true, - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "tar": { - "version": "4.4.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "chownr": "1.0.1", - "fs-minipass": "1.2.5", - "minipass": "2.2.4", - "minizlib": "1.1.0", - "mkdirp": "0.5.1", - "safe-buffer": "5.1.1", - "yallist": "3.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "1.0.2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "yallist": { - "version": "3.0.2", - "bundled": true, - "dev": true - } - } - }, - "fstream": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.2" - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dev": true, - "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.3" - } - }, - "gaze": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", - "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", - "dev": true, - "optional": true, - "requires": { - "globule": "1.2.1" - } - }, - "generate-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", - "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", - "dev": true, - "optional": true - }, - "generate-object-property": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", - "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", - "dev": true, - "optional": true, - "requires": { - "is-property": "1.0.2" - } - }, - "get-caller-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", - "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", - "dev": true - }, - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "1.0.0" - } - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "dev": true, - "requires": { - "glob-parent": "2.0.0", - "is-glob": "2.0.1" - }, - "dependencies": { - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "requires": { - "is-glob": "2.0.1" - } - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - } - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "3.1.0", - "path-dirname": "1.0.2" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "2.1.1" - } - } - } - }, - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "dev": true - }, - "globby": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", - "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", - "dev": true, - "requires": { - "array-union": "1.0.2", - "dir-glob": "2.0.0", - "glob": "7.1.2", - "ignore": "3.3.8", - "pify": "3.0.0", - "slash": "1.0.0" - } - }, - "globule": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz", - "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==", - "dev": true, - "optional": true, - "requires": { - "glob": "7.1.2", - "lodash": "4.17.10", - "minimatch": "3.0.4" - } - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - }, - "handle-thing": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", - "integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=", - "dev": true - }, - "handlebars": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz", - "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", - "dev": true, - "requires": { - "async": "1.5.2", - "optimist": "0.6.1", - "source-map": "0.4.4", - "uglify-js": "2.8.29" - }, - "dependencies": { - "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true, - "requires": { - "amdefine": "1.0.1" - } - }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "dev": true, - "optional": true, - "requires": { - "source-map": "0.5.7", - "uglify-to-browserify": "1.0.2", - "yargs": "3.10.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "optional": true - } - } - } - } - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", - "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", - "dev": true, - "requires": { - "ajv": "5.5.2", - "har-schema": "2.0.0" - }, - "dependencies": { - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "requires": { - "co": "4.6.0", - "fast-deep-equal": "1.1.0", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1" - } - } - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "has-binary": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz", - "integrity": "sha1-aOYesWIQyVRaClzOBqhzkS/h5ow=", - "dev": true, - "requires": { - "isarray": "0.0.1" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - } - } - }, - "has-cors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", - "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", - "dev": true - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "requires": { - "get-value": "2.0.6", - "has-values": "1.0.0", - "isobject": "3.0.1" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" - }, - "dependencies": { - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.2" - } - }, - "hash.js": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.4.tgz", - "integrity": "sha512-A6RlQvvZEtFS5fLU43IDu0QUmBy+fDO9VMdTXvufKwIkt/rFfvICAViCax5fbDO4zdNzaC3/27ZhKUok5bAJyw==", - "dev": true, - "requires": { - "inherits": "2.0.3", - "minimalistic-assert": "1.0.1" - } - }, - "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", - "dev": true, - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - } - }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "requires": { - "hash.js": "1.1.4", - "minimalistic-assert": "1.0.1", - "minimalistic-crypto-utils": "1.0.1" - } - }, - "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", - "dev": true - }, - "hosted-git-info": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz", - "integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw==", - "dev": true - }, - "hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "obuf": "1.1.2", - "readable-stream": "2.3.6", - "wbuf": "1.7.3" - } - }, - "html-entities": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", - "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=", - "dev": true - }, - "html-minifier": { - "version": "3.5.16", - "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.16.tgz", - "integrity": "sha512-zP5EfLSpiLRp0aAgud4CQXPQZm9kXwWjR/cF0PfdOj+jjWnOaCgeZcll4kYXSvIBPeUMmyaSc7mM4IDtA+kboA==", - "dev": true, - "requires": { - "camel-case": "3.0.0", - "clean-css": "4.1.11", - "commander": "2.15.1", - "he": "1.1.1", - "param-case": "2.1.1", - "relateurl": "0.2.7", - "uglify-js": "3.3.28" - } - }, - "html-webpack-plugin": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", - "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", - "dev": true, - "requires": { - "html-minifier": "3.5.16", - "loader-utils": "0.2.17", - "lodash": "4.17.10", - "pretty-error": "2.1.1", - "tapable": "1.0.0", - "toposort": "1.0.7", - "util.promisify": "1.0.0" - }, - "dependencies": { - "loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", - "dev": true, - "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1", - "object-assign": "4.1.1" - } - } - } - }, - "htmlparser2": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", - "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", - "dev": true, - "requires": { - "domelementtype": "1.3.0", - "domhandler": "2.1.0", - "domutils": "1.1.6", - "readable-stream": "1.0.34" - }, - "dependencies": { - "domutils": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.1.6.tgz", - "integrity": "sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU=", - "dev": true, - "requires": { - "domelementtype": "1.3.0" - } - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } - } - }, - "http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", - "dev": true - }, - "http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "dev": true, - "requires": { - "depd": "1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": "1.4.0" - } - }, - "http-parser-js": { - "version": "0.4.13", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.13.tgz", - "integrity": "sha1-O9bW/ebjFyyTNMOzO2wZPYD+ETc=", - "dev": true - }, - "http-proxy": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", - "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", - "dev": true, - "requires": { - "eventemitter3": "3.1.0", - "follow-redirects": "1.5.0", - "requires-port": "1.0.0" - } - }, - "http-proxy-middleware": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", - "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", - "dev": true, - "requires": { - "http-proxy": "1.17.0", - "is-glob": "4.0.0", - "lodash": "4.17.10", - "micromatch": "3.1.10" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "jsprim": "1.4.1", - "sshpk": "1.14.2" - } - }, - "https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", - "dev": true - }, - "https-proxy-agent": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", - "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", - "dev": true, - "requires": { - "agent-base": "4.2.0", - "debug": "3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "iconv-lite": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", - "dev": true - }, - "ieee754": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", - "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==", - "dev": true - }, - "iferr": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", - "dev": true - }, - "ignore": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.8.tgz", - "integrity": "sha512-pUh+xUQQhQzevjRHHFqqcTy0/dP/kS9I8HSrUydhihjuD09W6ldVWFtIrwhXdUJHis3i2rZNqEHpZH/cbinFbg==", - "dev": true - }, - "image-size": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", - "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", - "dev": true, - "optional": true - }, - "immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", - "dev": true - }, - "import-local": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz", - "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==", - "dev": true, - "requires": { - "pkg-dir": "2.0.0", - "resolve-cwd": "2.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "in-publish": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", - "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=", - "dev": true, - "optional": true - }, - "indent-string": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", - "dev": true, - "requires": { - "repeating": "2.0.1" - } - }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true - }, - "internal-ip": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-1.2.0.tgz", - "integrity": "sha1-rp+/k7mEh4eF1QqN4bNWlWBYz1w=", - "dev": true, - "requires": { - "meow": "3.7.0" - } - }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "requires": { - "loose-envify": "1.3.1" - } - }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", - "dev": true - }, - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", - "dev": true - }, - "ipaddr.js": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", - "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs=", - "dev": true - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "requires": { - "binary-extensions": "1.11.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-builtin-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", - "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", - "dev": true, - "requires": { - "builtin-modules": "1.1.1" - } - }, - "is-callable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", - "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=", - "dev": true - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", - "dev": true - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "is-directory": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", - "dev": true - }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", - "dev": true - }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", - "dev": true, - "requires": { - "is-primitive": "2.0.0" - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-glob": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", - "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", - "dev": true, - "requires": { - "is-extglob": "2.1.1" - } - }, - "is-my-ip-valid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz", - "integrity": "sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ==", - "dev": true, - "optional": true - }, - "is-my-json-valid": { - "version": "2.17.2", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz", - "integrity": "sha512-IBhBslgngMQN8DDSppmgDv7RNrlFotuuDsKcrCP3+HbFaVivIBU7u9oiiErw8sH4ynx3+gOGQ3q2otkgiSi6kg==", - "dev": true, - "optional": true, - "requires": { - "generate-function": "2.0.0", - "generate-object-property": "1.2.0", - "is-my-ip-valid": "1.0.0", - "jsonpointer": "4.0.1", - "xtend": "4.0.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-odd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz", - "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==", - "dev": true, - "requires": { - "is-number": "4.0.0" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true - } - } - }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", - "dev": true - }, - "is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", - "dev": true, - "requires": { - "is-path-inside": "1.0.1" - } - }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, - "requires": { - "path-is-inside": "1.0.2" - } - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "3.0.1" - } - }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", - "dev": true - }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", - "dev": true - }, - "is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", - "dev": true, - "optional": true - }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "dev": true, - "requires": { - "has": "1.0.3" - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-symbol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", - "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", - "dev": true - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isbinaryfile": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.2.tgz", - "integrity": "sha1-Sj6XTsDLqQBNP8bN5yCeppNopiE=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "istanbul": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", - "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", - "dev": true, - "requires": { - "abbrev": "1.0.9", - "async": "1.5.2", - "escodegen": "1.8.1", - "esprima": "2.7.3", - "glob": "5.0.15", - "handlebars": "4.0.11", - "js-yaml": "3.12.0", - "mkdirp": "0.5.1", - "nopt": "3.0.6", - "once": "1.4.0", - "resolve": "1.1.7", - "supports-color": "3.2.3", - "which": "1.3.1", - "wordwrap": "1.0.0" - }, - "dependencies": { - "glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", - "dev": true, - "requires": { - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "istanbul-api": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-1.3.1.tgz", - "integrity": "sha512-duj6AlLcsWNwUpfyfHt0nWIeRiZpuShnP40YTxOGQgtaN8fd6JYSxsvxUphTDy8V5MfDXo4s/xVCIIvVCO808g==", - "dev": true, - "requires": { - "async": "2.6.1", - "compare-versions": "3.3.0", - "fileset": "2.0.3", - "istanbul-lib-coverage": "1.2.0", - "istanbul-lib-hook": "1.2.1", - "istanbul-lib-instrument": "1.10.1", - "istanbul-lib-report": "1.1.4", - "istanbul-lib-source-maps": "1.2.5", - "istanbul-reports": "1.3.0", - "js-yaml": "3.12.0", - "mkdirp": "0.5.1", - "once": "1.4.0" - }, - "dependencies": { - "async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", - "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", - "dev": true, - "requires": { - "lodash": "4.17.10" - } - } - } - }, - "istanbul-instrumenter-loader": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-3.0.1.tgz", - "integrity": "sha512-a5SPObZgS0jB/ixaKSMdn6n/gXSrK2S6q/UfRJBT3e6gQmVjwZROTODQsYW5ZNwOu78hG62Y3fWlebaVOL0C+w==", - "dev": true, - "requires": { - "convert-source-map": "1.5.1", - "istanbul-lib-instrument": "1.10.1", - "loader-utils": "1.1.0", - "schema-utils": "0.3.0" - }, - "dependencies": { - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "requires": { - "co": "4.6.0", - "fast-deep-equal": "1.1.0", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1" - } - }, - "schema-utils": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz", - "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=", - "dev": true, - "requires": { - "ajv": "5.5.2" - } - } - } - }, - "istanbul-lib-coverage": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.0.tgz", - "integrity": "sha512-GvgM/uXRwm+gLlvkWHTjDAvwynZkL9ns15calTrmhGgowlwJBbWMYzWbKqE2DT6JDP1AFXKa+Zi0EkqNCUqY0A==", - "dev": true - }, - "istanbul-lib-hook": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.2.1.tgz", - "integrity": "sha512-eLAMkPG9FU0v5L02lIkcj/2/Zlz9OuluaXikdr5iStk8FDbSwAixTK9TkYxbF0eNnzAJTwM2fkV2A1tpsIp4Jg==", - "dev": true, - "requires": { - "append-transform": "1.0.0" - } - }, - "istanbul-lib-instrument": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.1.tgz", - "integrity": "sha512-1dYuzkOCbuR5GRJqySuZdsmsNKPL3PTuyPevQfoCXJePT9C8y1ga75neU+Tuy9+yS3G/dgx8wgOmp2KLpgdoeQ==", - "dev": true, - "requires": { - "babel-generator": "6.26.1", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "istanbul-lib-coverage": "1.2.0", - "semver": "5.5.0" - } - }, - "istanbul-lib-report": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-1.1.4.tgz", - "integrity": "sha512-Azqvq5tT0U09nrncK3q82e/Zjkxa4tkFZv7E6VcqP0QCPn6oNljDPfrZEC/umNXds2t7b8sRJfs6Kmpzt8m2kA==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "1.2.0", - "mkdirp": "0.5.1", - "path-parse": "1.0.5", - "supports-color": "3.2.3" - }, - "dependencies": { - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.5.tgz", - "integrity": "sha512-8O2T/3VhrQHn0XcJbP1/GN7kXMiRAlPi+fj3uEHrjBD8Oz7Py0prSC25C09NuAZS6bgW1NNKAvCSHZXB0irSGA==", - "dev": true, - "requires": { - "debug": "3.1.0", - "istanbul-lib-coverage": "1.2.0", - "mkdirp": "0.5.1", - "rimraf": "2.6.2", - "source-map": "0.5.7" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "istanbul-reports": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.3.0.tgz", - "integrity": "sha512-y2Z2IMqE1gefWUaVjrBm0mSKvUkaBy9Vqz8iwr/r40Y9hBbIteH5wqHG/9DLTfJ9xUnUT2j7A3+VVJ6EaYBllA==", - "dev": true, - "requires": { - "handlebars": "4.0.11" - } - }, - "jasmine": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz", - "integrity": "sha1-awicChFXax8W3xG4AUbZHU6Lij4=", - "dev": true, - "requires": { - "exit": "0.1.2", - "glob": "7.1.2", - "jasmine-core": "2.8.0" - }, - "dependencies": { - "jasmine-core": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", - "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=", - "dev": true - } - } - }, - "jasmine-core": { - "version": "2.99.1", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz", - "integrity": "sha1-5kAN8ea1bhMLYcS80JPap/boyhU=", - "dev": true - }, - "jasmine-spec-reporter": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-4.2.1.tgz", - "integrity": "sha512-FZBoZu7VE5nR7Nilzy+Np8KuVIOxF4oXDPDknehCYBDE080EnlPu0afdZNmpGDBRCUBv3mj5qgqCRmk6W/K8vg==", - "dev": true, - "requires": { - "colors": "1.1.2" - } - }, - "jasminewd2": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz", - "integrity": "sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=", - "dev": true - }, - "js-base64": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.5.tgz", - "integrity": "sha512-aUnNwqMOXw3yvErjMPSQu6qIIzUmT1e5KcU1OZxRDU1g/am6mzBvcrmLAYwzmB59BHPrh5/tKaiF4OPhqRWESQ==", - "dev": true, - "optional": true - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "js-yaml": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", - "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", - "dev": true, - "requires": { - "argparse": "1.0.10", - "esprima": "4.0.0" - }, - "dependencies": { - "esprima": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", - "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", - "dev": true - } - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true, - "optional": true - }, - "jsesc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", - "dev": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "dev": true, - "optional": true, - "requires": { - "jsonify": "0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "json3": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", - "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", - "dev": true - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", - "dev": true, - "optional": true - }, - "jsonpointer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", - "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", - "dev": true, - "optional": true - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "jszip": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.1.5.tgz", - "integrity": "sha512-5W8NUaFRFRqTOL7ZDDrx5qWHJyBXy6velVudIzQUSoqAAYqzSh2Z7/m0Rf1QbmQJccegD0r+YZxBjzqoBiEeJQ==", - "dev": true, - "requires": { - "core-js": "2.3.0", - "es6-promise": "3.0.2", - "lie": "3.1.1", - "pako": "1.0.6", - "readable-stream": "2.0.6" - }, - "dependencies": { - "core-js": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.3.0.tgz", - "integrity": "sha1-+rg/uwstjchfpjbEudNMdUIMbWU=", - "dev": true - }, - "es6-promise": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz", - "integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=", - "dev": true - }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true - }, - "readable-stream": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", - "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "0.10.31", - "util-deprecate": "1.0.2" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } - } - }, - "karma": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/karma/-/karma-1.7.1.tgz", - "integrity": "sha512-k5pBjHDhmkdaUccnC7gE3mBzZjcxyxYsYVaqiL2G5AqlfLyBO5nw2VdNK+O16cveEPd/gIOWULH7gkiYYwVNHg==", - "dev": true, - "requires": { - "bluebird": "3.5.1", - "body-parser": "1.18.2", - "chokidar": "1.7.0", - "colors": "1.1.2", - "combine-lists": "1.0.1", - "connect": "3.6.6", - "core-js": "2.5.7", - "di": "0.0.1", - "dom-serialize": "2.2.1", - "expand-braces": "0.1.2", - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "http-proxy": "1.17.0", - "isbinaryfile": "3.0.2", - "lodash": "3.10.1", - "log4js": "0.6.38", - "mime": "1.6.0", - "minimatch": "3.0.4", - "optimist": "0.6.1", - "qjobs": "1.2.0", - "range-parser": "1.2.0", - "rimraf": "2.6.2", - "safe-buffer": "5.1.2", - "socket.io": "1.7.3", - "source-map": "0.5.7", - "tmp": "0.0.31", - "useragent": "2.3.0" - }, - "dependencies": { - "anymatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", - "dev": true, - "requires": { - "micromatch": "2.3.11", - "normalize-path": "2.1.1" - } - }, - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "requires": { - "arr-flatten": "1.1.0" - } - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true - }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "dev": true, - "requires": { - "expand-range": "1.8.2", - "preserve": "0.2.0", - "repeat-element": "1.1.2" - } - }, - "chokidar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", - "dev": true, - "requires": { - "anymatch": "1.3.2", - "async-each": "1.0.1", - "fsevents": "1.2.4", - "glob-parent": "2.0.0", - "inherits": "2.0.3", - "is-binary-path": "1.0.1", - "is-glob": "2.0.1", - "path-is-absolute": "1.0.1", - "readdirp": "2.1.0" - } - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "dev": true, - "requires": { - "is-posix-bracket": "0.1.1" - } - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - }, - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "requires": { - "is-glob": "2.0.1" - } - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - }, - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", - "dev": true - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "2.0.0", - "array-unique": "0.2.1", - "braces": "1.8.5", - "expand-brackets": "0.1.5", - "extglob": "0.3.2", - "filename-regex": "2.0.1", - "is-extglob": "1.0.0", - "is-glob": "2.0.1", - "kind-of": "3.2.2", - "normalize-path": "2.1.1", - "object.omit": "2.0.1", - "parse-glob": "3.0.4", - "regex-cache": "0.4.4" - } - } - } - }, - "karma-chrome-launcher": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz", - "integrity": "sha512-uf/ZVpAabDBPvdPdveyk1EPgbnloPvFFGgmRhYLTDH7gEB4nZdSBk8yTU47w1g/drLSx5uMOkjKk7IWKfWg/+w==", - "dev": true, - "requires": { - "fs-access": "1.0.1", - "which": "1.3.1" - } - }, - "karma-coverage-istanbul-reporter": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-2.0.1.tgz", - "integrity": "sha512-UcgrHkFehI5+ivMouD8NH/UOHiX4oCAtwaANylzPFdcAuD52fnCUuelacq2gh8tZ4ydhU3+xiXofSq7j5Ehygw==", - "dev": true, - "requires": { - "istanbul-api": "1.3.1", - "minimatch": "3.0.4" - } - }, - "karma-jasmine": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-1.1.2.tgz", - "integrity": "sha1-OU8rJf+0pkS5rabyLUQ+L9CIhsM=", - "dev": true - }, - "karma-jasmine-html-reporter": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-0.2.2.tgz", - "integrity": "sha1-SKjl7xiAdhfuK14zwRlMNbQ5Ukw=", - "dev": true, - "requires": { - "karma-jasmine": "1.1.2" - } - }, - "karma-source-map-support": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.3.0.tgz", - "integrity": "sha512-HcPqdAusNez/ywa+biN4EphGz62MmQyPggUsDfsHqa7tSe4jdsxgvTKuDfIazjL+IOxpVWyT7Pr4dhAV+sxX5Q==", - "dev": true, - "requires": { - "source-map-support": "0.5.6" - } - }, - "killable": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.0.tgz", - "integrity": "sha1-2ouEvUfeU5WHj5XWTQLyRJ/gXms=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", - "dev": true, - "optional": true - }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "dev": true, - "requires": { - "invert-kv": "1.0.0" - } - }, - "leb": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/leb/-/leb-0.3.0.tgz", - "integrity": "sha1-Mr7p+tFoMo1q6oUi2DP0GA7tHaM=", - "dev": true - }, - "less": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/less/-/less-3.0.4.tgz", - "integrity": "sha512-q3SyEnPKbk9zh4l36PGeW2fgynKu+FpbhiUNx/yaiBUQ3V0CbACCgb9FzYWcRgI2DJlP6eI4jc8XPrCTi55YcQ==", - "dev": true, - "requires": { - "errno": "0.1.7", - "graceful-fs": "4.1.11", - "image-size": "0.5.5", - "mime": "1.6.0", - "mkdirp": "0.5.1", - "promise": "7.3.1", - "request": "2.87.0", - "source-map": "0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - } - } - }, - "less-loader": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-4.1.0.tgz", - "integrity": "sha512-KNTsgCE9tMOM70+ddxp9yyt9iHqgmSs0yTZc5XH5Wo+g80RWRIYNqE58QJKm/yMud5wZEvz50ugRDuzVIkyahg==", - "dev": true, - "requires": { - "clone": "2.1.1", - "loader-utils": "1.1.0", - "pify": "3.0.0" - } - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "1.1.2", - "type-check": "0.3.2" - } - }, - "license-webpack-plugin": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-1.3.1.tgz", - "integrity": "sha512-NqAFodJdpBUuf1iD+Ij8hQvF0rCFKlO2KaieoQzAPhFgzLCtJnC7Z7x5gQbGNjoe++wOKAtAmwVEIBLqq2Yp1A==", - "dev": true, - "requires": { - "ejs": "2.6.1" - } - }, - "lie": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", - "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", - "dev": true, - "requires": { - "immediate": "3.0.6" - } - }, - "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "parse-json": "2.2.0", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "strip-bom": "2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "loader-runner": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", - "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=", - "dev": true - }, - "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", - "dev": true, - "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "2.0.0", - "path-exists": "3.0.0" - } - }, - "lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", - "dev": true - }, - "lodash.assign": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", - "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", - "dev": true, - "optional": true - }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, - "lodash.mergewith": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz", - "integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==", - "dev": true, - "optional": true - }, - "lodash.tail": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz", - "integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=", - "dev": true - }, - "log-symbols": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", - "dev": true, - "requires": { - "chalk": "2.2.2" - } - }, - "log4js": { - "version": "0.6.38", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-0.6.38.tgz", - "integrity": "sha1-LElBFmldb7JUgJQ9P8hy5mKlIv0=", - "dev": true, - "requires": { - "readable-stream": "1.0.34", - "semver": "4.3.6" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" - } - }, - "semver": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", - "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=", - "dev": true - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } - } - }, - "loglevel": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz", - "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=", - "dev": true - }, - "loglevelnext": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/loglevelnext/-/loglevelnext-1.0.5.tgz", - "integrity": "sha512-V/73qkPuJmx4BcBF19xPBr+0ZRVBhc4POxvZTZdMeXpJ4NItXSJ/MSwuFT0kQJlCbXvdlZoQQ/418bS1y9Jh6A==", - "dev": true, - "requires": { - "es6-symbol": "3.1.1", - "object.assign": "4.1.0" - } - }, - "long": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", - "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=", - "dev": true - }, - "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true - }, - "loose-envify": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", - "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", - "dev": true, - "requires": { - "js-tokens": "3.0.2" - } - }, - "loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", - "dev": true, - "requires": { - "currently-unhandled": "0.4.1", - "signal-exit": "3.0.2" - } - }, - "lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", - "dev": true - }, - "lru-cache": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", - "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", - "dev": true, - "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" - } - }, - "make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "dev": true, - "requires": { - "pify": "3.0.0" - } - }, - "make-error": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.4.tgz", - "integrity": "sha512-0Dab5btKVPhibSalc9QGXb559ED7G7iLjFXBaj9Wq8O3vorueR5K5jaE3hkG6ZQINyhA/JgG6Qk4qdFQjsYV6g==", - "dev": true - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "requires": { - "object-visit": "1.0.1" - } - }, - "math-random": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz", - "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=", - "dev": true - }, - "md5.js": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", - "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", - "dev": true, - "requires": { - "hash-base": "3.0.4", - "inherits": "2.0.3" - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "dev": true - }, - "mem": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", - "dev": true, - "requires": { - "mimic-fn": "1.2.0" - } - }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dev": true, - "requires": { - "errno": "0.1.7", - "readable-stream": "2.3.6" - } - }, - "meow": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", - "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", - "dev": true, - "requires": { - "camelcase-keys": "2.1.0", - "decamelize": "1.2.0", - "loud-rejection": "1.6.0", - "map-obj": "1.0.1", - "minimist": "1.2.0", - "normalize-package-data": "2.4.0", - "object-assign": "4.1.1", - "read-pkg-up": "1.0.1", - "redent": "1.0.0", - "trim-newlines": "1.0.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", - "dev": true - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "braces": "2.3.2", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "extglob": "2.0.4", - "fragment-cache": "0.2.1", - "kind-of": "6.0.2", - "nanomatch": "1.2.9", - "object.pick": "1.3.0", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" - } - }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "brorand": "1.1.0" - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true - }, - "mime-db": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", - "dev": true - }, - "mime-types": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", - "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", - "dev": true, - "requires": { - "mime-db": "1.33.0" - } - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, - "mini-css-extract-plugin": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.4.0.tgz", - "integrity": "sha512-2Zik6PhUZ/MbiboG6SDS9UTPL4XXy4qnyGjSdCIWRrr8xb6PwLtHE+AYOjkXJWdF0OG8vo/yrJ8CgS5WbMpzIg==", - "dev": true, - "requires": { - "loader-utils": "1.1.0", - "webpack-sources": "1.1.0" - } - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "1.1.11" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mississippi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", - "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", - "dev": true, - "requires": { - "concat-stream": "1.6.2", - "duplexify": "3.6.0", - "end-of-stream": "1.4.1", - "flush-write-stream": "1.0.3", - "from2": "2.3.0", - "parallel-transform": "1.1.0", - "pump": "2.0.1", - "pumpify": "1.5.1", - "stream-each": "1.2.2", - "through2": "2.0.3" - } - }, - "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", - "dev": true, - "requires": { - "for-in": "1.0.2", - "is-extendable": "1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "2.0.4" - } - } - } - }, - "mixin-object": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", - "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=", - "dev": true, - "requires": { - "for-in": "0.1.8", - "is-extendable": "0.1.1" - }, - "dependencies": { - "for-in": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz", - "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=", - "dev": true - } - } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "moment": { - "version": "2.22.2", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz", - "integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=" - }, - "moment-timezone": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.21.tgz", - "integrity": "sha512-j96bAh4otsgj3lKydm3K7kdtA3iKf2m6MY2iSYCzCm5a1zmHo1g+aK3068dDEeocLZQIS9kU8bsdQHLqEvgW0A==", - "requires": { - "moment": "2.22.2" - } - }, - "move-concurrently": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", - "dev": true, - "requires": { - "aproba": "1.2.0", - "copy-concurrently": "1.0.5", - "fs-write-stream-atomic": "1.0.10", - "mkdirp": "0.5.1", - "rimraf": "2.6.2", - "run-queue": "1.0.3" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "multicast-dns": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", - "dev": true, - "requires": { - "dns-packet": "1.3.1", - "thunky": "1.0.2" - } - }, - "multicast-dns-service-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", - "dev": true - }, - "nan": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", - "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", - "dev": true, - "optional": true - }, - "nanomatch": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", - "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==", - "dev": true, - "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "fragment-cache": "0.2.1", - "is-odd": "2.0.0", - "is-windows": "1.0.2", - "kind-of": "6.0.2", - "object.pick": "1.3.0", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" - } - }, - "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", - "dev": true - }, - "neo-async": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.5.1.tgz", - "integrity": "sha512-3KL3fvuRkZ7s4IFOMfztb7zJp3QaVWnBeGoJlgB38XnCRPj/0tLzzLG5IB8NYOHbJ8g8UGrgZv44GLDk6CxTxA==", - "dev": true - }, - "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", - "dev": true - }, - "no-case": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", - "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", - "dev": true, - "requires": { - "lower-case": "1.1.4" - } - }, - "node-forge": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz", - "integrity": "sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==", - "dev": true - }, - "node-gyp": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.7.0.tgz", - "integrity": "sha512-qDQE/Ft9xXP6zphwx4sD0t+VhwV7yFaloMpfbL2QnnDZcyaiakWlLdtFGGQfTAwpFHdpbRhRxVhIHN1OKAjgbg==", - "dev": true, - "optional": true, - "requires": { - "fstream": "1.0.11", - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "mkdirp": "0.5.1", - "nopt": "3.0.6", - "npmlog": "4.1.2", - "osenv": "0.1.5", - "request": "2.81.0", - "rimraf": "2.6.2", - "semver": "5.3.0", - "tar": "2.2.1", - "which": "1.3.1" - }, - "dependencies": { - "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", - "dev": true, - "optional": true, - "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" - } - }, - "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", - "dev": true, - "optional": true - }, - "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", - "dev": true, - "optional": true - }, - "form-data": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", - "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", - "dev": true, - "optional": true, - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.6", - "mime-types": "2.1.18" - } - }, - "har-schema": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", - "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", - "dev": true, - "optional": true - }, - "har-validator": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", - "dev": true, - "optional": true, - "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" - } - }, - "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.1", - "sshpk": "1.14.2" - } - }, - "performance-now": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", - "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", - "dev": true, - "optional": true - }, - "qs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", - "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", - "dev": true, - "optional": true - }, - "request": { - "version": "2.81.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", - "dev": true, - "optional": true, - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.7.0", - "caseless": "0.12.0", - "combined-stream": "1.0.6", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.18", - "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "5.1.2", - "stringstream": "0.0.6", - "tough-cookie": "2.3.4", - "tunnel-agent": "0.6.0", - "uuid": "3.2.1" - } - }, - "semver": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", - "dev": true, - "optional": true - } - } - }, - "node-libs-browser": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz", - "integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==", - "dev": true, - "requires": { - "assert": "1.4.1", - "browserify-zlib": "0.2.0", - "buffer": "4.9.1", - "console-browserify": "1.1.0", - "constants-browserify": "1.0.0", - "crypto-browserify": "3.12.0", - "domain-browser": "1.2.0", - "events": "1.1.1", - "https-browserify": "1.0.0", - "os-browserify": "0.3.0", - "path-browserify": "0.0.0", - "process": "0.11.10", - "punycode": "1.4.1", - "querystring-es3": "0.2.1", - "readable-stream": "2.3.6", - "stream-browserify": "2.0.1", - "stream-http": "2.8.3", - "string_decoder": "1.1.1", - "timers-browserify": "2.0.10", - "tty-browserify": "0.0.0", - "url": "0.11.0", - "util": "0.10.4", - "vm-browserify": "0.0.4" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - } - } - }, - "node-sass": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.9.0.tgz", - "integrity": "sha512-QFHfrZl6lqRU3csypwviz2XLgGNOoWQbo2GOvtsfQqOfL4cy1BtWnhx/XUeAO9LT3ahBzSRXcEO6DdvAH9DzSg==", - "dev": true, - "optional": true, - "requires": { - "async-foreach": "0.1.3", - "chalk": "1.1.3", - "cross-spawn": "3.0.1", - "gaze": "1.1.3", - "get-stdin": "4.0.1", - "glob": "7.1.2", - "in-publish": "2.0.0", - "lodash.assign": "4.2.0", - "lodash.clonedeep": "4.5.0", - "lodash.mergewith": "4.6.1", - "meow": "3.7.0", - "mkdirp": "0.5.1", - "nan": "2.10.0", - "node-gyp": "3.7.0", - "npmlog": "4.1.2", - "request": "2.79.0", - "sass-graph": "2.2.4", - "stdout-stream": "1.4.0", - "true-case-path": "1.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", - "dev": true, - "optional": true - }, - "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", - "dev": true, - "optional": true - }, - "caseless": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", - "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=", - "dev": true, - "optional": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - } - }, - "form-data": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", - "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", - "dev": true, - "optional": true, - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.6", - "mime-types": "2.1.18" - } - }, - "har-validator": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", - "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", - "dev": true, - "optional": true, - "requires": { - "chalk": "1.1.3", - "commander": "2.15.1", - "is-my-json-valid": "2.17.2", - "pinkie-promise": "2.0.1" - } - }, - "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.1", - "sshpk": "1.14.2" - } - }, - "qs": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.3.2.tgz", - "integrity": "sha1-51vV9uJoEioqDgvaYwslUMFmUCw=", - "dev": true, - "optional": true - }, - "request": { - "version": "2.79.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.79.0.tgz", - "integrity": "sha1-Tf5b9r6LjNw3/Pk+BLZVd3InEN4=", - "dev": true, - "optional": true, - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.7.0", - "caseless": "0.11.0", - "combined-stream": "1.0.6", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "2.0.6", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.18", - "oauth-sign": "0.8.2", - "qs": "6.3.2", - "stringstream": "0.0.6", - "tough-cookie": "2.3.4", - "tunnel-agent": "0.4.3", - "uuid": "3.2.1" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - }, - "tunnel-agent": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", - "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", - "dev": true, - "optional": true - } - } - }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "dev": true, - "requires": { - "abbrev": "1.0.9" - } - }, - "normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", - "dev": true, - "requires": { - "hosted-git-info": "2.6.0", - "is-builtin-module": "1.0.0", - "semver": "5.5.0", - "validate-npm-package-license": "3.0.3" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "1.1.0" - } - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", - "dev": true - }, - "npm-package-arg": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.0.tgz", - "integrity": "sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA==", - "dev": true, - "requires": { - "hosted-git-info": "2.6.0", - "osenv": "0.1.5", - "semver": "5.5.0", - "validate-npm-package-name": "3.0.0" - } - }, - "npm-registry-client": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/npm-registry-client/-/npm-registry-client-8.5.1.tgz", - "integrity": "sha512-7rjGF2eA7hKDidGyEWmHTiKfXkbrcQAsGL/Rh4Rt3x3YNRNHhwaTzVJfW3aNvvlhg4G62VCluif0sLCb/i51Hg==", - "dev": true, - "requires": { - "concat-stream": "1.6.2", - "graceful-fs": "4.1.11", - "normalize-package-data": "2.4.0", - "npm-package-arg": "6.1.0", - "npmlog": "4.1.2", - "once": "1.4.0", - "request": "2.87.0", - "retry": "0.10.1", - "safe-buffer": "5.1.2", - "semver": "5.5.0", - "slide": "1.1.6", - "ssri": "5.3.0" - } - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "2.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dev": true, - "requires": { - "are-we-there-yet": "1.1.5", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" - } - }, - "nth-check": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", - "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=", - "dev": true, - "requires": { - "boolbase": "1.0.0" - } - }, - "null-check": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz", - "integrity": "sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=", - "dev": true - }, - "num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", - "dev": true - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object-component": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", - "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", - "dev": true - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "0.1.1", - "define-property": "0.2.5", - "kind-of": "3.2.2" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "object-keys": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", - "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=", - "dev": true - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "requires": { - "isobject": "3.0.1" - } - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "1.1.2", - "function-bind": "1.1.1", - "has-symbols": "1.0.0", - "object-keys": "1.0.11" - } - }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", - "dev": true, - "requires": { - "define-properties": "1.1.2", - "es-abstract": "1.12.0" - } - }, - "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", - "dev": true, - "requires": { - "for-own": "0.1.5", - "is-extendable": "0.1.1" - }, - "dependencies": { - "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "dev": true, - "requires": { - "for-in": "1.0.2" - } - } - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "requires": { - "isobject": "3.0.1" - } - }, - "obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", - "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1.0.2" - } - }, - "opn": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz", - "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==", - "dev": true, - "requires": { - "is-wsl": "1.1.0" - } - }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, - "requires": { - "minimist": "0.0.8", - "wordwrap": "0.0.3" - }, - "dependencies": { - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", - "dev": true - } - } - }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, - "requires": { - "deep-is": "0.1.3", - "fast-levenshtein": "2.0.6", - "levn": "0.3.0", - "prelude-ls": "1.1.2", - "type-check": "0.3.2", - "wordwrap": "1.0.0" - } - }, - "options": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", - "integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8=", - "dev": true - }, - "original": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/original/-/original-1.0.1.tgz", - "integrity": "sha512-IEvtB5vM5ULvwnqMxWBLxkS13JIEXbakizMSo3yoPNPCIWzg8TG3Usn/UhXoZFM/m+FuEA20KdzPSFq/0rS+UA==", - "dev": true, - "requires": { - "url-parse": "1.4.1" - } - }, - "os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", - "dev": true - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, - "os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "dev": true, - "optional": true, - "requires": { - "lcid": "1.0.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "dev": true, - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - } - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "1.3.0" - } - }, - "p-map": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", - "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", - "dev": true - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "pako": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", - "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", - "dev": true - }, - "parallel-transform": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", - "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", - "dev": true, - "requires": { - "cyclist": "0.2.2", - "inherits": "2.0.3", - "readable-stream": "2.3.6" - } - }, - "param-case": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", - "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", - "dev": true, - "requires": { - "no-case": "2.3.2" - } - }, - "parse-asn1": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", - "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", - "dev": true, - "requires": { - "asn1.js": "4.10.1", - "browserify-aes": "1.2.0", - "create-hash": "1.2.0", - "evp_bytestokey": "1.0.3", - "pbkdf2": "3.0.16" - } - }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "dev": true, - "requires": { - "glob-base": "0.3.0", - "is-dotfile": "1.0.3", - "is-extglob": "1.0.0", - "is-glob": "2.0.1" - }, - "dependencies": { - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - } - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "1.3.1" - } - }, - "parse5": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", - "dev": true - }, - "parsejson": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/parsejson/-/parsejson-0.0.3.tgz", - "integrity": "sha1-q343WfIJ7OmUN5c/fQ8fZK4OZKs=", - "dev": true, - "requires": { - "better-assert": "1.0.2" - } - }, - "parseqs": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", - "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", - "dev": true, - "requires": { - "better-assert": "1.0.2" - } - }, - "parseuri": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", - "dev": true, - "requires": { - "better-assert": "1.0.2" - } - }, - "parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", - "dev": true - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, - "path-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", - "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", - "dev": true - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", - "dev": true - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", - "dev": true - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "3.0.0" - } - }, - "pbkdf2": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.16.tgz", - "integrity": "sha512-y4CXP3thSxqf7c0qmOF+9UeOTrifiVTIM+u7NWlq+PRsHbr7r7dpCmvzrZxa96JJUNi0Y5w9VqG5ZNeCVMoDcA==", - "dev": true, - "requires": { - "create-hash": "1.2.0", - "create-hmac": "1.1.7", - "ripemd160": "2.0.2", - "safe-buffer": "5.1.2", - "sha.js": "2.4.11" - } - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "2.0.4" - } - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "2.1.0" - } - }, - "portfinder": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.13.tgz", - "integrity": "sha1-uzLs2HwnEErm7kS1o8y/Drsa7ek=", - "dev": true, - "requires": { - "async": "1.5.2", - "debug": "2.6.9", - "mkdirp": "0.5.1" - } - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true - }, - "postcss": { - "version": "6.0.22", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz", - "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==", - "dev": true, - "requires": { - "chalk": "2.4.1", - "source-map": "0.6.1", - "supports-color": "5.4.0" - }, - "dependencies": { - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "postcss-import": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-11.1.0.tgz", - "integrity": "sha512-5l327iI75POonjxkXgdRCUS+AlzAdBx4pOvMEhTKTCjb1p8IEeVR9yx3cPbmN7LIWJLbfnIXxAhoB4jpD0c/Cw==", - "dev": true, - "requires": { - "postcss": "6.0.22", - "postcss-value-parser": "3.3.0", - "read-cache": "1.0.0", - "resolve": "1.7.1" - } - }, - "postcss-load-config": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-1.2.0.tgz", - "integrity": "sha1-U56a/J3chiASHr+djDZz4M5Q0oo=", - "dev": true, - "requires": { - "cosmiconfig": "2.2.2", - "object-assign": "4.1.1", - "postcss-load-options": "1.2.0", - "postcss-load-plugins": "2.3.0" - } - }, - "postcss-load-options": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-load-options/-/postcss-load-options-1.2.0.tgz", - "integrity": "sha1-sJixVZ3awt8EvAuzdfmaXP4rbYw=", - "dev": true, - "requires": { - "cosmiconfig": "2.2.2", - "object-assign": "4.1.1" - } - }, - "postcss-load-plugins": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/postcss-load-plugins/-/postcss-load-plugins-2.3.0.tgz", - "integrity": "sha1-dFdoEWWZrKLwCfrUJrABdQSdjZI=", - "dev": true, - "requires": { - "cosmiconfig": "2.2.2", - "object-assign": "4.1.1" - } - }, - "postcss-loader": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-2.1.5.tgz", - "integrity": "sha512-pV7kB5neJ0/1tZ8L1uGOBNTVBCSCXQoIsZMsrwvO8V2rKGa2tBl/f80GGVxow2jJnRJ2w1ocx693EKhZAb9Isg==", - "dev": true, - "requires": { - "loader-utils": "1.1.0", - "postcss": "6.0.22", - "postcss-load-config": "1.2.0", - "schema-utils": "0.4.5" - } - }, - "postcss-url": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/postcss-url/-/postcss-url-7.3.2.tgz", - "integrity": "sha512-QMV5mA+pCYZQcUEPQkmor9vcPQ2MT+Ipuu8qdi1gVxbNiIiErEGft+eny1ak19qALoBkccS5AHaCaCDzh7b9MA==", - "dev": true, - "requires": { - "mime": "1.6.0", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "postcss": "6.0.22", - "xxhashjs": "0.2.2" - } - }, - "postcss-value-parser": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", - "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=", - "dev": true - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", - "dev": true - }, - "pretty-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", - "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", - "dev": true, - "requires": { - "renderkid": "2.0.1", - "utila": "0.4.0" - } - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true - }, - "promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "dev": true, - "optional": true, - "requires": { - "asap": "2.0.6" - } - }, - "promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", - "dev": true - }, - "protractor": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.3.2.tgz", - "integrity": "sha512-pw4uwwiy5lHZjIguxNpkEwJJa7hVz+bJsvaTI+IbXlfn2qXwzbF8eghW/RmrZwE2sGx82I8etb8lVjQ+JrjejA==", - "dev": true, - "requires": { - "@types/node": "6.0.113", - "@types/q": "0.0.32", - "@types/selenium-webdriver": "2.53.43", - "blocking-proxy": "1.0.1", - "chalk": "1.1.3", - "glob": "7.1.2", - "jasmine": "2.8.0", - "jasminewd2": "2.2.0", - "optimist": "0.6.1", - "q": "1.4.1", - "saucelabs": "1.5.0", - "selenium-webdriver": "3.6.0", - "source-map-support": "0.4.18", - "webdriver-js-extender": "1.0.0", - "webdriver-manager": "12.0.6" - }, - "dependencies": { - "@types/node": { - "version": "6.0.113", - "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.113.tgz", - "integrity": "sha512-f9XXUWFqryzjkZA1EqFvJHSFyqyasV17fq8zCDIzbRV4ctL7RrJGKvG+lcex86Rjbzd1GrER9h9VmF5sSjV0BQ==", - "dev": true - }, - "adm-zip": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.11.tgz", - "integrity": "sha512-L8vcjDTCOIJk7wFvmlEUN7AsSb8T+2JrdP7KINBjzr24TJ5Mwj590sLu3BC7zNZowvJWa/JtPmD8eJCzdtDWjA==", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - } - }, - "del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", - "dev": true, - "requires": { - "globby": "5.0.0", - "is-path-cwd": "1.0.0", - "is-path-in-cwd": "1.0.1", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "rimraf": "2.6.2" - } - }, - "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", - "dev": true, - "requires": { - "array-union": "1.0.2", - "arrify": "1.0.1", - "glob": "7.1.2", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "dev": true, - "requires": { - "source-map": "0.5.7" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - }, - "webdriver-manager": { - "version": "12.0.6", - "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.0.6.tgz", - "integrity": "sha1-PfGkgZdwELTL+MnYXHpXeCjA5ws=", - "dev": true, - "requires": { - "adm-zip": "0.4.11", - "chalk": "1.1.3", - "del": "2.2.2", - "glob": "7.1.2", - "ini": "1.3.5", - "minimist": "1.2.0", - "q": "1.4.1", - "request": "2.87.0", - "rimraf": "2.6.2", - "semver": "5.5.0", - "xml2js": "0.4.19" - } - } - } - }, - "proxy-addr": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", - "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", - "dev": true, - "requires": { - "forwarded": "0.1.2", - "ipaddr.js": "1.6.0" - } - }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", - "dev": true - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "public-encrypt": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.2.tgz", - "integrity": "sha512-4kJ5Esocg8X3h8YgJsKAuoesBgB7mqH3eowiDzMUPKiRDDE7E/BqqZD1hnTByIaAFiwAw246YEltSq7tdrOH0Q==", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "browserify-rsa": "4.0.1", - "create-hash": "1.2.0", - "parse-asn1": "5.1.1", - "randombytes": "2.0.6" - } - }, - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, - "requires": { - "end-of-stream": "1.4.1", - "once": "1.4.0" - } - }, - "pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "dev": true, - "requires": { - "duplexify": "3.6.0", - "inherits": "2.0.3", - "pump": "2.0.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "q": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", - "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", - "dev": true - }, - "qjobs": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", - "dev": true - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "dev": true - }, - "querystringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.0.0.tgz", - "integrity": "sha512-eTPo5t/4bgaMNZxyjWx6N2a6AuE0mq51KWvpc7nU/MAqixcI6v6KrGUKES0HaomdnolQBBXU/++X6/QQ9KL4tw==", - "dev": true - }, - "randomatic": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.0.0.tgz", - "integrity": "sha512-VdxFOIEY3mNO5PtSRkkle/hPJDHvQhK21oa73K4yAc9qmp6N429gAyF1gZMOTMeS0/AYzaV/2Trcef+NaIonSA==", - "dev": true, - "requires": { - "is-number": "4.0.0", - "kind-of": "6.0.2", - "math-random": "1.0.1" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true - } - } - }, - "randombytes": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", - "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", - "dev": true, - "requires": { - "safe-buffer": "5.1.2" - } - }, - "randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "requires": { - "randombytes": "2.0.6", - "safe-buffer": "5.1.2" - } - }, - "range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", - "dev": true - }, - "raw-body": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", - "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", - "dev": true, - "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.2", - "iconv-lite": "0.4.19", - "unpipe": "1.0.0" - }, - "dependencies": { - "depd": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", - "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", - "dev": true - }, - "http-errors": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", - "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", - "dev": true, - "requires": { - "depd": "1.1.1", - "inherits": "2.0.3", - "setprototypeof": "1.0.3", - "statuses": "1.4.0" - } - }, - "setprototypeof": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", - "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", - "dev": true - } - } - }, - "raw-loader": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz", - "integrity": "sha1-DD0L6u2KAclm2Xh793goElKpeao=", - "dev": true - }, - "read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", - "dev": true, - "requires": { - "pify": "2.3.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true, - "requires": { - "load-json-file": "1.1.0", - "normalize-package-data": "2.4.0", - "path-type": "1.1.0" - }, - "dependencies": { - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "dev": true, - "requires": { - "find-up": "1.1.2", - "read-pkg": "1.1.0" - }, - "dependencies": { - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "2.1.0", - "pinkie-promise": "2.0.1" - } - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "2.0.1" - } - } - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.2", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" - } - }, - "readdirp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", - "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "minimatch": "3.0.4", - "readable-stream": "2.3.6", - "set-immediate-shim": "1.0.1" - } - }, - "redent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", - "dev": true, - "requires": { - "indent-string": "2.1.0", - "strip-indent": "1.0.1" - } - }, - "reflect-metadata": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.12.tgz", - "integrity": "sha512-n+IyV+nGz3+0q3/Yf1ra12KpCyi001bi4XFxSjbiWWjfqb52iTTtpGXmCCAOWWIAn9KEuFZKGqBERHmrtScZ3A==", - "dev": true - }, - "regenerate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", - "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", - "dev": true - }, - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true - }, - "regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", - "dev": true, - "requires": { - "is-equal-shallow": "0.1.3" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "requires": { - "extend-shallow": "3.0.2", - "safe-regex": "1.1.0" - } - }, - "regexpu-core": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", - "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", - "dev": true, - "requires": { - "regenerate": "1.4.0", - "regjsgen": "0.2.0", - "regjsparser": "0.1.5" - } - }, - "regjsgen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", - "dev": true - }, - "regjsparser": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", - "dev": true, - "requires": { - "jsesc": "0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - } - } - }, - "relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", - "dev": true - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "renderkid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.1.tgz", - "integrity": "sha1-iYyr/Ivt5Le5ETWj/9Mj5YwNsxk=", - "dev": true, - "requires": { - "css-select": "1.2.0", - "dom-converter": "0.1.4", - "htmlparser2": "3.3.0", - "strip-ansi": "3.0.1", - "utila": "0.3.3" - }, - "dependencies": { - "utila": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", - "integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY=", - "dev": true - } - } - }, - "repeat-element": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, - "requires": { - "is-finite": "1.0.2" - } - }, - "request": { - "version": "2.87.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", - "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", - "dev": true, - "requires": { - "aws-sign2": "0.7.0", - "aws4": "1.7.0", - "caseless": "0.12.0", - "combined-stream": "1.0.6", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.3.2", - "har-validator": "5.0.3", - "http-signature": "1.2.0", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.18", - "oauth-sign": "0.8.2", - "performance-now": "2.1.0", - "qs": "6.5.2", - "safe-buffer": "5.1.2", - "tough-cookie": "2.3.4", - "tunnel-agent": "0.6.0", - "uuid": "3.2.1" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-from-string": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", - "integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=", - "dev": true - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true - }, - "resolve": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", - "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", - "dev": true, - "requires": { - "path-parse": "1.0.5" - } - }, - "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", - "dev": true, - "requires": { - "resolve-from": "3.0.0" - } - }, - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", - "dev": true - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, - "retry": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", - "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=", - "dev": true - }, - "right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "dev": true, - "optional": true, - "requires": { - "align-text": "0.1.4" - } - }, - "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", - "dev": true, - "requires": { - "glob": "7.1.2" - } - }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "requires": { - "hash-base": "3.0.4", - "inherits": "2.0.3" - } - }, - "run-queue": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", - "dev": true, - "requires": { - "aproba": "1.2.0" - } - }, - "rxjs": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.1.tgz", - "integrity": "sha512-OwMxHxmnmHTUpgO+V7dZChf3Tixf4ih95cmXjzzadULziVl/FKhHScGLj4goEw9weePVOH2Q0+GcCBUhKCZc/g==", - "requires": { - "tslib": "1.9.2" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "requires": { - "ret": "0.1.15" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "sass-graph": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", - "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", - "dev": true, - "optional": true, - "requires": { - "glob": "7.1.2", - "lodash": "4.17.10", - "scss-tokenizer": "0.2.3", - "yargs": "7.1.0" - }, - "dependencies": { - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true, - "optional": true - }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true, - "optional": true, - "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wrap-ansi": "2.1.0" - } - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true, - "optional": true - }, - "yargs": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", - "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", - "dev": true, - "optional": true, - "requires": { - "camelcase": "3.0.0", - "cliui": "3.2.0", - "decamelize": "1.2.0", - "get-caller-file": "1.0.2", - "os-locale": "1.4.0", - "read-pkg-up": "1.0.1", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "1.0.2", - "which-module": "1.0.0", - "y18n": "3.2.1", - "yargs-parser": "5.0.0" - } - } - } - }, - "sass-loader": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.0.3.tgz", - "integrity": "sha512-iaSFtQcGo4SSgDw5Aes5p4VTrA5jCGSA7sGmhPIcOloBlgI1VktM2MUrk2IHHjbNagckXlPz+HWq1vAAPrcYxA==", - "dev": true, - "requires": { - "clone-deep": "2.0.2", - "loader-utils": "1.1.0", - "lodash.tail": "4.1.1", - "neo-async": "2.5.1", - "pify": "3.0.0" - } - }, - "saucelabs": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz", - "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==", - "dev": true, - "requires": { - "https-proxy-agent": "2.2.1" - } - }, - "sax": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz", - "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=", - "dev": true - }, - "schema-utils": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.5.tgz", - "integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==", - "dev": true, - "requires": { - "ajv": "6.4.0", - "ajv-keywords": "3.2.0" - } - }, - "scss-tokenizer": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", - "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", - "dev": true, - "optional": true, - "requires": { - "js-base64": "2.4.5", - "source-map": "0.4.4" - }, - "dependencies": { - "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true, - "optional": true, - "requires": { - "amdefine": "1.0.1" - } - } - } - }, - "select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", - "dev": true - }, - "selenium-webdriver": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", - "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", - "dev": true, - "requires": { - "jszip": "3.1.5", - "rimraf": "2.6.2", - "tmp": "0.0.30", - "xml2js": "0.4.19" - }, - "dependencies": { - "tmp": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", - "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", - "dev": true, - "requires": { - "os-tmpdir": "1.0.2" - } - } - } - }, - "selfsigned": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.3.tgz", - "integrity": "sha512-vmZenZ+8Al3NLHkWnhBQ0x6BkML1eCP2xEi3JE+f3D9wW9fipD9NNJHYtE9XJM4TsPaHGZJIamrSI6MTg1dU2Q==", - "dev": true, - "requires": { - "node-forge": "0.7.5" - } - }, - "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", - "dev": true - }, - "semver-dsl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/semver-dsl/-/semver-dsl-1.0.1.tgz", - "integrity": "sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=", - "dev": true, - "requires": { - "semver": "5.5.0" - } - }, - "semver-intersect": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/semver-intersect/-/semver-intersect-1.3.1.tgz", - "integrity": "sha1-j6hKnhAovSOeRTDRo+GB5pjYhLo=", - "dev": true, - "requires": { - "semver": "5.5.0" - } - }, - "send": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", - "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", - "dev": true, - "requires": { - "debug": "2.6.9", - "depd": "1.1.2", - "destroy": "1.0.4", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "etag": "1.8.1", - "fresh": "0.5.2", - "http-errors": "1.6.3", - "mime": "1.4.1", - "ms": "2.0.0", - "on-finished": "2.3.0", - "range-parser": "1.2.0", - "statuses": "1.4.0" - }, - "dependencies": { - "mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", - "dev": true - } - } - }, - "serialize-javascript": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.5.0.tgz", - "integrity": "sha512-Ga8c8NjAAp46Br4+0oZ2WxJCwIzwP60Gq1YPgU+39PiTVxyed/iKE/zyZI6+UlVYH5Q4PaQdHhcegIFPZTUfoQ==", - "dev": true - }, - "serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", - "dev": true, - "requires": { - "accepts": "1.3.5", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "1.0.3", - "http-errors": "1.6.3", - "mime-types": "2.1.18", - "parseurl": "1.3.2" - } - }, - "serve-static": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", - "dev": true, - "requires": { - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "parseurl": "1.3.2", - "send": "0.16.2" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", - "dev": true - }, - "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", - "dev": true, - "requires": { - "extend-shallow": "2.0.1", - "is-extendable": "0.1.1", - "is-plain-object": "2.0.4", - "split-string": "3.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - } - } - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.2" - } - }, - "shallow-clone": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-1.0.0.tgz", - "integrity": "sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA==", - "dev": true, - "requires": { - "is-extendable": "0.1.1", - "kind-of": "5.1.0", - "mixin-object": "2.0.1" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "silent-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/silent-error/-/silent-error-1.1.0.tgz", - "integrity": "sha1-IglwbxyFCp8dENDYQJGLRvJuG8k=", - "dev": true, - "requires": { - "debug": "2.6.9" - } - }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true - }, - "slide": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", - "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", - "dev": true - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "base": "0.11.2", - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "map-cache": "0.2.2", - "source-map": "0.5.7", - "source-map-resolve": "0.5.2", - "use": "3.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "1.0.0", - "isobject": "3.0.1", - "snapdragon-util": "3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "1.0.2" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "socket.io": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-1.7.3.tgz", - "integrity": "sha1-uK+cq6AJSeVo42nxMn6pvp6iRhs=", - "dev": true, - "requires": { - "debug": "2.3.3", - "engine.io": "1.8.3", - "has-binary": "0.1.7", - "object-assign": "4.1.0", - "socket.io-adapter": "0.5.0", - "socket.io-client": "1.7.3", - "socket.io-parser": "2.3.1" - }, - "dependencies": { - "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", - "dev": true, - "requires": { - "ms": "0.7.2" - } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - }, - "object-assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", - "integrity": "sha1-ejs9DpgGPUP0wD8uiubNUahog6A=", - "dev": true - } - } - }, - "socket.io-adapter": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz", - "integrity": "sha1-y21LuL7IHhB4uZZ3+c7QBGBmu4s=", - "dev": true, - "requires": { - "debug": "2.3.3", - "socket.io-parser": "2.3.1" - }, - "dependencies": { - "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", - "dev": true, - "requires": { - "ms": "0.7.2" - } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - } - } - }, - "socket.io-client": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-1.7.3.tgz", - "integrity": "sha1-sw6GqhDV7zVGYBwJzeR2Xjgdo3c=", - "dev": true, - "requires": { - "backo2": "1.0.2", - "component-bind": "1.0.0", - "component-emitter": "1.2.1", - "debug": "2.3.3", - "engine.io-client": "1.8.3", - "has-binary": "0.1.7", - "indexof": "0.0.1", - "object-component": "0.0.3", - "parseuri": "0.0.5", - "socket.io-parser": "2.3.1", - "to-array": "0.1.4" - }, - "dependencies": { - "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", - "dev": true, - "requires": { - "ms": "0.7.2" - } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - } - } - }, - "socket.io-parser": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.3.1.tgz", - "integrity": "sha1-3VMgJRA85Clpcya+/WQAX8/ltKA=", - "dev": true, - "requires": { - "component-emitter": "1.1.2", - "debug": "2.2.0", - "isarray": "0.0.1", - "json3": "3.3.2" - }, - "dependencies": { - "component-emitter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz", - "integrity": "sha1-KWWU8nU9qmOZbSrwjRWpURbJrsM=", - "dev": true - }, - "debug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", - "dev": true, - "requires": { - "ms": "0.7.1" - } - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "ms": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", - "dev": true - } - } - }, - "sockjs": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", - "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", - "dev": true, - "requires": { - "faye-websocket": "0.10.0", - "uuid": "3.2.1" - } - }, - "sockjs-client": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.4.tgz", - "integrity": "sha1-W6vjhrd15M8U51IJEUUmVAFsixI=", - "dev": true, - "requires": { - "debug": "2.6.9", - "eventsource": "0.1.6", - "faye-websocket": "0.11.1", - "inherits": "2.0.3", - "json3": "3.3.2", - "url-parse": "1.4.1" - }, - "dependencies": { - "faye-websocket": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", - "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", - "dev": true, - "requires": { - "websocket-driver": "0.7.0" - } - } - } - }, - "source-list-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", - "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", - "dev": true, - "requires": { - "atob": "2.1.1", - "decode-uri-component": "0.2.0", - "resolve-url": "0.2.1", - "source-map-url": "0.4.0", - "urix": "0.1.0" - } - }, - "source-map-support": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.6.tgz", - "integrity": "sha512-N4KXEz7jcKqPf2b2vZF11lQIz9W5ZMuUcIOGj243lduidkf2fjkVKJS9vNxVWn3u/uxX38AcE8U9nnH9FPcq+g==", - "dev": true, - "requires": { - "buffer-from": "1.1.0", - "source-map": "0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, - "spdx-correct": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", - "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", - "dev": true, - "requires": { - "spdx-expression-parse": "3.0.0", - "spdx-license-ids": "3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", - "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, - "requires": { - "spdx-exceptions": "2.1.0", - "spdx-license-ids": "3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", - "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==", - "dev": true - }, - "spdy": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-3.4.7.tgz", - "integrity": "sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw=", - "dev": true, - "requires": { - "debug": "2.6.9", - "handle-thing": "1.2.5", - "http-deceiver": "1.2.7", - "safe-buffer": "5.1.2", - "select-hose": "2.0.0", - "spdy-transport": "2.1.0" - } - }, - "spdy-transport": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-2.1.0.tgz", - "integrity": "sha512-bpUeGpZcmZ692rrTiqf9/2EUakI6/kXX1Rpe0ib/DyOzbiexVfXkw6GnvI9hVGvIwVaUhkaBojjCZwLNRGQg1g==", - "dev": true, - "requires": { - "debug": "2.6.9", - "detect-node": "2.0.3", - "hpack.js": "2.1.6", - "obuf": "1.1.2", - "readable-stream": "2.3.6", - "safe-buffer": "5.1.2", - "wbuf": "1.7.3" - } - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "requires": { - "extend-shallow": "3.0.2" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "sshpk": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", - "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", - "dev": true, - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jsbn": "0.1.1", - "safer-buffer": "2.1.2", - "tweetnacl": "0.14.5" - } - }, - "ssri": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", - "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.2" - } - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "requires": { - "define-property": "0.2.5", - "object-copy": "0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - } - } - }, - "stats-webpack-plugin": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/stats-webpack-plugin/-/stats-webpack-plugin-0.6.2.tgz", - "integrity": "sha1-LFlJtTHgf4eojm6k3PrFOqjHWis=", - "dev": true, - "requires": { - "lodash": "4.17.10" - } - }, - "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", - "dev": true - }, - "stdout-stream": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.0.tgz", - "integrity": "sha1-osfIWH5U2UJ+qe2zrD8s1SLfN4s=", - "dev": true, - "optional": true, - "requires": { - "readable-stream": "2.3.6" - } - }, - "stream-browserify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", - "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.6" - } - }, - "stream-each": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.2.tgz", - "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==", - "dev": true, - "requires": { - "end-of-stream": "1.4.1", - "stream-shift": "1.0.0" - } - }, - "stream-http": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", - "dev": true, - "requires": { - "builtin-status-codes": "3.0.0", - "inherits": "2.0.3", - "readable-stream": "2.3.6", - "to-arraybuffer": "1.0.1", - "xtend": "4.0.1" - } - }, - "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", - "dev": true - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "5.1.2" - } - }, - "stringstream": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz", - "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==", - "dev": true - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "0.2.1" - } - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "strip-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", - "dev": true, - "requires": { - "get-stdin": "4.0.1" - } - }, - "style-loader": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.21.0.tgz", - "integrity": "sha512-T+UNsAcl3Yg+BsPKs1vd22Fr8sVT+CJMtzqc6LEw9bbJZb43lm9GoeIfUcDEefBSWC0BhYbcdupV1GtI4DGzxg==", - "dev": true, - "requires": { - "loader-utils": "1.1.0", - "schema-utils": "0.4.5" - } - }, - "stylus": { - "version": "0.54.5", - "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.5.tgz", - "integrity": "sha1-QrlWCTHKcJDOhRWnmLqeaqPW3Hk=", - "dev": true, - "requires": { - "css-parse": "1.7.0", - "debug": "2.6.9", - "glob": "7.0.6", - "mkdirp": "0.5.1", - "sax": "0.5.8", - "source-map": "0.1.43" - }, - "dependencies": { - "glob": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz", - "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=", - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "source-map": { - "version": "0.1.43", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", - "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", - "dev": true, - "requires": { - "amdefine": "1.0.1" - } - } - } - }, - "stylus-loader": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-3.0.2.tgz", - "integrity": "sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA==", - "dev": true, - "requires": { - "loader-utils": "1.1.0", - "lodash.clonedeep": "4.5.0", - "when": "3.6.4" - } - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "3.0.0" - } - }, - "symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", - "dev": true - }, - "tapable": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.0.0.tgz", - "integrity": "sha512-dQRhbNQkRnaqauC7WqSJ21EEksgT0fYZX2lqXzGkpo8JNig9zGZTYoMGvyI2nWmXlE2VSVXVDu7wLVGu/mQEsg==", - "dev": true - }, - "tar": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", - "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", - "dev": true, - "optional": true, - "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" - } - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "through2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", - "dev": true, - "requires": { - "readable-stream": "2.3.6", - "xtend": "4.0.1" - } - }, - "thunky": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.2.tgz", - "integrity": "sha1-qGLgGOP7HqLsP85dVWBc9X8kc3E=", - "dev": true - }, - "timers-browserify": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", - "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", - "dev": true, - "requires": { - "setimmediate": "1.0.5" - } - }, - "tmp": { - "version": "0.0.31", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", - "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=", - "dev": true, - "requires": { - "os-tmpdir": "1.0.2" - } - }, - "to-array": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", - "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", - "dev": true - }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", - "dev": true - }, - "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", - "dev": true - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "requires": { - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "regex-not": "1.0.2", - "safe-regex": "1.1.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "3.0.0", - "repeat-string": "1.6.1" - } - }, - "toposort": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", - "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=", - "dev": true - }, - "tough-cookie": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", - "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", - "dev": true, - "requires": { - "punycode": "1.4.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - } - } - }, - "tree-kill": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.0.tgz", - "integrity": "sha512-DlX6dR0lOIRDFxI0mjL9IYg6OTncLm/Zt+JiBhE5OlFcAR8yc9S7FFXU9so0oda47frdM/JFsk7UjNt9vscKcg==", - "dev": true - }, - "trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", - "dev": true - }, - "trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", - "dev": true - }, - "true-case-path": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.2.tgz", - "integrity": "sha1-fskRMJJHZsf1c74wIMNPj9/QDWI=", - "dev": true, - "optional": true, - "requires": { - "glob": "6.0.4" - }, - "dependencies": { - "glob": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", - "dev": true, - "optional": true, - "requires": { - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - } - } - }, - "ts-node": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-5.0.1.tgz", - "integrity": "sha512-XK7QmDcNHVmZkVtkiwNDWiERRHPyU8nBqZB1+iv2UhOG0q3RQ9HsZ2CMqISlFbxjrYFGfG2mX7bW4dAyxBVzUw==", - "dev": true, - "requires": { - "arrify": "1.0.1", - "chalk": "2.4.1", - "diff": "3.5.0", - "make-error": "1.3.4", - "minimist": "1.2.0", - "mkdirp": "0.5.1", - "source-map-support": "0.5.6", - "yn": "2.0.0" - }, - "dependencies": { - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "tsickle": { - "version": "0.29.0", - "resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.29.0.tgz", - "integrity": "sha512-JpID0Lv8/irRtPmqJJxb5fCwfZhjZeKmav9Zna7UjqVuJoSbI49Wue/c2PPybX1SbRrjl7bbI/JsCl0dSUJygA==", - "dev": true, - "requires": { - "minimist": "1.2.0", - "mkdirp": "0.5.1", - "source-map": "0.6.1", - "source-map-support": "0.5.6" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "tslib": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.2.tgz", - "integrity": "sha512-AVP5Xol3WivEr7hnssHDsaM+lVrVXWUvd1cfXTRkTj80b//6g2wIFEH6hZG0muGZRnHGrfttpdzRk3YlBkWjKw==" - }, - "tslint": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.9.1.tgz", - "integrity": "sha1-ElX4ej/1frCw4fDmEKi0dIBGya4=", - "dev": true, - "requires": { - "babel-code-frame": "6.26.0", - "builtin-modules": "1.1.1", - "chalk": "2.4.1", - "commander": "2.15.1", - "diff": "3.5.0", - "glob": "7.1.2", - "js-yaml": "3.12.0", - "minimatch": "3.0.4", - "resolve": "1.7.1", - "semver": "5.5.0", - "tslib": "1.9.2", - "tsutils": "2.29.0" - }, - "dependencies": { - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" - } - } - } - }, - "tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", - "dev": true, - "requires": { - "tslib": "1.9.2" - } - }, - "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", - "dev": true - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "5.1.2" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true, - "optional": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "1.1.2" - } - }, - "type-is": { - "version": "1.6.16", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", - "dev": true, - "requires": { - "media-typer": "0.3.0", - "mime-types": "2.1.18" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "typescript": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.7.2.tgz", - "integrity": "sha512-p5TCYZDAO0m4G344hD+wx/LATebLWZNkkh2asWUFqSsD2OrDNhbAHuSjobrmsUmdzjJjEeZVU9g1h3O6vpstnw==", - "dev": true - }, - "uglify-js": { - "version": "3.3.28", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.3.28.tgz", - "integrity": "sha512-68Rc/aA6cswiaQ5SrE979UJcXX+ADA1z33/ZsPd+fbAiVdjZ16OXdbtGO+rJUUBgK6qdf3SOPhQf3K/ybF5Miw==", - "dev": true, - "requires": { - "commander": "2.15.1", - "source-map": "0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "dev": true, - "optional": true - }, - "uglifyjs-webpack-plugin": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.5.tgz", - "integrity": "sha512-hIQJ1yxAPhEA2yW/i7Fr+SXZVMp+VEI3d42RTHBgQd2yhp/1UdBcR3QEWPV5ahBxlqQDMEMTuTEvDHSFINfwSw==", - "dev": true, - "requires": { - "cacache": "10.0.4", - "find-cache-dir": "1.0.0", - "schema-utils": "0.4.5", - "serialize-javascript": "1.5.0", - "source-map": "0.6.1", - "uglify-es": "3.3.9", - "webpack-sources": "1.1.0", - "worker-farm": "1.6.0" - }, - "dependencies": { - "commander": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", - "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "uglify-es": { - "version": "3.3.9", - "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", - "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", - "dev": true, - "requires": { - "commander": "2.13.0", - "source-map": "0.6.1" - } - } - } - }, - "ultron": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", - "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=", - "dev": true - }, - "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", - "dev": true, - "requires": { - "arr-union": "3.1.0", - "get-value": "2.0.6", - "is-extendable": "0.1.1", - "set-value": "0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "2.0.1", - "is-extendable": "0.1.1", - "is-plain-object": "2.0.4", - "to-object-path": "0.3.0" - } - } - } - }, - "unique-filename": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.0.tgz", - "integrity": "sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM=", - "dev": true, - "requires": { - "unique-slug": "2.0.0" - } - }, - "unique-slug": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz", - "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=", - "dev": true, - "requires": { - "imurmurhash": "0.1.4" - } - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "dev": true - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "requires": { - "has-value": "0.3.1", - "isobject": "3.0.1" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "2.0.6", - "has-values": "0.1.4", - "isobject": "2.1.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - } - } - }, - "upath": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", - "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", - "dev": true - }, - "upper-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", - "dev": true - }, - "uri-js": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-3.0.2.tgz", - "integrity": "sha1-+QuFhQf4HepNz7s8TD2/orVX+qo=", - "dev": true, - "requires": { - "punycode": "2.1.1" - } - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - } - } - }, - "url-join": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.0.tgz", - "integrity": "sha1-TTNA6AfTdzvamZH4MFrNzCpmXSo=", - "dev": true - }, - "url-loader": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.0.1.tgz", - "integrity": "sha512-rAonpHy7231fmweBKUFe0bYnlGDty77E+fm53NZdij7j/YOpyGzc7ttqG1nAXl3aRs0k41o0PC3TvGXQiw2Zvw==", - "dev": true, - "requires": { - "loader-utils": "1.1.0", - "mime": "2.3.1", - "schema-utils": "0.4.5" - }, - "dependencies": { - "mime": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", - "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==", - "dev": true - } - } - }, - "url-parse": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.1.tgz", - "integrity": "sha512-x95Td74QcvICAA0+qERaVkRpTGKyBHHYdwL2LXZm5t/gBtCB9KQSO/0zQgSTYEV1p0WcvSg79TLNPSvd5IDJMQ==", - "dev": true, - "requires": { - "querystringify": "2.0.0", - "requires-port": "1.0.0" - } - }, - "use": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.0.tgz", - "integrity": "sha512-6UJEQM/L+mzC3ZJNM56Q4DFGLX/evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq/UJUY4U5dh7Fal++XbNg1uzpRAw==", - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "useragent": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz", - "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==", - "dev": true, - "requires": { - "lru-cache": "4.1.3", - "tmp": "0.0.31" - } - }, - "util": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", - "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", - "dev": true, - "requires": { - "inherits": "2.0.3" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "util.promisify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", - "dev": true, - "requires": { - "define-properties": "1.1.2", - "object.getownpropertydescriptors": "2.0.3" - } - }, - "utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", - "dev": true - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "dev": true - }, - "uuid": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", - "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", - "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", - "dev": true, - "requires": { - "spdx-correct": "3.0.0", - "spdx-expression-parse": "3.0.0" - } - }, - "validate-npm-package-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", - "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", - "dev": true, - "requires": { - "builtins": "1.0.3" - } - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "dev": true - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "1.3.0" - } - }, - "vm-browserify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", - "dev": true, - "requires": { - "indexof": "0.0.1" - } - }, - "void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", - "dev": true - }, - "watchpack": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", - "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", - "dev": true, - "requires": { - "chokidar": "2.0.3", - "graceful-fs": "4.1.11", - "neo-async": "2.5.1" - } - }, - "wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "dev": true, - "requires": { - "minimalistic-assert": "1.0.1" - } - }, - "webassemblyjs": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webassemblyjs/-/webassemblyjs-1.4.3.tgz", - "integrity": "sha512-4lOV1Lv6olz0PJkDGQEp82HempAn147e6BXijWDzz9g7/2nSebVP9GVg62Fz5ZAs55mxq13GA0XLyvY8XkyDjg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.4.3", - "@webassemblyjs/validation": "1.4.3", - "@webassemblyjs/wasm-parser": "1.4.3", - "@webassemblyjs/wast-parser": "1.4.3", - "long": "3.2.0" - } - }, - "webdriver-js-extender": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-1.0.0.tgz", - "integrity": "sha1-gcUzqeM9W/tZe05j4s2yW1R3dRU=", - "dev": true, - "requires": { - "@types/selenium-webdriver": "2.53.43", - "selenium-webdriver": "2.53.3" - }, - "dependencies": { - "sax": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-0.6.1.tgz", - "integrity": "sha1-VjsZx8HeiS4Jv8Ty/DDjwn8JUrk=", - "dev": true - }, - "selenium-webdriver": { - "version": "2.53.3", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-2.53.3.tgz", - "integrity": "sha1-0p/1qVff8aG0ncRXdW5OS/vc4IU=", - "dev": true, - "requires": { - "adm-zip": "0.4.4", - "rimraf": "2.6.2", - "tmp": "0.0.24", - "ws": "1.1.2", - "xml2js": "0.4.4" - } - }, - "tmp": { - "version": "0.0.24", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.24.tgz", - "integrity": "sha1-1qXhmNFKmDXMby18PZ4wJCjIzxI=", - "dev": true - }, - "xml2js": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.4.tgz", - "integrity": "sha1-MREBAAMAiuGSQOuhdJe1fHKcVV0=", - "dev": true, - "requires": { - "sax": "0.6.1", - "xmlbuilder": "9.0.7" - } - } - } - }, - "webpack": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.8.3.tgz", - "integrity": "sha512-/hfAjBISycdK597lxONjKEFX7dSIU1PsYwC3XlXUXoykWBlv9QV5HnO+ql3HvrrgfBJ7WXdnjO9iGPR2aAc5sw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.4.3", - "@webassemblyjs/wasm-edit": "1.4.3", - "@webassemblyjs/wasm-parser": "1.4.3", - "acorn": "5.6.2", - "acorn-dynamic-import": "3.0.0", - "ajv": "6.4.0", - "ajv-keywords": "3.2.0", - "chrome-trace-event": "0.1.3", - "enhanced-resolve": "4.0.0", - "eslint-scope": "3.7.1", - "loader-runner": "2.3.0", - "loader-utils": "1.1.0", - "memory-fs": "0.4.1", - "micromatch": "3.1.10", - "mkdirp": "0.5.1", - "neo-async": "2.5.1", - "node-libs-browser": "2.1.0", - "schema-utils": "0.4.5", - "tapable": "1.0.0", - "uglifyjs-webpack-plugin": "1.2.5", - "watchpack": "1.6.0", - "webpack-sources": "1.1.0" - } - }, - "webpack-core": { - "version": "0.6.9", - "resolved": "https://registry.npmjs.org/webpack-core/-/webpack-core-0.6.9.tgz", - "integrity": "sha1-/FcViMhVjad76e+23r3Fo7FyvcI=", - "dev": true, - "requires": { - "source-list-map": "0.1.8", - "source-map": "0.4.4" - }, - "dependencies": { - "source-list-map": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz", - "integrity": "sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY=", - "dev": true - }, - "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true, - "requires": { - "amdefine": "1.0.1" - } - } - } - }, - "webpack-dev-middleware": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.1.3.tgz", - "integrity": "sha512-I6Mmy/QjWU/kXwCSFGaiOoL5YEQIVmbb0o45xMoCyQAg/mClqZVTcsX327sPfekDyJWpCxb+04whNyLOIxpJdQ==", - "dev": true, - "requires": { - "loud-rejection": "1.6.0", - "memory-fs": "0.4.1", - "mime": "2.3.1", - "path-is-absolute": "1.0.1", - "range-parser": "1.2.0", - "url-join": "4.0.0", - "webpack-log": "1.2.0" - }, - "dependencies": { - "mime": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", - "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==", - "dev": true - } - } - }, - "webpack-dev-server": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.4.tgz", - "integrity": "sha512-itcIUDFkHuj1/QQxzUFOEXXmxOj5bku2ScLEsOFPapnq2JRTm58gPdtnBphBJOKL2+M3p6+xygL64bI+3eyzzw==", - "dev": true, - "requires": { - "ansi-html": "0.0.7", - "array-includes": "3.0.3", - "bonjour": "3.5.0", - "chokidar": "2.0.3", - "compression": "1.7.2", - "connect-history-api-fallback": "1.5.0", - "debug": "3.1.0", - "del": "3.0.0", - "express": "4.16.3", - "html-entities": "1.2.1", - "http-proxy-middleware": "0.18.0", - "import-local": "1.0.0", - "internal-ip": "1.2.0", - "ip": "1.1.5", - "killable": "1.0.0", - "loglevel": "1.6.1", - "opn": "5.3.0", - "portfinder": "1.0.13", - "selfsigned": "1.10.3", - "serve-index": "1.9.1", - "sockjs": "0.3.19", - "sockjs-client": "1.1.4", - "spdy": "3.4.7", - "strip-ansi": "3.0.1", - "supports-color": "5.4.0", - "webpack-dev-middleware": "3.1.3", - "webpack-log": "1.2.0", - "yargs": "11.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "dev": true, - "requires": { - "string-width": "2.1.1", - "strip-ansi": "4.0.0", - "wrap-ansi": "2.1.0" - }, - "dependencies": { - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "3.0.0" - } - } - } - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", - "dev": true, - "requires": { - "execa": "0.7.0", - "lcid": "1.0.0", - "mem": "1.1.0" - } - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "3.0.0" - } - } - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true - }, - "yargs": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.0.0.tgz", - "integrity": "sha512-Rjp+lMYQOWtgqojx1dEWorjCofi1YN7AoFvYV7b1gx/7dAAeuI4kN5SZiEvr0ZmsZTOpDRcCqrpI10L31tFkBw==", - "dev": true, - "requires": { - "cliui": "4.1.0", - "decamelize": "1.2.0", - "find-up": "2.1.0", - "get-caller-file": "1.0.2", - "os-locale": "2.1.0", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "2.1.1", - "which-module": "2.0.0", - "y18n": "3.2.1", - "yargs-parser": "9.0.2" - } - }, - "yargs-parser": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", - "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", - "dev": true, - "requires": { - "camelcase": "4.1.0" - } - } - } - }, - "webpack-log": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-1.2.0.tgz", - "integrity": "sha512-U9AnICnu50HXtiqiDxuli5gLB5PGBo7VvcHx36jRZHwK4vzOYLbImqT4lwWwoMHdQWwEKw736fCHEekokTEKHA==", - "dev": true, - "requires": { - "chalk": "2.2.2", - "log-symbols": "2.2.0", - "loglevelnext": "1.0.5", - "uuid": "3.2.1" - } - }, - "webpack-merge": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.1.3.tgz", - "integrity": "sha512-zxwAIGK7nKdu5CIZL0BjTQoq3elV0t0MfB7rUC1zj668geid52abs6hN/ACwZdK6LeMS8dC9B6WmtF978zH5mg==", - "dev": true, - "requires": { - "lodash": "4.17.10" - } - }, - "webpack-sources": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz", - "integrity": "sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw==", - "dev": true, - "requires": { - "source-list-map": "2.0.0", - "source-map": "0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "webpack-subresource-integrity": { - "version": "1.1.0-rc.4", - "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.1.0-rc.4.tgz", - "integrity": "sha1-xcTj1pD50vZKlVDgeodn+Xlqpdg=", - "dev": true, - "requires": { - "webpack-core": "0.6.9" - } - }, - "websocket-driver": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", - "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", - "dev": true, - "requires": { - "http-parser-js": "0.4.13", - "websocket-extensions": "0.1.3" - } - }, - "websocket-extensions": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", - "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", - "dev": true - }, - "when": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/when/-/when-3.6.4.tgz", - "integrity": "sha1-RztRfsFZ4rhQBUl6E5g/CVQS404=", - "dev": true - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "2.0.0" - } - }, - "which-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", - "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "requires": { - "string-width": "1.0.2" - } - }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", - "dev": true, - "optional": true - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, - "worker-farm": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz", - "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==", - "dev": true, - "requires": { - "errno": "0.1.7" - } - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "ws": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.2.tgz", - "integrity": "sha1-iiRPoFJAHgjJiGz0SoUYnh/UBn8=", - "dev": true, - "requires": { - "options": "0.0.6", - "ultron": "1.0.2" - } - }, - "wtf-8": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wtf-8/-/wtf-8-1.0.0.tgz", - "integrity": "sha1-OS2LotDxw00e4tYw8V0O+2jhBIo=", - "dev": true - }, - "xml2js": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", - "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", - "dev": true, - "requires": { - "sax": "1.2.4", - "xmlbuilder": "9.0.7" - }, - "dependencies": { - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - } - } - }, - "xmlbuilder": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", - "dev": true - }, - "xmlhttprequest-ssl": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz", - "integrity": "sha1-GFqIjATspGw+QHDZn3tJ3jUomS0=", - "dev": true - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true - }, - "xxhashjs": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz", - "integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==", - "dev": true, - "requires": { - "cuint": "0.2.2" - } - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, - "optional": true, - "requires": { - "camelcase": "1.2.1", - "cliui": "2.1.0", - "decamelize": "1.2.0", - "window-size": "0.1.0" - } - }, - "yargs-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", - "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", - "dev": true, - "optional": true, - "requires": { - "camelcase": "3.0.0" - }, - "dependencies": { - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true, - "optional": true - } - } - }, - "yeast": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", - "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", - "dev": true - }, - "yn": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", - "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", - "dev": true - }, - "zone.js": { - "version": "0.8.26", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.26.tgz", - "integrity": "sha512-W9Nj+UmBJG251wkCacIkETgra4QgBo/vgoEkb4a2uoLzpQG7qF9nzwoLXWU5xj3Fg2mxGvEDh47mg24vXccYjA==" - } - } -} diff --git a/gae/frontend/package.json b/gae/frontend/package.json deleted file mode 100644 index 1511ee3..0000000 --- a/gae/frontend/package.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "name": "frontend", - "version": "0.0.0", - "scripts": { - "ng": "ng", - "start": "ng serve", - "build": "ng build", - "test": "ng test", - "lint": "ng lint", - "e2e": "ng e2e" - }, - "private": true, - "dependencies": { - "@angular/animations": "^6.0.6", - "@angular/cdk": "^6.2.1", - "@angular/common": "^6.0.6", - "@angular/compiler": "^6.0.6", - "@angular/core": "^6.0.6", - "@angular/flex-layout": "^6.0.0-beta.16", - "@angular/forms": "^6.0.6", - "@angular/http": "^6.0.6", - "@angular/material": "^6.2.1", - "@angular/platform-browser": "^6.0.6", - "@angular/platform-browser-dynamic": "^6.0.6", - "@angular/router": "^6.0.6", - "core-js": "^2.5.4", - "moment": "^2.22.2", - "moment-timezone": "^0.5.21", - "rxjs": "^6.0.0", - "zone.js": "^0.8.26" - }, - "devDependencies": { - "@angular-devkit/build-angular": "~0.6.8", - "@angular/cli": "~6.0.8", - "@angular/compiler-cli": "^6.0.6", - "@angular/language-service": "^6.0.6", - "@types/jasmine": "~2.8.6", - "@types/jasminewd2": "~2.0.3", - "@types/node": "~8.9.4", - "codelyzer": "~4.2.1", - "jasmine-core": "~2.99.1", - "jasmine-spec-reporter": "~4.2.1", - "karma": "~1.7.1", - "karma-chrome-launcher": "~2.2.0", - "karma-coverage-istanbul-reporter": "~2.0.0", - "karma-jasmine": "~1.1.1", - "karma-jasmine-html-reporter": "^0.2.2", - "protractor": "~5.3.0", - "ts-node": "~5.0.1", - "tslint": "^5.9.1", - "typescript": "^2.7.2" - } -} diff --git a/gae/frontend/src/app/app.component.html b/gae/frontend/src/app/app.component.html deleted file mode 100644 index 8f21391..0000000 --- a/gae/frontend/src/app/app.component.html +++ /dev/null @@ -1,35 +0,0 @@ -<!-- Copyright (C) 2018 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<header> - <div> - <app-nav-bar id="nav-bar"></app-nav-bar> - </div> -</header> -<mat-sidenav-container> - <mat-sidenav-content> - <router-outlet id="router-outlet"></router-outlet> - </mat-sidenav-content> - <mat-sidenav #sidenav mode="over" position="end" [(opened)]="sideNavOpened"> - <button mat-button (click)="sidenav.toggle()"> - <mat-icon>clear</mat-icon> - </button> - <mat-list> - <mat-list-item *ngFor="let property of selectedEntity"> - <h4 id="property-name" mat-line>{{property.name}}</h4> - <p id="property-value" mat-line *ngFor="let each of property.value">{{each}}</p> - </mat-list-item> - </mat-list> - </mat-sidenav> -</mat-sidenav-container> diff --git a/gae/frontend/src/app/app.component.scss b/gae/frontend/src/app/app.component.scss deleted file mode 100644 index d818d0e..0000000 --- a/gae/frontend/src/app/app.component.scss +++ /dev/null @@ -1,28 +0,0 @@ -mat-sidenav { - width: 400px; - padding: 30px 10px; -} - -#property-name { - color: rgba(0, 0, 0, 0.66); - font-size: 12px; - margin-bottom: 2px; -} - -#property-value { - font-size: 12px; -} - -.mat-button { - position: absolute; - top: 10px; - right: 10px; - min-width: 28px; - width: 28px; - height: 28px; - padding: 0; - .mat-icon { - width: 24px; - height: 24px; - } -} diff --git a/gae/frontend/src/app/app.component.spec.ts b/gae/frontend/src/app/app.component.spec.ts deleted file mode 100644 index e69de29..0000000 --- a/gae/frontend/src/app/app.component.spec.ts +++ /dev/null diff --git a/gae/frontend/src/app/app.component.ts b/gae/frontend/src/app/app.component.ts deleted file mode 100644 index 1a2ef06..0000000 --- a/gae/frontend/src/app/app.component.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Component } from '@angular/core'; - -import { AppService } from "./appservice"; - - -@Component({ - selector: 'app-root', - templateUrl: './app.component.html', - styleUrls: ['./app.component.scss'] -}) -export class AppComponent { - _sideNavOpened = false; - get sideNavOpened(): boolean { - return this._sideNavOpened; - } - set sideNavOpened(value: boolean) { - this._sideNavOpened = value; - if (!value) { - this.selectedEntity = this.selectedEntity.slice(); - } - } - selectedEntity: {name: string; value: any[]}[] = []; - - constructor(private appService: AppService) { - appService.closeSideNavEmitter.subscribe(() => {this.sideNavOpened = false}); - appService.showDetailsEmitter.subscribe( - (entity) => { - this.selectedEntity.length = 0; - if (entity) { - let self = this; - Object.keys(entity).forEach(function(value){ - if (value !== 'urlsafe_key') { - self.selectedEntity.push({ - name: value, - value: (entity[value] instanceof Array) ? entity[value] : [entity[value]] - }); - } - }); - } - this.sideNavOpened = !this.sideNavOpened; - }, - (error) => { - console.log(error); - } - ) - } -} diff --git a/gae/frontend/src/app/app.module.ts b/gae/frontend/src/app/app.module.ts deleted file mode 100644 index ec940db..0000000 --- a/gae/frontend/src/app/app.module.ts +++ /dev/null @@ -1,141 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -// Angular modules. -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { BrowserModule } from '@angular/platform-browser'; -import { FormsModule } from '@angular/forms'; -import { HttpClientModule } from '@angular/common/http'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; - -// Angular Material modules -import { MatButtonModule } from '@angular/material/button'; -import { MatCardModule } from '@angular/material/card'; -import { MatChipsModule } from '@angular/material/chips'; -import { MatExpansionModule } from '@angular/material/expansion'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatIconModule } from '@angular/material'; -import { MatInputModule } from '@angular/material/input'; -import { MatListModule } from '@angular/material/list'; -import { MatPaginatorModule } from '@angular/material/paginator'; -import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; -import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { MatSelectModule } from '@angular/material/select'; -import { MatSidenavModule } from '@angular/material/sidenav'; -import { MatSortModule } from '@angular/material/sort'; -import { MatTableModule } from '@angular/material/table'; -import { MatTabsModule } from '@angular/material/tabs'; - -// User components. -import { AppComponent } from './app.component'; -import { BuildComponent } from './menu/build/build.component'; -import { DashboardComponent } from './menu/dashboard/dashboard.component'; -import { DeviceComponent } from './menu/device/device.component'; -import { FilterComponent } from './shared/filter/filter.component'; -import { JobComponent } from './menu/job/job.component'; -import { LabComponent } from './menu/lab/lab.component'; -import { ScheduleComponent } from './menu/schedule/schedule.component'; - -// User modules. -import { NavModule } from './shared/navbar/navbar'; - -// Other dependencies. -import { FlexLayoutModule } from '@angular/flex-layout'; - -// User directives for CDK (Component Development Kit). -import { CdkDetailRowDirective } from './menu/cdk-detail-row.directive'; - - -const appRoutes: Routes = [ - { path: 'device', component: DeviceComponent }, - { path: 'build', component: BuildComponent }, - { path: 'job', component: JobComponent }, - { path: 'lab', component: LabComponent }, - { path: 'schedule', component: ScheduleComponent }, - { path: '', component: DashboardComponent }, - { path: '**', redirectTo: '/', pathMatch: 'full' } -]; - - -@NgModule({ - imports: [ - MatButtonModule, - MatCardModule, - MatChipsModule, - MatExpansionModule, - MatFormFieldModule, - MatIconModule, - MatInputModule, - MatListModule, - MatPaginatorModule, - MatProgressSpinnerModule, - MatSnackBarModule, - MatSelectModule, - MatSidenavModule, - MatSortModule, - MatTableModule, - MatTabsModule, - ], - exports: [ - MatButtonModule, - MatCardModule, - MatChipsModule, - MatExpansionModule, - MatFormFieldModule, - MatIconModule, - MatInputModule, - MatListModule, - MatPaginatorModule, - MatProgressSpinnerModule, - MatSnackBarModule, - MatSelectModule, - MatSidenavModule, - MatSortModule, - MatTableModule, - MatTabsModule, - ] -}) -export class MaterialModule {} - - -@NgModule({ - declarations: [ - AppComponent, - BuildComponent, - CdkDetailRowDirective, - DashboardComponent, - DeviceComponent, - FilterComponent, - JobComponent, - LabComponent, - ScheduleComponent, - ], - imports: [ - BrowserAnimationsModule, - BrowserModule, - FlexLayoutModule, - FormsModule, - HttpClientModule, - MaterialModule, - NavModule, - RouterModule.forRoot( - appRoutes - ), - ], - providers: [], - bootstrap: [AppComponent] -}) -export class AppModule { } diff --git a/gae/frontend/src/app/appservice.ts b/gae/frontend/src/app/appservice.ts deleted file mode 100644 index 6b303f0..0000000 --- a/gae/frontend/src/app/appservice.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import {EventEmitter, Injectable, Output} from '@angular/core'; - - -@Injectable({ - providedIn: 'root', -}) -export class AppService { - @Output() closeSideNavEmitter = new EventEmitter(); - @Output() showDetailsEmitter = new EventEmitter(); - constructor() { - } - - /** Emits an EventEmitter to display entity in the side nav window. */ - showDetails(entity) { - this.showDetailsEmitter.emit(entity); - } - - /** Emits an EventEmitter to close the side nav window. */ - closeSideNav() { - this.closeSideNavEmitter.emit(); - } -} diff --git a/gae/frontend/src/app/menu/build/build.component.html b/gae/frontend/src/app/menu/build/build.component.html deleted file mode 100644 index d8ae525..0000000 --- a/gae/frontend/src/app/menu/build/build.component.html +++ /dev/null @@ -1,75 +0,0 @@ -<!-- Copyright (C) 2018 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<div class="entity-filter"> - <app-filter (applyFilters)="applyFilters($event)" [disabled]="loading"></app-filter> -</div> -<div class="mat-elevation-z2 entity-table" [ngStyle]="{'opacity': (loading) ? 0.2 : 1 }"> - <mat-table #table [dataSource]="dataSource"> - <!-- Index Column --> - <ng-container matColumnDef="_index"> - <mat-header-cell *matHeaderCellDef>No.</mat-header-cell> - <mat-cell *matCellDef="let i = index"> {{i+1+pageSize*pageIndex}} </mat-cell> - </ng-container> - - <!-- Manifest Branch Column --> - <ng-container matColumnDef="artifact_type"> - <mat-header-cell *matHeaderCellDef>Artifact Type</mat-header-cell> - <mat-cell *matCellDef="let build"> {{build.artifact_type}} </mat-cell> - </ng-container> - - <!-- Manifest Branch Column --> - <ng-container matColumnDef="manifest_branch"> - <mat-header-cell *matHeaderCellDef>Manifest Branch</mat-header-cell> - <mat-cell *matCellDef="let build"> {{build.manifest_branch}} </mat-cell> - </ng-container> - - <!-- Build ID Column --> - <ng-container matColumnDef="build_id"> - <mat-header-cell *matHeaderCellDef>Build ID</mat-header-cell> - <mat-cell *matCellDef="let build"> {{build.build_id}} </mat-cell> - </ng-container> - - <!-- Build Target Column --> - <ng-container matColumnDef="build_target"> - <mat-header-cell *matHeaderCellDef>Build Target</mat-header-cell> - <mat-cell *matCellDef="let build"> {{build.build_target}} </mat-cell> - </ng-container> - - <!-- Build Type Column --> - <ng-container matColumnDef="build_type"> - <mat-header-cell *matHeaderCellDef>Build Type</mat-header-cell> - <mat-cell *matCellDef="let build"> {{build.build_type}} </mat-cell> - </ng-container> - - <!-- Signed Column --> - <ng-container matColumnDef="signed"> - <mat-header-cell *matHeaderCellDef>Signed</mat-header-cell> - <mat-cell *matCellDef="let build"> {{build.signed}} </mat-cell> - </ng-container> - - <mat-header-row *matHeaderRowDef="columnTitles"></mat-header-row> - <mat-row *matRowDef="let row; columns: columnTitles;"></mat-row> - </mat-table> - - <mat-paginator [length]="count" - [pageSize]="pageSize" - [pageSizeOptions]="pageSizeOptions" - [pageIndex]="pageIndex" - (page)="pageEvent = onPageEvent($event)"> - </mat-paginator> -</div> -<div class="loading-spinner" *ngIf="loading"> - <mat-spinner color="primary"></mat-spinner> -</div> diff --git a/gae/frontend/src/app/menu/build/build.component.scss b/gae/frontend/src/app/menu/build/build.component.scss deleted file mode 100644 index e69de29..0000000 --- a/gae/frontend/src/app/menu/build/build.component.scss +++ /dev/null diff --git a/gae/frontend/src/app/menu/build/build.component.ts b/gae/frontend/src/app/menu/build/build.component.ts deleted file mode 100644 index e4c7325..0000000 --- a/gae/frontend/src/app/menu/build/build.component.ts +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { Component, OnInit, ViewChild } from '@angular/core'; -import { MatSnackBar, MatTableDataSource, PageEvent } from '@angular/material'; - -import { AppService } from '../../appservice'; -import { Build } from '../../model/build'; -import { BuildService } from './build.service'; -import { FilterComponent } from '../../shared/filter/filter.component'; -import { FilterItem } from '../../model/filter_item'; -import { MenuBaseClass } from '../menu_base'; - - -/** Component that handles build menu. */ -@Component({ - selector: 'app-build', - templateUrl: './build.component.html', - providers: [ BuildService ], - styleUrls: ['./build.component.scss'], -}) -export class BuildComponent extends MenuBaseClass implements OnInit { - columnTitles = [ - '_index', - 'artifact_type', - 'manifest_branch', - 'build_id', - 'build_target', - 'build_type', - 'signed']; - dataSource = new MatTableDataSource<Build>(); - pageEvent: PageEvent; - appliedFilters: FilterItem[]; - - @ViewChild(FilterComponent) filterComponent: FilterComponent; - - constructor(private buildService: BuildService, - appService: AppService, - snackBar: MatSnackBar) { - super(appService, snackBar); - } - - ngOnInit(): void { - this.filterComponent.setSelectorList(Build); - this.getCount(); - this.getBuilds(this.pageSize, this.pageSize * this.pageIndex); - } - - /** Gets a total count of builds. */ - getCount(observer = this.getDefaultCountObservable()) { - const filterJSON = (this.appliedFilters) ? JSON.stringify(this.appliedFilters) : ''; - this.buildService.getCount(filterJSON).subscribe(observer); - } - - /** Gets builds. - * @param size A number, at most this many results will be returned. - * @param offset A Number of results to skip. - */ - getBuilds(size = 0, offset = 0) { - this.loading = true; - const filterJSON = (this.appliedFilters) ? JSON.stringify(this.appliedFilters) : ''; - this.buildService.getBuilds(size, offset, filterJSON, '', '') - .subscribe( - (response) => { - this.loading = false; - if (this.count >= 0) { - let length = 0; - if (response.builds) { - length = response.builds.length; - } - const total = length + offset; - if (response.has_next) { - if (length !== this.pageSize) { - this.showSnackbar('Received unexpected number of entities.'); - } else if (this.count <= total) { - this.getCount(); - } - } else { - if (this.count !== total) { - if (length !== this.count) { - this.getCount(); - } else if (this.count > total) { - const countObservable = this.getDefaultCountObservable([ - () => { - this.pageIndex = Math.floor(this.count / this.pageSize); - this.getBuilds(this.pageSize, this.pageSize * this.pageIndex); - } - ]); - this.getCount(countObservable); - } - } - } - } - this.dataSource.data = response.builds; - }, - (error) => this.showSnackbar(`[${error.status}] ${error.name}`) - ); - } - - /** Hooks a page event and handles properly. */ - onPageEvent(event: PageEvent) { - this.pageSize = event.pageSize; - this.pageIndex = event.pageIndex; - this.getBuilds(this.pageSize, this.pageSize * this.pageIndex); - return event; - } - - /** Applies a filter and get entities with it. */ - applyFilters(filters) { - this.pageIndex = 0; - this.appliedFilters = filters; - this.getCount(); - this.getBuilds(this.pageSize, this.pageSize * this.pageIndex); - } -} diff --git a/gae/frontend/src/app/menu/build/build.service.ts b/gae/frontend/src/app/menu/build/build.service.ts deleted file mode 100644 index d60f790..0000000 --- a/gae/frontend/src/app/menu/build/build.service.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; - -import { catchError } from 'rxjs/operators'; -import { Observable } from 'rxjs'; - -import { BuildWrapper } from '../../model/build_wrapper'; -import { environment } from '../../../environments/environment'; -import { ServiceBase } from '../../shared/servicebase'; - - -@Injectable() -export class BuildService extends ServiceBase { - constructor(public httpClient: HttpClient) { - super(httpClient); - this.url = environment['baseURL'] + '/build/v1/'; - } - - getBuilds(size: number, - offset: number, - filterInfo: string, - sort: string, - direction: string): Observable<BuildWrapper> { - const url = this.url + 'get'; - return this.httpClient.post<BuildWrapper>(url, {size: size, offset: offset, filter: filterInfo, sort: sort, direction: direction}) - .pipe(catchError(this.handleError)); - } -} diff --git a/gae/frontend/src/app/menu/cdk-detail-row.directive.ts b/gae/frontend/src/app/menu/cdk-detail-row.directive.ts deleted file mode 100644 index 60b490a..0000000 --- a/gae/frontend/src/app/menu/cdk-detail-row.directive.ts +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import {Directive, EventEmitter, HostBinding, HostListener, Input, Output, TemplateRef, ViewContainerRef} from '@angular/core'; - - -@Directive({ - selector: '[appCdkDetailRow]' -}) -export class CdkDetailRowDirective { - private row: any; - private tRef: TemplateRef<any>; - private opened: boolean; - - @HostBinding('class.expanded') - get expended(): boolean { - return this.opened; - } - - @Input() - set appCdkDetailRow(value: any) { - if (value !== this.row) { - this.row = value; - // this.render(); - } - } - - @Input('appCdkDetailRowTpl') - set template(value: TemplateRef<any>) { - if (value !== this.tRef) { - this.tRef = value; - } - } - - @Output() toggleChange = new EventEmitter<CdkDetailRowDirective>(); - - constructor(public vcRef: ViewContainerRef) { } - - @HostListener('click') - onClick(): void { - this.toggle(); - } - - toggle(): void { - if (this.opened) { - this.vcRef.clear(); - } else { - this.render(); - } - this.opened = this.vcRef.length > 0; - this.toggleChange.emit(this); - } - - private render(): void { - this.vcRef.clear(); - if (this.tRef && this.row) { - this.vcRef.createEmbeddedView(this.tRef, { $implicit: this.row }); - } - } -} diff --git a/gae/frontend/src/app/menu/dashboard/dashboard.component.html b/gae/frontend/src/app/menu/dashboard/dashboard.component.html deleted file mode 100644 index b91e80c..0000000 --- a/gae/frontend/src/app/menu/dashboard/dashboard.component.html +++ /dev/null @@ -1,30 +0,0 @@ -<!-- Copyright (C) 2018 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<div fxLayout="row"> - <mat-card> - <mat-card-title>Build</mat-card-title> - <mat-card-subtitle>Last updated: {{getRelativeTime(lastBuildUpdateTime)}}</mat-card-subtitle> - <button mat-raised-button (click)="getLatestBuild()"> - <mat-icon>refresh</mat-icon> - </button> - </mat-card> - <mat-card> - <mat-card-title>Schedule</mat-card-title> - <mat-card-subtitle>Last updated: {{getRelativeTime(lastScheduleUpdateTime)}}</mat-card-subtitle> - <button mat-raised-button (click)="getLastestSchedule()"> - <mat-icon>refresh</mat-icon> - </button> - </mat-card> -</div> diff --git a/gae/frontend/src/app/menu/dashboard/dashboard.component.scss b/gae/frontend/src/app/menu/dashboard/dashboard.component.scss deleted file mode 100644 index a17cb36..0000000 --- a/gae/frontend/src/app/menu/dashboard/dashboard.component.scss +++ /dev/null @@ -1,17 +0,0 @@ -.mat-card { - width: 50%; - - .mat-raised-button { - position: absolute; - top: 10px; - right: 10px; - min-width: 28px; - width: 28px; - height: 28px; - padding: 0; - .mat-icon { - width: 24px; - height: 24px; - } - } -} diff --git a/gae/frontend/src/app/menu/dashboard/dashboard.component.ts b/gae/frontend/src/app/menu/dashboard/dashboard.component.ts deleted file mode 100644 index 0157ea8..0000000 --- a/gae/frontend/src/app/menu/dashboard/dashboard.component.ts +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { Component, OnInit } from '@angular/core'; -import { MatSnackBar } from '@angular/material'; - -import { AppService } from '../../appservice'; -import { BuildService } from "../build/build.service"; -import { MenuBaseClass } from "../menu_base"; -import { ScheduleService } from "../schedule/schedule.service"; - -/** Component that handles dashboard. */ -@Component({ - selector: 'app-dashboard', - templateUrl: './dashboard.component.html', - providers: [ BuildService, ScheduleService ], - styleUrls: ['./dashboard.component.scss'] -}) -export class DashboardComponent extends MenuBaseClass implements OnInit { - lastBuildUpdateTime: any = '---'; - lastScheduleUpdateTime: any = '---'; - - constructor(private buildService: BuildService, - private scheduleService: ScheduleService, - appService: AppService, - snackBar: MatSnackBar) { - super(appService, snackBar); - } - - ngOnInit(): void { - this.getLatestBuild(); - this.getLastestSchedule(); - } - - /** Fetches the most recently updated build and gets timestamp from it. */ - getLatestBuild() { - this.lastBuildUpdateTime = '---'; - this.buildService.getBuilds(1, 0, '', 'timestamp', 'desc') - .subscribe( - (response) => { - if (response.builds) { - this.lastBuildUpdateTime = response.builds[0].timestamp; - } - }, - (error) => this.showSnackbar(`[${error.status}] ${error.name}`) - ); - } - - /** Fetches the most recently updated schedule and gets timestamp from it. */ - getLastestSchedule() { - this.lastScheduleUpdateTime = '---'; - this.scheduleService.getSchedules(1, 0, '', 'timestamp', 'desc') - .subscribe( - (response) => { - if (response.schedules) { - this.lastScheduleUpdateTime = response.schedules[0].timestamp; - } - }, - (error) => this.showSnackbar(`[${error.status}] ${error.name}`) - ); - } -} diff --git a/gae/frontend/src/app/menu/device/device.component.html b/gae/frontend/src/app/menu/device/device.component.html deleted file mode 100644 index b36491e..0000000 --- a/gae/frontend/src/app/menu/device/device.component.html +++ /dev/null @@ -1,80 +0,0 @@ -<!-- Copyright (C) 2018 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<div class="entity-filter"> - <app-filter (applyFilters)="applyFilters($event)" [disabled]="loading"></app-filter> -</div> -<div class="mat-elevation-z2 entity-table" [ngStyle]="{'opacity': (loading) ? 0.2 : 1 }"> - <mat-table [dataSource]="dataSource" matSort matSortActive="hostname" matSortDirection="asc"> - <!-- Index Column --> - <ng-container matColumnDef="_index"> - <mat-header-cell *matHeaderCellDef>No.</mat-header-cell> - <mat-cell *matCellDef="let i = index"> {{i+1+pageSize*pageIndex}} </mat-cell> - </ng-container> - - <!-- Host Name Column --> - <ng-container matColumnDef="hostname"> - <mat-header-cell *matHeaderCellDef mat-sort-header disabled>Host Name</mat-header-cell> - <mat-cell *matCellDef="let device"> {{device.hostname}} </mat-cell> - </ng-container> - - <!-- Product Column --> - <ng-container matColumnDef="product"> - <mat-header-cell *matHeaderCellDef>Product</mat-header-cell> - <mat-cell *matCellDef="let device"> {{device.product}} </mat-cell> - </ng-container> - - <!-- Serial Column --> - <ng-container matColumnDef="serial"> - <mat-header-cell *matHeaderCellDef>Serial</mat-header-cell> - <mat-cell *matCellDef="let device"> {{device.serial}} </mat-cell> - </ng-container> - - <!-- Status Column --> - <ng-container matColumnDef="status"> - <mat-header-cell *matHeaderCellDef>Status</mat-header-cell> - <mat-cell *matCellDef="let device"> {{deviceStatusEnum[device.status]}} </mat-cell> - </ng-container> - - <!-- Scheduling Status Column --> - <ng-container matColumnDef="scheduling_status"> - <mat-header-cell *matHeaderCellDef>Scheduling Status</mat-header-cell> - <mat-cell *matCellDef="let device"> {{schedulingStatusEnum[device.scheduling_status]}} </mat-cell> - </ng-container> - - <!-- Equipment Column --> - <ng-container matColumnDef="device_equipment"> - <mat-header-cell *matHeaderCellDef>Equipment</mat-header-cell> - <mat-cell *matCellDef="let device"> {{device.device_equipment ? device.device_equipment.join(", ") : "None"}} </mat-cell> - </ng-container> - - <!-- Timestamp Column --> - <ng-container matColumnDef="timestamp"> - <mat-header-cell *matHeaderCellDef>Timestamp</mat-header-cell> - <mat-cell *matCellDef="let device">{{getRelativeTime(device.timestamp)}}</mat-cell> - </ng-container> - - <mat-header-row *matHeaderRowDef="columnTitles"></mat-header-row> - <mat-row *matRowDef="let row; columns: columnTitles;"></mat-row> - </mat-table> - <mat-paginator [length]="count" - [pageSize]="pageSize" - [pageSizeOptions]="pageSizeOptions" - [pageIndex]="pageIndex" - (page)="pageEvent = onPageEvent($event)"> - </mat-paginator> -</div> -<div class="loading-spinner" *ngIf="loading"> - <mat-spinner color="primary"></mat-spinner> -</div> diff --git a/gae/frontend/src/app/menu/device/device.component.scss b/gae/frontend/src/app/menu/device/device.component.scss deleted file mode 100644 index 165de43..0000000 --- a/gae/frontend/src/app/menu/device/device.component.scss +++ /dev/null @@ -1,7 +0,0 @@ -.mat-header-cell { - padding: 0 10px 0 10px; -} - -.mat-cell { - padding: 0 10px 0 10px; -} diff --git a/gae/frontend/src/app/menu/device/device.component.ts b/gae/frontend/src/app/menu/device/device.component.ts deleted file mode 100644 index 6258aed..0000000 --- a/gae/frontend/src/app/menu/device/device.component.ts +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { Component, OnInit, ViewChild } from '@angular/core'; -import { MatSnackBar, MatTableDataSource, PageEvent } from '@angular/material'; - -import { AppService } from '../../appservice'; -import { Device } from '../../model/device'; -import { DeviceService } from './device.service'; -import { DeviceStatus, SchedulingStatus } from '../../shared/vtslab_status'; -import { FilterComponent } from '../../shared/filter/filter.component'; -import { FilterItem } from '../../model/filter_item'; -import { MenuBaseClass } from '../menu_base'; - - -/** Component that handles device menu. */ -@Component({ - selector: 'app-device', - templateUrl: './device.component.html', - providers: [ DeviceService ], - styleUrls: ['./device.component.scss'], -}) -export class DeviceComponent extends MenuBaseClass implements OnInit { - columnTitles = [ - '_index', - 'hostname', - 'product', - 'serial', - 'status', - 'scheduling_status', - 'device_equipment', - 'timestamp', - ]; - dataSource = new MatTableDataSource<Device>(); - pageEvent: PageEvent; - deviceStatusEnum = DeviceStatus; - schedulingStatusEnum = SchedulingStatus; - appliedFilters: FilterItem[]; - - sort = ''; - sortDirection = ''; - - @ViewChild(FilterComponent) filterComponent: FilterComponent; - - constructor(private deviceService: DeviceService, - appService: AppService, - snackBar: MatSnackBar) { - super(appService, snackBar); - } - - ngOnInit(): void { - this.sort = 'hostname'; - this.sortDirection = 'asc'; - - this.filterComponent.setSelectorList(Device); - this.getCount(); - this.getDevices(this.pageSize, this.pageSize * this.pageIndex); - } - - /** Gets a total count of devices. */ - getCount(observer = this.getDefaultCountObservable()) { - const filterJSON = (this.appliedFilters) ? JSON.stringify(this.appliedFilters) : ''; - this.deviceService.getCount(filterJSON).subscribe(observer); - } - - /** Gets devices. - * @param size A number, at most this many results will be returned. - * @param offset A Number of results to skip. - */ - getDevices(size = 0, offset = 0) { - this.loading = true; - const filterJSON = (this.appliedFilters) ? JSON.stringify(this.appliedFilters) : ''; - this.deviceService.getDevices(size, offset, filterJSON, this.sort, this.sortDirection) - .subscribe( - (response) => { - this.loading = false; - if (this.count >= 0) { - let length = 0; - if (response.devices) { - length = response.devices.length; - } - const total = length + offset; - if (response.has_next) { - if (length !== this.pageSize) { - this.showSnackbar('Received unexpected number of entities.'); - } else if (this.count <= total) { - this.getCount(); - } - } else { - if (this.count !== total) { - if (length !== this.count) { - this.getCount(); - } else if (this.count > total) { - const countObservable = this.getDefaultCountObservable([ - () => { - this.pageIndex = Math.floor(this.count / this.pageSize); - this.getDevices(this.pageSize, this.pageSize * this.pageIndex); - } - ]); - this.getCount(countObservable); - } - } - } - } - this.dataSource.data = response.devices; - }, - (error) => this.showSnackbar(`[${error.status}] ${error.name}`) - ); - } - - /** Hooks a page event and handles properly. */ - onPageEvent(event: PageEvent) { - this.pageSize = event.pageSize; - this.pageIndex = event.pageIndex; - this.getDevices(this.pageSize, this.pageSize * this.pageIndex); - return event; - } - - /** Applies a filter and get entities with it. */ - applyFilters(filters) { - this.pageIndex = 0; - this.appliedFilters = filters; - this.getCount(); - this.getDevices(this.pageSize, this.pageSize * this.pageIndex); - } -} diff --git a/gae/frontend/src/app/menu/device/device.service.ts b/gae/frontend/src/app/menu/device/device.service.ts deleted file mode 100644 index 2a465ff..0000000 --- a/gae/frontend/src/app/menu/device/device.service.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; - -import { catchError } from 'rxjs/operators'; -import { Observable } from 'rxjs'; - -import { environment } from '../../../environments/environment'; -import { DeviceWrapper } from '../../model/device_wrapper'; -import { ServiceBase } from '../../shared/servicebase'; - - -@Injectable() -export class DeviceService extends ServiceBase { - constructor(public httpClient: HttpClient) { - super(httpClient); - this.url = environment['baseURL'] + '/host/v1/'; - } - - getDevices(size: number, - offset: number, - filterInfo: string, - sort: string, - direction: string): Observable<DeviceWrapper> { - const url = this.url + 'get'; - return this.httpClient.post<DeviceWrapper>(url, {size: size, offset: offset, filter: filterInfo, sort: sort, direction: direction}) - .pipe(catchError(this.handleError)); - } -} diff --git a/gae/frontend/src/app/menu/job/_job-theme.scss b/gae/frontend/src/app/menu/job/_job-theme.scss deleted file mode 100644 index 084f1fb..0000000 --- a/gae/frontend/src/app/menu/job/_job-theme.scss +++ /dev/null @@ -1,7 +0,0 @@ -@mixin schedule-theme($theme) { - $primary: map-get($theme, primary); - $accent: map-get($theme, accent); - $warn: map-get($theme, warn); - $background: map-get($theme, background); - $foreground: map-get($theme, foreground); -} diff --git a/gae/frontend/src/app/menu/job/job.component.html b/gae/frontend/src/app/menu/job/job.component.html deleted file mode 100644 index 634402d..0000000 --- a/gae/frontend/src/app/menu/job/job.component.html +++ /dev/null @@ -1,192 +0,0 @@ -<!-- Copyright (C) 2018 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<div class="statistics-table" [ngStyle]="{'opacity': (loading) ? 0.2 : 1 }"> - <mat-table [dataSource]="statDataSource"> - <ng-container matColumnDef="hours"> - <mat-header-cell *matHeaderCellDef>Stats</mat-header-cell> - <mat-cell *matCellDef="let stat"> {{stat.hours}} </mat-cell> - </ng-container> - - <ng-container matColumnDef="created"> - <mat-header-cell *matHeaderCellDef>Created</mat-header-cell> - <mat-cell *matCellDef="let stat"> {{stat.created}} </mat-cell> - </ng-container> - - <ng-container matColumnDef="completed"> - <mat-header-cell *matHeaderCellDef>Completed</mat-header-cell> - <mat-cell *matCellDef="let stat"> {{stat.completed}} ({{stat.created > 0 ? (stat.completed/stat.created*100 | number:'1.0-2') : 0}})% </mat-cell> - </ng-container> - - <ng-container matColumnDef="running"> - <mat-header-cell *matHeaderCellDef>Running/Ready</mat-header-cell> - <mat-cell *matCellDef="let stat"> {{stat.running}} ({{stat.created > 0 ? (stat.running/stat.created*100 | number:'1.0-2') : 0}})% </mat-cell> - </ng-container> - - <ng-container matColumnDef="bootup_err"> - <mat-header-cell *matHeaderCellDef>Boot-up Error</mat-header-cell> - <mat-cell *matCellDef="let stat"> {{stat.bootup_err}} ({{stat.created > 0 ? (stat.bootup_err/stat.created*100 | number:'1.0-2') : 0}})% </mat-cell> - </ng-container> - - <ng-container matColumnDef="infra_err"> - <mat-header-cell *matHeaderCellDef>Infra Error</mat-header-cell> - <mat-cell *matCellDef="let stat"> {{stat.infra_err}} ({{stat.created > 0 ? (stat.infra_err/stat.created*100 | number:'1.0-2') : 0}})% </mat-cell> - </ng-container> - - <ng-container matColumnDef="expired"> - <mat-header-cell *matHeaderCellDef>Expired</mat-header-cell> - <mat-cell *matCellDef="let stat"> {{stat.expired}} ({{stat.created > 0 ? (stat.expired/stat.created*100 | number:'1.0-2') : 0}})% </mat-cell> - </ng-container> - - <mat-header-row *matHeaderRowDef="statColumnTitles"></mat-header-row> - <mat-row *matRowDef="let row; columns: statColumnTitles;"></mat-row> - </mat-table> -</div> -<div class="entity-filter"> - <app-filter (applyFilters)="applyFilters($event)" [disabled]="loading"></app-filter> -</div> -<div class="mat-elevation-z2 entity-table" [ngStyle]="{'opacity': (loading) ? 0.2 : 1 }"> - <mat-table [dataSource]="dataSource" matSort matSortActive="timestamp" matSortDirection="desc"> - <!-- Index Column --> - <ng-container matColumnDef="_index"> - <mat-header-cell *matHeaderCellDef>No.</mat-header-cell> - <mat-cell *matCellDef="let i = index"> {{i+1+pageSize*pageIndex}} </mat-cell> - </ng-container> - - <!-- Test Type Column --> - <ng-container matColumnDef="test_type"> - <mat-header-cell *matHeaderCellDef>Test Type</mat-header-cell> - <mat-cell *matCellDef="let job"> {{getTestTypeText(job.test_type)}} </mat-cell> - </ng-container> - - <!-- Test Name Column --> - <ng-container matColumnDef="test_name"> - <mat-header-cell *matHeaderCellDef>Test Name</mat-header-cell> - <mat-cell *matCellDef="let job"> {{job.test_name}} </mat-cell> - </ng-container> - - <!-- Host Name Column --> - <ng-container matColumnDef="hostname"> - <mat-header-cell *matHeaderCellDef>Hostname</mat-header-cell> - <mat-cell *matCellDef="let job"> {{job.hostname}} </mat-cell> - </ng-container> - - <!-- Device Column --> - <ng-container matColumnDef="device"> - <mat-header-cell *matHeaderCellDef>Device</mat-header-cell> - <mat-cell *matCellDef="let job"> {{job.device}} </mat-cell> - </ng-container> - - <!-- Serial Column --> - <ng-container matColumnDef="serial"> - <mat-header-cell *matHeaderCellDef>Serial</mat-header-cell> - <mat-cell *matCellDef="let job"> {{job.serial ? job.serial.join('\n') : ""}} </mat-cell> - </ng-container> - - <!-- Device Branch Column --> - <ng-container matColumnDef="manifest_branch"> - <mat-header-cell *matHeaderCellDef>Device Branch</mat-header-cell> - <mat-cell *matCellDef="let job"> {{job.manifest_branch}} </mat-cell> - </ng-container> - - <!-- Device Build Target Column --> - <ng-container matColumnDef="build_target"> - <mat-header-cell *matHeaderCellDef>Device Build Target</mat-header-cell> - <mat-cell *matCellDef="let job"> {{job.build_target}} </mat-cell> - </ng-container> - - <!-- Device Build ID Column --> - <ng-container matColumnDef="build_id"> - <mat-header-cell *matHeaderCellDef>Device Build ID</mat-header-cell> - <mat-cell *matCellDef="let job"> {{job.build_id}} </mat-cell> - </ng-container> - - <!-- GSI Branch Column --> - <ng-container matColumnDef="gsi_branch"> - <mat-header-cell *matHeaderCellDef>GSI Branch</mat-header-cell> - <mat-cell *matCellDef="let job"> {{job.gsi_branch}} </mat-cell> - </ng-container> - - <!-- GSI Build Target Column --> - <ng-container matColumnDef="gsi_build_target"> - <mat-header-cell *matHeaderCellDef>GSI Build Target</mat-header-cell> - <mat-cell *matCellDef="let job"> {{job.gsi_build_target}} </mat-cell> - </ng-container> - - <!-- Device Build ID Column --> - <ng-container matColumnDef="gsi_build_id"> - <mat-header-cell *matHeaderCellDef>GSI Build ID</mat-header-cell> - <mat-cell *matCellDef="let job"> {{job.gsi_build_id}} </mat-cell> - </ng-container> - - <!-- Test Branch Column --> - <ng-container matColumnDef="test_branch"> - <mat-header-cell *matHeaderCellDef>Test Branch</mat-header-cell> - <mat-cell *matCellDef="let job"> {{job.test_branch}} </mat-cell> - </ng-container> - - <!-- Test Build Target Column --> - <ng-container matColumnDef="test_build_target"> - <mat-header-cell *matHeaderCellDef>Test Build Target</mat-header-cell> - <mat-cell *matCellDef="let job"> {{job.test_build_target}} </mat-cell> - </ng-container> - - <!-- Test Build ID Column --> - <ng-container matColumnDef="test_build_id"> - <mat-header-cell *matHeaderCellDef>Test Build ID</mat-header-cell> - <mat-cell *matCellDef="let job"> {{job.test_build_id}} </mat-cell> - </ng-container> - - <!-- Status Column --> - <ng-container matColumnDef="status"> - <mat-header-cell *matHeaderCellDef>Status</mat-header-cell> - <mat-cell *matCellDef="let job"> {{jobStatusEnum[job.status]}} </mat-cell> - </ng-container> - - <!-- Timestamp Column --> - <ng-container matColumnDef="timestamp"> - <mat-header-cell *matHeaderCellDef mat-sort-header disabled>Timestamp</mat-header-cell> - <mat-cell *matCellDef="let job"> {{getRelativeTime(job.timestamp)}} </mat-cell> - </ng-container> - - <!-- Heartbeat stamp Column --> - <ng-container matColumnDef="heartbeat_stamp"> - <mat-header-cell *matHeaderCellDef>Heartbeat</mat-header-cell> - <mat-cell *matCellDef="let job"> {{getRelativeTime(job.heartbeat_stamp)}} </mat-cell> - </ng-container> - - <mat-header-row *matHeaderRowDef="columnTitles"></mat-header-row> - <mat-row *matRowDef="let row; columns: columnTitles;" - matRipple - class="element-row" - [appCdkDetailRow]="row" [appCdkDetailRowTpl]="job_detail"></mat-row> - </mat-table> - <mat-paginator [length]="count" - [pageSize]="pageSize" - [pageSizeOptions]="pageSizeOptions" - [pageIndex]="pageIndex" - (page)="pageEvent = onPageEvent($event)"> - </mat-paginator> -</div> -<ng-template #job_detail let-job> - <div class="mat-row div-expandable" [@detailExpand] style="overflow: hidden"> - <a href="{{job.infra_log_url}}" download><button mat-raised-button [disabled]="(!job.infra_log_url)">Download Infra Log</button></a> - <button mat-raised-button (click)="onShowDetailsClicked(job)"> - Show Details - </button> - </div> -</ng-template> -<div class="loading-spinner" *ngIf="loading"> - <mat-spinner color="primary"></mat-spinner> -</div> diff --git a/gae/frontend/src/app/menu/job/job.component.scss b/gae/frontend/src/app/menu/job/job.component.scss deleted file mode 100644 index c8aee00..0000000 --- a/gae/frontend/src/app/menu/job/job.component.scss +++ /dev/null @@ -1,24 +0,0 @@ -.mat-header-cell { - padding: 0 10px 0 10px; -} - -.mat-cell { - padding: 0 10px 0 10px; -} - -.element-row { - position: relative; - overflow: hidden; -} - -.element-row:not(.expanded) { - cursor: pointer; -} - -.element-row:not(.expanded):hover { - background: #f5f5f5; -} - -.element-row.expanded { - border-bottom-color: transparent; -} diff --git a/gae/frontend/src/app/menu/job/job.component.ts b/gae/frontend/src/app/menu/job/job.component.ts deleted file mode 100644 index 4375581..0000000 --- a/gae/frontend/src/app/menu/job/job.component.ts +++ /dev/null @@ -1,221 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { Component, OnInit, ViewChild } from '@angular/core'; -import { MatSnackBar, MatTableDataSource, PageEvent } from '@angular/material'; -import { animate, state, style, transition, trigger } from '@angular/animations'; - -import { AppService } from '../../appservice'; -import { FilterComponent } from '../../shared/filter/filter.component'; -import { FilterCondition } from '../../model/filter_condition'; -import { FilterItem } from '../../model/filter_item'; -import { MenuBaseClass } from '../menu_base'; -import { Job } from '../../model/job'; -import { JobService } from './job.service'; -import { JobStatus, TestType } from '../../shared/vtslab_status'; - -import * as moment from 'moment-timezone'; - - -/** Component that handles job menu. */ -@Component({ - selector: 'app-job', - templateUrl: './job.component.html', - providers: [ JobService ], - styleUrls: ['./job.component.scss'], - animations: [ - trigger('detailExpand', [ - state('void', style({height: '0px', minHeight: '0', visibility: 'hidden'})), - state('*', style({height: '*', visibility: 'visible'})), - transition('void <=> *', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')), - ]), - ], -}) -export class JobComponent extends MenuBaseClass implements OnInit { - columnTitles = [ - '_index', - 'test_type', - 'test_name', - 'hostname', - 'device', - 'serial', - 'manifest_branch', - 'build_target', - 'build_id', - 'gsi_branch', - 'gsi_build_target', - 'gsi_build_id', - 'test_branch', - 'test_build_target', - 'test_build_id', - 'status', - 'timestamp', - 'heartbeat_stamp', - ]; - statColumnTitles = [ - 'hours', - 'created', - 'completed', - 'running', - 'bootup_err', - 'infra_err', - 'expired', - ]; - dataSource = new MatTableDataSource<Job>(); - statDataSource = new MatTableDataSource(); - pageEvent: PageEvent; - jobStatusEnum = JobStatus; - appliedFilters: FilterItem[]; - - @ViewChild(FilterComponent) filterComponent: FilterComponent; - - sort = ''; - sortDirection = ''; - - constructor(private jobService: JobService, - appService: AppService, - snackBar: MatSnackBar) { - super(appService, snackBar); - } - - ngOnInit(): void { - // By default, job page requires list in desc order by timestamp. - this.sort = 'timestamp'; - this.sortDirection = 'desc'; - - this.filterComponent.setSelectorList(Job); - this.getCount(); - this.getStatistics(); - this.getJobs(this.pageSize, this.pageSize * this.pageIndex); - } - - /** Gets a total count of jobs. */ - getCount(observer = this.getDefaultCountObservable()) { - const filterJSON = (this.appliedFilters) ? JSON.stringify(this.appliedFilters) : ''; - this.jobService.getCount(filterJSON).subscribe(observer); - } - - /** Gets jobs. - * @param size A number, at most this many results will be returned. - * @param offset A Number of results to skip. - */ - getJobs(size = 0, offset = 0) { - this.loading = true; - const filterJSON = (this.appliedFilters) ? JSON.stringify(this.appliedFilters) : ''; - this.jobService.getJobs(size, offset, filterJSON, this.sort, this.sortDirection) - .subscribe( - (response) => { - this.loading = false; - if (this.count >= 0) { - let length = 0; - if (response.jobs) { - length = response.jobs.length; - } - const total = length + offset; - if (response.has_next) { - if (length !== this.pageSize) { - this.showSnackbar('Received unexpected number of entities.'); - } else if (this.count <= total) { - this.getCount(); - } - } else { - if (this.count !== total) { - if (length !== this.count) { - this.getCount(); - } else if (this.count > total) { - const countObservable = this.getDefaultCountObservable([ - () => { - this.pageIndex = Math.floor(this.count / this.pageSize); - this.getJobs(this.pageSize, this.pageSize * this.pageIndex); - } - ]); - this.getCount(countObservable); - } - } - } - } - this.dataSource.data = response.jobs; - }, - (error) => this.showSnackbar(`[${error.status}] ${error.name}`) - ); - } - - /** Hooks a page event and handles properly. */ - onPageEvent(event: PageEvent) { - this.pageSize = event.pageSize; - this.pageIndex = event.pageIndex; - this.getJobs(this.pageSize, this.pageSize * this.pageIndex); - return event; - } - - /** Gets the recent jobs and calculate statistics */ - getStatistics() { - const timeFilter = new FilterItem(); - timeFilter.key = 'timestamp'; - timeFilter.method = FilterCondition.GreaterThan; - timeFilter.value = '72'; - const timeFilterString = JSON.stringify([timeFilter]); - this.jobService.getJobs(0, 0, timeFilterString, '', '') - .subscribe( - (response) => { - const stats_72hrs = this.buildStatisticsData('72 Hours', response.jobs); - const jobs_24hrs = (response.jobs == null || response.jobs.length === 0) ? undefined : response.jobs.filter( - job => (moment() - moment.tz(job.timestamp, 'YYYY-MM-DDThh:mm:ss', 'UTC')) / 3600000 < 24); - const stats_24hrs = this.buildStatisticsData('24 Hours', jobs_24hrs); - this.statDataSource.data = [stats_24hrs, stats_72hrs]; - }, - (error) => this.showSnackbar(`[${error.status}] ${error.name}`) - ); - } - - /** Builds statistics from given jobs list */ - buildStatisticsData(title, jobs) { - if (jobs == null || jobs.length === 0) { - return { hours: title, created: 0, completed: 0, running: 0, bootup_err: 0, infra_err: 0, expired: 0 }; - } - return { - hours: title, - created: jobs.length, - completed: jobs.filter(job => job.status != null && Number(job.status) === JobStatus.Complete).length, - running: jobs.filter(job => job.status != null && - (Number(job.status) === JobStatus.Leased || Number(job.status) === JobStatus.Ready)).length, - bootup_err: jobs.filter(job => job.status != null && Number(job.status) === JobStatus.Bootup_err).length, - infra_err: jobs.filter(job => job.status != null && Number(job.status) === JobStatus.Infra_err).length, - expired: jobs.filter(job => job.status != null && Number(job.status) === JobStatus.Expired).length, - }; - } - - /** Generates text to represent in HTML with given test type. */ - getTestTypeText(status: number) { - if (status === undefined || status & TestType.Unknown) { - return TestType[TestType.Unknown]; - } - - const text_list = []; - [TestType.ToT, TestType.OTA, TestType.Signed, TestType.Manual].forEach(function (value) { - if (status & value) { text_list.push(TestType[value]); } - }); - - return text_list.join(', '); - } - - /** Applies a filter and get entities with it. */ - applyFilters(filters) { - this.pageIndex = 0; - this.appliedFilters = filters; - this.getCount(); - this.getJobs(this.pageSize, this.pageSize * this.pageIndex); - } -} diff --git a/gae/frontend/src/app/menu/job/job.service.ts b/gae/frontend/src/app/menu/job/job.service.ts deleted file mode 100644 index 71b5417..0000000 --- a/gae/frontend/src/app/menu/job/job.service.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; - -import { catchError } from 'rxjs/operators'; -import { Observable } from 'rxjs'; - -import { environment } from '../../../environments/environment'; -import { JobWrapper } from '../../model/job_wrapper'; -import { ServiceBase } from '../../shared/servicebase'; - - -@Injectable() -export class JobService extends ServiceBase { - // url: string; - constructor(public httpClient: HttpClient) { - super(httpClient); - this.url = environment['baseURL'] + '/job/v1/'; - } - - getJobs(size: number, - offset: number, - filterInfo: string, - sort: string, - direction: string): Observable<JobWrapper> { - const url = this.url + 'get'; - return this.httpClient.post<JobWrapper>(url, {size: size, offset: offset, filter: filterInfo, sort: sort, direction: direction}) - .pipe(catchError(this.handleError)); - } -} diff --git a/gae/frontend/src/app/menu/lab/lab.component.html b/gae/frontend/src/app/menu/lab/lab.component.html deleted file mode 100644 index 0392e2d..0000000 --- a/gae/frontend/src/app/menu/lab/lab.component.html +++ /dev/null @@ -1,109 +0,0 @@ -<!-- Copyright (C) 2018 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<mat-tab-group> - <mat-tab label="Lab"> - <div class="mat-elevation-z2 entity-table"> - <mat-table #table [dataSource]="labDataSource"> - <!-- Index Column --> - <ng-container matColumnDef="_index"> - <mat-header-cell *matHeaderCellDef class="index-column">No.</mat-header-cell> - <mat-cell *matCellDef="let i = index" class="index-column"> {{i+1+pageSize*labPageIndex}} </mat-cell> - </ng-container> - - <!-- Name Column --> - <ng-container matColumnDef="name"> - <mat-header-cell *matHeaderCellDef>Name</mat-header-cell> - <mat-cell *matCellDef="let lab"> {{lab.name}} </mat-cell> - </ng-container> - - <!-- Owner Column --> - <ng-container matColumnDef="owner"> - <mat-header-cell *matHeaderCellDef>Owner</mat-header-cell> - <mat-cell *matCellDef="let lab"> {{lab.owner}} </mat-cell> - </ng-container> - - <!-- Admin Column --> - <ng-container matColumnDef="admin"> - <mat-header-cell *matHeaderCellDef>Admin</mat-header-cell> - <mat-cell *matCellDef="let lab"> {{lab.admin ? lab.admin.join(", ") : "None"}} </mat-cell> - </ng-container> - - <!-- Host Count Column --> - <ng-container matColumnDef="hostCount"> - <mat-header-cell *matHeaderCellDef># of Host</mat-header-cell> - <mat-cell *matCellDef="let lab"> {{ lab.hosts.length }} </mat-cell> - </ng-container> - - <mat-header-row *matHeaderRowDef="labColumnTitles"></mat-header-row> - <mat-row *matRowDef="let row; columns: labColumnTitles;"></mat-row> - </mat-table> - <mat-paginator [length]="labCount" - [pageSizeOptions]="pageSizeOptions" - [pageIndex]="labPageIndex"> - </mat-paginator> - </div> - </mat-tab> - <mat-tab label="Host"> - <div class="mat-elevation-z2 entity-table"> - <mat-table #table [dataSource]="hostDataSource"> - <!-- Index Column --> - <ng-container matColumnDef="_index"> - <mat-header-cell *matHeaderCellDef class="index-column">No.</mat-header-cell> - <mat-cell *matCellDef="let i = index" class="index-column"> {{i+1+pageSize*pageIndex}} </mat-cell> - </ng-container> - - <!-- Lab Column --> - <ng-container matColumnDef="name"> - <mat-header-cell *matHeaderCellDef>Lab</mat-header-cell> - <mat-cell *matCellDef="let host"> {{host.name}} </mat-cell> - </ng-container> - - <!-- Hostname Column --> - <ng-container matColumnDef="hostname"> - <mat-header-cell *matHeaderCellDef>Hostname</mat-header-cell> - <mat-cell *matCellDef="let host"> {{host.hostname}} </mat-cell> - </ng-container> - - <!-- IP Column --> - <ng-container matColumnDef="ip"> - <mat-header-cell *matHeaderCellDef>IP</mat-header-cell> - <mat-cell *matCellDef="let host"> {{host.ip}} </mat-cell> - </ng-container> - - <!-- Host Equipment Column --> - <ng-container matColumnDef="host_equipment"> - <mat-header-cell *matHeaderCellDef>Equipment</mat-header-cell> - <mat-cell *matCellDef="let host"> {{host.host_equipment}} </mat-cell> - </ng-container> - - <!-- Version Column --> - <ng-container matColumnDef="vtslab_version"> - <mat-header-cell *matHeaderCellDef>Version</mat-header-cell> - <mat-cell *matCellDef="let host"> {{host.vtslab_version}} </mat-cell> - </ng-container> - - <mat-header-row *matHeaderRowDef="hostColumnTitles"></mat-header-row> - <mat-row *matRowDef="let row; columns: hostColumnTitles;"></mat-row> - </mat-table> - <mat-paginator [length]="count" - [pageSizeOptions]="pageSizeOptions" - [pageIndex]="labPageIndex"> - </mat-paginator> - </div> - </mat-tab> -</mat-tab-group> -<div class="loading-spinner" *ngIf="loading"> - <mat-spinner color="primary"></mat-spinner> -</div> diff --git a/gae/frontend/src/app/menu/lab/lab.component.scss b/gae/frontend/src/app/menu/lab/lab.component.scss deleted file mode 100644 index fed2fb6..0000000 --- a/gae/frontend/src/app/menu/lab/lab.component.scss +++ /dev/null @@ -1,41 +0,0 @@ -.mat-table { - overflow: auto; -} - -.entity-table { - display: flex; - flex-direction: column; -} - -.mat-header-cell { - padding: 0 10px 0 10px; -} - -.index-column { - max-width: 40px; -} - -.mat-cell { - padding: 0 10px 0 10px; -} - -.element-row { - position: relative; - overflow: hidden; -} - -.element-row:not(.expanded) { - cursor: pointer; -} - -.element-row:not(.expanded):hover { - background: #f5f5f5; -} - -.element-row.expanded { - border-bottom-color: transparent; -} - -.div-expandable { - padding: 10px 20px 30px 20px; -} diff --git a/gae/frontend/src/app/menu/lab/lab.component.ts b/gae/frontend/src/app/menu/lab/lab.component.ts deleted file mode 100644 index bb7543b..0000000 --- a/gae/frontend/src/app/menu/lab/lab.component.ts +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { Component, OnInit } from '@angular/core'; -import { MatSnackBar, MatTableDataSource, PageEvent } from '@angular/material'; - -import { AppService } from '../../appservice'; -import { Host } from '../../model/host'; -import { Lab } from '../../model/lab'; -import { LabService } from './lab.service'; -import { MenuBaseClass } from '../menu_base'; - -/** Component that handles lab and host menu. */ -@Component({ - selector: 'app-lab', - templateUrl: './lab.component.html', - providers: [ LabService ], - styleUrls: ['./lab.component.scss'], -}) -export class LabComponent extends MenuBaseClass implements OnInit { - labColumnTitles = [ - '_index', - 'name', - 'owner', - 'admin', - 'hostCount', - ]; - hostColumnTitles = [ - '_index', - 'name', - 'hostname', - 'ip', - 'host_equipment', - 'vtslab_version', - ]; - labCount = -1; - labPageIndex = 0; - - constructor(private labService: LabService, - appService: AppService, - snackBar: MatSnackBar) { - super(appService, snackBar); - } - - labDataSource = new MatTableDataSource<Lab>(); - hostDataSource = new MatTableDataSource<Host>(); - - ngOnInit(): void { - // For labs and hosts, it does not use query pagination. - this.getHosts(); - } - - /** Gets hosts. - * @param size A number, at most this many results will be returned. - * @param offset A Number of results to skip. - */ - getHosts(size = 0, offset = 0) { - this.loading = true; - // Labs will not use filter for query. - const filterJSON = ''; - this.labService.getLabs(size, offset, filterJSON, '', '') - .subscribe( - (response) => { - this.loading = false; - if (response.labs) { - this.count = response.labs.length; - this.hostDataSource.data = response.labs; - this.setLabs(response.labs); - } - }, - (error) => this.showSnackbar(`[${error.status}] ${error.name}`) - ); - } - - /** Sets labs from given hosts. - * @param hosts A list of Host instances. - */ - setLabs(hosts: Host[]) { - if (hosts == null || hosts.length === 0) { return; } - const labMap = new Map(); - hosts.forEach(function(host) { - if (labMap.has(host.name)) { - labMap.get(host.name).hosts.push(host); - } else { - labMap.set(host.name, {name: host.name, owner: host.owner, admin: host.admin, hosts: [host]}); - } - }); - const labs: Lab[] = []; - labMap.forEach((value) => labs.push(value)); - this.labDataSource.data = labs; - } -} diff --git a/gae/frontend/src/app/menu/lab/lab.service.ts b/gae/frontend/src/app/menu/lab/lab.service.ts deleted file mode 100644 index 2d677b1..0000000 --- a/gae/frontend/src/app/menu/lab/lab.service.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; - -import { catchError } from 'rxjs/operators'; -import { Observable } from 'rxjs'; - -import { environment } from '../../../environments/environment'; -import { HostWrapper } from '../../model/host_wrapper'; -import { ServiceBase } from '../../shared/servicebase'; - - -@Injectable() -export class LabService extends ServiceBase { - // url: string; - constructor(public httpClient: HttpClient) { - super(httpClient); - this.url = environment['baseURL'] + '/lab/v1/'; - } - - getLabs(size: number, - offset: number, - filterInfo: string, - sort: string, - direction: string): Observable<HostWrapper> { - const url = this.url + 'get'; - return this.httpClient.post<HostWrapper>(url, {size: size, offset: offset, filter: filterInfo, sort: sort, direction: direction}) - .pipe(catchError(this.handleError)); - } -} diff --git a/gae/frontend/src/app/menu/menu-items.ts b/gae/frontend/src/app/menu/menu-items.ts deleted file mode 100644 index a544366..0000000 --- a/gae/frontend/src/app/menu/menu-items.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -export const MENUS = { - ['VTSLab Scheduler']: '/', - ['build']: '/build', - ['device']: '/device', - ['lab']: '/lab', - ['schedule']: '/schedule', - ['job']: '/job', -}; diff --git a/gae/frontend/src/app/menu/menu_base.ts b/gae/frontend/src/app/menu/menu_base.ts deleted file mode 100644 index 4d68f03..0000000 --- a/gae/frontend/src/app/menu/menu_base.ts +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** This class defines and/or implements the common properties and methods - * used among menus. - */ -import { AppService } from '../appservice'; -import { MatSnackBar } from '@angular/material'; -import moment from 'moment-timezone'; - - -export abstract class MenuBaseClass { - count = -1; - - loading = false; - pageSizeOptions = [20, 50, 100, 200]; - pageSize = 100; - pageIndex = 0; - - protected constructor(private appService: AppService, - public snackBar: MatSnackBar) { - this.appService.closeSideNav(); - this.snackBar.dismiss(); - } - - /** Returns an Observable which handles a response of count API. - * @param additionalOperations A list of lambda functions. - */ - getDefaultCountObservable(additionalOperations: any[] = []) { - return { - next: (response) => { - this.count = response.count; - for (const operation of additionalOperations) { - operation(response); - } - }, - error: (error) => this.showSnackbar(`[${error.status}] ${error.name}`) - }; - } - - getRelativeTime(timeString) { - return (moment.tz(timeString, 'YYYY-MM-DDThh:mm:ss', 'UTC').isValid() ? - moment.tz(timeString, 'YYYY-MM-DDThh:mm:ss', 'UTC').fromNow() : '---'); - } - - /** Checks whether timeString is expired from current time. */ - isExpired(timeString, hours=72) { - let currentTime = moment.tz(timeString, 'YYYY-MM-DDThh:mm:ss', 'UTC'); - if (!currentTime.isValid()) { return false; } - - let diff = moment().diff(currentTime); - let duration = moment.duration(diff); - return duration.asHours() > hours; - } - - /** Displays a snackbar notification. */ - showSnackbar(message = 'Error', duration = 5000) { - this.loading = false; - this.snackBar.open(message, 'DISMISS', {duration}); - } - - /** Displays a side nav window and lists all properties of selected entity. */ - onShowDetailsClicked(entity) { - this.appService.showDetails(entity); - } -} diff --git a/gae/frontend/src/app/menu/schedule/_schedule-theme.scss b/gae/frontend/src/app/menu/schedule/_schedule-theme.scss deleted file mode 100644 index 084f1fb..0000000 --- a/gae/frontend/src/app/menu/schedule/_schedule-theme.scss +++ /dev/null @@ -1,7 +0,0 @@ -@mixin schedule-theme($theme) { - $primary: map-get($theme, primary); - $accent: map-get($theme, accent); - $warn: map-get($theme, warn); - $background: map-get($theme, background); - $foreground: map-get($theme, foreground); -} diff --git a/gae/frontend/src/app/menu/schedule/schedule.component.html b/gae/frontend/src/app/menu/schedule/schedule.component.html deleted file mode 100644 index 910d45b..0000000 --- a/gae/frontend/src/app/menu/schedule/schedule.component.html +++ /dev/null @@ -1,121 +0,0 @@ -<!-- Copyright (C) 2018 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<div class="entity-filter"> - <app-filter (applyFilters)="applyFilters($event)" [disabled]="loading"></app-filter> -</div> -<div class="mat-elevation-z2 entity-table" [ngStyle]="{'opacity': (loading) ? 0.2 : 1 }"> - <mat-table [dataSource]="dataSource"> - <!-- Index Column --> - <ng-container matColumnDef="_index"> - <mat-header-cell *matHeaderCellDef>No.</mat-header-cell> - <mat-cell *matCellDef="let i = index"> {{i+1+pageSize*pageIndex}} </mat-cell> - </ng-container> - - <!-- Test Name Column --> - <ng-container matColumnDef="test_name"> - <mat-header-cell *matHeaderCellDef>Test Name</mat-header-cell> - <mat-cell *matCellDef="let schedule"> {{schedule.test_name}} </mat-cell> - </ng-container> - - <!-- Device Column --> - <ng-container matColumnDef="device"> - <mat-header-cell *matHeaderCellDef>Device</mat-header-cell> - <mat-cell *matCellDef="let schedule"> {{schedule.device ? schedule.device.join('\n') : ""}} </mat-cell> - </ng-container> - - <!-- Manifest Branch Column --> - <ng-container matColumnDef="manifest_branch"> - <mat-header-cell *matHeaderCellDef>Manifest Branch</mat-header-cell> - <mat-cell *matCellDef="let schedule"> {{schedule.manifest_branch}}</mat-cell> - </ng-container> - - <!-- Build Target Column --> - <ng-container matColumnDef="build_target"> - <mat-header-cell *matHeaderCellDef>Build Target</mat-header-cell> - <mat-cell *matCellDef="let schedule"> {{schedule.build_target}} </mat-cell> - </ng-container> - - <!-- GSI Branch Column --> - <ng-container matColumnDef="gsi_branch"> - <mat-header-cell *matHeaderCellDef>GSI Branch</mat-header-cell> - <mat-cell *matCellDef="let schedule"> {{schedule.gsi_branch}} </mat-cell> - </ng-container> - - <!-- GSI Build Target Column --> - <ng-container matColumnDef="gsi_build_target"> - <mat-header-cell *matHeaderCellDef>GSI Build Target</mat-header-cell> - <mat-cell *matCellDef="let schedule"> {{schedule.gsi_build_target}} </mat-cell> - </ng-container> - - <!-- Test Branch Column --> - <ng-container matColumnDef="test_branch"> - <mat-header-cell *matHeaderCellDef>Test Branch</mat-header-cell> - <mat-cell *matCellDef="let schedule"> {{schedule.test_branch}} </mat-cell> - </ng-container> - - <!-- Test Build Target Column --> - <ng-container matColumnDef="test_build_target"> - <mat-header-cell *matHeaderCellDef>Test Build Target</mat-header-cell> - <mat-cell *matCellDef="let schedule"> {{schedule.test_build_target}}</mat-cell> - </ng-container> - - <!-- Period Column --> - <ng-container matColumnDef="period"> - <mat-header-cell *matHeaderCellDef>Period</mat-header-cell> - <mat-cell *matCellDef="let schedule"> {{schedule.period}}</mat-cell> - </ng-container> - - <!-- Status Column --> - <ng-container matColumnDef="status"> - <mat-header-cell *matHeaderCellDef>Status</mat-header-cell> - <mat-cell *matCellDef="let schedule" - [ngStyle]="{color: (schedule.suspended || isExpired(schedule.timestamp)) ? '#FF0000' : '#000000'}"> - {{schedule.suspended ? "Suspended" : (isExpired(schedule.timestamp) ? "Expired" : "Active")}} - </mat-cell> - </ng-container> - - <!-- Timestamp Column --> - <ng-container matColumnDef="timestamp"> - <mat-header-cell *matHeaderCellDef>Timestamp</mat-header-cell> - <mat-cell *matCellDef="let schedule"> {{getRelativeTime(schedule.timestamp)}}</mat-cell> - </ng-container> - - <mat-header-row *matHeaderRowDef="columnTitles"></mat-header-row> - <mat-row *matRowDef="let row; columns: columnTitles;" - matRipple - class="element-row" - [appCdkDetailRow]="row" [appCdkDetailRowTpl]="schedule_detail"></mat-row> - </mat-table> - - <mat-paginator [length]="count" - [pageSize]="pageSize" - [pageSizeOptions]="pageSizeOptions" - [pageIndex]="pageIndex" - (page)="pageEvent = onPageEvent($event)"> - </mat-paginator> -</div> -<ng-template #schedule_detail let-schedule> - <div class="mat-row div-expandable" [@detailExpand] style="overflow: hidden"> - <button mat-raised-button (click)="suspendSchedule([{urlsafe_key: schedule.urlsafe_key, suspend: !schedule.suspended}])"> - {{(schedule.suspended ? "Resume" : "Suspend")}} - </button> - <button mat-raised-button (click)="onShowDetailsClicked(schedule)"> - Show Details - </button> - </div> -</ng-template> -<div class="loading-spinner" *ngIf="loading"> - <mat-spinner color="primary"></mat-spinner> -</div> diff --git a/gae/frontend/src/app/menu/schedule/schedule.component.scss b/gae/frontend/src/app/menu/schedule/schedule.component.scss deleted file mode 100644 index c8aee00..0000000 --- a/gae/frontend/src/app/menu/schedule/schedule.component.scss +++ /dev/null @@ -1,24 +0,0 @@ -.mat-header-cell { - padding: 0 10px 0 10px; -} - -.mat-cell { - padding: 0 10px 0 10px; -} - -.element-row { - position: relative; - overflow: hidden; -} - -.element-row:not(.expanded) { - cursor: pointer; -} - -.element-row:not(.expanded):hover { - background: #f5f5f5; -} - -.element-row.expanded { - border-bottom-color: transparent; -} diff --git a/gae/frontend/src/app/menu/schedule/schedule.component.ts b/gae/frontend/src/app/menu/schedule/schedule.component.ts deleted file mode 100644 index 5c1740e..0000000 --- a/gae/frontend/src/app/menu/schedule/schedule.component.ts +++ /dev/null @@ -1,160 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { Component, OnInit, ViewChild } from '@angular/core'; -import { MatSnackBar, MatTableDataSource, PageEvent } from '@angular/material'; -import { animate, state, style, transition, trigger } from "@angular/animations"; - -import { AppService } from '../../appservice'; -import { FilterComponent } from '../../shared/filter/filter.component'; -import { FilterItem } from '../../model/filter_item'; -import { MenuBaseClass } from '../menu_base'; -import { Schedule, ScheduleSuspendResponse } from '../../model/schedule'; -import { ScheduleService } from './schedule.service'; - - -/** Component that handles schedule menu. */ -@Component({ - selector: 'app-schedule', - templateUrl: './schedule.component.html', - providers: [ ScheduleService ], - styleUrls: ['./schedule.component.scss'], - animations: [ - trigger('detailExpand', [ - state('void', style({height: '0px', minHeight: '0', visibility: 'hidden'})), - state('*', style({height: '*', visibility: 'visible'})), - transition('void <=> *', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')), - ]), - ], -}) -export class ScheduleComponent extends MenuBaseClass implements OnInit { - columnTitles = [ - '_index', - 'test_name', - 'device', - 'manifest_branch', - 'build_target', - 'gsi_branch', - 'gsi_build_target', - 'test_branch', - 'test_build_target', - 'period', - 'status', - 'timestamp', - ]; - dataSource = new MatTableDataSource<Schedule>(); - pageEvent: PageEvent; - appliedFilters: FilterItem[]; - - @ViewChild(FilterComponent) filterComponent: FilterComponent; - - constructor(private scheduleService: ScheduleService, - appService: AppService, - snackBar: MatSnackBar) { - super(appService, snackBar); - } - - ngOnInit(): void { - this.filterComponent.setSelectorList(Schedule); - this.getCount(); - this.getSchedules(this.pageSize, this.pageSize * this.pageIndex); - } - - /** Gets a total count of schedules. */ - getCount(observer = this.getDefaultCountObservable()) { - const filterJSON = (this.appliedFilters) ? JSON.stringify(this.appliedFilters) : ''; - this.scheduleService.getCount(filterJSON).subscribe(observer); - } - - /** Gets schedules. - * @param size A number, at most this many results will be returned. - * @param offset A Number of results to skip. - */ - getSchedules(size = 0, offset = 0) { - this.loading = true; - const filterJSON = (this.appliedFilters) ? JSON.stringify(this.appliedFilters) : ''; - this.scheduleService.getSchedules(size, offset, filterJSON, '', '') - .subscribe( - (response) => { - this.loading = false; - if (this.count >= 0) { - let length = 0; - if (response.schedules) { - length = response.schedules.length; - } - const total = length + offset; - if (response.has_next) { - if (length !== this.pageSize) { - this.showSnackbar('Received unexpected number of entities.'); - } else if (this.count <= total) { - this.getCount(); - } - } else { - if (this.count !== total) { - if (length !== this.count) { - this.getCount(); - } else if (this.count > total) { - const countObservable = this.getDefaultCountObservable([ - () => { - this.pageIndex = Math.floor(this.count / this.pageSize); - this.getSchedules(this.pageSize, this.pageSize * this.pageIndex); - } - ]); - this.getCount(countObservable); - } - } - } - } - this.dataSource.data = response.schedules; - }, - (error) => this.showSnackbar(`[${error.status}] ${error.name}`) - ); - } - - /** Toggles a schedule from suspend to resume, or vice versa. */ - suspendSchedule(schedules: ScheduleSuspendResponse[]) { - this.scheduleService.suspendSchedule(schedules) - .subscribe( - (response) => { - if (response.schedules) { - let self = this; - response.schedules.forEach(function(schedule) { - const original = self.dataSource.data.filter(x => x.urlsafe_key === schedule.urlsafe_key); - if (original) { - original[0].suspended = schedule.suspend; - } - }) - } - }, - (error) => this.showSnackbar(`[${error.status}] ${error.name}`) - ); - } - - /** Hooks a page event and handles properly. */ - onPageEvent(event: PageEvent) { - this.pageSize = event.pageSize; - this.pageIndex = event.pageIndex; - this.getSchedules(this.pageSize, this.pageSize * this.pageIndex); - return event; - } - - /** Applies a filter and get entities with it. */ - applyFilters(filters) { - this.pageIndex = 0; - this.appliedFilters = filters; - this.getCount(); - this.getSchedules(this.pageSize, this.pageSize * this.pageIndex); - } -} diff --git a/gae/frontend/src/app/menu/schedule/schedule.service.ts b/gae/frontend/src/app/menu/schedule/schedule.service.ts deleted file mode 100644 index ae534dd..0000000 --- a/gae/frontend/src/app/menu/schedule/schedule.service.ts +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; - -import { catchError } from 'rxjs/operators'; -import { Observable } from 'rxjs'; - -import { environment } from '../../../environments/environment'; -import { ScheduleWrapper } from '../../model/schedule_wrapper'; -import { ServiceBase } from '../../shared/servicebase'; -import { ScheduleSuspendResponse, ScheduleSuspendResponseWrapper } from '../../model/schedule'; - - -@Injectable() -export class ScheduleService extends ServiceBase { - // url: string; - constructor(public httpClient: HttpClient) { - super(httpClient); - this.url = environment['baseURL'] + '/schedule/v1/'; - } - - getSchedules(size: number, - offset: number, - filterInfo: string, - sort: string, - direction: string): Observable<ScheduleWrapper> { - const url = this.url + 'get'; - return this.httpClient.post<ScheduleWrapper>(url, {size: size, offset: offset, filter: filterInfo, sort: sort, direction: direction}) - .pipe(catchError(this.handleError)); - } - - suspendSchedule(schedules: ScheduleSuspendResponse[]): Observable<ScheduleSuspendResponseWrapper> { - const url = this.url + 'suspend'; - return this.httpClient.post<ScheduleSuspendResponseWrapper>(url, {schedules: schedules}) - .pipe(catchError(this.handleError)); - } -} diff --git a/gae/frontend/src/app/model/build.ts b/gae/frontend/src/app/model/build.ts deleted file mode 100644 index bf32a4a..0000000 --- a/gae/frontend/src/app/model/build.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -export class Build { - manifest_branch: string = void 0; - build_id: string = void 0; - build_target: string = void 0; - build_type: string = void 0; - artifact_type: string = void 0; - artifacts: string[] = void 0; - signed: boolean = void 0; - timestamp: any = void 0; -} diff --git a/gae/frontend/src/app/model/build_wrapper.ts b/gae/frontend/src/app/model/build_wrapper.ts deleted file mode 100644 index 797d097..0000000 --- a/gae/frontend/src/app/model/build_wrapper.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import {Build} from './build'; - -export interface BuildWrapper { - builds: Build[]; - has_next: boolean; -} diff --git a/gae/frontend/src/app/model/device.ts b/gae/frontend/src/app/model/device.ts deleted file mode 100644 index 21fdfb7..0000000 --- a/gae/frontend/src/app/model/device.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -export class Device { - serial: string = void 0; - product: string = void 0; - status: number = void 0; - scheduling_status: number = void 0; - hostname: string = void 0; - device_equipment: string[] = void 0; - timestamp: any = void 0; -} diff --git a/gae/frontend/src/app/model/device_wrapper.ts b/gae/frontend/src/app/model/device_wrapper.ts deleted file mode 100644 index af18dce..0000000 --- a/gae/frontend/src/app/model/device_wrapper.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import {Device} from './device'; - -export interface DeviceWrapper { - devices: Device[]; - has_next: boolean; -} diff --git a/gae/frontend/src/app/model/filter_condition.ts b/gae/frontend/src/app/model/filter_condition.ts deleted file mode 100644 index 9f76de9..0000000 --- a/gae/frontend/src/app/model/filter_condition.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -export enum FilterCondition { - EqualTo = 1, - LessThan, - GreaterThan, - LessThanOrEqualTo, - GreaterThanOrEqualTo, - NotEqualTo, - Has, -} diff --git a/gae/frontend/src/app/model/filter_item.ts b/gae/frontend/src/app/model/filter_item.ts deleted file mode 100644 index de457a1..0000000 --- a/gae/frontend/src/app/model/filter_item.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import {FilterCondition} from './filter_condition'; - -export class FilterItem { - key: string; - method: FilterCondition; - value: string; // back-end should handle type-casting. -} diff --git a/gae/frontend/src/app/model/host.ts b/gae/frontend/src/app/model/host.ts deleted file mode 100644 index 3836c30..0000000 --- a/gae/frontend/src/app/model/host.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -export class Host { - name: string = void 0; // lab name - owner: string = void 0; - admin: string[] = void 0; - hostname: string = void 0; - ip: string = void 0; - devices: string = void 0; - vtslab_version: string = void 0; - host_equipment: string[] = void 0; -} diff --git a/gae/frontend/src/app/model/host_wrapper.ts b/gae/frontend/src/app/model/host_wrapper.ts deleted file mode 100644 index ae18a04..0000000 --- a/gae/frontend/src/app/model/host_wrapper.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import {Host} from './host'; - -export interface HostWrapper { - // Back-end stores each host information as LabModel entity, so it sends - // host information as 'labs'. - labs: Host[]; - has_next: boolean; -} diff --git a/gae/frontend/src/app/model/job.ts b/gae/frontend/src/app/model/job.ts deleted file mode 100644 index 69e45b7..0000000 --- a/gae/frontend/src/app/model/job.ts +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -export class Job { - test_type: number = void 0; - - hostname: string = void 0; - priority: string = void 0; - test_name: string = void 0; - require_signed_device_build: boolean = void 0; - has_bootloader_img: boolean = void 0; - has_radio_img: boolean = void 0; - device: string = void 0; - serial: string = void 0; - - // device image information - build_storage_type: number = void 0; - manifest_branch: string = void 0; - build_target: string = void 0; - build_id: string = void 0; - pab_account_id: string = void 0; - - shards: number = void 0; - param: string = void 0; - status: number = void 0; - period: number = void 0; - - // GSI information - gsi_storage_type: number = void 0; - gsi_branch: string = void 0; - gsi_build_target: string = void 0; - gsi_build_id: string = void 0; - gsi_pab_account_id: string = void 0; - gsi_vendor_version: string = void 0; - - // test suite information - test_storage_type: number = void 0; - test_branch: string = void 0; - test_build_target: string = void 0; - test_build_id: string = void 0; - test_pab_account_id: string = void 0; - - retry_count: number = void 0; - - infra_log_url: string = void 0; - - image_package_repo_base: string = void 0; - - report_bucket: string = void 0; - report_spreadsheet_id: string = void 0; - - timestamp = void 0; - heartbeat_stamp = void 0; -} diff --git a/gae/frontend/src/app/model/job_wrapper.ts b/gae/frontend/src/app/model/job_wrapper.ts deleted file mode 100644 index 5a1f915..0000000 --- a/gae/frontend/src/app/model/job_wrapper.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import {Job} from './job'; - -export interface JobWrapper { - jobs: Job[]; - has_next: boolean; -} diff --git a/gae/frontend/src/app/model/lab.ts b/gae/frontend/src/app/model/lab.ts deleted file mode 100644 index 0f98360..0000000 --- a/gae/frontend/src/app/model/lab.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import {Host} from './host'; - -export class Lab { - name: string = void 0; - owner: string = void 0; - admin: string[] = void 0; - hosts: Host[] = void 0; -} diff --git a/gae/frontend/src/app/model/schedule.ts b/gae/frontend/src/app/model/schedule.ts deleted file mode 100644 index 9115a97..0000000 --- a/gae/frontend/src/app/model/schedule.ts +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -export class Schedule { - name: string = void 0; - schedule_type: string = void 0; - - // device image information - build_storage_type: number = void 0; - manifest_branch: string = void 0; - build_target: string = void 0; - device_pab_account_id: string = void 0; - require_signed_device_build: boolean = void 0; - has_bootloader_img: boolean = void 0; - has_radio_img: boolean = void 0; - - // GSI information - gsi_storage_type: number = void 0; - gsi_branch: string = void 0; - gsi_build_target: string = void 0; - gsi_pab_account_id: string = void 0; - gsi_vendor_version: string = void 0; - - // test suite information - test_storage_type: number = void 0; - test_branch: string = void 0; - test_build_target: string = void 0; - test_pab_account_id: string = void 0; - - test_name: string = void 0; - period: number = void 0; - schedule: string = void 0; - priority: string = void 0; - device: string[] = void 0; - shards: number = void 0; - param: string[] = void 0; - retry_count: number = void 0; - - required_host_equipment: string[] = void 0; - required_device_equipment: string[] = void 0; - - report_bucket: string[] = void 0; - report_spreadsheet_id: string[] = void 0; - report_persistent_url: string[] = void 0; - report_reference_url: string[] = void 0; - - image_package_repo_base: string = void 0; - timestamp = void 0; - owner: string[] = void 0; - - error_count: number = void 0; - suspended: boolean = void 0; - urlsafe_key: string = void 0; -} - -export interface ScheduleSuspendResponseWrapper { - schedules: ScheduleSuspendResponse[]; -} - -export interface ScheduleSuspendResponse { - urlsafe_key: string; - suspend: boolean; -} diff --git a/gae/frontend/src/app/model/schedule_wrapper.ts b/gae/frontend/src/app/model/schedule_wrapper.ts deleted file mode 100644 index 2dae800..0000000 --- a/gae/frontend/src/app/model/schedule_wrapper.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import {Schedule} from './schedule'; - -export interface ScheduleWrapper { - schedules: Schedule[]; - has_next: boolean; -} diff --git a/gae/frontend/src/app/model/tslint.json b/gae/frontend/src/app/model/tslint.json deleted file mode 100644 index eb9bcd8..0000000 --- a/gae/frontend/src/app/model/tslint.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "variable-name": [ - true, - "allow-snake-case" - ] - } -} diff --git a/gae/frontend/src/app/shared/dict.pipe.ts b/gae/frontend/src/app/shared/dict.pipe.ts deleted file mode 100644 index 44f5933..0000000 --- a/gae/frontend/src/app/shared/dict.pipe.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import {Pipe, PipeTransform} from '@angular/core'; - -@Pipe({name: 'dict'}) -export class DictPipe implements PipeTransform { - transform(value: Object): any { - const dict = []; - for (const key in value) { - if (value.hasOwnProperty(key)) { - dict.push({key: key, value: value[key]}); - } - } - return dict; - } -} diff --git a/gae/frontend/src/app/shared/filter/filter.component.html b/gae/frontend/src/app/shared/filter/filter.component.html deleted file mode 100644 index 7381359..0000000 --- a/gae/frontend/src/app/shared/filter/filter.component.html +++ /dev/null @@ -1,66 +0,0 @@ -<!-- Copyright (C) 2018 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<div fxLayout="column" id="filter-wrapper"> - <mat-expansion-panel id="expansion-panel" (opened)="panelOpenState = true" (closed)="panelOpenState = false" [ngStyle]="{'padding-bottom': (panelOpenState) ? '20px' : '0' }"> - <mat-expansion-panel-header> - <mat-panel-title> - Filter - </mat-panel-title> - <mat-panel-description> - {{ panelOpenState ? "" : appliedFilters.length + " filters are applied." }} - </mat-panel-description> - </mat-expansion-panel-header> - <mat-form-field> - <mat-select placeholder="Key" [(value)]="currentFilter.key"> - <mat-option *ngFor="let key of selectorList" [value]="key"> - {{ key }} - </mat-option> - </mat-select> - </mat-form-field> - <mat-form-field> - <mat-select [(value)]="currentFilter.method"> - <mat-option *ngFor="let method of filterMethods" [value]="method.value"> - {{ method.text }} - </mat-option> - </mat-select> - </mat-form-field> - <mat-form-field> - <input matInput [(ngModel)]="currentFilter.value"> - </mat-form-field> - <button mat-icon-button (click)="addFilter()" [disabled]="!currentFilter.key || !currentFilter.method || !currentFilter.value"> - <mat-icon>done</mat-icon> - </button> - <button mat-icon-button (click)="clearCurrentFilter()"> - <mat-icon>clear</mat-icon> - </button> - <mat-chip-list> - <mat-chip *ngFor="let filter of applyingFilters" [removable]="true" (removed)="removed(filter)"> - {{ filter.key }} {{ getSign(filter) }} {{ filter.value }} - <mat-icon matChipRemove>cancel</mat-icon> - </mat-chip> - </mat-chip-list> - <div fxLayout="row" id="row_buttons"> - <button mat-stroked-button (click)="onApplyClicked()" [disabled]="!applyingFilterChanged"> - <span>Apply</span> - </button> - <button mat-stroked-button (click)="onCancelChangesClicked()" [disabled]="!applyingFilterChanged"> - <span>Cancel Changes</span> - </button> - <button mat-stroked-button (click)="onClearAllClicked()" [disabled]="appliedFilters.length == 0"> - <span>Clear All</span> - </button> - </div> - </mat-expansion-panel> -</div> diff --git a/gae/frontend/src/app/shared/filter/filter.component.scss b/gae/frontend/src/app/shared/filter/filter.component.scss deleted file mode 100644 index 88ae569..0000000 --- a/gae/frontend/src/app/shared/filter/filter.component.scss +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#filter-wrapper { - position: relative; - - mat-form-field { - margin-right: 20px; - } -} - -#row_buttons { - float: right; -} - -.mat-stroked-button { - min-width: 80px; - min-height: 15px; - padding-left: 15px; - padding-right: 15px; - font-size: 12px; - margin-right: 15px; -} diff --git a/gae/frontend/src/app/shared/filter/filter.component.ts b/gae/frontend/src/app/shared/filter/filter.component.ts deleted file mode 100644 index 0ce66fe..0000000 --- a/gae/frontend/src/app/shared/filter/filter.component.ts +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { FilterItem } from '../../model/filter_item'; -import { FilterCondition } from '../../model/filter_condition'; - -@Component({ - selector: 'app-filter', - templateUrl: './filter.component.html', - styleUrls: ['./filter.component.scss'] -}) -export class FilterComponent implements OnInit { - currentFilter: FilterItem; - applyingFilters: FilterItem[] = []; - applyingFilterChanged = false; - appliedFilters: FilterItem[] = []; - selectorList: string[]; - - filterMethods = [ - {value: FilterCondition.EqualTo, text: 'is equal to', sign: '='}, - {value: FilterCondition.LessThan, text: 'is less than', sign: '<'}, - {value: FilterCondition.GreaterThan, text: 'is greater than', sign: '>'}, - {value: FilterCondition.LessThanOrEqualTo, text: 'is less than or equal to', sign: '<='}, - {value: FilterCondition.GreaterThanOrEqualTo, text: 'is greater than or equal to', sign: '>='}, - {value: FilterCondition.NotEqualTo, text: 'is not equal to', sign: '!='}, - {value: FilterCondition.Has, text: 'has', sign: 'has'}, - ]; - - @Output() applyFilters = new EventEmitter(); - @Input() disabled: boolean; - - panelOpenState = false; - - ngOnInit(): void { - this.currentFilter = new FilterItem(); - this.currentFilter.value = ''; - } - - /** Sets a filter key list with the given class. */ - setSelectorList(typeOfClass: any) { - const instance = new typeOfClass(); - this.selectorList = Object.getOwnPropertyNames(instance); - } - - /** Adds the current filter to the list of filters to be applied. */ - addFilter() { - this.applyingFilters.push(this.currentFilter); - this.currentFilter = new FilterItem(); - this.currentFilter.value = ''; - this.applyingFilterChanged = true; - } - - /** Clears the current filter. */ - clearCurrentFilter() { - this.currentFilter.key = undefined; - this.currentFilter.method = undefined; - this.currentFilter.value = ''; - } - - /** Removes the selected filter from the list of filters to be applied. */ - removed(filter: FilterItem) { - const index = this.applyingFilters.indexOf(filter); - if (index >= 0) { - this.applyingFilters.splice(index, 1); - this.applyingFilterChanged = true; - } - } - - /** Gets a filter sign with method value. */ - getSign(filter: FilterItem) { - return this.filterMethods.find((x) => x.value === filter.method).sign; - } - - /** Applies the list of filters. */ - onApplyClicked() { - this.applyFilters.emit(this.applyingFilters); - this.appliedFilters = this.applyingFilters.slice(); - this.applyingFilterChanged = false; - } - - /** Cancels the current changes and roll back to the last applied filters. */ - onCancelChangesClicked() { - this.applyingFilters = this.appliedFilters.slice(); - this.applyingFilterChanged = false; - } - - /** Reset all filters. */ - onClearAllClicked() { - this.applyingFilters = []; - this.appliedFilters = []; - this.applyFilters.emit(this.appliedFilters); - this.applyingFilterChanged = false; - } -} diff --git a/gae/frontend/src/app/shared/navbar/_navbar-theme.scss b/gae/frontend/src/app/shared/navbar/_navbar-theme.scss deleted file mode 100644 index bba0989..0000000 --- a/gae/frontend/src/app/shared/navbar/_navbar-theme.scss +++ /dev/null @@ -1,13 +0,0 @@ -@mixin nav-bar-theme($theme) { - $primary: map-get($theme, primary); - $accent: map-get($theme, accent); - $warn: map-get($theme, warn); - $background: map-get($theme, background); - $foreground: map-get($theme, foreground); - - mat-toolbar.main-toolbar { - .mat-list-item { - color: mat-color($primary, '600-contrast') !important; - } - } -} diff --git a/gae/frontend/src/app/shared/navbar/navbar.component.html b/gae/frontend/src/app/shared/navbar/navbar.component.html deleted file mode 100644 index 7719d56..0000000 --- a/gae/frontend/src/app/shared/navbar/navbar.component.html +++ /dev/null @@ -1,20 +0,0 @@ -<!-- Copyright (C) 2018 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<mat-toolbar class="mat-elevation-z6 main-toolbar" color="primary"> - <mat-nav-list> - <a mat-list-item *ngFor="let menu of (menus | dict)" [routerLink]="menu.value">{{ menu.key }}</a> - </mat-nav-list> - <span class="flex-spacer"></span> -</mat-toolbar> diff --git a/gae/frontend/src/app/shared/navbar/navbar.component.scss b/gae/frontend/src/app/shared/navbar/navbar.component.scss deleted file mode 100644 index 5b6dc86..0000000 --- a/gae/frontend/src/app/shared/navbar/navbar.component.scss +++ /dev/null @@ -1,10 +0,0 @@ -mat-toolbar { - .mat-list-item { - font-family: 'Google Sans', Roboto, sans-serif; - text-transform: capitalize; - } -} - -.mat-list-item { - float: left; -} diff --git a/gae/frontend/src/app/shared/navbar/navbar.component.ts b/gae/frontend/src/app/shared/navbar/navbar.component.ts deleted file mode 100644 index 3efe93f..0000000 --- a/gae/frontend/src/app/shared/navbar/navbar.component.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { Component } from '@angular/core'; - -import { MENUS } from '../../menu/menu-items'; - -@Component({ - selector: 'app-nav-bar', - templateUrl: './navbar.component.html', - styleUrls: ['./navbar.component.scss'] -}) -export class NavBarComponent { - get menus() { - return MENUS; - } -} diff --git a/gae/frontend/src/app/shared/navbar/navbar.ts b/gae/frontend/src/app/shared/navbar/navbar.ts deleted file mode 100644 index 805dcc5..0000000 --- a/gae/frontend/src/app/shared/navbar/navbar.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -// Angular modules. -import { BrowserModule } from '@angular/platform-browser'; -import { NgModule } from '@angular/core'; -import { RouterModule } from '@angular/router'; - -// Angular Material modules. -import { MatButtonModule } from '@angular/material/button'; -import { MatListModule } from '@angular/material/list'; -import { MatToolbarModule } from '@angular/material/toolbar'; - -// User modules. -import { DictPipe } from '../dict.pipe'; -import { NavBarComponent } from './navbar.component'; - -@NgModule({ - declarations: [ - DictPipe, - NavBarComponent, - ], - imports: [ - BrowserModule, - MatButtonModule, - MatToolbarModule, - MatListModule, - RouterModule, - ], - exports: [ - NavBarComponent, - ], - providers: [], -}) -export class NavModule { } diff --git a/gae/frontend/src/app/shared/servicebase.ts b/gae/frontend/src/app/shared/servicebase.ts deleted file mode 100644 index 9eaecf8..0000000 --- a/gae/frontend/src/app/shared/servicebase.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { HttpClient, HttpErrorResponse, HttpParams, HttpResponse } from '@angular/common/http'; -import { Observable, throwError } from 'rxjs'; - -export class ServiceBase { - url: string; - protected constructor(public httpClient: HttpClient) { - } - protected handleError(error: HttpErrorResponse) { - if (error.error instanceof ErrorEvent) { - // A client-side or network error occurred. Handle it accordingly. - console.error('An error occurred:', error.error.message); - } else { - // The backend returned an unsuccessful response code. - // The response body may contain clues as to what went wrong, - console.error( - `Backend returned code ${error.status}, ` + - `body was: ${error.error}`); - } - // return an observable with a user-facing error message - return throwError(error); - } - public getCount(filterInfo: string): Observable<number> { - const url = this.url + 'count'; - return this.httpClient.post<number>(url, {filter: filterInfo}); - } -} diff --git a/gae/frontend/src/app/shared/vtslab_status.ts b/gae/frontend/src/app/shared/vtslab_status.ts deleted file mode 100644 index 2836f97..0000000 --- a/gae/frontend/src/app/shared/vtslab_status.ts +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -export enum JobStatus { - Ready = 0, - Leased, - Complete, - Infra_err, - Expired, - Bootup_err, -} - -export enum DeviceStatus { - Unknown = 0, - Fastboot, - Online, - Ready, - Use, - Error, - No_response, -} - -export enum SchedulingStatus { - Free = 0, - Reserved, - Use, -} - -/** - * bit 0-1 : version related test type - * 00 - Unknown - * 01 - ToT - * 10 - OTA - * bit 2 : device signed build - * bit 3-4 : reserved for gerrit related test type - * 01 - pre-submit - * bit 5 : manually created test job - */ -export enum TestType { - Unknown = 0, - ToT = 1, - OTA = 1 << 1, - Signed = 1 << 2, - Presubmit = 1 << 3, - Manual = 1 << 5, -} diff --git a/gae/frontend/src/browserslist b/gae/frontend/src/browserslist deleted file mode 100644 index 3206b4e..0000000 --- a/gae/frontend/src/browserslist +++ /dev/null @@ -1,5 +0,0 @@ -# For autoprefixer to adjust CSS to support the below specified browsers -> 0.5% -last 2 versions -Firefox ESR -not dead diff --git a/gae/frontend/src/environments/environment.prod.ts b/gae/frontend/src/environments/environment.prod.ts deleted file mode 100644 index 8b051e6..0000000 --- a/gae/frontend/src/environments/environment.prod.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const environment = { - production: true, - baseURL: '/_ah/api', -}; diff --git a/gae/frontend/src/environments/environment.ts b/gae/frontend/src/environments/environment.ts deleted file mode 100644 index 387543e..0000000 --- a/gae/frontend/src/environments/environment.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const environment = { - production: false, - baseURL: 'http://localhost:8080/_ah/api', -}; diff --git a/gae/frontend/src/favicon.ico b/gae/frontend/src/favicon.ico Binary files differdeleted file mode 100644 index 8081c7c..0000000 --- a/gae/frontend/src/favicon.ico +++ /dev/null diff --git a/gae/frontend/src/index.html b/gae/frontend/src/index.html deleted file mode 100644 index 3234be2..0000000 --- a/gae/frontend/src/index.html +++ /dev/null @@ -1,15 +0,0 @@ -<html lang="en"> -<head> - <meta charset="utf-8"> - <title>VTSLab Scheduler</title> - <base href="/"> - - <meta name="viewport" content="width=device-width, initial-scale=1"> - <link rel="icon" type="image/x-icon" href="favicon.ico"> - <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> - <link href="https://fonts.googleapis.com/css?family=Google+Sans" rel="stylesheet"> -</head> -<body> - <app-root>Loading...</app-root> -</body> -</html> diff --git a/gae/frontend/src/karma.conf.js b/gae/frontend/src/karma.conf.js deleted file mode 100644 index b6e0042..0000000 --- a/gae/frontend/src/karma.conf.js +++ /dev/null @@ -1,31 +0,0 @@ -// Karma configuration file, see link for more information -// https://karma-runner.github.io/1.0/config/configuration-file.html - -module.exports = function (config) { - config.set({ - basePath: '', - frameworks: ['jasmine', '@angular-devkit/build-angular'], - plugins: [ - require('karma-jasmine'), - require('karma-chrome-launcher'), - require('karma-jasmine-html-reporter'), - require('karma-coverage-istanbul-reporter'), - require('@angular-devkit/build-angular/plugins/karma') - ], - client: { - clearContext: false // leave Jasmine Spec Runner output visible in browser - }, - coverageIstanbulReporter: { - dir: require('path').join(__dirname, '../coverage'), - reports: ['html', 'lcovonly'], - fixWebpackSourcePaths: true - }, - reporters: ['progress', 'kjhtml'], - port: 9876, - colors: true, - logLevel: config.LOG_INFO, - autoWatch: true, - browsers: ['Chrome'], - singleRun: false - }); -};
\ No newline at end of file diff --git a/gae/frontend/src/main.ts b/gae/frontend/src/main.ts deleted file mode 100644 index 91ec6da..0000000 --- a/gae/frontend/src/main.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { enableProdMode } from '@angular/core'; -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; - -import { AppModule } from './app/app.module'; -import { environment } from './environments/environment'; - -if (environment.production) { - enableProdMode(); -} - -platformBrowserDynamic().bootstrapModule(AppModule) - .catch(err => console.log(err)); diff --git a/gae/frontend/src/polyfills.ts b/gae/frontend/src/polyfills.ts deleted file mode 100644 index 25a0787..0000000 --- a/gae/frontend/src/polyfills.ts +++ /dev/null @@ -1,5 +0,0 @@ -// Evergreen browsers require these. -import 'core-js/es7/reflect'; - -// Zone JS is required by default for Angular itself. -import 'zone.js/dist/zone'; diff --git a/gae/frontend/src/styles.scss b/gae/frontend/src/styles.scss deleted file mode 100644 index 574f294..0000000 --- a/gae/frontend/src/styles.scss +++ /dev/null @@ -1,54 +0,0 @@ -@import '~@angular/material/theming'; -@import 'styles/app-theme'; - -body { - margin: 0; - font-family: Roboto, sans-serif; -} - -.entity-filter { - margin: 20px 20px 0 20px; - - filter { - width: 100%; - } -} - -.statistics-table { - margin: 10px 20px 20px 20px; - - table { - width: 100%; - } -} - -.mat-card { - margin: 20px; -} - -.entity-table { - margin: 10px 20px 20px 20px; - - table { - width: 100%; - } -} - -.loading-spinner { - display: flex; - align-items: center; - justify-content: center; - top: 0; - left: 0; - bottom: 0; - right: 0; - position: fixed; -} - -.div-expandable { - padding: 10px 20px 30px 20px; - - button { - margin-right: 20px; - } -} diff --git a/gae/frontend/src/styles/_app-theme.scss b/gae/frontend/src/styles/_app-theme.scss deleted file mode 100644 index 6c18f48..0000000 --- a/gae/frontend/src/styles/_app-theme.scss +++ /dev/null @@ -1,14 +0,0 @@ -@import '~@angular/material/theming'; -@import 'apply-theme'; -@import 'blue-theme'; - -@include mat-core(); - -$main-theme: $blue-theme; - -$primary: mat-palette($main-theme, 600, 100, 900); -$accent: mat-palette($main-theme, A200, A100, A400); -$app-theme: mat-light-theme($primary, $accent); - -@include angular-material-theme($app-theme); -@include apply-theme($app-theme); diff --git a/gae/frontend/src/styles/_apply-theme.scss b/gae/frontend/src/styles/_apply-theme.scss deleted file mode 100644 index fd5173f..0000000 --- a/gae/frontend/src/styles/_apply-theme.scss +++ /dev/null @@ -1,5 +0,0 @@ -@import '../app/shared/navbar/navbar-theme'; - -@mixin apply-theme($theme) { - @include nav-bar-theme($theme); -} diff --git a/gae/frontend/src/styles/_blue-theme.scss b/gae/frontend/src/styles/_blue-theme.scss deleted file mode 100644 index 53fd744..0000000 --- a/gae/frontend/src/styles/_blue-theme.scss +++ /dev/null @@ -1,34 +0,0 @@ -@import '~@angular/material/theming'; - -$blue-theme: ( - 50: #e8f0fe, - 100: #d2e3fc, - 200: #a1c2fa, - 300: #7baaf7, - 400: #5e97f6, - 500: #4285f4, - 600: #1a73e8, - 700: #1967d2, - 800: #185abc, - 900: #174ea6, - A100: #82b1ff, - A200: #448aff, - A400: #2979ff, - A700: #2962ff, - contrast: ( - 50: #1a73e8, - 100: #1a73e8, - 200: $black-87-opacity, - 300: $black-87-opacity, - 400: $black-87-opacity, - 500: white, - 600: white, - 700: white, - 800: white, - 900: white, - A100: $black-87-opacity, - A200: white, - A400: white, - A700: white, - ) -); diff --git a/gae/frontend/src/test.ts b/gae/frontend/src/test.ts deleted file mode 100644 index 1631789..0000000 --- a/gae/frontend/src/test.ts +++ /dev/null @@ -1,20 +0,0 @@ -// This file is required by karma.conf.js and loads recursively all the .spec and framework files - -import 'zone.js/dist/zone-testing'; -import { getTestBed } from '@angular/core/testing'; -import { - BrowserDynamicTestingModule, - platformBrowserDynamicTesting -} from '@angular/platform-browser-dynamic/testing'; - -declare const require: any; - -// First, initialize the Angular testing environment. -getTestBed().initTestEnvironment( - BrowserDynamicTestingModule, - platformBrowserDynamicTesting() -); -// Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); -// And load the modules. -context.keys().map(context); diff --git a/gae/frontend/src/tsconfig.app.json b/gae/frontend/src/tsconfig.app.json deleted file mode 100644 index 722c370..0000000 --- a/gae/frontend/src/tsconfig.app.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "outDir": "../out-tsc/app", - "module": "es2015", - "types": [] - }, - "exclude": [ - "src/test.ts", - "**/*.spec.ts" - ] -} diff --git a/gae/frontend/src/tsconfig.spec.json b/gae/frontend/src/tsconfig.spec.json deleted file mode 100644 index 8f7cede..0000000 --- a/gae/frontend/src/tsconfig.spec.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "outDir": "../out-tsc/spec", - "module": "commonjs", - "types": [ - "jasmine", - "node" - ] - }, - "files": [ - "test.ts", - "polyfills.ts" - ], - "include": [ - "**/*.spec.ts", - "**/*.d.ts" - ] -} diff --git a/gae/frontend/src/tslint.json b/gae/frontend/src/tslint.json deleted file mode 100644 index 52e2c1a..0000000 --- a/gae/frontend/src/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "app", - "camelCase" - ], - "component-selector": [ - true, - "element", - "app", - "kebab-case" - ] - } -} diff --git a/gae/frontend/tsconfig.json b/gae/frontend/tsconfig.json deleted file mode 100644 index ef44e28..0000000 --- a/gae/frontend/tsconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "compileOnSave": false, - "compilerOptions": { - "baseUrl": "./", - "outDir": "./dist/out-tsc", - "sourceMap": true, - "declaration": false, - "moduleResolution": "node", - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "target": "es5", - "typeRoots": [ - "node_modules/@types" - ], - "lib": [ - "es2017", - "dom" - ] - } -} diff --git a/gae/frontend/tslint.json b/gae/frontend/tslint.json deleted file mode 100644 index 259320f..0000000 --- a/gae/frontend/tslint.json +++ /dev/null @@ -1,130 +0,0 @@ -{ - "rulesDirectory": [ - "node_modules/codelyzer" - ], - "rules": { - "arrow-return-shorthand": true, - "callable-types": true, - "class-name": true, - "comment-format": [ - true, - "check-space" - ], - "curly": true, - "deprecation": { - "severity": "warn" - }, - "eofline": true, - "forin": true, - "import-blacklist": [ - true, - "rxjs/Rx" - ], - "import-spacing": true, - "indent": [ - true, - "spaces" - ], - "interface-over-type-literal": true, - "label-position": true, - "max-line-length": [ - true, - 140 - ], - "member-access": false, - "member-ordering": [ - true, - { - "order": [ - "static-field", - "instance-field", - "static-method", - "instance-method" - ] - } - ], - "no-arg": true, - "no-bitwise": false, - "no-console": [ - true, - "debug", - "info", - "time", - "timeEnd", - "trace" - ], - "no-construct": true, - "no-debugger": true, - "no-duplicate-super": true, - "no-empty": false, - "no-empty-interface": true, - "no-eval": true, - "no-inferrable-types": [ - true, - "ignore-params" - ], - "no-misused-new": true, - "no-non-null-assertion": true, - "no-shadowed-variable": true, - "no-string-literal": false, - "no-string-throw": true, - "no-switch-case-fall-through": true, - "no-trailing-whitespace": true, - "no-unnecessary-initializer": true, - "no-unused-expression": true, - "no-use-before-declare": true, - "no-var-keyword": true, - "object-literal-sort-keys": false, - "one-line": [ - true, - "check-open-brace", - "check-catch", - "check-else", - "check-whitespace" - ], - "prefer-const": true, - "quotemark": [ - true, - "single" - ], - "radix": true, - "semicolon": [ - true, - "always" - ], - "triple-equals": [ - true, - "allow-null-check" - ], - "typedef-whitespace": [ - true, - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - } - ], - "unified-signatures": true, - "variable-name": false, - "whitespace": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-separator", - "check-type" - ], - "no-output-on-prefix": true, - "use-input-property-decorator": true, - "use-output-property-decorator": true, - "use-host-property-decorator": true, - "no-input-rename": true, - "no-output-rename": true, - "use-life-cycle-interface": true, - "use-pipe-transform-interface": true, - "component-class-suffix": true, - "directive-class-suffix": true - } -} diff --git a/gae/hostv1openapi.json b/gae/hostv1openapi.json deleted file mode 100644 index 35116b2..0000000 --- a/gae/hostv1openapi.json +++ /dev/null @@ -1,243 +0,0 @@ -{ - "basePath": "/_ah/api", - "consumes": [ - "application/json" - ], - "definitions": { - "WebappSrcProtoModelBuildInfoMessage": { - "properties": { - "artifact_type": { - "type": "string" - }, - "artifacts": { - "items": { - "type": "string" - }, - "type": "array" - }, - "build_id": { - "type": "string" - }, - "build_target": { - "type": "string" - }, - "build_type": { - "type": "string" - }, - "manifest_branch": { - "type": "string" - }, - "signed": { - "type": "boolean" - } - }, - "type": "object" - }, - "WebappSrcProtoModelBuildResponseMessage": { - "properties": { - "builds": { - "description": "A message for representing an individual build entry.", - "items": { - "$ref": "#/definitions/#/definitions/WebappSrcProtoModelBuildInfoMessage" - }, - "type": "array" - }, - "has_next": { - "type": "boolean" - } - }, - "type": "object" - }, - "WebappSrcProtoModelCountRequestMessage": { - "properties": { - "filter": { - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelCountResponseMessage": { - "properties": { - "count": { - "format": "int64", - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelDefaultResponse": { - "properties": { - "return_code": { - "enum": [ - "SUCCESS", - "FAIL" - ], - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelDeviceInfoMessage": { - "properties": { - "product": { - "type": "string" - }, - "scheduling_status": { - "format": "int64", - "type": "string" - }, - "serial": { - "type": "string" - }, - "status": { - "format": "int64", - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelDeviceResponseMessage": { - "properties": { - "devices": { - "description": "A message for representing an individual host's device entry.", - "items": { - "$ref": "#/definitions/WebappSrcProtoModelDeviceInfoMessage" - }, - "type": "array" - }, - "has_next": { - "type": "boolean" - } - }, - "type": "object" - }, - "WebappSrcProtoModelGetRequestMessage": { - "properties": { - "direction": { - "type": "string" - }, - "filter": { - "type": "string" - }, - "offset": { - "format": "int64", - "type": "string" - }, - "size": { - "format": "int64", - "type": "string" - }, - "sort": { - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelHostInfoMessage": { - "properties": { - "devices": { - "description": "A message for representing an individual host's device entry.", - "items": { - "$ref": "#/definitions/WebappSrcProtoModelDeviceInfoMessage" - }, - "type": "array" - }, - "hostname": { - "type": "string" - } - }, - "type": "object" - } - }, - "host": "vtslab-schedule-prod.appspot.com", - "info": { - "description": "Endpoint API for host_info.", - "title": "host", - "version": "v1" - }, - "paths": { - "/host/v1/count": { - "post": { - "operationId": "HostInfoApi_count", - "parameters": [ - { - "in": "body", - "name": "body", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelCountRequestMessage" - } - } - ], - "responses": { - "200": { - "description": "A successful response", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelCountResponseMessage" - } - } - } - } - }, - "/host/v1/get": { - "post": { - "operationId": "HostInfoApi_get", - "parameters": [ - { - "in": "body", - "name": "body", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelGetRequestMessage" - } - } - ], - "responses": { - "200": { - "description": "A successful response", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelDeviceResponseMessage" - } - } - } - } - }, - "/host/v1/set": { - "post": { - "operationId": "HostInfoApi_set", - "parameters": [ - { - "in": "body", - "name": "body", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelHostInfoMessage" - } - } - ], - "responses": { - "200": { - "description": "A successful response", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelDefaultResponse" - } - } - } - } - } - }, - "produces": [ - "application/json" - ], - "schemes": [ - "https" - ], - "securityDefinitions": { - "google_id_token": { - "authorizationUrl": "", - "flow": "implicit", - "type": "oauth2", - "x-google-issuer": "https://accounts.google.com", - "x-google-jwks_uri": "https://www.googleapis.com/oauth2/v3/certs" - } - }, - "swagger": "2.0", - "x-google-api-name": "host" -}
\ No newline at end of file diff --git a/gae/index.yaml b/gae/index.yaml deleted file mode 100644 index edde291..0000000 --- a/gae/index.yaml +++ /dev/null @@ -1,112 +0,0 @@ -indexes: -- kind: DeviceModel - ancestor: no - properties: - - name: hostname - direction: asc - - name: product - - name: serial - - name: status - - name: scheduling_status - - name: timestamp - -- kind: BuildModel - ancestor: no - properties: - - name: manifest_branch - - name: build_id - direction: desc - - name: build_target - - name: build_type - - name: artifact_type - - name: artifacts - - name: timestamp - -- kind: ScheduleModel - ancestor: no - properties: - - name: manifest_branch - - name: build_target - - name: test_name - - name: require_signed_device_build - - name: has_bootloader_img - - name: has_radio_img - - name: period - - name: priority - - name: priority_value - - name: device - - name: shards - - name: param - - name: retry_count - - name: gsi_branch - - name: gsi_build_target - - name: gsi_pab_account_id - - name: test_branch - - name: test_build_target - - name: test_pab_account_id - - name: timestamp - - name: children_jobs - - name: suspended - - name: error_count - - name: image_package_repo_base - - name: required_host_equipment - - name: required_device_equipment - - name: report_bucket - - name: report_spreadsheet_id - - name: report_persistent_url - - name: report_reference_url - -- kind: LabModel - ancestor: no - properties: - - name: name - - name: owner - - name: admin - - name: hostname - - name: ip - - name: script - - name: devices - - name: timestamp - - name: vtslab_version - -- kind: JobModel - ancestor: no - properties: - - name: hostname - - name: priority - - name: period - - name: retry_count - - name: test_name - - name: device - - name: serial - - name: manifest_branch - - name: build_target - - name: shards - - name: param - - name: build_id - - name: status - - name: gsi_branch - - name: gsi_build_target - - name: gsi_pab_account_id - - name: test_branch - - name: test_build_target - - name: test_pab_account_id - - name: infra_log_url - - name: timestamp - direction: desc - - name: parent_schedule - - name: test_type - - name: require_signed_device_build - - name: has_bootloader_img - - name: has_radio_img - - name: image_package_repo_base - - name: report_bucket - - name: report_spreadsheet_id - - name: report_persistent_url - - name: report_reference_url - -- kind: JobModel - properties: - - name: hostname - - name: timestamp - direction: desc
\ No newline at end of file diff --git a/gae/jobv1openapi.json b/gae/jobv1openapi.json deleted file mode 100644 index e2badde..0000000 --- a/gae/jobv1openapi.json +++ /dev/null @@ -1,718 +0,0 @@ -{ - "basePath": "/_ah/api", - "consumes": [ - "application/json" - ], - "definitions": { - "WebappSrcProtoModelBuildInfoMessage": { - "properties": { - "artifact_type": { - "type": "string" - }, - "artifacts": { - "items": { - "type": "string" - }, - "type": "array" - }, - "build_id": { - "type": "string" - }, - "build_target": { - "type": "string" - }, - "build_type": { - "type": "string" - }, - "manifest_branch": { - "type": "string" - }, - "signed": { - "type": "boolean" - } - }, - "type": "object" - }, - "WebappSrcProtoModelBuildResponseMessage": { - "properties": { - "builds": { - "description": "A message for representing an individual build entry.", - "items": { - "$ref": "#/definitions/#/definitions/#/definitions/#/definitions/#/definitions/WebappSrcProtoModelBuildInfoMessage" - }, - "type": "array" - }, - "has_next": { - "type": "boolean" - } - }, - "type": "object" - }, - "WebappSrcProtoModelCountRequestMessage": { - "properties": { - "filter": { - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelCountResponseMessage": { - "properties": { - "count": { - "format": "int64", - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelDefaultResponse": { - "properties": { - "return_code": { - "enum": [ - "SUCCESS", - "FAIL" - ], - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelDeviceInfoMessage": { - "properties": { - "product": { - "type": "string" - }, - "scheduling_status": { - "format": "int64", - "type": "string" - }, - "serial": { - "type": "string" - }, - "status": { - "format": "int64", - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelDeviceResponseMessage": { - "properties": { - "devices": { - "description": "A message for representing an individual host's device entry.", - "items": { - "$ref": "#/definitions/#/definitions/#/definitions/#/definitions/WebappSrcProtoModelDeviceInfoMessage" - }, - "type": "array" - }, - "has_next": { - "type": "boolean" - } - }, - "type": "object" - }, - "WebappSrcProtoModelGetRequestMessage": { - "properties": { - "direction": { - "type": "string" - }, - "filter": { - "type": "string" - }, - "offset": { - "format": "int64", - "type": "string" - }, - "size": { - "format": "int64", - "type": "string" - }, - "sort": { - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelHostInfoMessage": { - "properties": { - "devices": { - "description": "A message for representing an individual host's device entry.", - "items": { - "$ref": "#/definitions/#/definitions/#/definitions/#/definitions/WebappSrcProtoModelDeviceInfoMessage" - }, - "type": "array" - }, - "hostname": { - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelJobLeaseResponse": { - "properties": { - "jobs": { - "description": "A message for representing an individual job entry.", - "items": { - "$ref": "#/definitions/WebappSrcProtoModelJobMessage" - }, - "type": "array" - }, - "return_code": { - "enum": [ - "SUCCESS", - "FAIL" - ], - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelJobMessage": { - "properties": { - "build_id": { - "type": "string" - }, - "build_storage_type": { - "format": "int64", - "type": "string" - }, - "build_target": { - "type": "string" - }, - "device": { - "type": "string" - }, - "gsi_branch": { - "type": "string" - }, - "gsi_build_id": { - "type": "string" - }, - "gsi_build_target": { - "type": "string" - }, - "gsi_pab_account_id": { - "type": "string" - }, - "gsi_storage_type": { - "format": "int64", - "type": "string" - }, - "gsi_vendor_version": { - "type": "string" - }, - "has_bootloader_img": { - "type": "boolean" - }, - "has_radio_img": { - "type": "boolean" - }, - "hostname": { - "type": "string" - }, - "image_package_repo_base": { - "type": "string" - }, - "infra_log_url": { - "type": "string" - }, - "manifest_branch": { - "type": "string" - }, - "pab_account_id": { - "type": "string" - }, - "param": { - "items": { - "type": "string" - }, - "type": "array" - }, - "period": { - "format": "int64", - "type": "string" - }, - "priority": { - "type": "string" - }, - "report_bucket": { - "items": { - "type": "string" - }, - "type": "array" - }, - "report_persistent_url": { - "items": { - "type": "string" - }, - "type": "array" - }, - "report_reference_url": { - "items": { - "type": "string" - }, - "type": "array" - }, - "report_spreadsheet_id": { - "items": { - "type": "string" - }, - "type": "array" - }, - "require_signed_device_build": { - "type": "boolean" - }, - "retry_count": { - "format": "int64", - "type": "string" - }, - "serial": { - "items": { - "type": "string" - }, - "type": "array" - }, - "shards": { - "format": "int64", - "type": "string" - }, - "status": { - "format": "int64", - "type": "string" - }, - "test_branch": { - "type": "string" - }, - "test_build_id": { - "type": "string" - }, - "test_build_target": { - "type": "string" - }, - "test_name": { - "type": "string" - }, - "test_pab_account_id": { - "type": "string" - }, - "test_storage_type": { - "format": "int64", - "type": "string" - }, - "test_type": { - "format": "int64", - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelJobResponseMessage": { - "properties": { - "has_next": { - "type": "boolean" - }, - "jobs": { - "description": "A message for representing an individual job entry.", - "items": { - "$ref": "#/definitions/WebappSrcProtoModelJobMessage" - }, - "type": "array" - } - }, - "type": "object" - }, - "WebappSrcProtoModelLabDeviceInfoMessage": { - "properties": { - "device_equipment": { - "items": { - "type": "string" - }, - "type": "array" - }, - "product": { - "type": "string" - }, - "serial": { - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelLabHostInfoMessage": { - "properties": { - "device": { - "description": "A message for representing an individual lab host's device entry.", - "items": { - "$ref": "#/definitions/#/definitions/#/definitions/WebappSrcProtoModelLabDeviceInfoMessage" - }, - "type": "array" - }, - "host_equipment": { - "items": { - "type": "string" - }, - "type": "array" - }, - "hostname": { - "type": "string" - }, - "ip": { - "type": "string" - }, - "script": { - "type": "string" - }, - "vtslab_version": { - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelLabInfoMessage": { - "properties": { - "admin": { - "items": { - "type": "string" - }, - "type": "array" - }, - "host": { - "description": "A message for representing an individual lab's host entry.", - "items": { - "$ref": "#/definitions/#/definitions/#/definitions/WebappSrcProtoModelLabHostInfoMessage" - }, - "type": "array" - }, - "name": { - "type": "string" - }, - "owner": { - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelLabMessage": { - "properties": { - "admin": { - "items": { - "type": "string" - }, - "type": "array" - }, - "devices": { - "type": "string" - }, - "host_equipment": { - "items": { - "type": "string" - }, - "type": "array" - }, - "hostname": { - "type": "string" - }, - "ip": { - "type": "string" - }, - "name": { - "type": "string" - }, - "owner": { - "type": "string" - }, - "vtslab_version": { - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelLabResponseMessage": { - "properties": { - "has_next": { - "type": "boolean" - }, - "labs": { - "description": "A model for representing a LabModel entity.", - "items": { - "$ref": "#/definitions/#/definitions/#/definitions/WebappSrcProtoModelLabMessage" - }, - "type": "array" - } - }, - "type": "object" - }, - "WebappSrcProtoModelScheduleInfoMessage": { - "properties": { - "build_storage_type": { - "format": "int64", - "type": "string" - }, - "build_target": { - "type": "string" - }, - "device": { - "items": { - "type": "string" - }, - "type": "array" - }, - "device_pab_account_id": { - "type": "string" - }, - "gsi_branch": { - "type": "string" - }, - "gsi_build_target": { - "type": "string" - }, - "gsi_pab_account_id": { - "type": "string" - }, - "gsi_storage_type": { - "format": "int64", - "type": "string" - }, - "gsi_vendor_version": { - "type": "string" - }, - "has_bootloader_img": { - "type": "boolean" - }, - "has_radio_img": { - "type": "boolean" - }, - "image_package_repo_base": { - "type": "string" - }, - "manifest_branch": { - "type": "string" - }, - "name": { - "type": "string" - }, - "owner": { - "items": { - "type": "string" - }, - "type": "array" - }, - "param": { - "items": { - "type": "string" - }, - "type": "array" - }, - "period": { - "format": "int64", - "type": "string" - }, - "priority": { - "type": "string" - }, - "report_bucket": { - "items": { - "type": "string" - }, - "type": "array" - }, - "report_persistent_url": { - "items": { - "type": "string" - }, - "type": "array" - }, - "report_reference_url": { - "items": { - "type": "string" - }, - "type": "array" - }, - "report_spreadsheet_id": { - "items": { - "type": "string" - }, - "type": "array" - }, - "require_signed_device_build": { - "type": "boolean" - }, - "required_device_equipment": { - "items": { - "type": "string" - }, - "type": "array" - }, - "required_host_equipment": { - "items": { - "type": "string" - }, - "type": "array" - }, - "retry_count": { - "format": "int64", - "type": "string" - }, - "schedule": { - "type": "string" - }, - "schedule_type": { - "type": "string" - }, - "shards": { - "format": "int64", - "type": "string" - }, - "test_branch": { - "type": "string" - }, - "test_build_target": { - "type": "string" - }, - "test_name": { - "type": "string" - }, - "test_pab_account_id": { - "type": "string" - }, - "test_storage_type": { - "format": "int64", - "type": "string" - }, - "timestamp": { - "format": "date-time", - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelScheduleResponseMessage": { - "properties": { - "has_next": { - "type": "boolean" - }, - "schedules": { - "description": "A message for representing an individual schedule entry.", - "items": { - "$ref": "#/definitions/#/definitions/WebappSrcProtoModelScheduleInfoMessage" - }, - "type": "array" - } - }, - "type": "object" - } - }, - "host": "vtslab-schedule-prod.appspot.com", - "info": { - "description": "Endpoint API for job_queue.", - "title": "job", - "version": "v1" - }, - "paths": { - "/job/v1/count": { - "post": { - "operationId": "JobQueueApi_count", - "parameters": [ - { - "in": "body", - "name": "body", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelCountRequestMessage" - } - } - ], - "responses": { - "200": { - "description": "A successful response", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelCountResponseMessage" - } - } - } - } - }, - "/job/v1/get": { - "post": { - "operationId": "JobQueueApi_get", - "parameters": [ - { - "in": "body", - "name": "body", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelGetRequestMessage" - } - } - ], - "responses": { - "200": { - "description": "A successful response", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelJobResponseMessage" - } - } - } - } - }, - "/job/v1/heartbeat": { - "post": { - "operationId": "JobQueueApi_heartbeat", - "parameters": [ - { - "in": "body", - "name": "body", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelJobMessage" - } - } - ], - "responses": { - "200": { - "description": "A successful response", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelJobLeaseResponse" - } - } - } - } - }, - "/job/v1/lease": { - "post": { - "operationId": "JobQueueApi_lease", - "parameters": [ - { - "in": "body", - "name": "body", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelJobMessage" - } - } - ], - "responses": { - "200": { - "description": "A successful response", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelJobLeaseResponse" - } - } - } - } - } - }, - "produces": [ - "application/json" - ], - "schemes": [ - "https" - ], - "securityDefinitions": { - "google_id_token": { - "authorizationUrl": "", - "flow": "implicit", - "type": "oauth2", - "x-google-issuer": "https://accounts.google.com", - "x-google-jwks_uri": "https://www.googleapis.com/oauth2/v3/certs" - } - }, - "swagger": "2.0", - "x-google-api-name": "job" -}
\ No newline at end of file diff --git a/gae/labv1openapi.json b/gae/labv1openapi.json deleted file mode 100644 index 37f31d2..0000000 --- a/gae/labv1openapi.json +++ /dev/null @@ -1,408 +0,0 @@ -{ - "basePath": "/_ah/api", - "consumes": [ - "application/json" - ], - "definitions": { - "WebappSrcProtoModelBuildInfoMessage": { - "properties": { - "artifact_type": { - "type": "string" - }, - "artifacts": { - "items": { - "type": "string" - }, - "type": "array" - }, - "build_id": { - "type": "string" - }, - "build_target": { - "type": "string" - }, - "build_type": { - "type": "string" - }, - "manifest_branch": { - "type": "string" - }, - "signed": { - "type": "boolean" - } - }, - "type": "object" - }, - "WebappSrcProtoModelBuildResponseMessage": { - "properties": { - "builds": { - "description": "A message for representing an individual build entry.", - "items": { - "$ref": "#/definitions/#/definitions/#/definitions/WebappSrcProtoModelBuildInfoMessage" - }, - "type": "array" - }, - "has_next": { - "type": "boolean" - } - }, - "type": "object" - }, - "WebappSrcProtoModelCountRequestMessage": { - "properties": { - "filter": { - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelCountResponseMessage": { - "properties": { - "count": { - "format": "int64", - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelDefaultResponse": { - "properties": { - "return_code": { - "enum": [ - "SUCCESS", - "FAIL" - ], - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelDeviceInfoMessage": { - "properties": { - "product": { - "type": "string" - }, - "scheduling_status": { - "format": "int64", - "type": "string" - }, - "serial": { - "type": "string" - }, - "status": { - "format": "int64", - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelDeviceResponseMessage": { - "properties": { - "devices": { - "description": "A message for representing an individual host's device entry.", - "items": { - "$ref": "#/definitions/#/definitions/WebappSrcProtoModelDeviceInfoMessage" - }, - "type": "array" - }, - "has_next": { - "type": "boolean" - } - }, - "type": "object" - }, - "WebappSrcProtoModelGetRequestMessage": { - "properties": { - "direction": { - "type": "string" - }, - "filter": { - "type": "string" - }, - "offset": { - "format": "int64", - "type": "string" - }, - "size": { - "format": "int64", - "type": "string" - }, - "sort": { - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelHostInfoMessage": { - "properties": { - "devices": { - "description": "A message for representing an individual host's device entry.", - "items": { - "$ref": "#/definitions/#/definitions/WebappSrcProtoModelDeviceInfoMessage" - }, - "type": "array" - }, - "hostname": { - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelLabDeviceInfoMessage": { - "properties": { - "device_equipment": { - "items": { - "type": "string" - }, - "type": "array" - }, - "product": { - "type": "string" - }, - "serial": { - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelLabHostInfoMessage": { - "properties": { - "device": { - "description": "A message for representing an individual lab host's device entry.", - "items": { - "$ref": "#/definitions/WebappSrcProtoModelLabDeviceInfoMessage" - }, - "type": "array" - }, - "host_equipment": { - "items": { - "type": "string" - }, - "type": "array" - }, - "hostname": { - "type": "string" - }, - "ip": { - "type": "string" - }, - "script": { - "type": "string" - }, - "vtslab_version": { - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelLabInfoMessage": { - "properties": { - "admin": { - "items": { - "type": "string" - }, - "type": "array" - }, - "host": { - "description": "A message for representing an individual lab's host entry.", - "items": { - "$ref": "#/definitions/WebappSrcProtoModelLabHostInfoMessage" - }, - "type": "array" - }, - "name": { - "type": "string" - }, - "owner": { - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelLabMessage": { - "properties": { - "admin": { - "items": { - "type": "string" - }, - "type": "array" - }, - "devices": { - "type": "string" - }, - "host_equipment": { - "items": { - "type": "string" - }, - "type": "array" - }, - "hostname": { - "type": "string" - }, - "ip": { - "type": "string" - }, - "name": { - "type": "string" - }, - "owner": { - "type": "string" - }, - "vtslab_version": { - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelLabResponseMessage": { - "properties": { - "has_next": { - "type": "boolean" - }, - "labs": { - "description": "A model for representing a LabModel entity.", - "items": { - "$ref": "#/definitions/WebappSrcProtoModelLabMessage" - }, - "type": "array" - } - }, - "type": "object" - } - }, - "host": "vtslab-schedule-prod.appspot.com", - "info": { - "description": "Endpoint API for lab_info.", - "title": "lab", - "version": "v1" - }, - "paths": { - "/lab/v1/clear": { - "post": { - "operationId": "LabInfoApi_clear", - "parameters": [ - { - "in": "body", - "name": "body", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelLabInfoMessage" - } - } - ], - "responses": { - "200": { - "description": "A successful response", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelDefaultResponse" - } - } - } - } - }, - "/lab/v1/count": { - "post": { - "operationId": "LabInfoApi_count", - "parameters": [ - { - "in": "body", - "name": "body", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelCountRequestMessage" - } - } - ], - "responses": { - "200": { - "description": "A successful response", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelCountResponseMessage" - } - } - } - } - }, - "/lab/v1/get": { - "post": { - "operationId": "LabInfoApi_get", - "parameters": [ - { - "in": "body", - "name": "body", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelGetRequestMessage" - } - } - ], - "responses": { - "200": { - "description": "A successful response", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelLabResponseMessage" - } - } - } - } - }, - "/lab/v1/set": { - "post": { - "operationId": "LabInfoApi_set", - "parameters": [ - { - "in": "body", - "name": "body", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelLabInfoMessage" - } - } - ], - "responses": { - "200": { - "description": "A successful response", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelDefaultResponse" - } - } - } - } - }, - "/lab/v1/set_version": { - "post": { - "operationId": "LabInfoApi_setVersion", - "parameters": [ - { - "in": "body", - "name": "body", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelLabHostInfoMessage" - } - } - ], - "responses": { - "200": { - "description": "A successful response", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelDefaultResponse" - } - } - } - } - } - }, - "produces": [ - "application/json" - ], - "schemes": [ - "https" - ], - "securityDefinitions": { - "google_id_token": { - "authorizationUrl": "", - "flow": "implicit", - "type": "oauth2", - "x-google-issuer": "https://accounts.google.com", - "x-google-jwks_uri": "https://www.googleapis.com/oauth2/v3/certs" - } - }, - "swagger": "2.0", - "x-google-api-name": "lab" -}
\ No newline at end of file diff --git a/gae/queue.yaml b/gae/queue.yaml deleted file mode 100644 index b460b11..0000000 --- a/gae/queue.yaml +++ /dev/null @@ -1,18 +0,0 @@ -queue: -- name: queue-schedule - mode: push - rate: 1/s - bucket_size: 5 - max_concurrent_requests: 1 - retry_parameters: - task_retry_limit: 2 - min_backoff_seconds: 1 - -- name: queue-indexing - mode: push - rate: 1/s - bucket_size: 5 - max_concurrent_requests: 1 - retry_parameters: - task_retry_limit: 7 - min_backoff_seconds: 1
\ No newline at end of file diff --git a/gae/requirements.txt b/gae/requirements.txt deleted file mode 100644 index e8c00fc..0000000 --- a/gae/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -google-api-python-client -google-endpoints - -pytz -stripe - diff --git a/gae/schedulev1openapi.json b/gae/schedulev1openapi.json deleted file mode 100644 index b1db4e9..0000000 --- a/gae/schedulev1openapi.json +++ /dev/null @@ -1,545 +0,0 @@ -{ - "basePath": "/_ah/api", - "consumes": [ - "application/json" - ], - "definitions": { - "WebappSrcProtoModelBuildInfoMessage": { - "properties": { - "artifact_type": { - "type": "string" - }, - "artifacts": { - "items": { - "type": "string" - }, - "type": "array" - }, - "build_id": { - "type": "string" - }, - "build_target": { - "type": "string" - }, - "build_type": { - "type": "string" - }, - "manifest_branch": { - "type": "string" - }, - "signed": { - "type": "boolean" - } - }, - "type": "object" - }, - "WebappSrcProtoModelBuildResponseMessage": { - "properties": { - "builds": { - "description": "A message for representing an individual build entry.", - "items": { - "$ref": "#/definitions/#/definitions/#/definitions/#/definitions/WebappSrcProtoModelBuildInfoMessage" - }, - "type": "array" - }, - "has_next": { - "type": "boolean" - } - }, - "type": "object" - }, - "WebappSrcProtoModelCountRequestMessage": { - "properties": { - "filter": { - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelCountResponseMessage": { - "properties": { - "count": { - "format": "int64", - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelDefaultResponse": { - "properties": { - "return_code": { - "enum": [ - "SUCCESS", - "FAIL" - ], - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelDeviceInfoMessage": { - "properties": { - "product": { - "type": "string" - }, - "scheduling_status": { - "format": "int64", - "type": "string" - }, - "serial": { - "type": "string" - }, - "status": { - "format": "int64", - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelDeviceResponseMessage": { - "properties": { - "devices": { - "description": "A message for representing an individual host's device entry.", - "items": { - "$ref": "#/definitions/#/definitions/#/definitions/WebappSrcProtoModelDeviceInfoMessage" - }, - "type": "array" - }, - "has_next": { - "type": "boolean" - } - }, - "type": "object" - }, - "WebappSrcProtoModelGetRequestMessage": { - "properties": { - "direction": { - "type": "string" - }, - "filter": { - "type": "string" - }, - "offset": { - "format": "int64", - "type": "string" - }, - "size": { - "format": "int64", - "type": "string" - }, - "sort": { - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelHostInfoMessage": { - "properties": { - "devices": { - "description": "A message for representing an individual host's device entry.", - "items": { - "$ref": "#/definitions/#/definitions/#/definitions/WebappSrcProtoModelDeviceInfoMessage" - }, - "type": "array" - }, - "hostname": { - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelLabDeviceInfoMessage": { - "properties": { - "device_equipment": { - "items": { - "type": "string" - }, - "type": "array" - }, - "product": { - "type": "string" - }, - "serial": { - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelLabHostInfoMessage": { - "properties": { - "device": { - "description": "A message for representing an individual lab host's device entry.", - "items": { - "$ref": "#/definitions/#/definitions/WebappSrcProtoModelLabDeviceInfoMessage" - }, - "type": "array" - }, - "host_equipment": { - "items": { - "type": "string" - }, - "type": "array" - }, - "hostname": { - "type": "string" - }, - "ip": { - "type": "string" - }, - "script": { - "type": "string" - }, - "vtslab_version": { - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelLabInfoMessage": { - "properties": { - "admin": { - "items": { - "type": "string" - }, - "type": "array" - }, - "host": { - "description": "A message for representing an individual lab's host entry.", - "items": { - "$ref": "#/definitions/#/definitions/WebappSrcProtoModelLabHostInfoMessage" - }, - "type": "array" - }, - "name": { - "type": "string" - }, - "owner": { - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelLabMessage": { - "properties": { - "admin": { - "items": { - "type": "string" - }, - "type": "array" - }, - "devices": { - "type": "string" - }, - "host_equipment": { - "items": { - "type": "string" - }, - "type": "array" - }, - "hostname": { - "type": "string" - }, - "ip": { - "type": "string" - }, - "name": { - "type": "string" - }, - "owner": { - "type": "string" - }, - "vtslab_version": { - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelLabResponseMessage": { - "properties": { - "has_next": { - "type": "boolean" - }, - "labs": { - "description": "A model for representing a LabModel entity.", - "items": { - "$ref": "#/definitions/#/definitions/WebappSrcProtoModelLabMessage" - }, - "type": "array" - } - }, - "type": "object" - }, - "WebappSrcProtoModelScheduleInfoMessage": { - "properties": { - "build_storage_type": { - "format": "int64", - "type": "string" - }, - "build_target": { - "type": "string" - }, - "device": { - "items": { - "type": "string" - }, - "type": "array" - }, - "device_pab_account_id": { - "type": "string" - }, - "gsi_branch": { - "type": "string" - }, - "gsi_build_target": { - "type": "string" - }, - "gsi_pab_account_id": { - "type": "string" - }, - "gsi_storage_type": { - "format": "int64", - "type": "string" - }, - "gsi_vendor_version": { - "type": "string" - }, - "has_bootloader_img": { - "type": "boolean" - }, - "has_radio_img": { - "type": "boolean" - }, - "image_package_repo_base": { - "type": "string" - }, - "manifest_branch": { - "type": "string" - }, - "name": { - "type": "string" - }, - "owner": { - "items": { - "type": "string" - }, - "type": "array" - }, - "param": { - "items": { - "type": "string" - }, - "type": "array" - }, - "period": { - "format": "int64", - "type": "string" - }, - "priority": { - "type": "string" - }, - "report_bucket": { - "items": { - "type": "string" - }, - "type": "array" - }, - "report_persistent_url": { - "items": { - "type": "string" - }, - "type": "array" - }, - "report_reference_url": { - "items": { - "type": "string" - }, - "type": "array" - }, - "report_spreadsheet_id": { - "items": { - "type": "string" - }, - "type": "array" - }, - "require_signed_device_build": { - "type": "boolean" - }, - "required_device_equipment": { - "items": { - "type": "string" - }, - "type": "array" - }, - "required_host_equipment": { - "items": { - "type": "string" - }, - "type": "array" - }, - "retry_count": { - "format": "int64", - "type": "string" - }, - "schedule": { - "type": "string" - }, - "schedule_type": { - "type": "string" - }, - "shards": { - "format": "int64", - "type": "string" - }, - "test_branch": { - "type": "string" - }, - "test_build_target": { - "type": "string" - }, - "test_name": { - "type": "string" - }, - "test_pab_account_id": { - "type": "string" - }, - "test_storage_type": { - "format": "int64", - "type": "string" - }, - "timestamp": { - "format": "date-time", - "type": "string" - } - }, - "type": "object" - }, - "WebappSrcProtoModelScheduleResponseMessage": { - "properties": { - "has_next": { - "type": "boolean" - }, - "schedules": { - "description": "A message for representing an individual schedule entry.", - "items": { - "$ref": "#/definitions/WebappSrcProtoModelScheduleInfoMessage" - }, - "type": "array" - } - }, - "type": "object" - } - }, - "host": "vtslab-schedule-prod.appspot.com", - "info": { - "description": "Endpoint API for schedule_info.", - "title": "schedule", - "version": "v1" - }, - "paths": { - "/schedule/v1/clear": { - "post": { - "operationId": "ScheduleInfoApi_clear", - "parameters": [ - { - "in": "body", - "name": "body", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelScheduleInfoMessage" - } - } - ], - "responses": { - "200": { - "description": "A successful response", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelDefaultResponse" - } - } - } - } - }, - "/schedule/v1/count": { - "post": { - "operationId": "ScheduleInfoApi_count", - "parameters": [ - { - "in": "body", - "name": "body", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelCountRequestMessage" - } - } - ], - "responses": { - "200": { - "description": "A successful response", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelCountResponseMessage" - } - } - } - } - }, - "/schedule/v1/get": { - "post": { - "operationId": "ScheduleInfoApi_get", - "parameters": [ - { - "in": "body", - "name": "body", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelGetRequestMessage" - } - } - ], - "responses": { - "200": { - "description": "A successful response", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelScheduleResponseMessage" - } - } - } - } - }, - "/schedule/v1/set": { - "post": { - "operationId": "ScheduleInfoApi_set", - "parameters": [ - { - "in": "body", - "name": "body", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelScheduleInfoMessage" - } - } - ], - "responses": { - "200": { - "description": "A successful response", - "schema": { - "$ref": "#/definitions/WebappSrcProtoModelDefaultResponse" - } - } - } - } - } - }, - "produces": [ - "application/json" - ], - "schemes": [ - "https" - ], - "securityDefinitions": { - "google_id_token": { - "authorizationUrl": "", - "flow": "implicit", - "type": "oauth2", - "x-google-issuer": "https://accounts.google.com", - "x-google-jwks_uri": "https://www.googleapis.com/oauth2/v3/certs" - } - }, - "swagger": "2.0", - "x-google-api-name": "schedule" -}
\ No newline at end of file diff --git a/gae/script/build.sh b/gae/script/build.sh deleted file mode 100755 index 6cc6a97..0000000 --- a/gae/script/build.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash -# -# Copyright 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -if [ "$#" -ne 1 ]; then - echo "usage: build.sh prod|test|public" - exit 1 -fi - -if [ $1 = "prod" ]; then - SERVICE="vtslab-schedule-prod.appspot.com" -elif [ $1 = "public" ]; then - SERVICE="vtslab-schedule.appspot.com" -else - SERVICE="vtslab-schedule-test.appspot.com" -fi - -echo "Building the webapp for $SERVICE ..." - -python lib/endpoints/endpointscfg.py get_openapi_spec webapp.src.endpoint.build_info.BuildInfoApi --hostname $SERVICE -python lib/endpoints/endpointscfg.py get_openapi_spec webapp.src.endpoint.host_info.HostInfoApi --hostname $SERVICE -python lib/endpoints/endpointscfg.py get_openapi_spec webapp.src.endpoint.schedule_info.ScheduleInfoApi --hostname $SERVICE -python lib/endpoints/endpointscfg.py get_openapi_spec webapp.src.endpoint.lab_info.LabInfoApi --hostname $SERVICE - -echo "Build complete." diff --git a/gae/script/create-datastore.sh b/gae/script/create-datastore.sh deleted file mode 100755 index 1101298..0000000 --- a/gae/script/create-datastore.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -# -# Copyright 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -gcloud datastore create-indexes index.yaml diff --git a/gae/script/deploy-endpoint.sh b/gae/script/deploy-endpoint.sh deleted file mode 100755 index a6cf10b..0000000 --- a/gae/script/deploy-endpoint.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash -# -# Copyright 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -if [ "$#" -ne 1 ]; then - echo "usage: deploy-endpoint.sh prod|test|public" - exit 1 -fi - -if [ $1 = "public" ]; then - SERVICE="vtslab-schedule" -else - SERVICE="vtslab-schedule-$1" -fi - -echo "Creating OpenAPI spec files for $SERVICE.appspot.com ..." -python lib/endpoints/endpointscfg.py get_openapi_spec webapp.src.endpoint.build_info.BuildInfoApi webapp.src.endpoint.host_info.HostInfoApi webapp.src.endpoint.lab_info.LabInfoApi webapp.src.endpoint.schedule_info.ScheduleInfoApi webapp.src.endpoint.job_queue.JobQueueApi --hostname $SERVICE.appspot.com --x-google-api-name - -echo "Depolying the endpoint API implementation to $SERVICE ..." - -gcloud endpoints services deploy buildv1openapi.json hostv1openapi.json labv1openapi.json schedulev1openapi.json jobv1openapi.json --project=$SERVICE -gcloud endpoints configs list --service=$SERVICE.appspot.com - -echo "Deployment done!" diff --git a/gae/script/deploy-webapp.sh b/gae/script/deploy-webapp.sh deleted file mode 100755 index 187ee7d..0000000 --- a/gae/script/deploy-webapp.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/bin/bash -# -# Copyright 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -if [ "$#" -lt 1 ]; then - echo "usage: deploy-webapp.sh prod|test|public|local [deploy options]" - exit 1 -fi - -NPM_PATH=$(which npm) -NG_PATH=$(which ng) -if [ ! -f "${NPM_PATH}" ]; then - echo "Cannot find npm in your PATH." - echo "Please install node.js and npm to deploy frontend." - exit 0 -fi -if [ ! -f "${NG_PATH}" ]; then - echo "Cannot find Angular CLI in your PATH." - echo "Please install Angular CLI to deploy frontend." - exit 0 -fi - -pushd frontend -echo "Installing frontend dependencies..." -npm install - -echo "Removing files in dist directory..." -rm -r dist/* - -echo "Building frontend codes..." -if [ $1 = "local" ]; then - ng build -else - ng build --prod -fi -popd - -echo "Copying frontend files to webapp/static directory..." -rm -rf webapp/static/ -mkdir webapp/static -cp -r frontend/dist/* webapp/static/ - -if [ $1 = "public" ]; then - SERVICE="vtslab-schedule" -elif [ $1 = "local" ]; then - dev_appserver.py ./ - exit 0 -else - SERVICE="vtslab-schedule-$1" -fi - -echo "Fetching endpoints service version of $SERVICE ..." -ENDPOINTS=$(gcloud endpoints configs list --service=$SERVICE.appspot.com) -arr=($ENDPOINTS) - -if [ ${#arr[@]} -lt 4 ]; then - echo "You need to deploy endpoints first." - exit 0 -else - VERSION=${arr[2]} - NAME=${arr[3]} - echo "ENDPOINTS_SERVICE_NAME: $NAME" - echo "ENDPOINTS_SERVICE_VERSION: $VERSION" -fi - -echo "Updating app.yaml ..." -if [ "$(uname)" == "Darwin" ]; then - sed -i "" "s/ENDPOINTS_SERVICE_NAME:.*/ENDPOINTS_SERVICE_NAME: $NAME/g" app.yaml - sed -i "" "s/ENDPOINTS_SERVICE_VERSION:.*/ENDPOINTS_SERVICE_VERSION: $VERSION/g" app.yaml -else - sed -i "s/ENDPOINTS_SERVICE_NAME:.*/ENDPOINTS_SERVICE_NAME: $NAME/g" app.yaml - sed -i "s/ENDPOINTS_SERVICE_VERSION:.*/ENDPOINTS_SERVICE_VERSION: $VERSION/g" app.yaml -fi - -echo "Deploying the web app to $SERVICE ..." - -gcloud app deploy app.yaml cron.yaml index.yaml queue.yaml worker.yaml --project=$SERVICE ${@:2} - -echo "Deployment done!" diff --git a/gae/script/install-pip.sh b/gae/script/install-pip.sh deleted file mode 100755 index 39601d6..0000000 --- a/gae/script/install-pip.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -# -# Copyright 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -pip install -t lib -r requirements.txt diff --git a/gae/testing/README.md b/gae/testing/README.md deleted file mode 100644 index e567288..0000000 --- a/gae/testing/README.md +++ /dev/null @@ -1,16 +0,0 @@ -## E2E Test - -A Makefile is provided to deploy and run the e2e test. - -To run: - - export GAE_PROJECT=your-project-id - make - -To manually run, install the requirements - - pip install -r e2e/requirements-dev.txt - -Finally, run the test - - python e2e/test_e2e.py diff --git a/gae/testing/e2e_test.py b/gae/testing/e2e_test.py deleted file mode 100644 index 3305862..0000000 --- a/gae/testing/e2e_test.py +++ /dev/null @@ -1,129 +0,0 @@ -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -"""App Engine local test runner. - -This program handles properly importing the App Engine SDK so that test modules -can use google.appengine.* APIs and the Google App Engine testbed. - -Example invocation: - - $ python testrunner.py [--sdk-path ~/google-cloud-sdk] -""" - -import argparse -import os -import subprocess -import sys -import unittest - - -def ExecuteOneShellCommand(cmd): - """Executes one shell command and returns (stdout, stderr, exit_code). - - Args: - cmd: string, a shell command. - - Returns: - tuple(string, string, int), containing stdout, stderr, exit_code of - the shell command. - """ - p = subprocess.Popen( - str(cmd), shell=True, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = p.communicate() - return (stdout, stderr, p.returncode) - - -def fixup_paths(path): - """Adds GAE SDK path to system path and appends it to the google path - if that already exists.""" - # Not all Google packages are inside namespace packages, which means - # there might be another non-namespace package named `google` already on - # the path and simply appending the App Engine SDK to the path will not - # work since the other package will get discovered and used first. - # This emulates namespace packages by first searching if a `google` package - # exists by importing it, and if so appending to its module search path. - try: - import google - google.__path__.append("{0}/google".format(path)) - except ImportError: - pass - - sys.path.insert(0, path) - - -def main(sdk_path, test_path, test_pattern): - - if not sdk_path: - # Get sdk path by running gcloud command. - stdout, stderr, _ = ExecuteOneShellCommand( - "gcloud info --format='value(installation.sdk_root)'") - - if stderr: - print("Cannot find google cloud sdk path.") - return 1 - sdk_path = str.strip(stdout) - - # If the SDK path points to a Google Cloud SDK installation - # then we should alter it to point to the GAE platform location. - if os.path.exists(os.path.join(sdk_path, 'platform/google_appengine')): - sdk_path = os.path.join(sdk_path, 'platform/google_appengine') - - # Make sure google.appengine.* modules are importable. - fixup_paths(sdk_path) - - # Make sure all bundled third-party packages are available. - import dev_appserver - dev_appserver.fix_sys_path() - - # Loading appengine_config from the current project ensures that any - # changes to configuration there are available to all tests (e.g. - # sys.path modifications, namespaces, etc.) - try: - import appengine_config - (appengine_config) - except ImportError: - print('Note: unable to import appengine_config.') - - # Discover and run tests. - suite = unittest.loader.TestLoader().discover(test_path, test_pattern) - print('Suite', suite) - return unittest.TextTestRunner(verbosity=2).run(suite) - - -if __name__ == '__main__': - parser = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawDescriptionHelpFormatter) - parser.add_argument( - '--sdk_path', - help='The path to the Google App Engine SDK or the Google Cloud SDK.', - default=None) - parser.add_argument( - '--test-path', - help='The path to look for tests, defaults to the current directory.', - default=os.getcwd()) - parser.add_argument( - '--test-pattern', - help='The file pattern for test modules, defaults to *_test.py.', - default='*_test.py') - - args = parser.parse_args() - - result = main(args.sdk_path, args.test_path, args.test_pattern) - - if not result.wasSuccessful(): - sys.exit(1) diff --git a/gae/testing/requirements-dev.txt b/gae/testing/requirements-dev.txt deleted file mode 100644 index ca0dee4..0000000 --- a/gae/testing/requirements-dev.txt +++ /dev/null @@ -1 +0,0 @@ -requests==2.9.1 diff --git a/gae/testing/test-endpoint.sh b/gae/testing/test-endpoint.sh deleted file mode 100755 index 66e5cfc..0000000 --- a/gae/testing/test-endpoint.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -curl -H "Content-Type: application/json" -X POST -d '{"devices": [{"serial": "123", "product": "myfish", "serial": "myserial", "status": 1}], "hostname": "hc1"}' https://vtslab-schedule-prod.appspot.com/_ah/api/host_info/v1/set - -curl -H "Content-Type: application/json" -X POST -d '{"manifest_branch": "master", "build_id": "a2131", "build_target": "sailfish", "build_type": "userdebug"}' https://vtslab-schedule-prod.appspot.com/_ah/api/build_info/v1/set - -curl -H "Content-Type: application/json" -X POST -d '{"device": [{"index": 1}], "host_name": "hc1"}' https://vtslab-schedule-prod.appspot.com/_ah/api/host_info/v1/set - -curl -H "Content-Type: application/json" -X POST -d '{"hostname": "vtslab-mtv43-2"}' https://vtslab-schedule-prod.appspot.com/_ah/api/job_queue/v1/get diff --git a/gae/testing/test_e2e.py b/gae/testing/test_e2e.py deleted file mode 100644 index 567c8ce..0000000 --- a/gae/testing/test_e2e.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import os -import requests - -URL = os.environ.get("URL") - - -def test_e2e(): - assert URL - print ("Running test against {}".format(URL)) - r = requests.get(URL) - assert b'VTS' in r.content - print("Success") - -if __name__ == "__main__": - test_e2e() diff --git a/gae/webapp/__init__.py b/gae/webapp/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/gae/webapp/__init__.py +++ /dev/null diff --git a/gae/webapp/src/__init__.py b/gae/webapp/src/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/gae/webapp/src/__init__.py +++ /dev/null diff --git a/gae/webapp/src/endpoint/__init__.py b/gae/webapp/src/endpoint/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/gae/webapp/src/endpoint/__init__.py +++ /dev/null diff --git a/gae/webapp/src/endpoint/build_info.py b/gae/webapp/src/endpoint/build_info.py deleted file mode 100644 index 0ba77bb..0000000 --- a/gae/webapp/src/endpoint/build_info.py +++ /dev/null @@ -1,95 +0,0 @@ -# Copyright 2017 Google Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Build Info APIs implemented using Google Cloud Endpoints.""" - -import datetime -import endpoints -import logging - -from webapp.src.endpoint import endpoint_base -from webapp.src.proto import model - -BUILD_INFO_RESOURCE = endpoints.ResourceContainer(model.BuildInfoMessage) - - -@endpoints.api(name="build", version="v1") -class BuildInfoApi(endpoint_base.EndpointBase): - """Endpoint API for build_info.""" - - @endpoints.method( - BUILD_INFO_RESOURCE, - model.DefaultResponse, - path="set", - http_method="POST", - name="set") - def set(self, request): - """Sets the build info based on the `request`.""" - build_query = model.BuildModel.query( - model.BuildModel.build_id == request.build_id, - model.BuildModel.build_target == request.build_target, - model.BuildModel.build_type == request.build_type, - model.BuildModel.artifact_type == request.artifact_type) - existing_builds = build_query.fetch() - - if existing_builds and len(existing_builds) > 1: - logging.warning( - "Duplicated builds found for [build_id]{} " - "[build_target]{} [build_type]{} [artifact_type]{}".format( - request.build_id, request.build_target, request.build_type, - request.artifact_type)) - - if existing_builds: - build = existing_builds[0] - if request.signed: - # only signed builds need to overwrite the exist entities. - build.signed = request.signed - else: - build = model.BuildModel() - common_attributes = self.GetCommonAttributes(request, - model.BuildModel) - for attr in common_attributes: - setattr(build, attr, getattr(request, attr)) - - build.timestamp = datetime.datetime.now() - build.put() - - return model.DefaultResponse( - return_code=model.ReturnCodeMessage.SUCCESS) - - @endpoints.method( - endpoint_base.GET_REQUEST_RESOURCE, - model.BuildResponseMessage, - path="get", - http_method="POST", - name="get") - def get(self, request): - """Gets the builds from datastore.""" - return_list, more = self.Get(request=request, - metaclass=model.BuildModel, - message=model.BuildInfoMessage) - return model.BuildResponseMessage(builds=return_list, has_next=more) - - @endpoints.method( - endpoint_base.COUNT_REQUEST_RESOURCE, - model.CountResponseMessage, - path="count", - http_method="POST", - name="count") - def count(self, request): - """Gets total number of BuildModel entities stored in datastore.""" - filters = self.CreateFilterList( - filter_string=request.filter, metaclass=model.BuildModel) - count = self.Count(metaclass=model.BuildModel, filters=filters) - - return model.CountResponseMessage(count=count) diff --git a/gae/webapp/src/endpoint/build_info_test.py b/gae/webapp/src/endpoint/build_info_test.py deleted file mode 100644 index 8b70831..0000000 --- a/gae/webapp/src/endpoint/build_info_test.py +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import unittest - -try: - from unittest import mock -except ImportError: - import mock - -from webapp.src.endpoint import build_info -from webapp.src.proto import model -from webapp.src.testing import unittest_base - - -class BuildInfoTest(unittest_base.UnitTestBase): - """A class to test build_info endpoint API.""" - - def setUp(self): - """Initializes test""" - super(BuildInfoTest, self).setUp() - - def testSetNewBuildModel(self): - """Asserts build_info/set API receives a new build.""" - builds = model.BuildModel.query().fetch() - self.assertEqual(len(builds), 0) - container = ( - build_info.BUILD_INFO_RESOURCE.combined_message_class( - manifest_branch=self.GetRandomString(), - build_id=self.GetRandomString(), - build_target=self.GetRandomString(), - build_type=self.GetRandomString(), - artifact_type=self.GetRandomString(), - )) - api = build_info.BuildInfoApi() - response = api.set(container) - - self.assertEqual(response.return_code, model.ReturnCodeMessage.SUCCESS) - builds = model.BuildModel.query().fetch() - self.assertEqual(len(builds), 1) - - def testSetDuplicatedBuildModel(self): - """Asserts build_info/set API receives a duplicated build.""" - manifest_branch = self.GetRandomString() - build_id = self.GetRandomString() - build_target = self.GetRandomString() - build_type = self.GetRandomString() - artifact_type = self.GetRandomString() - - builds = model.BuildModel.query().fetch() - self.assertEqual(len(builds), 0) - container = ( - build_info.BUILD_INFO_RESOURCE.combined_message_class( - manifest_branch=manifest_branch, - build_id=build_id, - build_target=build_target, - build_type=build_type, - artifact_type=artifact_type, - )) - api = build_info.BuildInfoApi() - response = api.set(container) - - self.assertEqual(response.return_code, model.ReturnCodeMessage.SUCCESS) - builds = model.BuildModel.query().fetch() - self.assertEqual(len(builds), 1) - - container = ( - build_info.BUILD_INFO_RESOURCE.combined_message_class( - manifest_branch=manifest_branch, - build_id=build_id, - build_target=build_target, - build_type=build_type, - artifact_type=artifact_type, - )) - api = build_info.BuildInfoApi() - response = api.set(container) - self.assertEqual(response.return_code, model.ReturnCodeMessage.SUCCESS) - builds = model.BuildModel.query().fetch() - self.assertEqual(len(builds), 1) - - def testUpdateSignedBuildModel(self): - """Asserts build_info/set API receives a duplicated build.""" - manifest_branch = self.GetRandomString() - build_id = self.GetRandomString() - build_target = self.GetRandomString() - build_type = self.GetRandomString() - artifact_type = self.GetRandomString() - - builds = model.BuildModel.query().fetch() - self.assertEqual(len(builds), 0) - container = ( - build_info.BUILD_INFO_RESOURCE.combined_message_class( - manifest_branch=manifest_branch, - build_id=build_id, - build_target=build_target, - build_type=build_type, - artifact_type=artifact_type, - signed=False, - )) - api = build_info.BuildInfoApi() - response = api.set(container) - - self.assertEqual(response.return_code, model.ReturnCodeMessage.SUCCESS) - builds = model.BuildModel.query().fetch() - self.assertEqual(len(builds), 1) - - container = ( - build_info.BUILD_INFO_RESOURCE.combined_message_class( - manifest_branch=manifest_branch, - build_id=build_id, - build_target=build_target, - build_type=build_type, - artifact_type=artifact_type, - signed=True - )) - api = build_info.BuildInfoApi() - response = api.set(container) - self.assertEqual(response.return_code, model.ReturnCodeMessage.SUCCESS) - builds = model.BuildModel.query().fetch() - self.assertEqual(len(builds), 1) - self.assertEqual(builds[0].signed, True) - - -if __name__ == "__main__": - unittest.main() diff --git a/gae/webapp/src/endpoint/endpoint_base.py b/gae/webapp/src/endpoint/endpoint_base.py deleted file mode 100644 index d0dddd5..0000000 --- a/gae/webapp/src/endpoint/endpoint_base.py +++ /dev/null @@ -1,330 +0,0 @@ -# Copyright 2018 Google Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import datetime -import inspect -import logging -import json - -import endpoints -from protorpc import messages -from protorpc import remote -from google.appengine.ext import ndb - -from webapp.src import vtslab_status as Status -from webapp.src.proto import model - -MAX_QUERY_SIZE = 1000 - -COUNT_REQUEST_RESOURCE = endpoints.ResourceContainer(model.CountRequestMessage) -GET_REQUEST_RESOURCE = endpoints.ResourceContainer(model.GetRequestMessage) - - -class EndpointBase(remote.Service): - """A base class for endpoint implementation.""" - - def GetCommonAttributes(self, resource, reference): - """Gets a list of common attribute names. - - This method finds the attributes assigned in 'resource' instance, and - filters out if the attributes are not a member of 'reference' class. - - Args: - resource: either a protorpc.messages.Message instance, - or a ndb.Model instance. - reference: either a protorpc.messages.Message class, - or a ndb.Model class. - - Returns: - a list of string, attribute names exist on resource and reference. - - Raises: - ValueError if resource or reference is not supported class. - """ - # check resource type and absorb list of assigned attributes. - resource_attrs = self.GetAttributes(resource, assigned_only=True) - reference_attrs = self.GetAttributes(reference) - return [x for x in resource_attrs if x in reference_attrs] - - def GetAttributes(self, value, assigned_only=False): - """Gets a list of attributes. - - Args: - value: a class instance or a class itself. - assigned_only: True to get only assigned attributes when value is - an instance, False to get all attributes. - - Raises: - ValueError if value is not supported class. - """ - attrs = [] - if inspect.isclass(value): - if assigned_only: - logging.warning( - "Please use a class instance for 'resource' argument.") - - if (issubclass(value, messages.Message) - or issubclass(value, ndb.Model)): - attrs = [ - x[0] for x in value.__dict__.items() - if not x[0].startswith("_") - ] - else: - raise ValueError("Only protorpc.messages.Message or ndb.Model " - "class are supported.") - else: - if isinstance(value, messages.Message): - attrs = [ - x.name for x in value.all_fields() - if not assigned_only or ( - value.get_assigned_value(x.name) not in [None, []]) - ] - elif isinstance(value, ndb.Model): - attrs = [ - x for x in list(value.to_dict()) - if not assigned_only or ( - getattr(value, x, None) not in [None, []]) - ] - else: - raise ValueError("Only protorpc.messages.Message or ndb.Model " - "class are supported.") - - return attrs - - def Count(self, metaclass, filters=None): - """Counts entities from datastore with options. - - Args: - metaclass: a metaclass for ndb model. - filters: a list of tuples. Each tuple consists of three values: - key, method, and value. - - Returns: - a number of entities. - """ - query, _ = self.CreateQueryFilter(metaclass=metaclass, filters=filters) - return query.count() - - def Fetch(self, - metaclass, - size, - offset=0, - filters=None, - sort_key="", - direction="asc"): - """Fetches entities from datastore with options. - - Args: - metaclass: a metaclass for ndb model. - size: an integer, max number of entities to fetch at once. - offset: an integer, number of query results to skip. - filters: a list of filter tuple, a form of (key: string, - method: integer, value: string). - sort_key: a string, key name to sort by. - direction: a string, "asc" for ascending order and "desc" for - descending order. - - Returns: - a list of fetched entities. - a boolean, True if there is next page or False if not. - """ - query, empty_repeated_field = self.CreateQueryFilter( - metaclass=metaclass, filters=filters) - sorted_query = self.SortQuery( - query=query, - metaclass=metaclass, - sort_key=sort_key, - direction=direction) - - if size: - entities, _, more = sorted_query.fetch_page( - page_size=size, offset=offset) - else: - entities = sorted_query.fetch() - more = False - - if empty_repeated_field: - entities = [ - x for x in entities - if all([not getattr(x, attr) for attr in empty_repeated_field]) - ] - - return entities, more - - def CreateQueryFilter(self, metaclass, filters): - """Creates a query with the given filters. - - Args: - metaclass: a metaclass for ndb model. - filters: a list of tuples. Each tuple consists of three values: - key, method, and value. - - Returns: - a filtered query for the given metaclass. - a list of strings that failed to create the query due to its empty - value for the repeated property. - """ - empty_repeated_field = [] - query = metaclass.query() - if not filters: - return query, empty_repeated_field - - for _filter in filters: - property_key = _filter["key"] - method = _filter["method"] - value = _filter["value"] - if type(value) is str or type(value) is unicode: - if isinstance(metaclass._properties[property_key], - ndb.BooleanProperty): - value = value.lower() in ("yes", "true", "1") - elif isinstance(metaclass._properties[property_key], - ndb.IntegerProperty): - value = int(value) - if metaclass._properties[property_key]._repeated: - if value: - value = [value] - if method == Status.FILTER_METHOD[Status.FILTER_Has]: - query = query.filter( - getattr(metaclass, property_key).IN(value)) - else: - logging.warning( - "You cannot compare repeated " - "properties except 'IN(has)' operation.") - else: - logging.debug("Empty repeated list cannot be queried.") - empty_repeated_field.append(value) - elif isinstance(metaclass._properties[property_key], - ndb.DateTimeProperty): - if method == Status.FILTER_METHOD[Status.FILTER_LessThan]: - query = query.filter( - getattr(metaclass, property_key) < datetime.datetime. - now() - datetime.timedelta(hours=int(value))) - elif method == Status.FILTER_METHOD[Status.FILTER_GreaterThan]: - query = query.filter( - getattr(metaclass, property_key) > datetime.datetime. - now() - datetime.timedelta(hours=int(value))) - else: - logging.debug("DateTimeProperty only allows <=(less than) " - "and >=(greater than) operation.") - else: - if method == Status.FILTER_METHOD[Status.FILTER_EqualTo]: - query = query.filter( - getattr(metaclass, property_key) == value) - elif method == Status.FILTER_METHOD[Status.FILTER_LessThan]: - query = query.filter( - getattr(metaclass, property_key) < value) - elif method == Status.FILTER_METHOD[Status.FILTER_GreaterThan]: - query = query.filter( - getattr(metaclass, property_key) > value) - elif method == Status.FILTER_METHOD[ - Status.FILTER_LessThanOrEqualTo]: - query = query.filter( - getattr(metaclass, property_key) <= value) - elif method == Status.FILTER_METHOD[ - Status.FILTER_GreaterThanOrEqualTo]: - query = query.filter( - getattr(metaclass, property_key) >= value) - elif method == Status.FILTER_METHOD[Status.FILTER_NotEqualTo]: - query = query.filter( - getattr(metaclass, property_key) != value).order( - getattr(metaclass, property_key), metaclass.key) - elif method == Status.FILTER_METHOD[Status.FILTER_Has]: - query = query.filter( - getattr(metaclass, property_key).IN(value)).order( - getattr(metaclass, property_key), metaclass.key) - else: - logging.warning( - "{} is not supported filter method.".format(method)) - return query, empty_repeated_field - - def SortQuery(self, query, metaclass, sort_key, direction): - """Sorts the given query with sort_key and direction. - - Args: - query: a ndb query to sort. - metaclass: a metaclass for ndb model. - sort_key: a string, key name to sort by. - direction: a string, "asc" for ascending order and "desc" for - descending order. - """ - if sort_key: - if direction == "desc": - query = query.order(-getattr(metaclass, sort_key)) - else: - query = query.order(getattr(metaclass, sort_key)) - - return query - - def CreateFilterList(self, filter_string, metaclass): - """Creates a list of filters. - - Args: - filter_string: a string, stringified JSON which contains 'key', - 'method', 'value' to build filter information. - metaclass: a metaclass for ndb model. - - Returns: - a list of tuples where each tuple consists of three values: - key, method, and value. - """ - model_properties = self.GetAttributes(metaclass) - filters = [] - if filter_string: - filters = json.loads(filter_string) - for _filter in filters: - if _filter["key"] not in model_properties: - filters.remove(_filter) - return filters - - def Get(self, request, metaclass, message): - """Handles a request through /get endpoints API to retrieves entities. - - Args: - request: a request body message received through /get API. - metaclass: a metaclass for ndb model. This method will fetch the - 'metaclass' type of model from datastore. - message: a Protocol RPC message class. Fetched entities will be - converted to this message class instances. - - Returns: - a list of fetched entities. - a boolean, True if there is next page or False if not. - """ - size = request.size if request.size else MAX_QUERY_SIZE - offset = request.offset if request.offset else 0 - - filters = self.CreateFilterList( - filter_string=request.filter, metaclass=metaclass) - - entities, more = self.Fetch( - metaclass=metaclass, - size=size, - filters=filters, - offset=offset, - sort_key=request.sort, - direction=request.direction, - ) - - return_list = [] - for entity in entities: - entity_dict = {} - assigned_attributes = self.GetCommonAttributes( - resource=entity, reference=message) - for attr in assigned_attributes: - entity_dict[attr] = getattr(entity, attr, None) - if hasattr(message, "urlsafe_key"): - entity_dict["urlsafe_key"] = entity.key.urlsafe() - return_list.append(entity_dict) - - return return_list, more diff --git a/gae/webapp/src/endpoint/endpoint_base_test.py b/gae/webapp/src/endpoint/endpoint_base_test.py deleted file mode 100644 index 2eb397a..0000000 --- a/gae/webapp/src/endpoint/endpoint_base_test.py +++ /dev/null @@ -1,256 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import endpoints -import json -import unittest - -try: - from unittest import mock -except ImportError: - import mock - -from webapp.src import vtslab_status as Status -from webapp.src.endpoint import endpoint_base -from webapp.src.proto import model -from webapp.src.testing import unittest_base - - -class EndpointBaseTest(unittest_base.UnitTestBase): - """A class to test endpoint_base.EndpointBase class. - - Attributes: - eb: An EndpointBase class instance. - """ - - def setUp(self): - """Initializes test""" - super(EndpointBaseTest, self).setUp() - self.eb = endpoint_base.EndpointBase() - - def testGetAssignedMessagesAttributes(self): - attrs = ["hostname", "priority", "test_branch"] - job_message = model.JobMessage() - for attr in attrs: - setattr(job_message, attr, attr) - result = self.eb.GetAttributes(job_message, assigned_only=True) - self.assertEqual(set(attrs), set(result)) - - def testGetAssignedModelAttributes(self): - attrs = ["hostname", "priority", "test_branch"] - job = model.JobModel() - for attr in attrs: - setattr(job, attr, attr) - result = self.eb.GetAttributes(job, assigned_only=True) - self.assertEqual(set(attrs), set(result)) - - def testGetAllMessagesAttributes(self): - attrs = ["hostname", "priority", "test_branch"] - full_attrs = [ - "test_type", "hostname", "priority", "test_name", - "require_signed_device_build", "has_bootloader_img", - "has_radio_img", "device", "serial", "build_storage_type", - "manifest_branch", "build_target", "build_id", "pab_account_id", - "shards", "param", "status", "period", "gsi_storage_type", - "gsi_branch", "gsi_build_target", "gsi_build_id", - "gsi_pab_account_id", "gsi_vendor_version", "test_storage_type", - "test_branch", "test_build_target", "test_build_id", - "test_pab_account_id", "retry_count", "infra_log_url", - "image_package_repo_base", "report_bucket", - "report_spreadsheet_id", "report_persistent_url", - "report_reference_url" - ] - job_message = model.JobMessage() - for attr in attrs: - setattr(job_message, attr, attr) - result = self.eb.GetAttributes(job_message, assigned_only=False) - self.assertTrue(set(full_attrs) <= set(result)) - - def testGetAllModelAttributes(self): - attrs = ["hostname", "priority", "test_branch"] - full_attrs = [ - "test_type", "hostname", "priority", "test_name", - "require_signed_device_build", "has_bootloader_img", - "has_radio_img", "device", "serial", "build_storage_type", - "manifest_branch", "build_target", "build_id", "pab_account_id", - "shards", "param", "status", "period", "gsi_storage_type", - "gsi_branch", "gsi_build_target", "gsi_build_id", - "gsi_pab_account_id", "gsi_vendor_version", "test_storage_type", - "test_branch", "test_build_target", "test_build_id", - "test_pab_account_id", "timestamp", "heartbeat_stamp", - "retry_count", "infra_log_url", "parent_schedule", - "image_package_repo_base", "report_bucket", - "report_spreadsheet_id", "report_persistent_url", - "report_reference_url" - ] - job = model.JobModel() - for attr in attrs: - setattr(job, attr, attr) - result = self.eb.GetAttributes(job, assigned_only=False) - self.assertTrue(set(full_attrs) <= set(result)) - - def testGetSingleEntity(self): - """Asserts to get a single entity.""" - device = self.GenerateDeviceModel() - device.put() - - request_body = (endpoints.ResourceContainer( - model.GetRequestMessage).combined_message_class( - size=0, - offset=0, - filter="", - sort="", - direction="", - )) - result, more = self.eb.Get( - request=request_body, - metaclass=model.DeviceModel, - message=model.DeviceInfoMessage) - self.assertEqual(len(result), 1) - self.assertFalse(more) - - def testGetHundredEntities(self): - """Asserts to get hundred entities.""" - for _ in xrange(100): - device = self.GenerateDeviceModel() - device.put() - - request_body = (endpoints.ResourceContainer( - model.GetRequestMessage).combined_message_class( - size=0, - offset=0, - filter="", - sort="", - direction="", - )) - result, more = self.eb.Get( - request=request_body, - metaclass=model.DeviceModel, - message=model.DeviceInfoMessage) - self.assertEqual(len(result), 100) - self.assertFalse(more) - - def testGetEntitiesWithPagination(self): - """Asserts to get entities with pagination.""" - for _ in xrange(100): - device = self.GenerateDeviceModel() - device.put() - - request_body = (endpoints.ResourceContainer( - model.GetRequestMessage).combined_message_class( - size=60, - offset=0, - filter="", - sort="", - direction="", - )) - result, more = self.eb.Get( - request=request_body, - metaclass=model.DeviceModel, - message=model.DeviceInfoMessage) - self.assertEqual(len(result), 60) - self.assertTrue(more) - - request_body = (endpoints.ResourceContainer( - model.GetRequestMessage).combined_message_class( - size=100, - offset=60, - filter="", - sort="", - direction="", - )) - result, more = self.eb.Get( - request=request_body, - metaclass=model.DeviceModel, - message=model.DeviceInfoMessage) - self.assertEqual(len(result), 40) - self.assertFalse(more) - - def testGetWithFilter(self): - """Asserts to get entities with filter.""" - for _ in xrange(50): - device = self.GenerateDeviceModel() - device.put() - - for _ in xrange(50): - device = self.GenerateDeviceModel(product="product") - device.put() - - filter = [{ - "key": "product", - "method": Status.FILTER_METHOD[Status.FILTER_EqualTo], - "value": "product" - }] - filter_string = json.dumps(filter) - request_body = (endpoints.ResourceContainer( - model.GetRequestMessage).combined_message_class( - size=0, - offset=0, - filter=filter_string, - sort="", - direction="", - )) - result, more = self.eb.Get( - request=request_body, - metaclass=model.DeviceModel, - message=model.DeviceInfoMessage) - self.assertEqual(len(result), 50) - self.assertFalse(more) - - def testGetWithSort(self): - """Asserts to get entities with sort.""" - for _ in xrange(100): - device = self.GenerateDeviceModel() - device.put() - - request_body = (endpoints.ResourceContainer( - model.GetRequestMessage).combined_message_class( - size=0, - offset=0, - filter="", - sort="serial", - direction="asc", - )) - - result, more = self.eb.Get( - request=request_body, - metaclass=model.DeviceModel, - message=model.DeviceInfoMessage) - self.assertEqual(len(result), 100) - for i in xrange(len(result) - 1): - self.assertTrue(result[i]["serial"] < result[i + 1]["serial"]) - - request_body = (endpoints.ResourceContainer( - model.GetRequestMessage).combined_message_class( - size=0, - offset=0, - filter="", - sort="serial", - direction="desc", - )) - - result, more = self.eb.Get( - request=request_body, - metaclass=model.DeviceModel, - message=model.DeviceInfoMessage) - self.assertEqual(len(result), 100) - for i in xrange(len(result) - 1): - self.assertTrue(result[i]["serial"] > result[i + 1]["serial"]) - - -if __name__ == "__main__": - unittest.main() diff --git a/gae/webapp/src/endpoint/host_info.py b/gae/webapp/src/endpoint/host_info.py deleted file mode 100644 index ac89a11..0000000 --- a/gae/webapp/src/endpoint/host_info.py +++ /dev/null @@ -1,134 +0,0 @@ -# Copyright 2017 Google Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Host Info APIs implemented using Google Cloud Endpoints.""" - -import datetime -import endpoints -import logging - -from google.appengine.api import users -from google.appengine.ext import ndb - -from webapp.src import vtslab_status as Status -from webapp.src.endpoint import endpoint_base -from webapp.src.proto import model - -HOST_INFO_RESOURCE = endpoints.ResourceContainer(model.HostInfoMessage) - -# Product type name for null device. -_NULL_DEVICE_PRODUCT_TYPE = "null" - - -def AddNullDevices(hostname, null_device_count): - """Adds null devices to DeviceModel data store. - - Args: - hostname: string, the host name. - null_device_count: integer, the number of null devices. - """ - device_query = model.DeviceModel.query( - model.DeviceModel.hostname == hostname, - model.DeviceModel.product == _NULL_DEVICE_PRODUCT_TYPE - ) - null_devices = device_query.fetch() - existing_null_device_count = len(null_devices) - - if existing_null_device_count < null_device_count: - devices_to_put = [] - for _ in range(null_device_count - existing_null_device_count): - device = model.DeviceModel() - device.hostname = hostname - device.serial = "n/a" - device.product = _NULL_DEVICE_PRODUCT_TYPE - device.status = Status.DEVICE_STATUS_DICT["ready"] - device.scheduling_status = Status.DEVICE_SCHEDULING_STATUS_DICT[ - "free"] - device.timestamp = datetime.datetime.now() - devices_to_put.append(device) - if devices_to_put: - ndb.put_multi(devices_to_put) - - -@endpoints.api(name='host', version='v1') -class HostInfoApi(endpoint_base.EndpointBase): - """Endpoint API for host_info.""" - - @endpoints.method( - HOST_INFO_RESOURCE, - model.DefaultResponse, - path='set', - http_method='POST', - name='set') - def set(self, request): - """Sets the host info based on the `request`.""" - if users.get_current_user(): - username = users.get_current_user().email() - else: - username = "anonymous" - - devices_to_put = [] - for request_device in request.devices: - device_query = model.DeviceModel.query( - model.DeviceModel.serial == request_device.serial - ) - existing_device = device_query.fetch() - if existing_device: - device = existing_device[0] - else: - device = model.DeviceModel() - device.serial = request_device.serial - device.scheduling_status = Status.DEVICE_SCHEDULING_STATUS_DICT[ - "free"] - if not device.product or request_device.product != "error": - device.product = request_device.product - - device.username = username - device.hostname = request.hostname - device.status = request_device.status - device.timestamp = datetime.datetime.now() - devices_to_put.append(device) - if devices_to_put: - ndb.put_multi(devices_to_put) - - return model.DefaultResponse( - return_code=model.ReturnCodeMessage.SUCCESS) - - @endpoints.method( - endpoint_base.GET_REQUEST_RESOURCE, - model.DeviceResponseMessage, - path="get", - http_method="POST", - name="get") - def get(self, request): - """Gets the devices from datastore.""" - return_list, more = self.Get(request=request, - metaclass=model.DeviceModel, - message=model.DeviceInfoMessage) - - return model.DeviceResponseMessage(devices=return_list, has_next=more) - - @endpoints.method( - endpoint_base.COUNT_REQUEST_RESOURCE, - model.CountResponseMessage, - path="count", - http_method="POST", - name="count") - def count(self, request): - """Gets total number of DeviceModel entities stored in datastore.""" - filters = self.CreateFilterList( - filter_string=request.filter, metaclass=model.DeviceModel) - - count = self.Count(metaclass=model.DeviceModel, filters=filters) - - return model.CountResponseMessage(count=count) diff --git a/gae/webapp/src/endpoint/host_info_test.py b/gae/webapp/src/endpoint/host_info_test.py deleted file mode 100644 index e41037c..0000000 --- a/gae/webapp/src/endpoint/host_info_test.py +++ /dev/null @@ -1,93 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import unittest - -try: - from unittest import mock -except ImportError: - import mock - -from webapp.src import vtslab_status as Status -from webapp.src.endpoint import host_info -from webapp.src.proto import model -from webapp.src.testing import unittest_base - - -class HostInfoTest(unittest_base.UnitTestBase): - """A class to test host_info endpoint API.""" - - def setUp(self): - """Initializes test""" - super(HostInfoTest, self).setUp() - - - def testUpdateExistingDevice(self): - """Asserts that device update does not create a duplicate.""" - hostname = self.GetRandomString() - serial = self.GetRandomString() - product = self.GetRandomString() - error_device = { - "serial": serial, - "product": "error", - } - container = ( - host_info.HOST_INFO_RESOURCE.combined_message_class( - hostname=hostname, - devices=[error_device], - )) - - api = host_info.HostInfoApi() - api.set(container) - - devices = model.DeviceModel.query().fetch() - self.assertEqual(len(devices), 1) - - # name "error" is allowed as initial name. - self.assertEqual(devices[0].product, "error") - - correct_device = { - "serial": serial, - "product": product, - } - container = ( - host_info.HOST_INFO_RESOURCE.combined_message_class( - hostname=hostname, - devices=[correct_device], - )) - api.set(container) - - devices = model.DeviceModel.query().fetch() - self.assertEqual(len(devices), 1) - # correct product name (which is not "error") should be overwritten. - self.assertEqual(devices[0].product, product) - - container = ( - host_info.HOST_INFO_RESOURCE.combined_message_class( - hostname=hostname, - devices=[error_device], - )) - api.set(container) - - devices = model.DeviceModel.query().fetch() - self.assertEqual(len(devices), 1) - # "error" should be ignored. - self.assertEqual(devices[0].product, product) - - -if __name__ == "__main__": - unittest.main() diff --git a/gae/webapp/src/endpoint/job_queue.py b/gae/webapp/src/endpoint/job_queue.py deleted file mode 100644 index 7416f8c..0000000 --- a/gae/webapp/src/endpoint/job_queue.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright 2017 Google Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Job Queue Info APIs implemented using Google Cloud Endpoints.""" - -import datetime -import endpoints -import logging -import re - -from webapp.src import vtslab_status as Status -from webapp.src.endpoint import endpoint_base -from webapp.src.proto import model -from webapp.src.utils import email_util -from webapp.src.utils import model_util - -from google.appengine.ext import ndb - -JOB_QUEUE_RESOURCE = endpoints.ResourceContainer(model.JobMessage) -GCS_URL_PREFIX = "gs://" -HTTP_HTTPS_REGEX = "^https?://" -STORAGE_API_URL = "https://storage.cloud.google.com/" - - -@endpoints.api(name='job', version='v1') -class JobQueueApi(endpoint_base.EndpointBase): - """Endpoint API for job_queue.""" - - @endpoints.method( - JOB_QUEUE_RESOURCE, - model.JobLeaseResponse, - path='lease', - http_method='POST', - name='lease') - def lease(self, request): - """Gets the job(s) based on the condition specified in `request`.""" - job_query = model.JobModel.query( - model.JobModel.hostname == request.hostname, - model.JobModel.status == Status.JOB_STATUS_DICT["ready"]) - existing_jobs = job_query.fetch() - - priority_sorted_jobs = sorted( - existing_jobs, - key=lambda x: (Status.GetPriorityValue(x.priority), x.timestamp)) - - if priority_sorted_jobs: - job = priority_sorted_jobs[0] - job.status = Status.JOB_STATUS_DICT["leased"] - job.put() - - job_message = model.JobMessage() - common_attributes = self.GetCommonAttributes(job, model.JobMessage) - for attr in common_attributes: - setattr(job_message, attr, getattr(job, attr)) - - device_query = model.DeviceModel.query( - model.DeviceModel.serial.IN(job.serial)) - devices = device_query.fetch() - devices_to_put = [] - for device in devices: - device.scheduling_status = Status.DEVICE_SCHEDULING_STATUS_DICT[ - "use"] - devices_to_put.append(device) - if devices_to_put: - ndb.put_multi(devices_to_put) - - return model.JobLeaseResponse( - return_code=model.ReturnCodeMessage.SUCCESS, - jobs=[job_message]) - else: - return model.JobLeaseResponse( - return_code=model.ReturnCodeMessage.FAIL, jobs=[]) - - @endpoints.method( - JOB_QUEUE_RESOURCE, - model.JobLeaseResponse, - path='heartbeat', - http_method='POST', - name='heartbeat') - def heartbeat(self, request): - """Processes the heartbeat signal from HC which leased queued job(s).""" - # minify jobs by query and confirm with serial from fetched jobs - job_query = model.JobModel.query( - model.JobModel.hostname == request.hostname, - model.JobModel.manifest_branch == request.manifest_branch, - model.JobModel.build_target == request.build_target, - model.JobModel.test_name == request.test_name, - model.JobModel.status == Status.JOB_STATUS_DICT["leased"]) - existing_jobs = job_query.fetch() - same_jobs = [ - x for x in existing_jobs if set(x.serial) == set(request.serial) - ] - - if len(same_jobs) > 1: - logging.warning("[heartbeat] more than one job is found!") - logging.warning( - "[heartbeat] <hostname>{} <manifest_branch>{} " - "<build_target>{} <test_name>{} <serials>{}".format( - request.hostname, request.manifest_branch, - request.build_target, request.test_name, request.serial)) - - if same_jobs: - job = same_jobs[0] - job_message = model.JobMessage() - common_attributes = self.GetCommonAttributes(job, model.JobMessage) - for attr in common_attributes: - setattr(job_message, attr, getattr(job, attr)) - - device_query = model.DeviceModel.query( - model.DeviceModel.serial.IN(job.serial)) - devices = device_query.fetch() - logging.debug("[heartbeat] heartbeat job: hostname={}, " - "test_name={}, job creation time={}".format( - job.hostname, job.test_name, job.timestamp)) - logging.debug("[heartbeat] request status: {}".format( - request.status)) - logging.debug("[heartbeat] - devices = {}".format( - ", ".join([device.serial for device in devices]))) - devices_to_put = [] - if request.status == Status.JOB_STATUS_DICT["complete"]: - job.status = request.status - for device in devices: - device.scheduling_status = ( - Status.DEVICE_SCHEDULING_STATUS_DICT["free"]) - devices_to_put.append(device) - elif (request.status in [ - Status.JOB_STATUS_DICT["infra-err"], - Status.JOB_STATUS_DICT["bootup-err"] - ]): - job.status = request.status - email_util.send_job_notification(job) - for device in devices: - device.scheduling_status = ( - Status.DEVICE_SCHEDULING_STATUS_DICT["free"]) - device.status = Status.DEVICE_STATUS_DICT["unknown"] - devices_to_put.append(device) - elif request.status == Status.JOB_STATUS_DICT["leased"]: - job.status = request.status - for device in devices: - device.timestamp = datetime.datetime.now() - devices_to_put.append(device) - else: - logging.error( - "[heartbeat] Unexpected job status is received. - {}". - format(request.serial)) - if devices_to_put: - ndb.put_multi(devices_to_put) - - if request.infra_log_url: - if request.infra_log_url.startswith(GCS_URL_PREFIX): - url = "{}{}".format( - STORAGE_API_URL, - request.infra_log_url[len(GCS_URL_PREFIX):]) - job.infra_log_url = url - elif re.match(HTTP_HTTPS_REGEX, request.infra_log_url): - job.infra_log_url = request.infra_log_url - else: - logging.debug("[heartbeat] Wrong infra_log_url address.") - - job.heartbeat_stamp = datetime.datetime.now() - job.put() - model_util.UpdateParentSchedule(job, request.status) - return model.JobLeaseResponse( - return_code=model.ReturnCodeMessage.SUCCESS, - jobs=[job_message]) - - return model.JobLeaseResponse( - return_code=model.ReturnCodeMessage.FAIL, jobs=[]) - - @endpoints.method( - endpoint_base.GET_REQUEST_RESOURCE, - model.JobResponseMessage, - path="get", - http_method="POST", - name="get") - def get(self, request): - """Gets the jobs from datastore.""" - return_list, more = self.Get(request=request, - metaclass=model.JobModel, - message=model.JobMessage) - - return model.JobResponseMessage(jobs=return_list, has_next=more) - - @endpoints.method( - endpoint_base.COUNT_REQUEST_RESOURCE, - model.CountResponseMessage, - path="count", - http_method="POST", - name="count") - def count(self, request): - """Gets total number of JobModel entities stored in datastore.""" - filters = self.CreateFilterList( - filter_string=request.filter, metaclass=model.JobModel) - count = self.Count(metaclass=model.JobModel, filters=filters) - - return model.CountResponseMessage(count=count) diff --git a/gae/webapp/src/endpoint/job_queue_test.py b/gae/webapp/src/endpoint/job_queue_test.py deleted file mode 100644 index 140c4f4..0000000 --- a/gae/webapp/src/endpoint/job_queue_test.py +++ /dev/null @@ -1,158 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import datetime -import unittest - -try: - from unittest import mock -except ImportError: - import mock - -from webapp.src import vtslab_status as Status -from webapp.src.endpoint import job_queue -from webapp.src.proto import model -from webapp.src.testing import unittest_base - - -class JobQueueTest(unittest_base.UnitTestBase): - """A class to test job_queue endpoint API.""" - - def setUp(self): - """Initializes test""" - super(JobQueueTest, self).setUp() - - def testGetJobModel(self): - """Asserts job_queue/get API receives a job lease request.""" - test_values = { - "test_type": Status.TEST_TYPE_DICT[Status.TEST_TYPE_TOT], - "hostname": self.GetRandomString(), - "priority": self.GetRandomString(), - "test_name": self.GetRandomString(), - "require_signed_device_build": False, - "has_bootloader_img": True, - "has_radio_img": False, - "device": self.GetRandomString(), - "serial": ["serial01", "serial02"], - "build_storage_type": Status.STORAGE_TYPE_DICT["GCS"], - "manifest_branch": self.GetRandomString(), - "build_target": self.GetRandomString(), - "build_id": self.GetRandomString(), - "pab_account_id": self.GetRandomString(), - "shards": 1, - "param": [""], - "status": Status.JOB_STATUS_DICT["ready"], - "period": 360, - "gsi_storage_type": Status.STORAGE_TYPE_DICT["GCS"], - "gsi_branch": self.GetRandomString(), - "gsi_build_target": self.GetRandomString(), - "gsi_build_id": self.GetRandomString(), - "gsi_pab_account_id": self.GetRandomString(), - "gsi_vendor_version": self.GetRandomString(), - "test_storage_type": Status.STORAGE_TYPE_DICT["GCS"], - "test_branch": self.GetRandomString(), - "test_build_target": self.GetRandomString(), - "test_build_id": self.GetRandomString(), - "test_pab_account_id": self.GetRandomString(), - "retry_count": 2, - "infra_log_url": self.GetRandomString(), - "image_package_repo_base": self.GetRandomString(), - "report_bucket": [self.GetRandomString()], - "report_spreadsheet_id": [self.GetRandomString()], - "report_persistent_url": [self.GetRandomString()], - "report_reference_url": [self.GetRandomString()], - } - - for serial in test_values["serial"]: - self.GenerateDeviceModel(serial=serial).put() - - job = model.JobModel() - for key in test_values: - setattr(job, key, test_values[key]) - job.timestamp = datetime.datetime.now() - job.put() - - container = (job_queue.JOB_QUEUE_RESOURCE.combined_message_class( - hostname=test_values["hostname"])) - api = job_queue.JobQueueApi() - response = api.lease(container) - - self.assertEqual(response.return_code, - model.ReturnCodeMessage.SUCCESS) - self.assertEqual(len(response.jobs), 1) - for key in test_values: - if key is "status": - self.assertEqual( - getattr(response.jobs[0], key), - Status.JOB_STATUS_DICT["leased"]) - else: - self.assertEqual( - getattr(response.jobs[0], key), test_values[key]) - - devices = model.DeviceModel.query().fetch() - for device in devices: - self.assertEqual(device.scheduling_status, - Status.DEVICE_SCHEDULING_STATUS_DICT["use"]) - - # test job heartbeat api - container = (job_queue.JOB_QUEUE_RESOURCE.combined_message_class( - hostname=response.jobs[0].hostname, - manifest_branch=response.jobs[0].manifest_branch, - build_target=response.jobs[0].build_target, - test_name=response.jobs[0].test_name, - serial=response.jobs[0].serial, - status=response.jobs[0].status, - )) - api = job_queue.JobQueueApi() - response = api.heartbeat(container) - self.assertEqual(response.return_code, - model.ReturnCodeMessage.SUCCESS) - - jobs = model.JobModel.query().fetch() - self.assertEqual(len(jobs), 1) - self.assertEqual(jobs[0].status, Status.JOB_STATUS_DICT["leased"]) - self.assertTrue(datetime.datetime.now() - jobs[0].heartbeat_stamp < - datetime.timedelta(seconds=1)) - - # test job heartbeat api to complete the job - container = (job_queue.JOB_QUEUE_RESOURCE.combined_message_class( - hostname=response.jobs[0].hostname, - manifest_branch=response.jobs[0].manifest_branch, - build_target=response.jobs[0].build_target, - test_name=response.jobs[0].test_name, - serial=response.jobs[0].serial, - status=Status.JOB_STATUS_DICT["complete"], - )) - api = job_queue.JobQueueApi() - response = api.heartbeat(container) - self.assertEqual(response.return_code, - model.ReturnCodeMessage.SUCCESS) - - jobs = model.JobModel.query().fetch() - self.assertEqual(len(jobs), 1) - self.assertEqual(jobs[0].status, Status.JOB_STATUS_DICT["complete"]) - self.assertTrue(datetime.datetime.now() - jobs[0].heartbeat_stamp < - datetime.timedelta(seconds=1)) - - devices = model.DeviceModel.query().fetch() - for device in devices: - self.assertEqual(device.scheduling_status, - Status.DEVICE_SCHEDULING_STATUS_DICT["free"]) - - -if __name__ == "__main__": - unittest.main() diff --git a/gae/webapp/src/endpoint/lab_info.py b/gae/webapp/src/endpoint/lab_info.py deleted file mode 100644 index d42148b..0000000 --- a/gae/webapp/src/endpoint/lab_info.py +++ /dev/null @@ -1,182 +0,0 @@ -# Copyright 2017 Google Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Lab Info APIs implemented using Google Cloud Endpoints.""" - -import datetime -import endpoints -import logging - -from google.appengine.ext import ndb - -from webapp.src import vtslab_status as Status -from webapp.src.endpoint import endpoint_base -from webapp.src.endpoint import host_info -from webapp.src.proto import model - -LAB_INFO_RESOURCE = endpoints.ResourceContainer(model.LabInfoMessage) -LAB_HOST_INFO_RESOURCE = endpoints.ResourceContainer(model.LabHostInfoMessage) - - -@endpoints.api(name='lab', version='v1') -class LabInfoApi(endpoint_base.EndpointBase): - """Endpoint API for lab_info.""" - - @endpoints.method( - LAB_INFO_RESOURCE, - model.DefaultResponse, - path="clear", - http_method="POST", - name="clear") - def clear(self, request): - """Clears lab info in DB.""" - lab_query = model.LabModel.query() - existing_labs = lab_query.fetch(keys_only=True) - if existing_labs and len(existing_labs) > 0: - ndb.delete_multi(existing_labs) - return model.DefaultResponse( - return_code=model.ReturnCodeMessage.SUCCESS) - - @endpoints.method( - LAB_INFO_RESOURCE, - model.DefaultResponse, - path="set", - http_method="POST", - name="set") - def set(self, request): - """Sets the lab info based on `request`.""" - if "host" in [x.name for x in request.all_fields()]: - labs_to_put = [] - for host in request.host: - duplicate_query = model.LabModel.query( - model.LabModel.name == request.name, - model.LabModel.owner == request.owner, - model.LabModel.hostname == host.hostname) - duplicates = duplicate_query.fetch() - if duplicates: - lab = duplicates[0] - else: - lab = model.LabModel() - lab.name = request.name - lab.owner = request.owner - lab.admin = request.admin - lab.hostname = host.hostname - lab.ip = host.ip - lab.script = host.script - - null_device_count = 0 - devices_to_put = [] - for config_device in host.device: - if config_device.product == "null": - null_device_count += 1 - continue - if config_device.serial and config_device.product: - device_query = model.DeviceModel.query( - model.DeviceModel.serial == config_device.serial) - devices = device_query.fetch() - if devices: - device = devices[0] - if (device.hostname != host.hostname) and ( - device.status != - Status.DEVICE_STATUS_DICT["no-response"]): - logging.error( - "{} is alive in another host.".format( - config_device.serial)) - # TODO: send an alert to lab.admin - continue - if device.hostname == host.hostname and set( - device.device_equipment) == set( - config_device.device_equipment): - # no need to update. - continue - else: - device = model.DeviceModel() - device.status = Status.DEVICE_STATUS_DICT[ - "no-response"] - device.product = config_device.product - device.serial = config_device.serial - device.hostname = host.hostname - device.scheduling_status = ( - Status.DEVICE_SCHEDULING_STATUS_DICT["free"]) - device.timestamp = datetime.datetime.now() - device.device_equipment = config_device.device_equipment - devices_to_put.append(device) - else: - logging.error("Lab config does not have device " - "information correctly; it should " - "specify device product and serial.") - if devices_to_put: - ndb.put_multi(devices_to_put) - - lab.timestamp = datetime.datetime.now() - labs_to_put.append(lab) - - if null_device_count > 0: - host_info.AddNullDevices(host.hostname, null_device_count) - - if labs_to_put: - ndb.put_multi(labs_to_put) - - return model.DefaultResponse( - return_code=model.ReturnCodeMessage.SUCCESS) - - @endpoints.method( - LAB_HOST_INFO_RESOURCE, - model.DefaultResponse, - path="set_version", - http_method="POST", - name="set_version") - def set_version(self, request): - """Sets vtslab version of the host <hostname>""" - lab_query = model.LabModel.query( - model.LabModel.hostname == request.hostname) - labs = lab_query.fetch() - - labs_to_put = [] - for lab in labs: - lab.vtslab_version = request.vtslab_version.split(":")[0] - labs_to_put.append(lab) - if labs_to_put: - ndb.put_multi(labs_to_put) - - return model.DefaultResponse( - return_code=model.ReturnCodeMessage.SUCCESS) - - @endpoints.method( - endpoint_base.GET_REQUEST_RESOURCE, - model.LabResponseMessage, - path="get", - http_method="POST", - name="get") - def get(self, request): - """Gets the labs from datastore.""" - return_list, more = self.Get(request=request, - metaclass=model.LabModel, - message=model.LabMessage) - - return model.LabResponseMessage(labs=return_list, has_next=more) - - @endpoints.method( - endpoint_base.COUNT_REQUEST_RESOURCE, - model.CountResponseMessage, - path="count", - http_method="POST", - name="count") - def count(self, request): - """Gets total number of BuildModel entities stored in datastore.""" - filters = self.CreateFilterList( - filter_string=request.filter, metaclass=model.LabModel) - - count = self.Count(metaclass=model.LabModel, filters=filters) - - return model.CountResponseMessage(count=count) diff --git a/gae/webapp/src/endpoint/lab_info_test.py b/gae/webapp/src/endpoint/lab_info_test.py deleted file mode 100644 index 7320c7b..0000000 --- a/gae/webapp/src/endpoint/lab_info_test.py +++ /dev/null @@ -1,137 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import unittest - -try: - from unittest import mock -except ImportError: - import mock - -from webapp.src.endpoint import lab_info -from webapp.src.proto import model -from webapp.src.testing import unittest_base - - -class LabInfoTest(unittest_base.UnitTestBase): - """A class to test lab_info endpoint API.""" - - def setUp(self): - """Initializes test""" - super(LabInfoTest, self).setUp() - - def testUpdateErrorDevice(self): - """Asserts that device update does not create a duplicate.""" - device_serial = self.GetRandomString() - product = self.GetRandomString() - device_equipment = [self.GetRandomString()] - device_info = { - "serial": device_serial, - "product": product, - "device_equipment": device_equipment - } - - hostname = self.GetRandomString() - host_info = { - "hostname": hostname, - "ip": self.GetRandomString(), - "script": self.GetRandomString(), - "device": [device_info], - "vtslab_version": self.GetRandomString(), - "host_equipment": [], - } - - lab_name = self.GetRandomString() - container = ( - lab_info.LAB_INFO_RESOURCE.combined_message_class( - name=lab_name, - owner=self.GetRandomString(), - admin=[self.GetRandomString()], - host=[host_info], - )) - - api = lab_info.LabInfoApi() - api.set(container) - - devices = model.DeviceModel.query().fetch() - self.assertEqual(len(devices), 1) - self.assertEqual(devices[0].product, product) - - # change device product name. - devices[0].product = "error" - devices[0].put() - - api.set(container) - - devices = model.DeviceModel.query().fetch() - # there should not be duplicates. - self.assertEqual(len(devices), 1) - # stored device name should be kept. - self.assertEqual(devices[0].product, "error") - - - def testUpdateExistingDevice(self): - """Asserts that device update does not create a duplicate.""" - device_serial = self.GetRandomString() - product = self.GetRandomString() - device_equipment = [self.GetRandomString()] - device_info = { - "serial": device_serial, - "product": product, - "device_equipment": device_equipment, - } - - hostname = self.GetRandomString() - host_info = { - "hostname": hostname, - "ip": self.GetRandomString(), - "script": self.GetRandomString(), - "device": [device_info], - "vtslab_version": self.GetRandomString(), - "host_equipment": [], - } - - lab_name = self.GetRandomString() - container = ( - lab_info.LAB_INFO_RESOURCE.combined_message_class( - name=lab_name, - owner=self.GetRandomString(), - admin=[self.GetRandomString()], - host=[host_info], - )) - - device = self.GenerateDeviceModel(product="error", - serial=device_serial, - hostname=hostname) - device.put() - - api = lab_info.LabInfoApi() - api.set(container) - - devices = model.DeviceModel.query().fetch() - self.assertEqual(len(devices), 1) - - # stored device name should be kept. - self.assertEqual(devices[0].product, "error") - - # device equipment should be updated. - self.assertEqual(set(devices[0].device_equipment), - set(device_equipment)) - - -if __name__ == "__main__": - unittest.main() diff --git a/gae/webapp/src/endpoint/schedule_info.py b/gae/webapp/src/endpoint/schedule_info.py deleted file mode 100644 index e353902..0000000 --- a/gae/webapp/src/endpoint/schedule_info.py +++ /dev/null @@ -1,218 +0,0 @@ -# Copyright 2017 Google Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Schedule Info APIs implemented using Google Cloud Endpoints.""" - -import datetime -import endpoints - -from google.appengine.ext import ndb - -from webapp.src import vtslab_status as Status -from webapp.src.endpoint import endpoint_base -from webapp.src.proto import model -from webapp.src.utils import email_util - -SCHEDULE_INFO_RESOURCE = endpoints.ResourceContainer(model.ScheduleInfoMessage) -SCHEDULE_SUSPEND_RESOURCE = endpoints.ResourceContainer( - model.ScheduleSuspendMessage) - - -@endpoints.api(name="schedule", version="v1") -class ScheduleInfoApi(endpoint_base.EndpointBase): - """Endpoint API for schedule_info.""" - - @endpoints.method( - SCHEDULE_INFO_RESOURCE, - model.DefaultResponse, - path="clear", - http_method="POST", - name="clear") - def clear(self, request): - """Clears test schedule info in DB.""" - schedule_query = model.ScheduleModel.query( - model.ScheduleModel.schedule_type != "green") - existing_schedules = schedule_query.fetch(keys_only=True) - if existing_schedules and len(existing_schedules) > 0: - ndb.delete_multi(existing_schedules) - return model.DefaultResponse( - return_code=model.ReturnCodeMessage.SUCCESS) - - @endpoints.method( - SCHEDULE_INFO_RESOURCE, - model.DefaultResponse, - path="set", - http_method="POST", - name="set") - def set(self, request): - """Sets the schedule info based on `request`.""" - exist_on_both = self.GetCommonAttributes(request, model.ScheduleModel) - # check duplicates - exclusions = [ - "name", "schedule_type", "schedule", "param", "timestamp", - "children_jobs", "error_count", "suspended" - ] - # list of protorpc message fields. - duplicate_checklist = [x for x in exist_on_both if x not in exclusions] - empty_list_field = [] - query = model.ScheduleModel.query() - for attr_name in duplicate_checklist: - if model.ScheduleModel._properties[attr_name]._repeated: - value = request.get_assigned_value(attr_name) - if value: - query = query.filter( - getattr(model.ScheduleModel, attr_name).IN( - request.get_assigned_value(attr_name))) - else: - # empty list cannot be queried. - empty_list_field.append(attr_name) - else: - query = query.filter( - getattr(model.ScheduleModel, attr_name) == - request.get_assigned_value(attr_name)) - duplicated_schedules = query.fetch() - - if empty_list_field: - duplicated_schedules = [ - schedule for schedule in duplicated_schedules - if all( - [not getattr(schedule, attr) for attr in empty_list_field]) - ] - - if duplicated_schedules: - schedule = duplicated_schedules[0] - else: - schedule = model.ScheduleModel() - for attr_name in exist_on_both: - setattr(schedule, attr_name, - request.get_assigned_value(attr_name)) - schedule.schedule_type = "test" - schedule.error_count = 0 - schedule.suspended = False - schedule.priority_value = Status.GetPriorityValue(schedule.priority) - - schedule.timestamp = datetime.datetime.now() - schedule.put() - - return model.DefaultResponse( - return_code=model.ReturnCodeMessage.SUCCESS) - - @endpoints.method( - endpoint_base.GET_REQUEST_RESOURCE, - model.ScheduleResponseMessage, - path="get", - http_method="POST", - name="get") - def get(self, request): - """Gets the schedules from datastore.""" - return_list, more = self.Get(request=request, - metaclass=model.ScheduleModel, - message=model.ScheduleInfoMessage) - - return model.ScheduleResponseMessage( - schedules=return_list, has_next=more) - - @endpoints.method( - endpoint_base.COUNT_REQUEST_RESOURCE, - model.CountResponseMessage, - path="count", - http_method="POST", - name="count") - def count(self, request): - """Gets total number of ScheduleModel entities stored in datastore.""" - filters = self.CreateFilterList( - filter_string=request.filter, metaclass=model.ScheduleModel) - - count = self.Count(metaclass=model.ScheduleModel, filters=filters) - - return model.CountResponseMessage(count=count) - - @endpoints.method( - SCHEDULE_SUSPEND_RESOURCE, - model.ScheduleSuspendMessage, - path="suspend", - http_method="POST", - name="suspend") - def suspend(self, request): - """Toggles a schedule from suspend to resume, or vice versa.""" - schedules_to_put = [] - schedules_to_return = [] - for schedule in request.schedules: - schedule_key = ndb.key.Key(urlsafe=schedule.urlsafe_key) - schedule_entity = schedule_key.get() - if schedule.suspend: # to suspend - schedule_entity.suspended = True - else: # to resume - schedule_entity.error_count = 0 - schedule_entity.suspended = False - schedules_to_put.append(schedule_entity) - schedules_to_return.append({"urlsafe_key": schedule.urlsafe_key, - "suspend": schedule_entity.suspended}) - # TODO(jongmok): Minimize a number of emails by merging schedules. - email_util.send_schedule_suspension_notification(schedule_entity) - - ndb.put_multi(schedules_to_put) - return model.ScheduleSuspendMessage(schedules=schedules_to_return) - - -@endpoints.api(name="green_schedule_info", version="v1") -class GreenScheduleInfoApi(endpoint_base.EndpointBase): - """Endpoint API for green_schedule_info.""" - - @endpoints.method( - SCHEDULE_INFO_RESOURCE, - model.DefaultResponse, - path="clear", - http_method="POST", - name="clear") - def clear(self, request): - """Clears green build schedule info in DB.""" - schedule_query = model.ScheduleModel.query( - model.ScheduleModel.schedule_type == "green") - existing_schedules = schedule_query.fetch(keys_only=True) - if existing_schedules and len(existing_schedules) > 0: - ndb.delete_multi(existing_schedules) - return model.DefaultResponse( - return_code=model.ReturnCodeMessage.SUCCESS) - - @endpoints.method( - SCHEDULE_INFO_RESOURCE, - model.DefaultResponse, - path="set", - http_method="POST", - name="set") - def set(self, request): - """Sets the green build schedule info based on `request`.""" - schedule = model.ScheduleModel() - schedule.name = request.name - schedule.manifest_branch = request.manifest_branch - schedule.build_target = request.build_target - schedule.device_pab_account_id = request.device_pab_account_id - schedule.test_name = request.test_name - schedule.schedule = request.schedule - schedule.priority = request.priority - schedule.device = request.device - schedule.shards = request.shards - schedule.gsi_branch = request.gsi_branch - schedule.gsi_build_target = request.gsi_build_target - schedule.gsi_pab_account_id = request.gsi_pab_account_id - schedule.gsi_vendor_version = request.gsi_vendor_version - schedule.test_branch = request.test_branch - schedule.test_build_target = request.test_build_target - schedule.test_pab_account_id = request.test_pab_account_id - schedule.timestamp = datetime.datetime.now() - schedule.schedule_type = "green" - schedule.put() - - return model.DefaultResponse( - return_code=model.ReturnCodeMessage.SUCCESS) diff --git a/gae/webapp/src/endpoint/schedule_info_test.py b/gae/webapp/src/endpoint/schedule_info_test.py deleted file mode 100644 index 61e69ae..0000000 --- a/gae/webapp/src/endpoint/schedule_info_test.py +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import unittest - -try: - from unittest import mock -except ImportError: - import mock - -from webapp.src import vtslab_status as Status -from webapp.src.endpoint import schedule_info -from webapp.src.proto import model -from webapp.src.testing import unittest_base - - -class ScheduleInfoTest(unittest_base.UnitTestBase): - """A class to test schedule_info endpoint API.""" - - def setUp(self): - """Initializes test""" - super(ScheduleInfoTest, self).setUp() - - def testSetWithSimpleMessage(self): - """Asserts schedule_info/set API receives a simple message.""" - # As of June 8, 2018, these are uploaded from host controller. - container = ( - schedule_info.SCHEDULE_INFO_RESOURCE.combined_message_class( - manifest_branch=self.GetRandomString(), - build_storage_type=Status.STORAGE_TYPE_DICT["PAB"], - build_target=self.GetRandomString(), - require_signed_device_build=False, - has_bootloader_img=True, - has_radio_img=True, - test_name=self.GetRandomString(), - period=360, - priority="high", - device=[self.GetRandomString()], - required_host_equipment=[self.GetRandomString()], - required_device_equipment=[self.GetRandomString()], - device_pab_account_id=self.GetRandomString(), - shards=1, - param=[self.GetRandomString()], - retry_count=1, - gsi_storage_type=Status.STORAGE_TYPE_DICT["PAB"], - gsi_branch=self.GetRandomString(), - gsi_build_target=self.GetRandomString(), - gsi_pab_account_id=self.GetRandomString(), - gsi_vendor_version=self.GetRandomString(), - test_storage_type=Status.STORAGE_TYPE_DICT["PAB"], - test_branch=self.GetRandomString(), - test_build_target=self.GetRandomString(), - test_pab_account_id=self.GetRandomString(), - image_package_repo_base=self.GetRandomString(), - report_bucket=[self.GetRandomString()], - report_spreadsheet_id=[self.GetRandomString()], - report_persistent_url=[self.GetRandomString()], - report_reference_url=[self.GetRandomString()], - )) - api = schedule_info.ScheduleInfoApi() - response = api.set(container) - - self.assertEqual(response.return_code, model.ReturnCodeMessage.SUCCESS) - - def testSetWithEmptyRepeatedField(self): - """Asserts schedule_info/set API receives a message. - - This test sets required_host_equipment to empty and sends to endpoint - method. - """ - # As of June 8, 2018, these are uploaded from host controller. - container = ( - schedule_info.SCHEDULE_INFO_RESOURCE.combined_message_class( - manifest_branch=self.GetRandomString(), - build_storage_type=Status.STORAGE_TYPE_DICT["PAB"], - build_target=self.GetRandomString(), - require_signed_device_build=False, - has_bootloader_img=True, - has_radio_img=True, - test_name=self.GetRandomString(), - period=360, - priority="high", - device=[self.GetRandomString()], - required_host_equipment=[self.GetRandomString()], - required_device_equipment=[self.GetRandomString()], - device_pab_account_id=self.GetRandomString(), - shards=1, - param=[self.GetRandomString()], - retry_count=1, - gsi_storage_type=Status.STORAGE_TYPE_DICT["PAB"], - gsi_branch=self.GetRandomString(), - gsi_build_target=self.GetRandomString(), - gsi_pab_account_id=self.GetRandomString(), - gsi_vendor_version=self.GetRandomString(), - test_storage_type=Status.STORAGE_TYPE_DICT["PAB"], - test_branch=self.GetRandomString(), - test_build_target=self.GetRandomString(), - test_pab_account_id=self.GetRandomString(), - image_package_repo_base=self.GetRandomString(), - report_bucket=[], - report_spreadsheet_id=[], - report_persistent_url=[], - report_reference_url=[], - )) - api = schedule_info.ScheduleInfoApi() - response = api.set(container) - - self.assertEqual(response.return_code, model.ReturnCodeMessage.SUCCESS) - - -if __name__ == "__main__": - unittest.main() diff --git a/gae/webapp/src/endpoint_main.py b/gae/webapp/src/endpoint_main.py deleted file mode 100644 index 090d6d6..0000000 --- a/gae/webapp/src/endpoint_main.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import endpoints - -from webapp.src.endpoint import build_info -from webapp.src.endpoint import host_info -from webapp.src.endpoint import job_queue -from webapp.src.endpoint import lab_info -from webapp.src.endpoint import schedule_info - -api = endpoints.api_server([ - build_info.BuildInfoApi, - host_info.HostInfoApi, - lab_info.LabInfoApi, - schedule_info.ScheduleInfoApi, - schedule_info.GreenScheduleInfoApi, - job_queue.JobQueueApi]) diff --git a/gae/webapp/src/handlers/__init__.py b/gae/webapp/src/handlers/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/gae/webapp/src/handlers/__init__.py +++ /dev/null diff --git a/gae/webapp/src/handlers/base.py b/gae/webapp/src/handlers/base.py deleted file mode 100644 index 2862ecb..0000000 --- a/gae/webapp/src/handlers/base.py +++ /dev/null @@ -1,215 +0,0 @@ -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import datetime -import httplib -import logging -import os -import urlparse - -from google.appengine.api import users -import stripe -import webapp2 -from webapp2_extras import jinja2 as wa2_jinja2 -from webapp2_extras import sessions - -import errors -from webapp.src.utils import datetime_util - - -class BaseHandler(webapp2.RequestHandler): - """BaseHandler for all requests.""" - - def initialize(self, request, response): - """Initializes this request handler.""" - webapp2.RequestHandler.initialize(self, request, response) - self.session_backend = 'datastore' - - def verify_origin(self): - """This function will check the request is comming from the same domain.""" - server_host = os.environ.get('ENDPOINTS_SERVICE_NAME') - request_host = self.request.headers.get('Host') - request_referer = self.request.headers.get('Referer') - if request_referer: - request_referer = urlparse.urlsplit(request_referer)[1] - else: - request_referer = request_host - logging.info('server: %s, request: %s', server_host, request_referer) - if server_host and request_referer and server_host != request_referer: - raise errors.Error(httplib.FORBIDDEN) - - def dispatch(self): - """Dispatch the request. - - This will first check if there's a handler_method defined - in the matched route, and if not it'll use the method correspondent to - the request method (get(), post() etc). - """ - self.session_store = sessions.get_store(request=self.request) - # Forwards the method for RESTful support. - self.forward_method() - # Security headers. - # https://www.owasp.org/index.php/List_of_useful_HTTP_headers - self.response.headers['x-content-type-options'] = 'nosniff' - self.response.headers['x-frame-options'] = 'SAMEORIGIN' - self.response.headers['x-xss-protection'] = '1; mode=block' - try: - webapp2.RequestHandler.dispatch(self) - finally: - self.session_store.save_sessions(self.response) - # Disabled for now because host is appspot.com in production. - #self.verify_origin() - - @webapp2.cached_property - def session(self): - # Returns a session using the default cookie key. - return self.session_store.get_session() - - def handle_exception(self, exception, debug=False): - """Render the exception as HTML.""" - logging.exception(exception) - - # Create response dictionary and status defaults. - tpl = 'error.html' - status = httplib.INTERNAL_SERVER_ERROR - resp_dict = { - 'message': 'A server error occurred.', - } - url_parts = self.urlsplit() - redirect_url = '%s?%s' % (url_parts[2], url_parts[4]) - - # Use error code if a HTTPException, or generic 500. - if isinstance(exception, webapp2.HTTPException): - status = exception.code - resp_dict['message'] = exception.detail - elif isinstance(exception, errors.FormValidationError): - status = exception.code - resp_dict['message'] = exception.msg - resp_dict['errors'] = exception.errors - self.session['form_errors'] = exception.errors - # Redirect user to current view URL. - return self.redirect(redirect_url) - elif isinstance(exception, stripe.StripeError): - status = exception.http_status - resp_dict['errors'] = exception.json_body['error']['message'] - self.session['form_errors'] = [ - exception.json_body['error']['message'] - ] - return self.redirect(redirect_url) - elif isinstance(exception, (errors.Error, errors.AclError)): - status = exception.code - resp_dict['message'] = exception.msg - - resp_dict['status'] = status - - # Render output. - self.response.status_int = status - self.response.status_message = httplib.responses[status] - # Render the exception response into the error template. - self.response.write(self.jinja2.render_template(tpl, **resp_dict)) - - # @Override - def get(self, *args, **kwargs): - self.abort(httplib.NOT_IMPLEMENTED) - - # @Override - def post(self, *args, **kwargs): - self.abort(httplib.NOT_IMPLEMENTED) - - # @Override - def put(self, *args, **kwargs): - self.abort(httplib.NOT_IMPLEMENTED) - - # @Override - def delete(self, *args, **kwargs): - self.abort(httplib.NOT_IMPLEMENTED) - - # @Override - def head(self, *args, **kwargs): - pass - - def urlsplit(self): - """Return a tuple of the URL.""" - return urlparse.urlsplit(self.request.url) - - def path(self): - """Returns the path of the current URL.""" - return self.urlsplit()[2] - - def forward_method(self): - """Check for a method override param and change in the request.""" - valid = (None, 'get', 'post', 'put', 'delete', 'head', 'options') - method = self.request.POST.get('__method__') - if not method: # Backbone's _method parameter. - method = self.request.POST.get('_method') - if method not in valid: - logging.debug('Invalid method %s requested!', method) - method = None - logging.debug('Method being changed from %s to %s by request', - self.request.route.handler_method, method) - self.request.route.handler_method = method - - def render(self, resp, status=httplib.OK): - """Render the response as HTML.""" - user = users.get_current_user() - if user: - url = users.create_logout_url(self.request.uri) - url_linktext = "Logout" - else: - url = users.create_login_url(self.request.uri) - url_linktext = "Login" - - resp.update({ - # Defaults go here. - 'now': datetime.datetime.now(), - 'dest_url': str(self.request.get('dest_url', '')), - 'form_errors': self.session.pop('form_errors', []), - 'user': user, - 'url': url, - 'url_linktext': url_linktext, - "convert_time": datetime_util.GetTimeWithTimezone - }) - - if 'preload' not in resp: - resp['preload'] = {} - - self.response.status_int = status - self.response.status_message = httplib.responses[status] - self.response.write(self.jinja2.render_template(self.template, **resp)) - - @webapp2.cached_property - def jinja2(self): - """Returns a Jinja2 renderer cached in the app registry.""" - jinja_config = { - 'template_path': - os.path.join(os.path.dirname(__file__), "../../static"), - 'compiled_path': - None, - 'force_compiled': - False, - 'environment_args': { - 'autoescape': True, - 'extensions': [ - 'jinja2.ext.autoescape', - 'jinja2.ext.with_', - ], - }, - 'globals': - None, - 'filters': - None, - } - return wa2_jinja2.Jinja2(app=self.app, config=jinja_config) diff --git a/gae/webapp/src/handlers/errors.py b/gae/webapp/src/handlers/errors.py deleted file mode 100644 index fde95c0..0000000 --- a/gae/webapp/src/handlers/errors.py +++ /dev/null @@ -1,80 +0,0 @@ -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import httplib - - -class Error(Exception): - """Base Exception for handlers. - - Attributes: - code: HTTP 1.1 status code - msg: the W3C names for HTTP 1.1 status code - """ - - def __init__(self, code=None, msg=None): - self.code = code or httplib.INTERNAL_SERVER_ERROR - self.msg = msg or httplib.responses[self.code] - super(Error, self).__init__(self.code, self.msg) - - def __str__(self): - return repr([self.code, self.msg]) - - -class FormValidationError(Error): - """Form Validtion Exception for handlers.""" - - def __init__(self, code=None, msg=None, errors=None): - self.code = code or httplib.BAD_REQUEST - self.msg = msg or httplib.responses[self.code] - self.errors = errors or [] - super(FormValidationError, self).__init__(self.code, self.msg) - - def __str__(self): - return repr([self.code, self.msg, self.errors]) - - -class AclError(Error): - """Base ACL error.""" - - def __init__(self, code): - self.code = code - self.msg = httplib.responses[code] - super(AclError, self).__init__(self.code, self.msg) - - def __str__(self): - return repr([self.code, self.msg]) - - -class NotFoundError(AclError): - """The item being access does not exist.""" - - def __init__(self): - super(NotFoundError, self).__init__(httplib.NOT_FOUND) - - -class UnauthorizedError(AclError): - """The current user is not authenticated.""" - - def __init__(self): - super(UnauthorizedError, self).__init__(httplib.UNAUTHORIZED) - - -class ForbiddenError(AclError): - """The current user does not have proper permission.""" - - def __init__(self): - super(ForbiddenError, self).__init__(httplib.FORBIDDEN) diff --git a/gae/webapp/src/proto/__init__.py b/gae/webapp/src/proto/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/gae/webapp/src/proto/__init__.py +++ /dev/null diff --git a/gae/webapp/src/proto/model.py b/gae/webapp/src/proto/model.py deleted file mode 100644 index 1b24154..0000000 --- a/gae/webapp/src/proto/model.py +++ /dev/null @@ -1,449 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -from google.appengine.ext import ndb - -from protorpc import messages -from protorpc import message_types - - -class BuildModel(ndb.Model): - """A model for representing an individual build entry.""" - manifest_branch = ndb.StringProperty() - build_id = ndb.StringProperty() - build_target = ndb.StringProperty() - build_type = ndb.StringProperty() - artifact_type = ndb.StringProperty() - artifacts = ndb.StringProperty(repeated=True) - timestamp = ndb.DateTimeProperty(auto_now=False) - signed = ndb.BooleanProperty() - - -class BuildInfoMessage(messages.Message): - """A message for representing an individual build entry.""" - manifest_branch = messages.StringField(1) - build_id = messages.StringField(2) - build_target = messages.StringField(3) - build_type = messages.StringField(4) - artifact_type = messages.StringField(5) - artifacts = messages.StringField(6, repeated=True) - signed = messages.BooleanField(7) - timestamp = message_types.DateTimeField(8) - - -class ScheduleControlModel(ndb.Model): - """A model for representing a schedule control data entry.""" - enabled = ndb.BooleanProperty() - # "global" or empty string to enable/disable all schedules. - schedule_name = ndb.StringProperty() - - -class ScheduleModel(ndb.Model): - """A model for representing an individual schedule entry.""" - # schedule name for green build schedule, optional. - name = ndb.StringProperty() - schedule_type = ndb.StringProperty() - - # device image information - build_storage_type = ndb.IntegerProperty() - manifest_branch = ndb.StringProperty() - build_target = ndb.StringProperty() # type:name - device_pab_account_id = ndb.StringProperty() - require_signed_device_build = ndb.BooleanProperty() - has_bootloader_img = ndb.BooleanProperty(default=True) - has_radio_img = ndb.BooleanProperty(default=True) - - # GSI information - gsi_storage_type = ndb.IntegerProperty() - gsi_branch = ndb.StringProperty() - gsi_build_target = ndb.StringProperty() - gsi_pab_account_id = ndb.StringProperty() - gsi_vendor_version = ndb.StringProperty() - - # test suite information - test_storage_type = ndb.IntegerProperty() - test_branch = ndb.StringProperty() - test_build_target = ndb.StringProperty() - test_pab_account_id = ndb.StringProperty() - - test_name = ndb.StringProperty() - period = ndb.IntegerProperty() - schedule = ndb.StringProperty() - priority = ndb.StringProperty() - priority_value = ndb.IntegerProperty() - device = ndb.StringProperty(repeated=True) - shards = ndb.IntegerProperty() - param = ndb.StringProperty(repeated=True) - timestamp = ndb.DateTimeProperty(auto_now=False) - retry_count = ndb.IntegerProperty() - - children_jobs = ndb.KeyProperty(kind="JobModel", repeated=True) - error_count = ndb.IntegerProperty() - suspended = ndb.BooleanProperty() - image_package_repo_base = ndb.StringProperty() - - required_host_equipment = ndb.StringProperty(repeated=True) - required_device_equipment = ndb.StringProperty(repeated=True) - - report_bucket = ndb.StringProperty(repeated=True) - report_spreadsheet_id = ndb.StringProperty(repeated=True) - report_persistent_url = ndb.StringProperty(repeated=True) - report_reference_url = ndb.StringProperty(repeated=True) - - owner = ndb.StringProperty(repeated=True) - - -class ScheduleControlInfoMessage(messages.Message): - """A message for representing a schedule control data entry.""" - enabled = messages.BooleanField(1) - schedule_name = messages.StringField(2) - - -class ScheduleInfoMessage(messages.Message): - """A message for representing an individual schedule entry.""" - # Next ID = 39 - # schedule name for green build schedule, optional. - name = messages.StringField(16) - schedule_type = messages.StringField(19) - - # device image information - build_storage_type = messages.IntegerField(21) - manifest_branch = messages.StringField(1) - build_target = messages.StringField(2) - device_pab_account_id = messages.StringField(17) - require_signed_device_build = messages.BooleanField(20) - has_bootloader_img = messages.BooleanField(27) - has_radio_img = messages.BooleanField(28) - - # GSI information - gsi_storage_type = messages.IntegerField(22) - gsi_branch = messages.StringField(9) - gsi_build_target = messages.StringField(10) - gsi_pab_account_id = messages.StringField(11) - gsi_vendor_version = messages.StringField(24) - - # test suite information - test_storage_type = messages.IntegerField(23) - test_branch = messages.StringField(12) - test_build_target = messages.StringField(13) - test_pab_account_id = messages.StringField(14) - - test_name = messages.StringField(3) - period = messages.IntegerField(4) - schedule = messages.StringField(18) - priority = messages.StringField(5) - device = messages.StringField(6, repeated=True) - shards = messages.IntegerField(7) - param = messages.StringField(8, repeated=True) - retry_count = messages.IntegerField(15) - - required_host_equipment = messages.StringField(25, repeated=True) - required_device_equipment = messages.StringField(26, repeated=True) - - report_bucket = messages.StringField(29, repeated=True) - report_spreadsheet_id = messages.StringField(30, repeated=True) - report_persistent_url = messages.StringField(32, repeated=True) - report_reference_url = messages.StringField(33, repeated=True) - - image_package_repo_base = messages.StringField(31) - timestamp = message_types.DateTimeField(34) - owner = messages.StringField(35, repeated=True) - - suspended = messages.BooleanField(36) - urlsafe_key = messages.StringField(37) - error_count = messages.IntegerField(38) - - -class LabModel(ndb.Model): - """A model for representing an individual lab entry.""" - name = ndb.StringProperty() - owner = ndb.StringProperty() - admin = ndb.StringProperty(repeated=True) - hostname = ndb.StringProperty() - ip = ndb.StringProperty() - # devices is a comma-separated list of serial=product pairs - devices = ndb.StringProperty() - timestamp = ndb.DateTimeProperty(auto_now=False) - vtslab_version = ndb.StringProperty() - host_equipment = ndb.StringProperty(repeated=True) - - -class LabDeviceInfoMessage(messages.Message): - """A message for representing an individual lab host's device entry.""" - serial = messages.StringField(1, repeated=False) - product = messages.StringField(2, repeated=False) - device_equipment = messages.StringField(3, repeated=True) - - -class LabHostInfoMessage(messages.Message): - """A message for representing an individual lab's host entry.""" - hostname = messages.StringField(1, repeated=False) - ip = messages.StringField(2, repeated=False) - script = messages.StringField(3) - device = messages.MessageField(LabDeviceInfoMessage, 4, repeated=True) - vtslab_version = messages.StringField(5) - host_equipment = messages.StringField(6, repeated=True) - - -class LabInfoMessage(messages.Message): - """A message for representing an individual lab entry.""" - name = messages.StringField(1) - owner = messages.StringField(2) - admin = messages.StringField(4, repeated=True) - host = messages.MessageField(LabHostInfoMessage, 3, repeated=True) - - -class LabMessage(messages.Message): - """A model for representing a LabModel entity.""" - name = messages.StringField(1) - owner = messages.StringField(2) - admin = messages.StringField(3, repeated=True) - hostname = messages.StringField(4) - ip = messages.StringField(5) - devices = messages.StringField(6) - vtslab_version = messages.StringField(7) - host_equipment = messages.StringField(8, repeated=True) - - -class DeviceModel(ndb.Model): - """A model for representing an individual device entry.""" - hostname = ndb.StringProperty() - product = ndb.StringProperty() - serial = ndb.StringProperty() - status = ndb.IntegerProperty() - scheduling_status = ndb.IntegerProperty() - timestamp = ndb.DateTimeProperty(auto_now=False) - device_equipment = ndb.StringProperty(repeated=True) - - -class DeviceInfoMessage(messages.Message): - """A message for representing an individual host's device entry.""" - serial = messages.StringField(1) - product = messages.StringField(2) - status = messages.IntegerField(3) - scheduling_status = messages.IntegerField(4) - hostname = messages.StringField(5) - device_equipment = messages.StringField(6, repeated=True) - timestamp = message_types.DateTimeField(7) - - -class HostInfoMessage(messages.Message): - """A message for representing an individual host entry.""" - hostname = messages.StringField(1) - devices = messages.MessageField(DeviceInfoMessage, 2, repeated=True) - - -class JobModel(ndb.Model): - """A model for representing an individual job entry.""" - test_type = ndb.IntegerProperty() - - hostname = ndb.StringProperty() - priority = ndb.StringProperty() - test_name = ndb.StringProperty() - require_signed_device_build = ndb.BooleanProperty() - has_bootloader_img = ndb.BooleanProperty() - has_radio_img = ndb.BooleanProperty() - device = ndb.StringProperty() - serial = ndb.StringProperty(repeated=True) - - # device image information - build_storage_type = ndb.IntegerProperty() - manifest_branch = ndb.StringProperty() - build_target = ndb.StringProperty() - build_id = ndb.StringProperty() - pab_account_id = ndb.StringProperty() - - shards = ndb.IntegerProperty() - param = ndb.StringProperty(repeated=True) - status = ndb.IntegerProperty() - period = ndb.IntegerProperty() - - # GSI information - gsi_storage_type = ndb.IntegerProperty() - gsi_branch = ndb.StringProperty() - gsi_build_target = ndb.StringProperty() - gsi_build_id = ndb.StringProperty() - gsi_pab_account_id = ndb.StringProperty() - gsi_vendor_version = ndb.StringProperty() - - # test suite information - test_storage_type = ndb.IntegerProperty() - test_branch = ndb.StringProperty() - test_build_target = ndb.StringProperty() - test_build_id = ndb.StringProperty() - test_pab_account_id = ndb.StringProperty() - - timestamp = ndb.DateTimeProperty(auto_now=False) - heartbeat_stamp = ndb.DateTimeProperty(auto_now=False) - retry_count = ndb.IntegerProperty() - - infra_log_url = ndb.StringProperty() - - parent_schedule = ndb.KeyProperty(kind="ScheduleModel") - - image_package_repo_base = ndb.StringProperty() - - report_bucket = ndb.StringProperty(repeated=True) - report_spreadsheet_id = ndb.StringProperty(repeated=True) - report_persistent_url = ndb.StringProperty(repeated=True) - report_reference_url = ndb.StringProperty(repeated=True) - - -class JobMessage(messages.Message): - """A message for representing an individual job entry.""" - # Next ID = 39 - test_type = messages.IntegerField(29) - - hostname = messages.StringField(1) - priority = messages.StringField(2) - test_name = messages.StringField(3) - require_signed_device_build = messages.BooleanField(23) - has_bootloader_img = messages.BooleanField(31) - has_radio_img = messages.BooleanField(32) - device = messages.StringField(4) - serial = messages.StringField(5, repeated=True) - - # device image information - build_storage_type = messages.IntegerField(25) - manifest_branch = messages.StringField(6) - build_target = messages.StringField(7) - build_id = messages.StringField(10) - pab_account_id = messages.StringField(20) - - shards = messages.IntegerField(8) - param = messages.StringField(9, repeated=True) - status = messages.IntegerField(11) - period = messages.IntegerField(12) - - # GSI information - gsi_storage_type = messages.IntegerField(26) - gsi_branch = messages.StringField(13) - gsi_build_target = messages.StringField(14) - gsi_build_id = messages.StringField(21) - gsi_pab_account_id = messages.StringField(15) - gsi_vendor_version = messages.StringField(28) - - # test suite information - test_storage_type = messages.IntegerField(27) - test_branch = messages.StringField(16) - test_build_target = messages.StringField(17) - test_build_id = messages.StringField(22) - test_pab_account_id = messages.StringField(18) - - retry_count = messages.IntegerField(19) - - infra_log_url = messages.StringField(24) - - image_package_repo_base = messages.StringField(30) - - report_bucket = messages.StringField(33, repeated=True) - report_spreadsheet_id = messages.StringField(34, repeated=True) - report_persistent_url = messages.StringField(35, repeated=True) - report_reference_url = messages.StringField(36, repeated=True) - - timestamp = message_types.DateTimeField(37) - heartbeat_stamp = message_types.DateTimeField(38) - - -class ReturnCodeMessage(messages.Enum): - """Enum for default return code.""" - SUCCESS = 0 - FAIL = 1 - - -class DefaultResponse(messages.Message): - """A default response proto message.""" - return_code = messages.EnumField(ReturnCodeMessage, 1) - - -class JobLeaseResponse(messages.Message): - """A job lease response proto message.""" - return_code = messages.EnumField(ReturnCodeMessage, 1) - jobs = messages.MessageField(JobMessage, 2, repeated=True) - - -class KeyValueModel(ndb.Model): - """A simple key-value model. - - This class uses name as key and store one value or more than one values - to store values which require continuous monitoring such as counters, - or flags. - """ - name = ndb.StringProperty() - string_value = ndb.StringProperty() - integer_value = ndb.IntegerProperty() - boolean_value = ndb.BooleanProperty() - - -class GetRequestMessage(messages.Message): - """A message to request entities through /get endpoints.""" - size = messages.IntegerField(1) - offset = messages.IntegerField(2) - filter = messages.StringField(3) - sort = messages.StringField(4) - direction = messages.StringField(5) - - -class BuildResponseMessage(messages.Message): - """A message containing build entities to respond to /get endpoints.""" - builds = messages.MessageField(BuildInfoMessage, 1, repeated=True) - has_next = messages.BooleanField(2) - - -class DeviceResponseMessage(messages.Message): - """A message containing device entities to respond to /get endpoints.""" - devices = messages.MessageField(DeviceInfoMessage, 1, repeated=True) - has_next = messages.BooleanField(2) - - -class JobResponseMessage(messages.Message): - """A message containing job entities to respond to /get endpoints.""" - jobs = messages.MessageField(JobMessage, 1, repeated=True) - has_next = messages.BooleanField(2) - - -class LabResponseMessage(messages.Message): - """A message containing lab entities to respond to /get endpoints.""" - labs = messages.MessageField(LabMessage, 1, repeated=True) - has_next = messages.BooleanField(2) - - -class ScheduleResponseMessage(messages.Message): - """A message containing schedule entities to respond to /get endpoints.""" - schedules = messages.MessageField(ScheduleInfoMessage, 1, repeated=True) - has_next = messages.BooleanField(2) - - -class CountRequestMessage(messages.Message): - """A message to request a count of entities through /count endpoints.""" - filter = messages.StringField(1) - - -class CountResponseMessage(messages.Message): - """A message of a count of entities to respond to /count endpoints.""" - count = messages.IntegerField(1) - - -class ScheduleSuspendMessage(messages.Message): - """A response message to schedule endpoint API's /suspend method.""" - - class SingleScheduleSuspendMessage(messages.Message): - urlsafe_key = messages.StringField(1) - suspend = messages.BooleanField(2) - - schedules = messages.MessageField( - SingleScheduleSuspendMessage, 1, repeated=True) diff --git a/gae/webapp/src/scheduler/__init__.py b/gae/webapp/src/scheduler/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/gae/webapp/src/scheduler/__init__.py +++ /dev/null diff --git a/gae/webapp/src/scheduler/device_heartbeat.py b/gae/webapp/src/scheduler/device_heartbeat.py deleted file mode 100644 index 730fc5f..0000000 --- a/gae/webapp/src/scheduler/device_heartbeat.py +++ /dev/null @@ -1,93 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import datetime -import logging -import webapp2 - -from google.appengine.ext import ndb - -from webapp.src import vtslab_status as Status -from webapp.src.proto import model -from webapp.src.utils import email_util -from webapp.src.utils import logger - -DEVICE_RESPONSE_TIMEOUT_SECONDS = 300 - - -class PeriodicDeviceHeartBeat(webapp2.RequestHandler): - """Main class for /tasks/device_heartbeat. - - Used to find lost devices and change their status properly. - - Attributes: - logger: Logger class - """ - logger = logger.Logger() - - def get(self): - """Generates an HTML page based on the task schedules kept in DB.""" - self.logger.Clear() - - device_query = model.DeviceModel.query( - model.DeviceModel.status != - Status.DEVICE_STATUS_DICT["no-response"]) - devices = device_query.fetch() - lost_devices = [ - x for x in devices - if (datetime.datetime.now() - x.timestamp - ).seconds >= DEVICE_RESPONSE_TIMEOUT_SECONDS - ] - devices_to_put = [] - labs_to_alert = {} - for device in lost_devices: - self.logger.Println("Device[{}] is not responding.".format( - device.serial)) - device.status = Status.DEVICE_STATUS_DICT["no-response"] - devices_to_put.append(device) - - # sending notification - lab_query = model.LabModel.query( - model.LabModel.hostname == device.hostname) - labs = lab_query.fetch() - if labs: - lab = labs[0] - if lab.name not in labs_to_alert: - labs_to_alert[lab.name] = {} - labs_to_alert[lab.name]["_recipients"] = [] - if device.hostname not in labs_to_alert[lab.name]: - labs_to_alert[lab.name][device.hostname] = [] - if lab.owner not in labs_to_alert[lab.name]["_recipients"]: - labs_to_alert[lab.name]["_recipients"].append(lab.owner) - labs_to_alert[lab.name]["_recipients"].extend([ - x for x in lab.admin - if x not in labs_to_alert[lab.name]["_recipients"] - ]) - labs_to_alert[lab.name][device.hostname].append(device.serial) - else: - logging.warning( - "Could not find a lab model for hostname {}".format( - device.hostname)) - continue - - if devices_to_put: - ndb.put_multi(devices_to_put) - if labs_to_alert: - email_util.send_device_notification(labs_to_alert) - - self.response.write( - "<pre>\n" + "\n".join(self.logger.Get()) + "\n</pre>") diff --git a/gae/webapp/src/scheduler/job_heartbeat.py b/gae/webapp/src/scheduler/job_heartbeat.py deleted file mode 100644 index af8994c..0000000 --- a/gae/webapp/src/scheduler/job_heartbeat.py +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import datetime -import logging -import webapp2 - -from google.appengine.ext import ndb - -from webapp.src import vtslab_status as Status -from webapp.src.proto import model -from webapp.src.utils import email_util -from webapp.src.utils import logger -from webapp.src.utils import model_util - -JOB_RESPONSE_TIMEOUT_SECONDS = 60 * 60 - - -class PeriodicJobHeartBeat(webapp2.RequestHandler): - """Main class for /tasks/job_heartbeat. - - Used to find lost jobs and change their status properly. - - Attributes: - logger: Logger class - """ - logger = logger.Logger() - - def get(self): - """Generates an HTML page based on the jobs kept in DB.""" - self.logger.Clear() - - job_query = model.JobModel.query( - model.JobModel.status == Status.JOB_STATUS_DICT["leased"]) - jobs = job_query.fetch() - - lost_jobs = [] - for job in jobs: - if job.heartbeat_stamp: - job_timestamp = job.heartbeat_stamp - else: - job_timestamp = job.timestamp - if (datetime.datetime.now() - - job_timestamp).seconds >= JOB_RESPONSE_TIMEOUT_SECONDS: - lost_jobs.append(job) - - lost_jobs_to_put = [] - devices_to_put = [] - for job in lost_jobs: - self.logger.Println("Lost job found") - self.logger.Println("[hostname]{} [device]{} [test_name]{}".format( - job.hostname, job.device, job.test_name)) - job.status = Status.JOB_STATUS_DICT["infra-err"] - lost_jobs_to_put.append(job) - model_util.UpdateParentSchedule( - job, Status.JOB_STATUS_DICT["infra-err"]) - - device_query = model.DeviceModel.query( - model.DeviceModel.serial.IN(job.serial)) - devices = device_query.fetch() - for device in devices: - self.logger.Println("Device serial: {}".format(device.serial)) - device.scheduling_status = Status.DEVICE_SCHEDULING_STATUS_DICT[ - "free"] - devices_to_put.append(device) - - if lost_jobs_to_put: - ndb.put_multi(lost_jobs_to_put) - email_util.send_job_notification(lost_jobs_to_put) - self.logger.Println("{} jobs are updated.".format( - len(lost_jobs_to_put))) - - if devices_to_put: - ndb.put_multi(devices_to_put) - self.logger.Println("{} devices are updated.".format( - len(devices_to_put))) - - lines = self.logger.Get() - logging.info("\n".join([line.strip() for line in lines])) - - self.response.write("<pre>\n" + "\n".join(lines) + "\n</pre>") diff --git a/gae/webapp/src/scheduler/job_heartbeat_test.py b/gae/webapp/src/scheduler/job_heartbeat_test.py deleted file mode 100644 index c9f56a5..0000000 --- a/gae/webapp/src/scheduler/job_heartbeat_test.py +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import datetime -import unittest - -try: - from unittest import mock -except ImportError: - import mock - -from webapp.src import vtslab_status as Status -from webapp.src.proto import model -from webapp.src.scheduler import job_heartbeat -from webapp.src.scheduler import schedule_worker -from webapp.src.testing import unittest_base - - -class JobHeartbeatTest(unittest_base.UnitTestBase): - """Tests for PeriodicJobHeartBeat cron class. - - Attributes: - testbed: A Testbed instance which provides local unit testing. - job_heartbeat: A mock job_heartbeat.PeriodicJobHeartBeat instance. - """ - - def setUp(self): - """Initializes test""" - super(JobHeartbeatTest, self).setUp() - # Mocking PeriodicJobHeartBeat and essential methods. - self.job_heartbeat = job_heartbeat.PeriodicJobHeartBeat(mock.Mock()) - self.job_heartbeat.response = mock.Mock() - self.job_heartbeat.response.write = mock.Mock() - - def testJobHearbeat(self): - """Asserts job heartbeat detects unavailable jobs.""" - num_of_devices = 2 - shards = 2 - - lab = self.GenerateLabModel() - lab.put() - - devices = [] - for _ in range(num_of_devices): - for i in range(shards): - device = self.GenerateDeviceModel( - hostname=lab.hostname, product="product{}".format(i)) - device.put() - devices.append(device) - - schedules = [] - for device in devices: - schedule = self.GenerateScheduleModel( - lab_model=lab, device_model=device, shards=shards) - schedule.put() - schedules.append(schedule) - - for schedule in schedules: - build_dict = self.GenerateBuildModel(schedule) - for key in build_dict: - build_dict[key].put() - - # Mocking ScheduleHandler and essential methods. - scheduler = schedule_worker.ScheduleHandler(mock.Mock()) - scheduler.response = mock.Mock() - scheduler.response.write = mock.Mock() - scheduler.request.get = mock.MagicMock(return_value="") - - # Creating jobs. - scheduler.post() - jobs = model.JobModel.query().fetch() - self.assertEqual(2, len(jobs)) - - # jobs[0] will get old enough so it will be timed out. - jobs[0].status = Status.JOB_STATUS_DICT["leased"] - jobs[0].timestamp = (datetime.datetime.now() - datetime.timedelta( - seconds=job_heartbeat.JOB_RESPONSE_TIMEOUT_SECONDS + 5)) - jobs[0].heartbeat_stamp = ( - datetime.datetime.now() - datetime.timedelta( - seconds=job_heartbeat.JOB_RESPONSE_TIMEOUT_SECONDS + 5)) - jobs[0].put() - - # jobs[1] will not exceed the timeout time. - jobs[1].status = Status.JOB_STATUS_DICT["leased"] - jobs[1].timestamp = (datetime.datetime.now() - datetime.timedelta( - seconds=job_heartbeat.JOB_RESPONSE_TIMEOUT_SECONDS - 5)) - jobs[1].heartbeat_stamp = ( - datetime.datetime.now() - datetime.timedelta( - seconds=job_heartbeat.JOB_RESPONSE_TIMEOUT_SECONDS - 5)) - jobs[1].put() - - # Creating jobs. - self.job_heartbeat.get() - - # One job(job[0]) should be changed to infra-err status. - jobs = model.JobModel.query().fetch() - infra_error_jobs = [ - x for x in jobs if x.status == Status.JOB_STATUS_DICT["infra-err"] - ] - self.assertEqual(len(infra_error_jobs), 1) - - # job[0]'s devices should be changed to free scheduling status. - serials = infra_error_jobs[0].serial - devices = model.DeviceModel.query( - model.DeviceModel.serial.IN(serials)).fetch() - for device in devices: - self.assertEqual(device.scheduling_status, - Status.DEVICE_SCHEDULING_STATUS_DICT["free"]) - - -if __name__ == "__main__": - unittest.main() diff --git a/gae/webapp/src/scheduler/periodic.py b/gae/webapp/src/scheduler/periodic.py deleted file mode 100644 index 627ec14..0000000 --- a/gae/webapp/src/scheduler/periodic.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import webapp2 - -from webapp.src.proto import model - -from google.appengine.api import taskqueue - - -class PeriodicScheduler(webapp2.RequestHandler): - """Main class for /tasks/schedule servlet. - - This class creates a task, which creates schedules, in given period. - """ - - def get(self): - """Enqueues a scheduling task if scheduler is enabled.""" - schedule_control = model.ScheduleControlModel.query() - schedule_control_dataset = schedule_control.fetch() - enabled = True - if schedule_control_dataset: - for schedule_control_data_tuple in schedule_control_dataset: - if (not schedule_control_data_tuple.schedule_name or - schedule_control_data_tuple.schedule_name == "global"): - enabled = schedule_control_data_tuple.enabled - - if not enabled: - self.response.write( - "<pre>\nScheduler not enabled.\n</pre>") - return - - task = taskqueue.add( - url="/worker/schedule_handler", - target="worker", - queue_name="queue-schedule", - transactional=False - ) - self.response.write( - "<pre>\nScheduling task is enqueued. ETA {}\n</pre>".format( - task.eta)) diff --git a/gae/webapp/src/scheduler/schedule_worker.py b/gae/webapp/src/scheduler/schedule_worker.py deleted file mode 100644 index 4c4b20f..0000000 --- a/gae/webapp/src/scheduler/schedule_worker.py +++ /dev/null @@ -1,549 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import datetime -import itertools -import logging -import re - -from google.appengine.ext import ndb - -from webapp.src import vtslab_status as Status -from webapp.src.proto import model -from webapp.src.utils import logger -import webapp2 - -MAX_LOG_CHARACTERS = 10000 # maximum number of characters per each log -BOOTUP_ERROR_RETRY_INTERVAL_IN_MINS = 60 # retry minutes when boot-up error is occurred - -CREATE_JOB_SUCCESS = "success" -CREATE_JOB_FAILED_NO_BUILD = "no_build" -CREATE_JOB_FAILED_NO_DEVICE = "no_device" - - -def GetTestVersionType(manifest_branch, gsi_branch, test_type=0): - """Compares manifest branch and gsi branch to get test type. - - This function only completes two LSBs which represent version related - test type. - - Args: - manifest_branch: a string, manifest branch name. - gsi_branch: a string, gsi branch name. - test_type: an integer, previous test type value. - - Returns: - An integer, test type value. - """ - if not test_type: - value = 0 - else: - # clear two bits - value = test_type & ~(1 | 1 << 1) - - if not manifest_branch: - logging.debug("manifest branch cannot be empty or None.") - return value | Status.TEST_TYPE_DICT[Status.TEST_TYPE_UNKNOWN] - - if not gsi_branch: - logging.debug("gsi_branch is empty.") - return value | Status.TEST_TYPE_DICT[Status.TEST_TYPE_TOT] - - gcs_pattern = "^gs://.*/v([0-9.]*)/.*" - q_pattern = "(git_)?(aosp-)?q.*" - p_pattern = "(git_)?(aosp-)?p.*" - o_mr1_pattern = "(git_)?(aosp-)?o[^-]*-m.*" - o_pattern = "(git_)?(aosp-)?o.*" - master_pattern = "(git_)?(aosp-)?master" - - gcs_search = re.search(gcs_pattern, manifest_branch) - if gcs_search: - device_version = gcs_search.group(1) - elif re.match(q_pattern, manifest_branch): - device_version = "10.0" - elif re.match(p_pattern, manifest_branch): - device_version = "9.0" - elif re.match(o_mr1_pattern, manifest_branch): - device_version = "8.1" - elif re.match(o_pattern, manifest_branch): - device_version = "8.0" - elif re.match(master_pattern, manifest_branch): - device_version = "master" - else: - logging.debug("Unknown device version.") - return value | Status.TEST_TYPE_DICT[Status.TEST_TYPE_UNKNOWN] - - gcs_search = re.search(gcs_pattern, gsi_branch) - if gcs_search: - gsi_version = gcs_search.group(1) - elif re.match(q_pattern, gsi_branch): - gsi_version = "10.0" - elif re.match(p_pattern, gsi_branch): - gsi_version = "9.0" - elif re.match(o_mr1_pattern, gsi_branch): - gsi_version = "8.1" - elif re.match(o_pattern, gsi_branch): - gsi_version = "8.0" - elif re.match(master_pattern, gsi_branch): - gsi_version = "master" - else: - logging.debug("Unknown gsi version.") - return value | Status.TEST_TYPE_DICT[Status.TEST_TYPE_UNKNOWN] - - if device_version == gsi_version: - return value | Status.TEST_TYPE_DICT[Status.TEST_TYPE_TOT] - else: - return value | Status.TEST_TYPE_DICT[Status.TEST_TYPE_OTA] - - -class ScheduleHandler(webapp2.RequestHandler): - """Background worker class for /worker/schedule_handler. - - This class pull tasks from 'queue-schedule' queue and processes in - background service 'worker'. - - Attributes: - logger: Logger class - """ - logger = logger.Logger() - - def ReserveDevices(self, target_device_serials): - """Reserves devices. - - Args: - target_device_serials: a list of strings, containing target device - serial numbers. - """ - device_query = model.DeviceModel.query( - model.DeviceModel.serial.IN(target_device_serials)) - devices = device_query.fetch() - devices_to_put = [] - for device in devices: - device.scheduling_status = Status.DEVICE_SCHEDULING_STATUS_DICT[ - "reserved"] - devices_to_put.append(device) - if devices_to_put: - ndb.put_multi(devices_to_put) - - def FindBuildId(self, artifact_type, manifest_branch, target, - signed=False): - """Finds a designated build ID. - - Args: - artifact_type: a string, build artifact type. - manifest_branch: a string, build manifest branch. - target: a string which build target and type are joined by '-'. - signed: a boolean to get a signed build. - - Return: - string, build ID found. - """ - build_id = "" - if "-" in target: - build_target, build_type = target.split("-") - else: - build_target = target - build_type = "" - if not artifact_type or not manifest_branch or not build_target: - self.logger.Println("The argument format is invalid.") - return build_id - build_query = model.BuildModel.query( - model.BuildModel.artifact_type == artifact_type, - model.BuildModel.manifest_branch == manifest_branch, - model.BuildModel.build_target == build_target, - model.BuildModel.build_type == build_type) - builds = build_query.fetch() - - if builds: - builds = [ - build for build in builds - if (build.timestamp > - datetime.datetime.now() - datetime.timedelta(hours=72)) - ] - - if builds: - self.logger.Println("-- Found build ID") - builds.sort(key=lambda x: x.build_id, reverse=True) - for build in builds: - if not signed or build.signed: - build_id = build.build_id - break - return build_id - - def post(self): - self.logger.Clear() - manual_job = False - schedule_key = self.request.get("schedule_key") - if schedule_key: - key = ndb.key.Key(urlsafe=schedule_key) - manual_job = True - schedules = [key.get()] - else: - schedule_query = model.ScheduleModel.query( - model.ScheduleModel.suspended != True) - schedules = schedule_query.fetch() - - if schedules: - # filter out the schedules which are not updated within 72 hours. - schedules = [ - schedule for schedule in schedules - if (schedule.timestamp > - datetime.datetime.now() - datetime.timedelta(hours=72)) - ] - schedules = self.FilterWithPeriod(schedules) - - if schedules: - schedules.sort(key=lambda x: self.GetProductName(x)) - group_by_product = [ - list(g) - for _, g in itertools.groupby(schedules, - lambda x: self.GetProductName(x)) - ] - for group in group_by_product: - group.sort(key=lambda x: x.priority_value if ( - x.priority_value) else Status.GetPriorityValue(x.priority)) - create_result = { - CREATE_JOB_SUCCESS: [], - CREATE_JOB_FAILED_NO_BUILD: [], - CREATE_JOB_FAILED_NO_DEVICE: [] - } - for schedule in group: - self.logger.Println("") - self.logger.Println("Schedule: %s (branch: %s)" % - (schedule.test_name, - schedule.manifest_branch)) - self.logger.Println( - "Build Target: %s" % schedule.build_target) - self.logger.Println("Device: %s" % schedule.device) - self.logger.Indent() - result, lab = self.CreateJob(schedule, manual_job) - if result == CREATE_JOB_SUCCESS: - create_result[result].append(lab) - else: - create_result[result].append(schedule) - self.logger.Unindent() - # if any schedule in group created a job, increase priority of - # the schedules which couldn't create due to out of devices. - schedules_to_put = [] - for lab in create_result[CREATE_JOB_SUCCESS]: - for schedule in create_result[CREATE_JOB_FAILED_NO_DEVICE]: - if any([lab in target for target in schedule.device - ]) and schedule not in schedules_to_put: - if schedule.priority_value is None: - schedule.priority_value = ( - Status.GetPriorityValue(schedule.priority)) - if schedule.priority_value > 0: - schedule.priority_value -= 1 - schedules_to_put.append(schedule) - if schedules_to_put: - ndb.put_multi(schedules_to_put) - - self.logger.Println("Scheduling completed.") - - lines = self.logger.Get() - lines = [line.strip() for line in lines] - outputs = [] - chars = 0 - for line in lines: - chars += len(line) - if chars > MAX_LOG_CHARACTERS: - logging.info("\n".join(outputs)) - outputs = [] - chars = len(line) - outputs.append(line) - logging.info("\n".join(outputs)) - - def CreateJob(self, schedule, manual_job=False): - """Creates a job for given schedule. - - Args: - schedule: model.ScheduleModel instance. - manual_job: True if a job is created by a user, False otherwise. - - Returns: - a string of job creation result message. - a string of lab name if job is created, otherwise empty string. - """ - target_host, target_device, target_device_serials = ( - self.SelectTargetLab(schedule)) - if not target_host: - return CREATE_JOB_FAILED_NO_DEVICE, "" - - self.logger.Println("- Target host: %s" % target_host) - self.logger.Println("- Target device: %s" % target_device) - self.logger.Println("- Target serials: %s" % target_device_serials) - - # create job and add. - new_job = model.JobModel() - new_job.hostname = target_host - new_job.priority = schedule.priority - new_job.test_name = schedule.test_name - new_job.require_signed_device_build = ( - schedule.require_signed_device_build) - new_job.device = target_device - new_job.period = schedule.period - new_job.serial.extend(target_device_serials) - new_job.build_storage_type = schedule.build_storage_type - new_job.manifest_branch = schedule.manifest_branch - new_job.build_target = schedule.build_target - new_job.pab_account_id = schedule.device_pab_account_id - new_job.shards = schedule.shards - new_job.param = schedule.param - new_job.retry_count = schedule.retry_count - new_job.gsi_storage_type = schedule.gsi_storage_type - new_job.gsi_branch = schedule.gsi_branch - new_job.gsi_build_target = schedule.gsi_build_target - new_job.gsi_pab_account_id = schedule.gsi_pab_account_id - new_job.gsi_vendor_version = schedule.gsi_vendor_version - new_job.test_storage_type = schedule.test_storage_type - new_job.test_branch = schedule.test_branch - new_job.test_build_target = schedule.test_build_target - new_job.test_pab_account_id = schedule.test_pab_account_id - new_job.parent_schedule = schedule.key - new_job.image_package_repo_base = schedule.image_package_repo_base - new_job.required_host_equipment = schedule.required_host_equipment - new_job.required_device_equipment = schedule.required_device_equipment - new_job.has_bootloader_img = schedule.has_bootloader_img - new_job.has_radio_img = schedule.has_radio_img - new_job.report_bucket = schedule.report_bucket - new_job.report_spreadsheet_id = schedule.report_spreadsheet_id - new_job.report_persistent_url = schedule.report_persistent_url - new_job.report_reference_url = schedule.report_reference_url - - # uses bit 0-1 to indicate version. - test_type = GetTestVersionType(schedule.manifest_branch, - schedule.gsi_branch) - # uses bit 2 - if schedule.require_signed_device_build: - test_type |= Status.TEST_TYPE_DICT[Status.TEST_TYPE_SIGNED] - - if manual_job: - test_type |= Status.TEST_TYPE_DICT[Status.TEST_TYPE_MANUAL] - - new_job.test_type = test_type - - new_job.build_id = "" - new_job.gsi_build_id = "" - new_job.test_build_id = "" - for artifact_type in ["device", "gsi", "test"]: - if artifact_type == "device": - storage_type_text = "build_storage_type" - manifest_branch_text = "manifest_branch" - build_target_text = "build_target" - build_id_text = "build_id" - signed = new_job.require_signed_device_build - else: - storage_type_text = artifact_type + "_storage_type" - manifest_branch_text = artifact_type + "_branch" - build_target_text = artifact_type + "_build_target" - build_id_text = artifact_type + "_build_id" - signed = False - - manifest_branch = getattr(new_job, manifest_branch_text) - build_target = getattr(new_job, build_target_text) - storage_type = getattr(new_job, storage_type_text) - if storage_type == Status.STORAGE_TYPE_DICT["PAB"]: - build_id = self.FindBuildId( - artifact_type=artifact_type, - manifest_branch=manifest_branch, - target=build_target, - signed=signed) - elif storage_type == Status.STORAGE_TYPE_DICT["GCS"]: - # temp value to distinguish from empty values. - build_id = "gcs" - else: - build_id = "" - self.logger.Println( - "Unexpected storage type (%s)." % storage_type) - setattr(new_job, build_id_text, build_id) - - if ((not new_job.manifest_branch or new_job.build_id) - and (not new_job.gsi_branch or new_job.gsi_build_id) - and (not new_job.test_branch or new_job.test_build_id)): - new_job.build_id = new_job.build_id.replace("gcs", "") - new_job.gsi_build_id = (new_job.gsi_build_id.replace("gcs", "")) - new_job.test_build_id = (new_job.test_build_id.replace("gcs", "")) - self.ReserveDevices(target_device_serials) - new_job.status = Status.JOB_STATUS_DICT["ready"] - new_job.timestamp = datetime.datetime.now() - new_job_key = new_job.put() - schedule.children_jobs.append(new_job_key) - schedule.priority_value = Status.GetPriorityValue( - schedule.priority) - schedule.put() - self.logger.Println("A new job has been created.") - labs = model.LabModel.query( - model.LabModel.hostname == target_host).fetch() - return CREATE_JOB_SUCCESS, labs[0].name - else: - self.logger.Println("Cannot find builds to create a job.") - self.logger.Println("- Device branch / build - {} / {}".format( - new_job.manifest_branch, new_job.build_id)) - self.logger.Println("- GSI branch / build - {} / {}".format( - new_job.gsi_branch, new_job.gsi_build_id)) - self.logger.Println("- Test branch / build - {} / {}".format( - new_job.test_branch, new_job.test_build_id)) - return CREATE_JOB_FAILED_NO_BUILD, "" - - def FilterWithPeriod(self, schedules): - """Filters schedules with period. - - This method filters schedules if any children jobs are created within - period time. - - Args: - schedules: a list of model.ScheduleModel instances. - - Returns: - a list of model.ScheduleModel instances which need to create a new - job. - """ - ret_list = [] - if not schedules: - return ret_list - - if type(schedules) is not list: - schedules = [schedules] - - for schedule in schedules: - if not schedule.children_jobs: - ret_list.append(schedule) - continue - - latest_job_key = schedule.children_jobs[-1] - latest_job = latest_job_key.get() - - if datetime.datetime.now() - latest_job.timestamp > ( - datetime.timedelta( - minutes=self.GetCorrectedPeriod(schedule))): - ret_list.append(schedule) - - return ret_list - - def SelectTargetLab(self, schedule): - """Find target host and devices to schedule a new job. - - Args: - schedule: a proto containing the information of a schedule. - - Returns: - a string which represents hostname, - a string containing target lab and product with '/' separator, - a list of selected devices serial (see whether devices will be - selected later when the job is picked up.) - """ - - available_devices = [] - for target_device in schedule.device: - if "/" not in target_device: - self.logger.Println( - "Device malformed - {}".format(target_device)) - continue - - target_lab, target_product_type = target_device.split("/") - self.logger.Println("- Lab %s" % target_lab) - self.logger.Indent() - host_query = model.LabModel.query( - model.LabModel.name == target_lab) - target_hosts = host_query.fetch() - - if target_hosts: - for host in target_hosts: - if not (set(schedule.required_host_equipment) <= set( - host.host_equipment)): - continue - self.logger.Println("- Host: %s" % host.hostname) - self.logger.Indent() - device_query = model.DeviceModel.query( - model.DeviceModel.hostname == host.hostname, - model.DeviceModel.scheduling_status == - Status.DEVICE_SCHEDULING_STATUS_DICT["free"], - model.DeviceModel.status.IN([ - Status.DEVICE_STATUS_DICT["fastboot"], - Status.DEVICE_STATUS_DICT["online"], - Status.DEVICE_STATUS_DICT["ready"] - ])) - host_devices = device_query.fetch() - host_devices = [ - x for x in host_devices - if x.product.lower() == target_product_type.lower() and - (set(schedule.required_device_equipment) <= set( - x.device_equipment)) - ] - if len(host_devices) < schedule.shards: - self.logger.Println( - "A host {} does not have enough devices. " - "# of devices = {}, shards = {}".format( - host.hostname, len(host_devices), - schedule.shards)) - self.logger.Unindent() - continue - host_devices.sort( - key=lambda x: (len(x.device_equipment) - if x.device_equipment else 0)) - available_devices.append((host_devices, target_device)) - self.logger.Unindent() - - self.logger.Unindent() - - if not available_devices: - self.logger.Println("No hosts have enough devices for schedule!") - return None, None, [] - - available_devices.sort(key=lambda x: ( - sum([len(y.device_equipment) for y in x[0][:schedule.shards]]))) - selected_host_devices = available_devices[0] - return selected_host_devices[0][0].hostname, selected_host_devices[ - 1], [x.serial for x in selected_host_devices[0][:schedule.shards]] - - def GetProductName(self, schedule): - """Gets a product name from schedule instance. - - Args: - schedule: a schedule instance. - - Returns: - a string, product name in lowercase. - """ - if not schedule or not schedule.device: - return "" - - if "/" not in schedule.device[0]: - return "" - - return schedule.device[0].split("/")[1].lower() - - def GetCorrectedPeriod(self, schedule): - """Corrects and returns period value based on latest children jobs. - - Args: - schedule: a model.ScheduleModel instance containing schedule - information. - - Returns: - an integer, corrected schedule period. - """ - if not schedule.error_count or not schedule.children_jobs or ( - schedule.period <= BOOTUP_ERROR_RETRY_INTERVAL_IN_MINS): - return schedule.period - - latest_job = schedule.children_jobs[-1].get() - - if latest_job.status == Status.JOB_STATUS_DICT["bootup-err"]: - return BOOTUP_ERROR_RETRY_INTERVAL_IN_MINS - else: - return schedule.period diff --git a/gae/webapp/src/scheduler/schedule_worker_test.py b/gae/webapp/src/scheduler/schedule_worker_test.py deleted file mode 100644 index 2dcf1e9..0000000 --- a/gae/webapp/src/scheduler/schedule_worker_test.py +++ /dev/null @@ -1,581 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import datetime -import unittest - -try: - from unittest import mock -except ImportError: - import mock - -from webapp.src import vtslab_status as Status -from webapp.src.proto import model -from webapp.src.scheduler import schedule_worker -from webapp.src.testing import unittest_base -from webapp.src.utils import model_util - - -class ScheduleHandlerTest(unittest_base.UnitTestBase): - """Tests for ScheduleHandler. - - Attributes: - scheduler: A mock schedule_worker.ScheduleHandler. - """ - - def setUp(self): - """Initializes test""" - super(ScheduleHandlerTest, self).setUp() - # Mocking ScheduleHandler and essential methods. - self.scheduler = schedule_worker.ScheduleHandler(mock.Mock()) - self.scheduler.response = mock.Mock() - self.scheduler.response.write = mock.Mock() - self.scheduler.request.get = mock.MagicMock(return_value="") - - def testSimpleJobCreation(self): - """Asserts a job is created. - - This test defines that each model only has a single entity, and asserts - that a job is created. - """ - lab = self.GenerateLabModel() - lab.put() - - device = self.GenerateDeviceModel(hostname=lab.hostname) - device.put() - - schedule = self.GenerateScheduleModel( - device_model=device, lab_model=lab) - schedule.put() - - build_dict = self.GenerateBuildModel(schedule) - for key in build_dict: - build_dict[key].put() - - self.scheduler.post() - self.assertEqual(1, len(model.JobModel.query().fetch())) - print("A job is created successfully.") - - device_query = model.DeviceModel.query( - model.DeviceModel.serial == device.serial) - device = device_query.fetch()[0] - self.assertEqual(Status.DEVICE_SCHEDULING_STATUS_DICT["reserved"], - device.scheduling_status) - print("A device is reserved successfully.") - - def testPriorityScheduling(self): - """Asserts job creation with priority scheduling.""" - product = "product" - high_priority_schedule_test_name = "high_test" - medium_priority_schedule_test_name = "medium_test" - - lab = self.GenerateLabModel() - lab.put() - - device = self.GenerateDeviceModel( - hostname=lab.hostname, product=product) - device.put() - - schedule_high = self.GenerateScheduleModel( - device_model=device, - lab_model=lab, - priority="high", - test_name=high_priority_schedule_test_name) - schedule_high.put() - - schedule_medium = self.GenerateScheduleModel( - device_model=device, - lab_model=lab, - priority="medium", - test_name=medium_priority_schedule_test_name) - schedule_medium.put() - - build_dict = self.GenerateBuildModel(schedule_high) - for key in build_dict: - build_dict[key].put() - - self.scheduler.post() - schedules = model.ScheduleModel.query().fetch() - self.assertEqual(schedules[0].test_name, - high_priority_schedule_test_name) - - def testPrioritySchedulingWithAging(self): - """Asserts job creation with priority scheduling with aging.""" - product = "product" - high_priority_schedule_test_name = "high_test" - medium_priority_schedule_test_name = "medium_test" - schedule_period_minute = 100 - - lab = self.GenerateLabModel() - lab.put() - - device = self.GenerateDeviceModel( - hostname=lab.hostname, product=product) - device.put() - - schedules = [] - schedule_high = self.GenerateScheduleModel( - device_model=device, - lab_model=lab, - test_name=high_priority_schedule_test_name, - period=schedule_period_minute, - priority="high") - schedule_high.put() - schedules.append(schedule_high) - - schedule_medium = self.GenerateScheduleModel( - device_model=device, - lab_model=lab, - test_name=medium_priority_schedule_test_name, - period=schedule_period_minute, - priority="medium") - schedule_medium.put() - schedules.append(schedule_medium) - - for schedule in schedules: - build_dict = self.GenerateBuildModel(schedule) - for key in build_dict: - build_dict[key].put() - - high_original_priority_value = schedule_high.priority_value - medium_original_priority_value = schedule_medium.priority_value - - # On first attempt, "high" priority will create a job. - self.scheduler.post() - jobs = model.JobModel.query().fetch() - self.assertEqual(jobs[0].test_name, high_priority_schedule_test_name) - - # medium priority schedule's priority value will be decreased. - self.assertEqual(medium_original_priority_value - 1, - schedule_medium.priority_value) - - self.PassTime(minutes=schedule_period_minute + 1) - self.ResetDevices() - - # On second attempt, "high" priority will create a job. - self.scheduler.post() - jobs = model.JobModel.query().fetch() - jobs.sort(key=lambda x: x.timestamp, reverse=True) # latest first - self.assertEqual(jobs[0].test_name, high_priority_schedule_test_name) - - # medium priority schedule's priority value will be decreased again. - self.assertEqual(medium_original_priority_value - 2, - schedule_medium.priority_value) - - while schedule_medium.priority_value >= high_original_priority_value: - self.PassTime(minutes=schedule_period_minute + 1) - self.ResetDevices() - self.scheduler.post() - - # at last, medium priority schedule should be able to create a job. - self.PassTime(minutes=schedule_period_minute + 1) - self.ResetDevices() - self.scheduler.post() - - jobs = model.JobModel.query().fetch() - jobs.sort(key=lambda x: x.timestamp, reverse=True) # latest first - self.assertEqual(jobs[0].test_name, medium_priority_schedule_test_name) - - # after a job is created, its priority value should be restored. - self.assertEqual(schedule_medium.priority_value, - medium_original_priority_value) - - def testPrioritySchedulingWithAgingForMultiDevices(self): - """Asserts job creation with priority scheduling for multi devices.""" - product1 = "product1" - product2 = "product2" - schedule_period_minute = 360 - - lab = self.GenerateLabModel() - lab.put() - - device1 = self.GenerateDeviceModel( - hostname=lab.hostname, product=product1) - device1.put() - - device2 = self.GenerateDeviceModel( - hostname=lab.hostname, product=product2) - device2.put() - - schedule1_l = self.GenerateScheduleModel( - device_model=device1, - lab_model=lab, - priority="low", - period=schedule_period_minute) - schedule1_l.put() - - schedule1_h = self.GenerateScheduleModel( - device_model=device1, - lab_model=lab, - priority="high", - period=schedule_period_minute) - schedule1_h.put() - - schedule2_m = self.GenerateScheduleModel( - device_model=device2, - lab_model=lab, - priority="medium", - period=schedule_period_minute) - schedule2_m.put() - - schedule2_h = self.GenerateScheduleModel( - device_model=device2, - lab_model=lab, - priority="high", - period=schedule_period_minute) - schedule2_h.put() - - schedule1_l_original_priority_value = schedule1_l.priority_value - schedule2_m_original_priority_value = schedule2_m.priority_value - - for schedule in [schedule2_m, schedule2_h]: - build_dict = self.GenerateBuildModel(schedule) - for key in build_dict: - build_dict[key].put() - - # create jobs - self.scheduler.post() - - # schedule2_m will not get a change to create a job. - jobs = model.JobModel.query().fetch() - self.assertTrue( - any([job.test_name == schedule2_h.test_name for job in jobs])) - self.assertFalse( - any([job.test_name == schedule2_m.test_name for job in jobs])) - - # schedule2_m's priority value should be decreased. - self.assertTrue(schedule2_m_original_priority_value - 1, - schedule2_m.priority_value) - - # schedule1_l's priority value should not be changed because all other - # schedules for device1 were also failed to created a job. - self.assertTrue(schedule1_l_original_priority_value, - schedule1_l.priority_value) - - for num in range(3): - self.assertTrue(schedule2_m_original_priority_value - 1 - num, - schedule2_m.priority_value) - self.PassTime(minutes=schedule_period_minute + 1) - self.ResetDevices() - self.scheduler.post() - self.assertFalse( - any([job.test_name == schedule2_m.test_name for job in jobs])) - self.assertTrue(schedule1_l_original_priority_value, - schedule1_l.priority_value) - - # device1 is ready for scheduling. - for schedule in [schedule1_l, schedule1_h]: - build_dict = self.GenerateBuildModel(schedule) - for key in build_dict: - build_dict[key].put() - - # after 4 times of failure, now schedule2_m can create a job. - self.PassTime(minutes=schedule_period_minute + 1) - self.ResetDevices() - self.scheduler.post() - - jobs = model.JobModel.query().fetch() - self.assertTrue( - any([job.test_name == schedule2_m.test_name for job in jobs])) - - # now schedule_1's priority value should be changed. - self.assertEqual(schedule1_l_original_priority_value - 1, - schedule1_l.priority_value) - - def testRetryAfterBootupError(self): - """Asserts a schedule's period is shortened after boot-up error.""" - long_period = 5760 - - lab = self.GenerateLabModel() - lab.put() - - device = self.GenerateDeviceModel(hostname=lab.hostname) - device.put() - - schedule = self.GenerateScheduleModel( - device_model=device, lab_model=lab, period=long_period) - schedule.put() - - build_dict = self.GenerateBuildModel(schedule) - for key in build_dict: - build_dict[key].put() - - # a job should be created. - self.scheduler.post() - jobs = model.JobModel.query().fetch() - self.assertEqual(1, len(jobs)) - - jobs[0].status = Status.JOB_STATUS_DICT["bootup-err"] - jobs[0].put() - model_util.UpdateParentSchedule(jobs[0], - Status.JOB_STATUS_DICT["bootup-err"]) - - self.PassTime( - minutes=schedule_worker.BOOTUP_ERROR_RETRY_INTERVAL_IN_MINS + 1) - self.ResetDevices() - - # new job should be created again. - self.scheduler.post() - jobs = model.JobModel.query().fetch() - self.assertEqual(2, len(jobs)) - - jobs.sort(key=lambda x: x.timestamp, reverse=True) # latest first - jobs[0].status = Status.JOB_STATUS_DICT["bootup-err"] - jobs[0].put() - model_util.UpdateParentSchedule(jobs[0], - Status.JOB_STATUS_DICT["bootup-err"]) - - self.PassTime( - minutes=schedule_worker.BOOTUP_ERROR_RETRY_INTERVAL_IN_MINS - 1) - self.ResetDevices() - - # time is not passed enough so there would be no new job. - self.scheduler.post() - jobs = model.JobModel.query().fetch() - self.assertEqual(2, len(jobs)) - - # if latest job is completed successfully, period should be recovered. - jobs[0].status = Status.JOB_STATUS_DICT["complete"] - jobs[0].put() - model_util.UpdateParentSchedule(jobs[0], - Status.JOB_STATUS_DICT["complete"]) - - # pass time to (period - 1) - self.PassTime(minutes=long_period - 1 - ( - schedule_worker.BOOTUP_ERROR_RETRY_INTERVAL_IN_MINS - 1)) - self.ResetDevices() - - # then no job will be created. - self.scheduler.post() - jobs = model.JobModel.query().fetch() - self.assertEqual(2, len(jobs)) - - # pass time to (period + 1) - self.PassTime(minutes=2) - - self.scheduler.post() - jobs = model.JobModel.query().fetch() - self.assertEqual(3, len(jobs)) - - def testSimpleDevicePriorityWithEquipment(self): - """Asserts a scheduler creates a job with minimum device equipment.""" - equipment_a = "equipment_a" - equipment_b = "equipment_b" - - device_product = "device_product" - lab = self.GenerateLabModel() - lab.put() - - device_a = self.GenerateDeviceModel( - product=device_product, - hostname=lab.hostname, - device_equipment=[equipment_a]) - device_a.put() - - device_b = self.GenerateDeviceModel( - product=device_product, - hostname=lab.hostname, - device_equipment=[equipment_b]) - device_b.put() - - device_c = self.GenerateDeviceModel( - product=device_product, hostname=lab.hostname) - device_c.put() - - schedule = self.GenerateScheduleModel( - device_target="{}-test".format(device_product), - lab_model=lab, - required_device_equipment=[equipment_b]) - schedule.put() - - build_dict = self.GenerateBuildModel(schedule) - for key in build_dict: - build_dict[key].put() - - # a job should be created and it should be created with equipment_b - self.scheduler.post() - jobs = model.JobModel.query().fetch() - self.assertEqual(1, len(jobs)) - self.assertIn(device_b.serial, jobs[0].serial) - - def testDevicePriorityWithEquipment(self): - """Asserts a scheduler creates a job with minimum device equipment.""" - lab_1 = "lab_1" - lab_2 = "lab_2" - - host_a = "host_a" - host_b = "host_b" - host_c = "host_c" - host_d = "host_d" - host_e = "host_e" - - equipment_a = "equipment_a" - equipment_b = "equipment_b" - equipment_c = "equipment_c" - - correct_product = "correct" - wrong_product = "wrong" - - self.GenerateLabModel(lab_name=lab_1, host_name=host_a).put() - self.GenerateLabModel(lab_name=lab_1, host_name=host_b).put() - self.GenerateLabModel(lab_name=lab_2, host_name=host_c).put() - self.GenerateLabModel(lab_name=lab_2, host_name=host_d).put() - self.GenerateLabModel(lab_name=lab_2, host_name=host_e).put() - - # setting devices through host a to e. - equipments = [[equipment_a], [equipment_a], [equipment_b], - [equipment_a, equipment_b]] - for equipment in equipments: - device = self.GenerateDeviceModel( - product=correct_product, hostname=host_a) - device.device_equipment = equipment - device.put() - - equipments = [[], [equipment_a], [equipment_a, equipment_b], - [equipment_a, equipment_b]] - for equipment in equipments: - device = self.GenerateDeviceModel( - product=correct_product, hostname=host_b) - device.device_equipment = equipment - device.put() - - equipments = [[equipment_a], [equipment_a], [equipment_b], - [equipment_b]] - for equipment in equipments: - device = self.GenerateDeviceModel( - product=correct_product, hostname=host_c) - device.device_equipment = equipment - device.put() - - equipments = [[equipment_a], [equipment_a, equipment_b, equipment_c], - [equipment_a, equipment_b]] - for equipment in equipments: - device = self.GenerateDeviceModel( - product=correct_product, hostname=host_d) - device.device_equipment = equipment - device.put() - - products = [correct_product, correct_product, wrong_product] - for product in products: - device = self.GenerateDeviceModel(product=product, hostname=host_e) - device.device_equipment = [equipment_a] - device.put() - - schedule = self.GenerateScheduleModel( - device_target="{}-test".format(correct_product), shards=3) - schedule.required_device_equipment = [equipment_a] - schedule.device = [ - "{}/{}".format(lab_1, correct_product), "{}/{}".format( - lab_2, correct_product) - ] - schedule.put() - - build_dict = self.GenerateBuildModel(schedule) - for key in build_dict: - build_dict[key].put() - - # a job should be created on host_a - self.scheduler.post() - jobs = model.JobModel.query().fetch() - self.assertEqual(1, len(jobs)) - - host_a_devices = model.DeviceModel.query( - model.DeviceModel.hostname == host_a).fetch() - host_a_devices_serial = [x.serial for x in host_a_devices] - - for job_device in jobs[0].serial: - self.assertIn(job_device, host_a_devices_serial) - - def testSelectTargetLab(self): - """Asserts SelectTargetLab() method.""" - lab = self.GenerateLabModel() - lab.put() - - device = self.GenerateDeviceModel(hostname=lab.hostname) - device.put() - - schedule = self.GenerateScheduleModel( - device_model=device, lab_model=lab) - schedule.put() - - ret_host, ret_device, ret_serials = ( - self.scheduler.SelectTargetLab(schedule)) - - self.assertEqual(lab.hostname, ret_host) - self.assertEqual("{}/{}".format(lab.name, device.product), ret_device) - self.assertEqual([device.serial], ret_serials) - - def testSimpleJobCreationWithOutdatedBuild(self): - """Asserts an outdated build is filtered out.""" - lab = self.GenerateLabModel() - lab.put() - - device = self.GenerateDeviceModel(hostname=lab.hostname) - device.put() - - schedule = self.GenerateScheduleModel( - device_model=device, lab_model=lab) - schedule.put() - - build_dict = self.GenerateBuildModel(schedule) - for key in build_dict: - build_dict[key].timestamp = datetime.datetime.now( - ) - datetime.timedelta(hours=73) - build_dict[key].put() - - self.scheduler.post() - self.assertEqual(0, len(model.JobModel.query().fetch())) - - builds = model.BuildModel().query().fetch() - for build in builds: - build.timestamp = datetime.datetime.now() - build.put() - - self.scheduler.post() - self.assertEqual(1, len(model.JobModel.query().fetch())) - - def testSimpleJobCreationWithOutdatedSchedule(self): - """Asserts an outdated schedule is filtered out.""" - lab = self.GenerateLabModel() - lab.put() - - device = self.GenerateDeviceModel(hostname=lab.hostname) - device.put() - - schedule = self.GenerateScheduleModel( - device_model=device, lab_model=lab) - schedule.timestamp = datetime.datetime.now() - datetime.timedelta( - hours=73) - schedule.put() - - build_dict = self.GenerateBuildModel(schedule) - for key in build_dict: - build_dict[key].put() - - self.scheduler.post() - self.assertEqual(0, len(model.JobModel.query().fetch())) - - schedule = model.ScheduleModel().query().fetch()[0] - schedule.timestamp = datetime.datetime.now() - schedule.put() - - self.scheduler.post() - self.assertEqual(1, len(model.JobModel.query().fetch())) - - -if __name__ == "__main__": - unittest.main() diff --git a/gae/webapp/src/tasks/__init__.py b/gae/webapp/src/tasks/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/gae/webapp/src/tasks/__init__.py +++ /dev/null diff --git a/gae/webapp/src/tasks/indexing.py b/gae/webapp/src/tasks/indexing.py deleted file mode 100644 index 2ebe687..0000000 --- a/gae/webapp/src/tasks/indexing.py +++ /dev/null @@ -1,185 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License") + "\n</pre>"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import logging - -from webapp.src import vtslab_status as Status -from webapp.src.proto import model -from webapp.src.scheduler import schedule_worker - -import webapp2 -from google.appengine.api import taskqueue -from google.appengine.ext import ndb - -PAGING_SIZE = 1000 -DICT_MODELS = { - "build": model.BuildModel, - "device": model.DeviceModel, - "lab": model.LabModel, - "job": model.JobModel, - "schedule": model.ScheduleModel -} - - -class CreateIndex(webapp2.RequestHandler): - """Cron class for /tasks/indexing/{model}.""" - - def get(self, arg): - """Creates a task to re-index, with given URL format.""" - index_list = [] - if arg: - if arg.startswith("/") and arg[1:].lower() in DICT_MODELS.keys(): - index_list.append(arg[1:].lower()) - else: - self.response.write("<pre>Access Denied. Please visit " - "/tasks/indexing/{model}</pre>") - return - else: - # accessed by /tasks/indexing - index_list.extend(DICT_MODELS.keys()) - self.response.write( - "<pre>Re-indexing task{} for {} {} going to be created.</pre>". - format("s" - if len(index_list) > 1 else "", ", ".join(index_list), "are" - if len(index_list) > 1 else "is")) - - for model_type in index_list: - task = taskqueue.add( - url="/worker/indexing", - target="worker", - queue_name="queue-indexing", - transactional=False, - params={ - "model_type": model_type - }) - self.response.write( - "<pre>Re-indexing task for {} is created. ETA: {}</pre>". - format(model_type, task.eta)) - - -class IndexingHandler(webapp2.RequestHandler): - """Task queue handler class to re-index ndb model.""" - - def post(self): - """Fetch entities and process model specific jobs.""" - reload(model) - model_type = self.request.get("model_type") - - num_updated = 0 - next_cursor = None - more = True - - while more: - query = DICT_MODELS[model_type].query() - entities, next_cursor, more = query.fetch_page( - PAGING_SIZE, start_cursor=next_cursor) - - to_put = [] - for entity in entities: - if model_type == "build": - pass - elif model_type == "device": - pass - elif model_type == "lab": - pass - elif model_type == "job": - # uses bits 0-1 to indicate version. - test_type = schedule_worker.GetTestVersionType( - entity.manifest_branch, entity.gsi_branch) - # uses bit 2 - if entity.require_signed_device_build: - test_type |= ( - Status.TEST_TYPE_DICT[Status.TEST_TYPE_SIGNED]) - entity.test_type = test_type - - if not entity.parent_schedule: - # finds and links to a parent schedule. - parent_schedule_query = model.ScheduleModel.query( - model.ScheduleModel.priority == entity.priority, - model.ScheduleModel.test_name == entity.test_name, - model.ScheduleModel.period == entity.period, - model.ScheduleModel.build_storage_type == ( - entity.build_storage_type), - model.ScheduleModel.manifest_branch == ( - entity.manifest_branch), - model.ScheduleModel.build_target == ( - entity.build_target), - model.ScheduleModel.device_pab_account_id == ( - entity.pab_account_id), - model.ScheduleModel.shards == entity.shards, - model.ScheduleModel.retry_count == ( - entity.retry_count), - model.ScheduleModel.gsi_storage_type == ( - entity.gsi_storage_type), - model.ScheduleModel.gsi_branch == ( - entity.gsi_branch), - model.ScheduleModel.gsi_build_target == ( - entity.gsi_build_target), - model.ScheduleModel.gsi_pab_account_id == ( - entity.gsi_pab_account_id), - model.ScheduleModel.gsi_vendor_version == ( - entity.gsi_vendor_version), - model.ScheduleModel.test_storage_type == ( - entity.test_storage_type), - model.ScheduleModel.test_branch == ( - entity.test_branch), - model.ScheduleModel.test_build_target == ( - entity.test_build_target), - model.ScheduleModel.test_pab_account_id == ( - entity.test_pab_account_id)) - parent_schedules = parent_schedule_query.fetch() - if not parent_schedules: - logging.error("Parent not found.") - else: - parent_schedule = parent_schedules[0] - parent_schedule.children_jobs.append(entity.key) - entity.parent_schedule = parent_schedule.key - to_put.append(parent_schedule) - - elif model_type == "schedule": - if entity.error_count is None: - entity.error_count = 0 - if entity.suspended is None: - entity.suspended = False - if entity.build_storage_type is None: - entity.build_storage_type = Status.STORAGE_TYPE_DICT[ - "PAB"] - # remove None children jobs. - if entity.children_jobs: - entity.children_jobs = [ - x for x in entity.children_jobs if x] - else: - entity.children_jobs = [] - - for attr in ["has_bootloader_img", "has_radio_img"]: - if getattr(entity, attr, None) is None: - setattr(entity, attr, True) - - # set priority_value for old schedules. - if entity.priority_value is None: - entity.priority_value = Status.GetPriorityValue( - entity.priority) - else: - pass - to_put.append(entity) - - if to_put: - ndb.put_multi(to_put) - num_updated += len(to_put) - - logging.info("{} indexing complete with {} updates!".format( - model_type, num_updated)) diff --git a/gae/webapp/src/tasks/indexing_test.py b/gae/webapp/src/tasks/indexing_test.py deleted file mode 100644 index 5d14a56..0000000 --- a/gae/webapp/src/tasks/indexing_test.py +++ /dev/null @@ -1,191 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import unittest - -try: - from unittest import mock -except ImportError: - import mock - -from webapp.src.proto import model -from webapp.src.tasks import indexing -from webapp.src.testing import unittest_base - - -class IndexingHandlerTest(unittest_base.UnitTestBase): - """Tests for IndexingHandler. - - Attributes: - testbed: A Testbed instance which provides local unit testing. - indexing_handler: A mock IndexingHandler instance. - """ - - def setUp(self): - """Initializes test""" - super(IndexingHandlerTest, self).setUp() - # Mocking IndexingHandler. - self.indexing_handler = indexing.IndexingHandler(mock.Mock()) - self.indexing_handler.request = mock.Mock() - - def testSingleJobReindexing(self): - """Asserts re-indexing links job and schedule successfully.""" - - print("\nCreating a single schedule...") - schedule = self.GenerateScheduleModel() - schedule.put() - - schedules = model.ScheduleModel.query().fetch() - self.assertEqual(1, len(schedules)) - - print("Creating a job for stored schedule...") - for schedule in schedules: - job = model.JobModel() - job.priority = schedule.priority - job.test_name = schedule.test_name - job.period = schedule.period - job.build_storage_type = schedule.build_storage_type - job.manifest_branch = schedule.manifest_branch - job.build_target = schedule.build_target - job.pab_account_id = schedule.device_pab_account_id - job.shards = schedule.shards - job.retry_count = schedule.retry_count - job.gsi_storage_type = schedule.gsi_storage_type - job.gsi_branch = schedule.gsi_branch - job.gsi_build_target = schedule.gsi_build_target - job.gsi_pab_account_id = schedule.gsi_pab_account_id - job.gsi_vendor_version = schedule.gsi_vendor_version - job.test_storage_type = schedule.test_storage_type - job.test_branch = schedule.test_branch - job.test_build_target = schedule.test_build_target - job.test_pab_account_id = schedule.test_pab_account_id - job.put() - - jobs = model.JobModel.query().fetch() - self.assertEqual(1, len(jobs)) - - print("Seeking children jobs before re-indexing...") - jobs = model.JobModel.query().fetch() - for job in jobs: - parent_key = job.parent_schedule - self.assertIsNone(parent_key) - - print("Seeking children jobs after re-indexing...") - self.indexing_handler.request.get = mock.MagicMock(return_value="job") - self.indexing_handler.post() - jobs = model.JobModel.query().fetch() - for job in jobs: - parent_key = job.parent_schedule - parent_schedule = parent_key.get() - self.assertEqual( - True, - ((parent_schedule.priority == job.priority) and - (parent_schedule.test_name == job.test_name) and - (parent_schedule.period == job.period) and - (parent_schedule.build_storage_type == job.build_storage_type) - and (parent_schedule.manifest_branch == job.manifest_branch) - and (parent_schedule.build_target == job.build_target) and - (parent_schedule.device_pab_account_id == job.pab_account_id) - and (parent_schedule.shards == job.shards) and - (parent_schedule.retry_count == job.retry_count) and - (parent_schedule.gsi_storage_type == job.gsi_storage_type) and - (parent_schedule.gsi_branch == job.gsi_branch) and - (parent_schedule.gsi_build_target == job.gsi_build_target) and - (parent_schedule.gsi_pab_account_id == job.gsi_pab_account_id) - and - (parent_schedule.test_storage_type == job.test_storage_type) - and (parent_schedule.test_branch == job.test_branch) and - (parent_schedule.test_build_target == job.test_build_target) - and (parent_schedule.test_pab_account_id == - job.test_pab_account_id))) - - def testMultiJobReindexing(self): - """Asserts re-indexing links job and schedule successfully.""" - print("\nCreating four schedules...") - for num in xrange(4): - schedule = self.GenerateScheduleModel(test_name=str(num + 1)) - schedule.put() - schedule.put() - - schedules = model.ScheduleModel.query().fetch() - self.assertEqual(4, len(schedules)) - - print("Creating jobs as number of test_name...") - for schedule in schedules: - for _ in xrange(int(schedule.test_name)): - job = model.JobModel() - job.priority = schedule.priority - job.test_name = schedule.test_name - job.period = schedule.period - job.build_storage_type = schedule.build_storage_type - job.manifest_branch = schedule.manifest_branch - job.build_target = schedule.build_target - job.pab_account_id = schedule.device_pab_account_id - job.shards = schedule.shards - job.retry_count = schedule.retry_count - job.gsi_storage_type = schedule.gsi_storage_type - job.gsi_branch = schedule.gsi_branch - job.gsi_build_target = schedule.gsi_build_target - job.gsi_pab_account_id = schedule.gsi_pab_account_id - job.gsi_vendor_version = schedule.gsi_vendor_version - job.test_storage_type = schedule.test_storage_type - job.test_branch = schedule.test_branch - job.test_build_target = schedule.test_build_target - job.test_pab_account_id = schedule.test_pab_account_id - job.put() - - jobs = model.JobModel.query().fetch() - self.assertEqual(10, len(jobs)) - - print("Seeking children jobs before re-indexing...") - jobs = model.JobModel.query().fetch() - for job in jobs: - parent_key = job.parent_schedule - self.assertIsNone(parent_key) - - print("Seeking children jobs after re-indexing...") - self.indexing_handler.request.get = mock.MagicMock(return_value="job") - self.indexing_handler.post() - jobs = model.JobModel.query().fetch() - for job in jobs: - parent_key = job.parent_schedule - parent_schedule = parent_key.get() - self.assertEqual( - True, - ((parent_schedule.priority == job.priority) and - (parent_schedule.test_name == job.test_name) and - (parent_schedule.period == job.period) and - (parent_schedule.build_storage_type == job.build_storage_type) - and (parent_schedule.manifest_branch == job.manifest_branch) - and (parent_schedule.build_target == job.build_target) and - (parent_schedule.device_pab_account_id == job.pab_account_id) - and (parent_schedule.shards == job.shards) and - (parent_schedule.retry_count == job.retry_count) and - (parent_schedule.gsi_storage_type == job.gsi_storage_type) and - (parent_schedule.gsi_branch == job.gsi_branch) and - (parent_schedule.gsi_build_target == job.gsi_build_target) and - (parent_schedule.gsi_pab_account_id == job.gsi_pab_account_id) - and - (parent_schedule.test_storage_type == job.test_storage_type) - and (parent_schedule.test_branch == job.test_branch) and - (parent_schedule.test_build_target == job.test_build_target) - and (parent_schedule.test_pab_account_id == - job.test_pab_account_id))) - - -if __name__ == "__main__": - unittest.main() diff --git a/gae/webapp/src/tasks/removing_outdated_devices.py b/gae/webapp/src/tasks/removing_outdated_devices.py deleted file mode 100644 index 6a4eeab..0000000 --- a/gae/webapp/src/tasks/removing_outdated_devices.py +++ /dev/null @@ -1,37 +0,0 @@ -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import datetime -import webapp2 - -from google.appengine.ext import ndb - -from webapp.src.proto import model - -OUTDATED_DEVICE_REMOVE_TIME_IN_HOURS = 48 - - -class RemoveOutdatedDevices(webapp2.RequestHandler): - """Main class for /tasks/remove_outdated_devices. - - Used to find outdated devices and remove them. - """ - - def get(self): - device_query = model.DeviceModel.query( - model.DeviceModel.timestamp < datetime.datetime.now() - - datetime.timedelta(hours=OUTDATED_DEVICE_REMOVE_TIME_IN_HOURS)) - outdated_devices = device_query.fetch(keys_only=True) - ndb.delete_multi(outdated_devices) diff --git a/gae/webapp/src/tasks/removing_outdated_devices_test.py b/gae/webapp/src/tasks/removing_outdated_devices_test.py deleted file mode 100644 index 6ff57d4..0000000 --- a/gae/webapp/src/tasks/removing_outdated_devices_test.py +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import datetime -import unittest - -try: - from unittest import mock -except ImportError: - import mock - -from webapp.src.proto import model -from webapp.src.tasks import removing_outdated_devices -from webapp.src.testing import unittest_base - - -class RemoveOutdatedDevicesTest(unittest_base.UnitTestBase): - """Tests for RemoveOutdatedDevices cron class. - - Attributes: - remove_outdated_device: A mock device_heartbeat.RemoveOutdatedDevices - instance. - """ - - def setUp(self): - """Initializes test""" - super(RemoveOutdatedDevicesTest, self).setUp() - # Mocking RemoveOutdatedDevices and essential methods. - self.remove_outdated_device = ( - removing_outdated_devices.RemoveOutdatedDevices(mock.Mock())) - self.remove_outdated_device.response = mock.Mock() - self.remove_outdated_device.response.write = mock.Mock() - - def testRemoveOutdatedDevicesTest(self): - """Asserts job heartbeat detects unavailable jobs.""" - device_a_serial = "a" - device_b_serial = "b" - device_c_serial = "c" - device_d_serial = "c" - - # create a device A, which is outdated. - device = self.GenerateDeviceModel(serial=device_a_serial) - device.timestamp = datetime.datetime.now() - datetime.timedelta( - hours=100) - device.put() - - # create a device B, which is offline for a day. - device = self.GenerateDeviceModel(serial=device_b_serial) - device.timestamp = datetime.datetime.now() - datetime.timedelta( - hours=24) - device.put() - - # create a device C and D, which are alive. - for serial in [device_c_serial, device_d_serial]: - device = self.GenerateDeviceModel(serial=serial) - device.timestamp = datetime.datetime.now() - device.put() - - # Remove outdated devices. - self.remove_outdated_device.get() - - devices = model.DeviceModel.query().fetch() - - # device A should not be included. - self.assertEqual(len(devices), 3) - self.assertTrue(device_a_serial not in [x.serial for x in devices]) - - # change devices' timestamp - for device in devices: - device.timestamp = device.timestamp - datetime.timedelta(hours=25) - device.put() - - # Remove outdated devices. - self.remove_outdated_device.get() - - devices = model.DeviceModel.query().fetch() - - # Now device B should not be included also. - self.assertEqual(len(devices), 2) - self.assertTrue(device_b_serial not in [x.serial for x in devices]) - - # change devices' timestamp - for device in devices: - device.timestamp = device.timestamp - datetime.timedelta(hours=25) - device.put() - - # Remove outdated devices. - self.remove_outdated_device.get() - - devices = model.DeviceModel.query().fetch() - - # Now there should not be no devices. - self.assertEqual(len(devices), 0) - - -if __name__ == "__main__": - unittest.main() diff --git a/gae/webapp/src/testing/__init__.py b/gae/webapp/src/testing/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/gae/webapp/src/testing/__init__.py +++ /dev/null diff --git a/gae/webapp/src/testing/unittest_base.py b/gae/webapp/src/testing/unittest_base.py deleted file mode 100644 index 0e47ee0..0000000 --- a/gae/webapp/src/testing/unittest_base.py +++ /dev/null @@ -1,347 +0,0 @@ -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import datetime -import random -import string -import unittest - -try: - from unittest import mock -except ImportError: - import mock - -from google.appengine.ext import ndb -from google.appengine.ext import testbed - -from webapp.src import vtslab_status as Status -from webapp.src.proto import model - - -class UnitTestBase(unittest.TestCase): - """Base class for unittest. - - Attributes: - testbed: A Testbed instance which provides local unit testing. - random_strs: a list of strings generated by GetRandomString() method - in order to avoid duplicates. - """ - random_strs = [] - - def setUp(self): - """Initializes unittest.""" - # Create the Testbed class instance and initialize service stubs. - self.testbed = testbed.Testbed() - self.testbed.activate() - self.testbed.init_datastore_v3_stub() - self.testbed.init_memcache_stub() - self.testbed.init_mail_stub() - self.testbed.setup_env(app_id="vtslab-schedule-unittest") - # Clear cache between tests. - ndb.get_context().clear_cache() - - def tearDown(self): - self.testbed.deactivate() - - def GetRandomString(self, length=7): - """Generates and returns a random string. - - Args: - length: an integer, string length. - - Returns: - a random string. - """ - new_str = "" - while new_str == "" or new_str in self.random_strs: - new_str = "".join( - random.choice(string.ascii_letters + string.digits) - for _ in range(length)) - return new_str - - def GenerateLabModel(self, lab_name=None, host_name=None): - """Builds model.LabModel with given information. - - Args: - lab_name: a string, lab name. - host_name: a string, host name. - - Returns: - model.LabModel instance. - """ - lab = model.LabModel() - lab.name = lab_name if lab_name else self.GetRandomString() - lab.hostname = host_name if host_name else self.GetRandomString() - lab.owner = "test@abc.com" - lab.ip = "100.100.100.100" - return lab - - def GenerateDeviceModel( - self, - status=Status.DEVICE_STATUS_DICT["fastboot"], - scheduling_status=Status.DEVICE_SCHEDULING_STATUS_DICT["free"], - **kwargs): - """Builds model.DeviceModel with given information. - - Args: - status: an integer, device's initial status. - scheduling_status: an integer, device's initial scheduling status. - **kwargs: the optional arguments. - - Returns: - model.DeviceModel instance. - """ - device = model.DeviceModel() - device.status = status - device.scheduling_status = scheduling_status - device.timestamp = datetime.datetime.now() - - skip_list = ["status", "scheduling_status", "timestamp"] - set_or_empty = [] - for arg in device._properties: - if arg in skip_list or (arg in set_or_empty and arg not in kwargs): - continue - if arg in kwargs: - value = kwargs[arg] - elif isinstance(device._properties[arg], ndb.StringProperty): - value = self.GetRandomString() - elif isinstance(device._properties[arg], ndb.IntegerProperty): - value = 0 - elif isinstance(device._properties[arg], ndb.BooleanProperty): - value = False - else: - print("A type of property '{}' is not supported.".format(arg)) - continue - if device._properties[arg]._repeated and type(value) is not list: - value = [value] - setattr(device, arg, value) - return device - - def GenerateScheduleModel( - self, - device_model=None, - lab_model=None, - priority="medium", - period=360, - retry_count=1, - shards=1, - lab_name=None, - device_storage_type=Status.STORAGE_TYPE_DICT["PAB"], - device_branch=None, - device_target=None, - gsi_storage_type=Status.STORAGE_TYPE_DICT["PAB"], - gsi_build_target=None, - test_storage_type=Status.STORAGE_TYPE_DICT["PAB"], - test_build_target=None, - required_signed_device_build=False, - **kwargs): - """Builds model.ScheduleModel with given information. - - Args: - device_model: a model.DeviceModel instance to refer device product. - lab_model: a model.LabModel instance to refer host name. - priority: a string, scheduling priority - period: an integer, scheduling period. - retry_count: an integer, scheduling retry count. - shards: an integer, # ways of device shards. - lab_name: a string, target lab name. - device_storage_type: an integer, device storage type - device_branch: a string, device build branch. - device_target: a string, device build target. - gsi_storage_type: an integer, GSI storage type - gsi_build_target: a string, GSI build target. - test_storage_type: an integer, test storage type - test_build_target: a string, test build target. - required_signed_device_build: a boolean, True to schedule for signed - device build, False if not. - **kwargs: the optional arguments. - - Returns: - model.ScheduleModel instance. - """ - - if device_model: - device_product = device_model.product - device_target = self.GetRandomString(4) - elif device_target: - device_product, device_target = device_target.split("-") - else: - device_product = self.GetRandomString(7) - device_target = self.GetRandomString(4) - - if lab_model: - lab = lab_model.name - elif lab_name: - lab = lab_name - else: - lab = self.GetRandomString() - - schedule = model.ScheduleModel() - schedule.priority = priority - schedule.priority_value = Status.GetPriorityValue(schedule.priority) - schedule.period = period - schedule.shards = shards - schedule.retry_count = retry_count - schedule.required_signed_device_build = required_signed_device_build - schedule.build_storage_type = device_storage_type - schedule.manifest_branch = (device_branch if device_branch else - self.GetRandomString()) - schedule.build_target = "-".join([device_product, device_target]) - - schedule.gsi_storage_type = gsi_storage_type - schedule.gsi_build_target = (gsi_build_target - if gsi_build_target else "-".join([ - self.GetRandomString(), - self.GetRandomString(4) - ])) - schedule.test_storage_type = test_storage_type - schedule.test_build_target = (test_build_target - if test_build_target else "-".join([ - self.GetRandomString(), - self.GetRandomString(4) - ])) - schedule.device = [] - schedule.device.append("/".join([lab, device_product])) - - schedule.timestamp = datetime.datetime.now() - - skip_list = [ - "priority", "priority_value", "period", "shards", - "retry_count", "required_signed_device_build", - "build_storage_type", "manifest_branch", "build_target", - "gsi_storage_type", "gsi_build_target", - "test_storage_type", "test_build_target", "device", - "children_jobs"] - set_or_empty = ["required_host_equipment", "required_device_equipment"] - for arg in schedule._properties: - if arg in skip_list or (arg in set_or_empty and arg not in kwargs): - continue - if arg in kwargs: - value = kwargs[arg] - elif isinstance(schedule._properties[arg], ndb.StringProperty): - value = self.GetRandomString() - elif isinstance(schedule._properties[arg], ndb.IntegerProperty): - value = 0 - elif isinstance(schedule._properties[arg], ndb.BooleanProperty): - value = False - else: - print("A type of property '{}' is not supported.".format(arg)) - continue - if schedule._properties[arg]._repeated and type(value) is not list: - value = [value] - setattr(schedule, arg, value) - - return schedule - - def GenerateBuildModel(self, schedule, targets=None): - """Builds model.BuildModel with given information. - - Args: - schedule: a model.ScheduleModel instance to look up build info. - targets: a list of strings which indicates artifact type. - - Returns: - model.BuildModel instance. - """ - build_dict = {} - if targets is None: - targets = ["device", "gsi", "test"] - for target in targets: - build = model.BuildModel() - build.artifact_type = target - build.timestamp = datetime.datetime.now() - if target == "device": - build.signed = schedule.required_signed_device_build - build.manifest_branch = schedule.manifest_branch - build.build_target, build.build_type = ( - schedule.build_target.split("-")) - elif target == "gsi": - build.manifest_branch = schedule.gsi_branch - build.build_target, build.build_type = ( - schedule.gsi_build_target.split("-")) - elif target == "test": - build.manifest_branch = schedule.test_branch - build.build_target, build.build_type = ( - schedule.test_build_target.split("-")) - build.build_id = self.GetNewBuildId(build) - build_dict[target] = build - return build_dict - - def GetNewBuildId(self, build): - """Generates build ID. - - This method always generates newest (higher number) build ID than other - builds stored in testbed datastore. - - Args: - build: a model.BuildModel instance to look up build information - from testbed datastore. - - Returns: - a string, build ID. - """ - format_string = "{0:07d}" - build_query = model.BuildModel.query( - model.BuildModel.artifact_type == build.artifact_type, - model.BuildModel.build_target == build.build_target, - model.BuildModel.signed == build.signed, - model.BuildModel.manifest_branch == build.manifest_branch) - exiting_builds = build_query.fetch() - if exiting_builds: - exiting_builds.sort(key=lambda x: x.build_id, reverse=True) - latest_build_id = int(exiting_builds[0].build_id) - return format_string.format(latest_build_id + 1) - else: - return format_string.format(1) - - def PassTime(self, hours=0, minutes=0, seconds=0): - """Assumes that a certain amount of time has passed. - - This method changes does not change actual system time but changes all - jobs timestamp to assume time has passed. - - Args: - hours: an integer, number of hours to pass time. - minutes: an integer, number of minutes to pass time. - seconds: an integer, number of seconds to pass time. - """ - if not hours and not minutes and not seconds: - return - - jobs = model.JobModel.query().fetch() - to_put = [] - for job in jobs: - if job.timestamp: - job.timestamp -= datetime.timedelta( - hours=hours, minutes=minutes, seconds=seconds) - if job.heartbeat_stamp: - job.heartbeat_stamp -= datetime.timedelta( - hours=hours, minutes=minutes, seconds=seconds) - to_put.append(job) - if to_put: - ndb.put_multi(to_put) - - def ResetDevices(self): - """Resets all devices to ready status.""" - devices = model.DeviceModel.query().fetch() - to_put = [] - for device in devices: - device.status = Status.DEVICE_STATUS_DICT["fastboot"] - device.scheduling_status = Status.DEVICE_SCHEDULING_STATUS_DICT[ - "free"] - to_put.append(device) - if to_put: - ndb.put_multi(to_put) diff --git a/gae/webapp/src/utils/__init__.py b/gae/webapp/src/utils/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/gae/webapp/src/utils/__init__.py +++ /dev/null diff --git a/gae/webapp/src/utils/datetime_util.py b/gae/webapp/src/utils/datetime_util.py deleted file mode 100644 index a1cff67..0000000 --- a/gae/webapp/src/utils/datetime_util.py +++ /dev/null @@ -1,39 +0,0 @@ -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import logging -import pytz - - -def GetTimeWithTimezone(dt, timezone="US/Pacific"): - """Converts timezone of datetime.datetime() instance. - - Args: - dt: datetime.datetime() instance. - timezone: a string representing timezone listed in TZ database. - - Returns: - datetime.datetime() instance with the given timezone. - """ - if not dt: - return None - utc_time = dt.replace(tzinfo=pytz.utc) - try: - converted_time = utc_time.astimezone(pytz.timezone(timezone)) - except pytz.UnknownTimeZoneError as e: - logging.exception(e) - converted_time = dt - return converted_time diff --git a/gae/webapp/src/utils/email_util.py b/gae/webapp/src/utils/email_util.py deleted file mode 100644 index 2ef795e..0000000 --- a/gae/webapp/src/utils/email_util.py +++ /dev/null @@ -1,295 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import datetime -import logging -import re - -from google.appengine.api import app_identity -from google.appengine.api import mail - -from webapp.src import vtslab_status as Status -from webapp.src.proto import model -from webapp.src.utils import datetime_util - -SENDER_ADDRESS = "noreply@{}.appspotmail.com" - -SEND_NOTIFICATION_FOOTER = ( - "You are receiving this email because you are " - "listed as an owner, or an administrator of the " - "lab {}.\nIf you received this email by mistake, " - "please send an email to VTS Lab infra development " - "team. Thank you.") - -SEND_DEVICE_NOTIFICATION_TITLE = ("[VTS lab] Devices not responding in lab {} " - "({})") -SEND_DEVICE_NOTIFICATION_HEADER = "Devices in lab {} are not responding." - -SEND_JOB_NOTIFICATION_TITLE = ("[VTS lab] Job error has been occurred in " - "lab {} ({})") -SEND_JOB_NOTIFICATION_HEADER = ("Jobs in lab {} have been completed " - "unexpectedly.") -SEND_SCHEDULE_SUSPENSION_NOTIFICATION_TITLE = ( - "[VTS lab] A job schedule has been {}. ({})") -SEND_SCHEDULE_SUSPENSION_NOTIFICATION_HEADER = ("The below job schedule has " - "been {}.") -SEND_SCHEDULE_SUSPENSION_NOTIFICATION_FOOTER = ( - "You are receiving this email because one or more labs which you are " - "listed as an owner or an administrator are affected.\nIf you received " - "this email by mistake, please send an email to VTS Lab infra development " - "team. Thank you.") - - -def send_device_notification(devices): - """Sends notification for not responding devices. - - Args: - devices: a dict containing lab and host information of no-response - devices. - """ - for lab in devices: - email_message = mail.EmailMessage() - email_message.sender = SENDER_ADDRESS.format( - app_identity.get_application_id()) - try: - email_message.to = verify_recipient_address( - devices[lab]["_recipients"]) - except ValueError as e: - logging.error(e) - continue - email_message.subject = SEND_DEVICE_NOTIFICATION_TITLE.format( - lab, - datetime_util.GetTimeWithTimezone( - datetime.datetime.now()).strftime("%Y-%m-%d")) - message = "" - message += SEND_DEVICE_NOTIFICATION_HEADER.format(lab) - message += "\n\n" - for host in devices[lab]: - if host == "_recipients" or not devices[lab][host]: - continue - message += "hostname\n" - message += host - message += "\n\ndevices\n" - message += "\n".join(devices[lab][host]) - message += "\n\n\n" - message += "\n\n" - message += SEND_NOTIFICATION_FOOTER.format(lab) - - try: - email_message.body = message - email_message.check_initialized() - email_message.send() - except mail.MissingRecipientError as e: - logging.exception(e) - - -def send_job_notification(jobs): - """Sends notification for job error. - - Args: - jobs: a JobModel entity, or a list of JobModel entities. - """ - if not jobs: - return - if type(jobs) is not list: - jobs = [jobs] - - # grouping jobs by lab to send to each lab owner and admins at once. - labs_to_alert = {} - for job in jobs: - lab_query = model.LabModel.query( - model.LabModel.hostname == job.hostname) - labs = lab_query.fetch() - if labs: - lab = labs[0] - if lab.name not in labs_to_alert: - labs_to_alert[lab.name] = {} - labs_to_alert[lab.name]["jobs"] = [] - labs_to_alert[lab.name]["_recipients"] = [] - if lab.owner not in labs_to_alert[lab.name]["_recipients"]: - labs_to_alert[lab.name]["_recipients"].append(lab.owner) - labs_to_alert[lab.name]["_recipients"].extend([ - x for x in lab.admin - if x not in labs_to_alert[lab.name]["_recipients"] - ]) - labs_to_alert[lab.name]["jobs"].append(job) - else: - logging.warning( - "Could not find a lab model for hostname {}".format( - job.hostname)) - continue - - for lab in labs_to_alert: - email_message = mail.EmailMessage() - email_message.sender = SENDER_ADDRESS.format( - app_identity.get_application_id()) - try: - email_message.to = verify_recipient_address( - labs_to_alert[lab]["_recipients"]) - except ValueError as e: - logging.error(e) - continue - email_message.subject = SEND_JOB_NOTIFICATION_TITLE.format( - lab, - datetime_util.GetTimeWithTimezone( - datetime.datetime.now()).strftime("%Y-%m-%d")) - message = "" - message += SEND_JOB_NOTIFICATION_HEADER.format(lab) - message += "\n\n" - message += "http://{}.appspot.com/job".format( - app_identity.get_application_id()) - message += "\n\n" - for job in labs_to_alert[lab]["jobs"]: - message += "hostname: {}\n\n".format(job.hostname) - message += "device: {}\n".format(job.device.split("/")[1]) - message += "device serial: {}\n".format(", ".join(job.serial)) - message += ( - "device: branch - {}, target - {}, build_id - {}\n").format( - job.manifest_branch, job.build_target, job.build_id) - message += "gsi: branch - {}, target - {}, build_id - {}\n".format( - job.gsi_branch, job.gsi_build_target, job.gsi_build_id) - message += "test: branch - {}, target - {}, build_id - {}\n".format( - job.test_branch, job.test_build_target, job.test_build_id) - message += "job created: {}\n".format( - datetime_util.GetTimeWithTimezone( - job.timestamp).strftime("%Y-%m-%d %H:%M:%S %Z")) - message += "job status: {}\n".format([ - key for key, value in Status.JOB_STATUS_DICT.items() - if value == job.status - ][0]) - message += "\n\n\n" - message += "\n\n" - message += SEND_NOTIFICATION_FOOTER.format(lab) - - try: - email_message.body = message - email_message.check_initialized() - email_message.send() - except mail.MissingRecipientError as e: - logging.exception(e) - - -def send_schedule_suspension_notification(schedule): - """Sends notification when a schedule is suspended, or resumed. - - Args: - schedule: a ScheduleModel entity. - """ - if not schedule: - return - - if not schedule.device: - return - - email_message = mail.EmailMessage() - email_message.sender = SENDER_ADDRESS.format( - app_identity.get_application_id()) - - lab_names = [] - for device in schedule.device: - if not "/" in device: - continue - lab_name = device.split("/")[0] - lab_names.append(lab_name) - - recipients = [] - for lab_name in lab_names: - lab_query = model.LabModel.query(model.LabModel.name == lab_name) - labs = lab_query.fetch() - if labs: - lab = labs[0] - if lab.owner not in recipients: - recipients.append(lab.owner) - recipients.extend([x for x in lab.admin if x not in recipients]) - else: - logging.warning( - "Could not find a lab model for lab {}".format(lab_name)) - - try: - email_message.to = verify_recipient_address(recipients) - except ValueError as e: - logging.error(e) - return - - status_text = "suspended" if schedule.suspended else "resumed" - email_message.subject = SEND_SCHEDULE_SUSPENSION_NOTIFICATION_TITLE.format( - status_text, - datetime_util.GetTimeWithTimezone( - datetime.datetime.now()).strftime("%Y-%m-%d")) - message = "" - message += SEND_SCHEDULE_SUSPENSION_NOTIFICATION_HEADER.format(status_text) - message += "\n\n" - message += "\n\ndevices\n" - message += "\n".join(schedule.device) - message += "\n\ndevice branch\n" - message += schedule.manifest_branch - message += "\n\ndevice build target\n" - message += schedule.build_target - message += "\n\ngsi branch\n" - message += schedule.gsi_branch - message += "\n\ngsi build target\n" - message += schedule.gsi_build_target - message += "\n\ntest branch\n" - message += schedule.test_branch - message += "\n\ntest build target\n" - message += schedule.test_build_target - message += "\n\n" - message += ("Please see the details in the following link: " - "http://{}.appspot.com/schedule".format( - app_identity.get_application_id())) - message += "\n\n\n\n" - message += SEND_SCHEDULE_SUSPENSION_NOTIFICATION_FOOTER - - try: - email_message.body = message - email_message.check_initialized() - email_message.send() - except mail.MissingRecipientError as e: - logging.exception(e) - - -def verify_recipient_address(address): - """Verifies recipients address. - - Args: - address: a list of strings or a string, recipient(s) address. - - Returns: - A list of verified addresses if list type argument is given, or - a string of a verified address if str type argument is given. - - Raises: - ValueError if type of address is neither list nor str. - """ - # pattern for 'any@google.com', and 'any name <any@google.com>' - verify_patterns = [ - re.compile(".*@google\.com$"), - re.compile(".*<.*@google\.com>$") - ] - if not address: - return None - if type(address) is list: - verified_address = [ - x for x in address - if any(pattern.match(x) for pattern in verify_patterns) - ] - return verified_address - elif type(address) is str: - return address if any( - pattern.match(address) for pattern in verify_patterns) else None - else: - raise ValueError("Wrong type - {}.".format(type(address))) diff --git a/gae/webapp/src/utils/logger.py b/gae/webapp/src/utils/logger.py deleted file mode 100644 index 20c03d2..0000000 --- a/gae/webapp/src/utils/logger.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - - -class Logger(object): - """A class to log messages to a list of strings. - - Attributes: - log_message: a list of strings, containing the log messages. - log_indent: integer, representing the index level - """ - - def __init__(self): - self.log_message = [] - self.log_indent = 0 - - def Clear(self): - """Clears the log buffer.""" - self.log_message = [] - self.log_indent = 0 - - def Get(self): - """Retruns a list of all log message strings.""" - return self.log_message - - def Println(self, msg): - """Stores a new string `msg` to the log buffer.""" - indent = " " * self.log_indent - if msg and type(msg) is not str: - msg = str(msg) - self.log_message.append(indent + msg) - - def Indent(self): - """Increase indent of log message.""" - self.log_indent += 1 - - def Unindent(self): - """Decrease indent of log message.""" - if self.log_indent > 0: - self.log_indent -= 1 diff --git a/gae/webapp/src/utils/model_util.py b/gae/webapp/src/utils/model_util.py deleted file mode 100644 index aa07a63..0000000 --- a/gae/webapp/src/utils/model_util.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -from webapp.src import vtslab_status as Status -from webapp.src.utils import email_util - - -def UpdateParentSchedule(job, status): - """Updates a parent schedule of the given job with status. - - Args: - job: a JobModel entity. - status: an integer, job status value. - """ - if status not in [ - Status.JOB_STATUS_DICT["complete"], - Status.JOB_STATUS_DICT["infra-err"], - Status.JOB_STATUS_DICT["expired"], - Status.JOB_STATUS_DICT["bootup-err"] - ]: - return - - if job.parent_schedule: - schedule = job.parent_schedule.get() - if schedule: - previous_suspended = schedule.suspended - if schedule.error_count is None: - schedule.error_count = 0 - if status == Status.JOB_STATUS_DICT["complete"]: - schedule.error_count = 0 - schedule.suspended = False - elif status in [ - Status.JOB_STATUS_DICT["infra-err"], - Status.JOB_STATUS_DICT["expired"], - Status.JOB_STATUS_DICT["bootup-err"] - ]: - schedule.error_count += 1 - if schedule.error_count >= Status.NUM_ERRORS_FOR_SUSPENSION: - schedule.suspended = True - schedule.put() - if previous_suspended != schedule.suspended: - email_util.send_schedule_suspension_notification(schedule) diff --git a/gae/webapp/src/utils/model_util_test.py b/gae/webapp/src/utils/model_util_test.py deleted file mode 100644 index 4be54b1..0000000 --- a/gae/webapp/src/utils/model_util_test.py +++ /dev/null @@ -1,170 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import datetime -import unittest - -try: - from unittest import mock -except ImportError: - import mock - -from webapp.src import vtslab_status as Status -from webapp.src.proto import model -from webapp.src.scheduler import schedule_worker -from webapp.src.testing import unittest_base -from webapp.src.utils import model_util - - -class ModelTest(unittest_base.UnitTestBase): - """Tests for PeriodicJobHeartBeat cron class.""" - - def testJobAndScheduleModel(self): - """Asserts JobModel and ScheduleModel. - - When JobModel's status is changed, ScheduleModel's error_count is - changed based on the status. This should not be applied before JobModel - entity is updated to Datastore. - """ - period = 360 - - lab = self.GenerateLabModel() - lab.put() - - device = self.GenerateDeviceModel(hostname=lab.hostname) - device.put() - - schedule = self.GenerateScheduleModel( - device_model=device, lab_model=lab, period=period) - schedule.put() - - build_dict = self.GenerateBuildModel(schedule) - for key in build_dict: - build_dict[key].put() - - # Mocking ScheduleHandler and essential methods. - scheduler = schedule_worker.ScheduleHandler(mock.Mock()) - scheduler.response = mock.Mock() - scheduler.response.write = mock.Mock() - scheduler.request.get = mock.MagicMock(return_value="") - - print("\nCreating a job...") - scheduler.post() - jobs = model.JobModel.query().fetch() - self.assertEqual(1, len(jobs)) - - print("Occurring infra error...") - job = jobs[0] - job.status = Status.JOB_STATUS_DICT["infra-err"] - parent_schedule = job.parent_schedule.get() - parent_from_db = model.ScheduleModel.query().fetch()[0] - - # in test error_count could be None but in real there will be no None. - self.assertNotEqual(1, parent_schedule.error_count) - self.assertNotEqual(1, parent_from_db.error_count) - - # error count should be changed after put - job.put() - model_util.UpdateParentSchedule(job, job.status) - self.assertEqual(1, parent_schedule.error_count) - self.assertEqual(1, parent_from_db.error_count) - - print("Suspending a job...") - for num in xrange(2): - jobs = model.JobModel.query().fetch() - for job in jobs: - job.timestamp = datetime.datetime.now() - datetime.timedelta( - minutes=(period + 10)) - job.put() - - parent_from_db = model.ScheduleModel.query().fetch()[0] - self.assertEqual(1 + num, parent_schedule.error_count) - self.assertEqual(1 + num, parent_from_db.error_count) - - # reset a device manually to re-schedule - device = model.DeviceModel.query().fetch()[0] - device.status = Status.DEVICE_STATUS_DICT["fastboot"] - device.scheduling_status = ( - Status.DEVICE_SCHEDULING_STATUS_DICT["free"]) - device.timestamp = datetime.datetime.now() - device.put() - - scheduler.post() - jobs = model.JobModel.query().fetch() - self.assertEqual(2 + num, len(jobs)) - - ready_jobs = model.JobModel.query( - model.JobModel.status == Status.JOB_STATUS_DICT[ - "ready"]).fetch() - self.assertEqual(1, len(ready_jobs)) - - ready_job = ready_jobs[0] - ready_job.status = Status.JOB_STATUS_DICT["infra-err"] - parent_schedule = ready_job.parent_schedule.get() - parent_from_db = model.ScheduleModel.query().fetch()[0] - self.assertEqual(1 + num, parent_schedule.error_count) - self.assertEqual(1 + num, parent_from_db.error_count) - - # # error count should be changed after put - ready_job.put() - model_util.UpdateParentSchedule(ready_job, ready_job.status) - self.assertEqual(2 + num, parent_schedule.error_count) - self.assertEqual(2 + num, parent_from_db.error_count) - - print("Asserting a schedule's suspend status...") - # after three errors the schedule should be suspended. - schedule_from_db = model.ScheduleModel.query().fetch()[0] - schedule_from_db.put() - self.assertEqual(3, schedule_from_db.error_count) - self.assertEqual(True, schedule_from_db.suspended) - - # reset a device manually to re-schedule - device = model.DeviceModel.query().fetch()[0] - device.status = Status.DEVICE_STATUS_DICT["fastboot"] - device.scheduling_status = ( - Status.DEVICE_SCHEDULING_STATUS_DICT["free"]) - device.timestamp = datetime.datetime.now() - device.put() - - print("Asserting that job creation is blocked...") - jobs = model.JobModel.query().fetch() - self.assertEqual(3, len(jobs)) - - for job in jobs: - job.timestamp = datetime.datetime.now() - datetime.timedelta( - minutes=(period + 10)) - job.put() - - scheduler.post() - - # a job should not be created. - jobs = model.JobModel.query().fetch() - self.assertEqual(3, len(jobs)) - - print("Asserting that job creation is allowed after resuming...") - schedule_from_db = model.ScheduleModel.query().fetch()[0] - schedule_from_db.suspended = False - schedule_from_db.put() - - scheduler.post() - - jobs = model.JobModel.query().fetch() - self.assertEqual(4, len(jobs)) - - -if __name__ == "__main__": - unittest.main() diff --git a/gae/webapp/src/vtslab_status.py b/gae/webapp/src/vtslab_status.py deleted file mode 100644 index c1d1363..0000000 --- a/gae/webapp/src/vtslab_status.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Status dict updated from HC. -DEVICE_STATUS_DICT = { - # default state, currently not in use. - "unknown": 0, - # for devices detected via "fastboot devices" shell command. - "fastboot": 1, - # for devices detected via "adb devices" shell command. - "online": 2, - # currently not in use. - "ready": 3, - # currently not in use. - "use": 4, - # for devices in error state. - "error": 5, - # for devices which timed out (not detected either via fastboot or adb). - "no-response": 6 -} - -# Scheduling status dict based on the status of each jobs in job queue. -DEVICE_SCHEDULING_STATUS_DICT = { - # for devices detected but not scheduled. - "free": 0, - # for devices scheduled but not running. - "reserved": 1, - # for devices scheduled for currently leased job(s). - "use": 2 -} - -# Job status dict -JOB_STATUS_DICT = { - # scheduled but not leased yet - "ready": 0, - # scheduled and in running - "leased": 1, - # completed job - "complete": 2, - # unexpected error during running - "infra-err": 3, - # never leased within schedule period - "expired": 4, - # device boot error after flashing the given img sets - "bootup-err": 5 -} - -JOB_PRIORITY_DICT = { - "top": 3, - "high": 6, - "medium": 9, - "low": 12, - "other": 15 -} - - -STORAGE_TYPE_DICT = { - "unknown": 0, - "PAB": 1, - "GCS": 2 -} - - -TEST_TYPE_UNKNOWN = "unknown" -TEST_TYPE_TOT = "ToT" -TEST_TYPE_OTA = "OTA" -TEST_TYPE_SIGNED = "signed" -TEST_TYPE_PRESUBMIT = "presubmit" -TEST_TYPE_MANUAL = "manual" - -# a dict, where keys indicate test type and values have bitwise values. -# bit 0-1 : version related test type -# 00 - Unknown -# 01 - ToT -# 10 - OTA -# bit 2 : device signed build -# bit 3-4 : reserved for gerrit related test type -# 01 - pre-submit -# bit 5 : manually created test job -TEST_TYPE_DICT = { - TEST_TYPE_UNKNOWN: 0, - TEST_TYPE_TOT: 1, - TEST_TYPE_OTA: 1 << 1, - TEST_TYPE_SIGNED: 1 << 2, - TEST_TYPE_PRESUBMIT: 1 << 3, - TEST_TYPE_MANUAL: 1 << 5 -} - -# # of errors in a row to suspend a schedule -NUM_ERRORS_FOR_SUSPENSION = 3 - -# filter methods -FILTER_EqualTo = "EqualTo" -FILTER_LessThan = "LessThan" -FILTER_GreaterThan = "GreaterThan" -FILTER_LessThanOrEqualTo = "LessThanOrEqualTo" -FILTER_GreaterThanOrEqualTo = "GreaterThanOrEqualTo" -FILTER_NotEqualTo = "NotEqualTo" -FILTER_Has = "Has" - -FILTER_METHOD = { - FILTER_EqualTo: 1, - FILTER_LessThan: 2, - FILTER_GreaterThan: 3, - FILTER_LessThanOrEqualTo: 4, - FILTER_GreaterThanOrEqualTo: 5, - FILTER_NotEqualTo: 6, - FILTER_Has: 7, -} - - -def GetPriorityValue(priority): - """Helper function to sort jobs based on priority. - - Args: - priority: string, the job priority. - - Returns: - int, priority order (the lower, the higher) - """ - if priority: - priority = priority.lower() - if priority in JOB_PRIORITY_DICT: - return JOB_PRIORITY_DICT[priority] - return JOB_PRIORITY_DICT["other"] diff --git a/gae/webapp/src/webapp_main.py b/gae/webapp/src/webapp_main.py deleted file mode 100644 index 2587f4b..0000000 --- a/gae/webapp/src/webapp_main.py +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import os - -import webapp2 - -from webapp.src.handlers import base -from webapp.src.scheduler import device_heartbeat -from webapp.src.scheduler import job_heartbeat -from webapp.src.scheduler import periodic -from webapp.src.tasks import indexing -from webapp.src.tasks import removing_outdated_devices - - -class RedirectHandler(base.BaseHandler): - """Redirect handler to redirect to specific appspot version.""" - def get(self, arg): - if arg: - return self.redirect("https://{}.appspot.com/".format(arg)) - - -class MainPage(base.BaseHandler): - """Main web page request handler.""" - - def get(self): - """Generates an HTML page.""" - self.template = "index.html" - - template_values = {} - - self.render(template_values) - - -config = {} -config['webapp2_extras.sessions'] = { - 'secret_key': os.environ.get('SESSION_SECRET_KEY'), -} - -app = webapp2.WSGIApplication( - [ - ("/tasks/schedule", periodic.PeriodicScheduler), - ("/tasks/device_heartbeat", device_heartbeat.PeriodicDeviceHeartBeat), - ("/tasks/job_heartbeat", job_heartbeat.PeriodicJobHeartBeat), - ("/tasks/remove_outdated_devices", - removing_outdated_devices.RemoveOutdatedDevices), - ("/tasks/indexing([/]?.*)", indexing.CreateIndex), - ("/redirect/(.*)", RedirectHandler), - ], - config=config, - debug=False) diff --git a/gae/webapp/src/worker_main.py b/gae/webapp/src/worker_main.py deleted file mode 100644 index 40afdf9..0000000 --- a/gae/webapp/src/worker_main.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -from webapp.src.tasks import indexing -from webapp.src.scheduler import schedule_worker -import webapp2 - - -app = webapp2.WSGIApplication([ - ("/worker/schedule_handler", schedule_worker.ScheduleHandler), - ("/worker/indexing", indexing.IndexingHandler) - ], debug=True) diff --git a/gae/worker.yaml b/gae/worker.yaml deleted file mode 100644 index 7d6b859..0000000 --- a/gae/worker.yaml +++ /dev/null @@ -1,22 +0,0 @@ -runtime: python27 -api_version: 1 -threadsafe: true -service: worker - -handlers: -- url: /.* - script: webapp.src.worker_main.app - login: admin - -# [START exclude] -skip_files: -- ^(.*/)?#.*#$ -- ^(.*/)?.*~$ -- ^(.*/)?.*\.py[co]$ -- ^(.*/)?.*/RCS/.*$ -- ^(.*/)?\..*$ -- ^script/*$ -- .*_test.py$ -- ^(.*/)?frontend/(.*) -- ^(.*/)?\.idea/(.*) -# [END exclude] diff --git a/proto/GreenBuildScheduleConfigMessage.proto b/proto/GreenBuildScheduleConfigMessage.proto deleted file mode 100644 index 3ca5679..0000000 --- a/proto/GreenBuildScheduleConfigMessage.proto +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2018 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto2"; - -package android.test.lab; - -// To specify a green build scheduling policy. -message GreenBuildScheduleConfigMessage { - // Schedule name. - optional bytes name = 1; - - // Schedule description (e.g., daily/HH:MM) - optional bytes schedule = 2; - - // Scheduling priority (e.g., top, high, medium, low) - optional bytes priority = 3; - - // if GSI (General System Image) is used, GSI manifest branch name. - optional bytes gsi_branch = 11; - - // if GSI (General System Image) is used, GSI PAB account ID. - optional bytes gsi_pab_account_id = 12; - - // Test package's manifest branch name. - optional bytes test_branch = 21; - - // PAB account ID for test package. - optional bytes test_pab_account_id = 22; - - repeated GreenBuildTestScheduleConfigMessage test = 31; -} - -// To specify a test plan scheduling policy. -message GreenBuildTestScheduleConfigMessage { - // Test name (e.g., test_group/test_plan) - optional bytes test_name = 1; - - // Test package's build target name. - optional bytes test_build_target = 11; - - // Device manifest branch name. - optional bytes device_branch = 21; - - // Device PAB account ID. - optional bytes device_pab_account_id = 22; - - repeated GreenBuildDeviceScheduleConfigMessage device = 31; -} - -// To specify a device scheduling policy. -message GreenBuildDeviceScheduleConfigMessage { - // Device type (format: <lab>/<device type>) - optional bytes device = 1; - - // Number of shards (0 for auto selection) - optional int32 shards = 11; - - // Device manifest branch name if different. - optional bytes device_branch = 21; - - // Device build target if different. - optional bytes device_build_target = 22; - - // Device PAB account ID if different. - optional bytes device_pab_account_id = 23; - - // GSI's build target name. - optional bytes gsi_build_target = 31; -} diff --git a/proto/GreenBuildScheduleConfigMessage_pb2.py b/proto/GreenBuildScheduleConfigMessage_pb2.py deleted file mode 100644 index a001a7a..0000000 --- a/proto/GreenBuildScheduleConfigMessage_pb2.py +++ /dev/null @@ -1,261 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: GreenBuildScheduleConfigMessage.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -from google.protobuf import descriptor_pb2 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='GreenBuildScheduleConfigMessage.proto', - package='android.test.lab', - syntax='proto2', - serialized_pb=_b('\n%GreenBuildScheduleConfigMessage.proto\x12\x10\x61ndroid.test.lab\"\xfa\x01\n\x1fGreenBuildScheduleConfigMessage\x12\x0c\n\x04name\x18\x01 \x01(\x0c\x12\x10\n\x08schedule\x18\x02 \x01(\x0c\x12\x10\n\x08priority\x18\x03 \x01(\x0c\x12\x12\n\ngsi_branch\x18\x0b \x01(\x0c\x12\x1a\n\x12gsi_pab_account_id\x18\x0c \x01(\x0c\x12\x13\n\x0btest_branch\x18\x15 \x01(\x0c\x12\x1b\n\x13test_pab_account_id\x18\x16 \x01(\x0c\x12\x43\n\x04test\x18\x1f \x03(\x0b\x32\x35.android.test.lab.GreenBuildTestScheduleConfigMessage\"\xd2\x01\n#GreenBuildTestScheduleConfigMessage\x12\x11\n\ttest_name\x18\x01 \x01(\x0c\x12\x19\n\x11test_build_target\x18\x0b \x01(\x0c\x12\x15\n\rdevice_branch\x18\x15 \x01(\x0c\x12\x1d\n\x15\x64\x65vice_pab_account_id\x18\x16 \x01(\x0c\x12G\n\x06\x64\x65vice\x18\x1f \x03(\x0b\x32\x37.android.test.lab.GreenBuildDeviceScheduleConfigMessage\"\xb4\x01\n%GreenBuildDeviceScheduleConfigMessage\x12\x0e\n\x06\x64\x65vice\x18\x01 \x01(\x0c\x12\x0e\n\x06shards\x18\x0b \x01(\x05\x12\x15\n\rdevice_branch\x18\x15 \x01(\x0c\x12\x1b\n\x13\x64\x65vice_build_target\x18\x16 \x01(\x0c\x12\x1d\n\x15\x64\x65vice_pab_account_id\x18\x17 \x01(\x0c\x12\x18\n\x10gsi_build_target\x18\x1f \x01(\x0c') -) -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - - - - -_GREENBUILDSCHEDULECONFIGMESSAGE = _descriptor.Descriptor( - name='GreenBuildScheduleConfigMessage', - full_name='android.test.lab.GreenBuildScheduleConfigMessage', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='name', full_name='android.test.lab.GreenBuildScheduleConfigMessage.name', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='schedule', full_name='android.test.lab.GreenBuildScheduleConfigMessage.schedule', index=1, - number=2, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='priority', full_name='android.test.lab.GreenBuildScheduleConfigMessage.priority', index=2, - number=3, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='gsi_branch', full_name='android.test.lab.GreenBuildScheduleConfigMessage.gsi_branch', index=3, - number=11, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='gsi_pab_account_id', full_name='android.test.lab.GreenBuildScheduleConfigMessage.gsi_pab_account_id', index=4, - number=12, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='test_branch', full_name='android.test.lab.GreenBuildScheduleConfigMessage.test_branch', index=5, - number=21, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='test_pab_account_id', full_name='android.test.lab.GreenBuildScheduleConfigMessage.test_pab_account_id', index=6, - number=22, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='test', full_name='android.test.lab.GreenBuildScheduleConfigMessage.test', index=7, - number=31, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto2', - extension_ranges=[], - oneofs=[ - ], - serialized_start=60, - serialized_end=310, -) - - -_GREENBUILDTESTSCHEDULECONFIGMESSAGE = _descriptor.Descriptor( - name='GreenBuildTestScheduleConfigMessage', - full_name='android.test.lab.GreenBuildTestScheduleConfigMessage', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='test_name', full_name='android.test.lab.GreenBuildTestScheduleConfigMessage.test_name', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='test_build_target', full_name='android.test.lab.GreenBuildTestScheduleConfigMessage.test_build_target', index=1, - number=11, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='device_branch', full_name='android.test.lab.GreenBuildTestScheduleConfigMessage.device_branch', index=2, - number=21, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='device_pab_account_id', full_name='android.test.lab.GreenBuildTestScheduleConfigMessage.device_pab_account_id', index=3, - number=22, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='device', full_name='android.test.lab.GreenBuildTestScheduleConfigMessage.device', index=4, - number=31, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto2', - extension_ranges=[], - oneofs=[ - ], - serialized_start=313, - serialized_end=523, -) - - -_GREENBUILDDEVICESCHEDULECONFIGMESSAGE = _descriptor.Descriptor( - name='GreenBuildDeviceScheduleConfigMessage', - full_name='android.test.lab.GreenBuildDeviceScheduleConfigMessage', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='device', full_name='android.test.lab.GreenBuildDeviceScheduleConfigMessage.device', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='shards', full_name='android.test.lab.GreenBuildDeviceScheduleConfigMessage.shards', index=1, - number=11, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='device_branch', full_name='android.test.lab.GreenBuildDeviceScheduleConfigMessage.device_branch', index=2, - number=21, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='device_build_target', full_name='android.test.lab.GreenBuildDeviceScheduleConfigMessage.device_build_target', index=3, - number=22, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='device_pab_account_id', full_name='android.test.lab.GreenBuildDeviceScheduleConfigMessage.device_pab_account_id', index=4, - number=23, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='gsi_build_target', full_name='android.test.lab.GreenBuildDeviceScheduleConfigMessage.gsi_build_target', index=5, - number=31, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto2', - extension_ranges=[], - oneofs=[ - ], - serialized_start=526, - serialized_end=706, -) - -_GREENBUILDSCHEDULECONFIGMESSAGE.fields_by_name['test'].message_type = _GREENBUILDTESTSCHEDULECONFIGMESSAGE -_GREENBUILDTESTSCHEDULECONFIGMESSAGE.fields_by_name['device'].message_type = _GREENBUILDDEVICESCHEDULECONFIGMESSAGE -DESCRIPTOR.message_types_by_name['GreenBuildScheduleConfigMessage'] = _GREENBUILDSCHEDULECONFIGMESSAGE -DESCRIPTOR.message_types_by_name['GreenBuildTestScheduleConfigMessage'] = _GREENBUILDTESTSCHEDULECONFIGMESSAGE -DESCRIPTOR.message_types_by_name['GreenBuildDeviceScheduleConfigMessage'] = _GREENBUILDDEVICESCHEDULECONFIGMESSAGE - -GreenBuildScheduleConfigMessage = _reflection.GeneratedProtocolMessageType('GreenBuildScheduleConfigMessage', (_message.Message,), dict( - DESCRIPTOR = _GREENBUILDSCHEDULECONFIGMESSAGE, - __module__ = 'GreenBuildScheduleConfigMessage_pb2' - # @@protoc_insertion_point(class_scope:android.test.lab.GreenBuildScheduleConfigMessage) - )) -_sym_db.RegisterMessage(GreenBuildScheduleConfigMessage) - -GreenBuildTestScheduleConfigMessage = _reflection.GeneratedProtocolMessageType('GreenBuildTestScheduleConfigMessage', (_message.Message,), dict( - DESCRIPTOR = _GREENBUILDTESTSCHEDULECONFIGMESSAGE, - __module__ = 'GreenBuildScheduleConfigMessage_pb2' - # @@protoc_insertion_point(class_scope:android.test.lab.GreenBuildTestScheduleConfigMessage) - )) -_sym_db.RegisterMessage(GreenBuildTestScheduleConfigMessage) - -GreenBuildDeviceScheduleConfigMessage = _reflection.GeneratedProtocolMessageType('GreenBuildDeviceScheduleConfigMessage', (_message.Message,), dict( - DESCRIPTOR = _GREENBUILDDEVICESCHEDULECONFIGMESSAGE, - __module__ = 'GreenBuildScheduleConfigMessage_pb2' - # @@protoc_insertion_point(class_scope:android.test.lab.GreenBuildDeviceScheduleConfigMessage) - )) -_sym_db.RegisterMessage(GreenBuildDeviceScheduleConfigMessage) - - -# @@protoc_insertion_point(module_scope) diff --git a/proto/TestLabConfigMessage.proto b/proto/TestLabConfigMessage.proto deleted file mode 100644 index 2324ead..0000000 --- a/proto/TestLabConfigMessage.proto +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto2"; - -package android.test.lab; - -// To specify the whole test execution schedule of a manifest branch. -message LabConfigMessage { - // Lab name where format is labtype-location-instance (e.g., vtslab-mtv32-main). - optional bytes name = 1; - optional bytes owner = 2; - repeated bytes admin = 3; - - // For the IP address. - repeated HostConfigMessage host = 11; -} - -// To specify the basic information about a host computer. -message HostConfigMessage { - optional bytes hostname = 1; - optional bytes ip = 2; - - // host management scripts - optional bytes script = 3; - optional bytes setup_script = 4; - - // list of equipment installed on this host. - repeated bytes host_equipment = 5; - - repeated DeviceConfigMessage device = 11; -} - -// To specify information about a DUT (Device Under Test). -message DeviceConfigMessage { - // device serial number (retrieved by fastboot or adb). - optional bytes serial = 1; - - // optional - device index for a host starting from 0 to 13 (max). - optional int32 index = 2; - - // device product type (e.g., taimen or walleye). - optional bytes product = 11; - - // list of equipment installed on this device. - repeated bytes device_equipment = 21; -} - diff --git a/proto/TestLabConfigMessage_pb2.py b/proto/TestLabConfigMessage_pb2.py deleted file mode 100644 index eb1799c..0000000 --- a/proto/TestLabConfigMessage_pb2.py +++ /dev/null @@ -1,226 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: TestLabConfigMessage.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -from google.protobuf import descriptor_pb2 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='TestLabConfigMessage.proto', - package='android.test.lab', - syntax='proto2', - serialized_pb=_b('\n\x1aTestLabConfigMessage.proto\x12\x10\x61ndroid.test.lab\"q\n\x10LabConfigMessage\x12\x0c\n\x04name\x18\x01 \x01(\x0c\x12\r\n\x05owner\x18\x02 \x01(\x0c\x12\r\n\x05\x61\x64min\x18\x03 \x03(\x0c\x12\x31\n\x04host\x18\x0b \x03(\x0b\x32#.android.test.lab.HostConfigMessage\"\xa6\x01\n\x11HostConfigMessage\x12\x10\n\x08hostname\x18\x01 \x01(\x0c\x12\n\n\x02ip\x18\x02 \x01(\x0c\x12\x0e\n\x06script\x18\x03 \x01(\x0c\x12\x14\n\x0csetup_script\x18\x04 \x01(\x0c\x12\x16\n\x0ehost_equipment\x18\x05 \x03(\x0c\x12\x35\n\x06\x64\x65vice\x18\x0b \x03(\x0b\x32%.android.test.lab.DeviceConfigMessage\"_\n\x13\x44\x65viceConfigMessage\x12\x0e\n\x06serial\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\x05\x12\x0f\n\x07product\x18\x0b \x01(\x0c\x12\x18\n\x10\x64\x65vice_equipment\x18\x15 \x03(\x0c') -) -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - - - - -_LABCONFIGMESSAGE = _descriptor.Descriptor( - name='LabConfigMessage', - full_name='android.test.lab.LabConfigMessage', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='name', full_name='android.test.lab.LabConfigMessage.name', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='owner', full_name='android.test.lab.LabConfigMessage.owner', index=1, - number=2, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='admin', full_name='android.test.lab.LabConfigMessage.admin', index=2, - number=3, type=12, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='host', full_name='android.test.lab.LabConfigMessage.host', index=3, - number=11, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto2', - extension_ranges=[], - oneofs=[ - ], - serialized_start=48, - serialized_end=161, -) - - -_HOSTCONFIGMESSAGE = _descriptor.Descriptor( - name='HostConfigMessage', - full_name='android.test.lab.HostConfigMessage', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='hostname', full_name='android.test.lab.HostConfigMessage.hostname', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='ip', full_name='android.test.lab.HostConfigMessage.ip', index=1, - number=2, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='script', full_name='android.test.lab.HostConfigMessage.script', index=2, - number=3, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='setup_script', full_name='android.test.lab.HostConfigMessage.setup_script', index=3, - number=4, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='host_equipment', full_name='android.test.lab.HostConfigMessage.host_equipment', index=4, - number=5, type=12, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='device', full_name='android.test.lab.HostConfigMessage.device', index=5, - number=11, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto2', - extension_ranges=[], - oneofs=[ - ], - serialized_start=164, - serialized_end=330, -) - - -_DEVICECONFIGMESSAGE = _descriptor.Descriptor( - name='DeviceConfigMessage', - full_name='android.test.lab.DeviceConfigMessage', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='serial', full_name='android.test.lab.DeviceConfigMessage.serial', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='index', full_name='android.test.lab.DeviceConfigMessage.index', index=1, - number=2, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='product', full_name='android.test.lab.DeviceConfigMessage.product', index=2, - number=11, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='device_equipment', full_name='android.test.lab.DeviceConfigMessage.device_equipment', index=3, - number=21, type=12, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto2', - extension_ranges=[], - oneofs=[ - ], - serialized_start=332, - serialized_end=427, -) - -_LABCONFIGMESSAGE.fields_by_name['host'].message_type = _HOSTCONFIGMESSAGE -_HOSTCONFIGMESSAGE.fields_by_name['device'].message_type = _DEVICECONFIGMESSAGE -DESCRIPTOR.message_types_by_name['LabConfigMessage'] = _LABCONFIGMESSAGE -DESCRIPTOR.message_types_by_name['HostConfigMessage'] = _HOSTCONFIGMESSAGE -DESCRIPTOR.message_types_by_name['DeviceConfigMessage'] = _DEVICECONFIGMESSAGE - -LabConfigMessage = _reflection.GeneratedProtocolMessageType('LabConfigMessage', (_message.Message,), dict( - DESCRIPTOR = _LABCONFIGMESSAGE, - __module__ = 'TestLabConfigMessage_pb2' - # @@protoc_insertion_point(class_scope:android.test.lab.LabConfigMessage) - )) -_sym_db.RegisterMessage(LabConfigMessage) - -HostConfigMessage = _reflection.GeneratedProtocolMessageType('HostConfigMessage', (_message.Message,), dict( - DESCRIPTOR = _HOSTCONFIGMESSAGE, - __module__ = 'TestLabConfigMessage_pb2' - # @@protoc_insertion_point(class_scope:android.test.lab.HostConfigMessage) - )) -_sym_db.RegisterMessage(HostConfigMessage) - -DeviceConfigMessage = _reflection.GeneratedProtocolMessageType('DeviceConfigMessage', (_message.Message,), dict( - DESCRIPTOR = _DEVICECONFIGMESSAGE, - __module__ = 'TestLabConfigMessage_pb2' - # @@protoc_insertion_point(class_scope:android.test.lab.DeviceConfigMessage) - )) -_sym_db.RegisterMessage(DeviceConfigMessage) - - -# @@protoc_insertion_point(module_scope) diff --git a/proto/TestScheduleConfigMessage.proto b/proto/TestScheduleConfigMessage.proto deleted file mode 100644 index c2f7991..0000000 --- a/proto/TestScheduleConfigMessage.proto +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto2"; - -package android.test.lab; - -// Type of a command. -enum BuildStorageType { - UNKNOWN_BUILD_STORAGE_TYPE = 0; - // For Partner Android Build (PAB) - BUILD_STORAGE_TYPE_PAB = 1; - // For a Google Cloud Storage (GCS) bucket - BUILD_STORAGE_TYPE_GCS = 2; -} - -// To specify the whole test execution schedule of a manifest branch. -message ScheduleConfigMessage { - // Manifest branch name. - optional bytes manifest_branch = 1; - - // PAB account ID. - optional bytes pab_account_id = 2; - - // Device image storage type (e.g., PAB or GCS) - optional BuildStorageType build_storage_type = 3 [default = BUILD_STORAGE_TYPE_PAB]; - - // For the IP address. - repeated BuildScheduleConfigMessage build_target = 11; -} - -message BuildScheduleConfigMessage { - // build target name (e.g., aosp_taimen-userdebug) - optional bytes name = 1; - - // whether to use signed build. - optional bool require_signed_device_build = 2; - - // whether the build target has bootloader.img - optional bool has_bootloader_img = 3 [default = true]; - - // whether the build target has radio.img - optional bool has_radio_img = 4 [default = true]; - - repeated TestScheduleConfigMessage test_schedule = 11; -} - -message TestScheduleConfigMessage { - // Test name (e.g., test_group/test_plan) - optional bytes test_name = 1; - - // Scheduling period in minutes - optional int32 period = 2; - - // Priority such as high, low, or medium - optional bytes priority = 3; - - // such as <lab>/<device type> - repeated bytes device = 4; - - // number of shards (-1 for auto selection?) - optional int32 shards = 5; - - // required host-side equipment. - repeated bytes required_host_equipment = 6; - - // required device-side equipment such as a SIM card. - repeated bytes required_device_equipment = 7; - - optional bytes param = 11; - - // GSI storage type (e.g., PAB or GCS) - optional BuildStorageType gsi_storage_type = 24 [default = BUILD_STORAGE_TYPE_PAB]; - - // if GSI (General System Image) is used, GSI manifest branch name. - optional bytes gsi_branch = 21; - - // if GSI (General System Image) is used, GSI build target name. - optional bytes gsi_build_target = 22; - - // if GSI (General System Image) is used, GSI PAB account ID. - optional bytes gsi_pab_account_id = 23; - - // if GSI (General System Image) is used, the version of a vendor.img - // to use GSI with (e.g., 8.1.0). - optional bytes gsi_vendor_version = 25; - - // Test package's storage type (e.g., PAB or GCS) - optional BuildStorageType test_storage_type = 34 [default = BUILD_STORAGE_TYPE_PAB]; - - // Test package's manifest branch name. - optional bytes test_branch = 31; - - // Test package's build target name. - optional bytes test_build_target = 32; - - // PAB account ID for test package. - optional bytes test_pab_account_id = 33; - - // number of retry count. - optional int32 retry_count = 41; - - // whether to disable this schedule. - optional bool disable = 51 [default = false]; - - // Base GCS url for the repack command. - optional bytes image_package_repo_base = 61; - - // the GCS url where test results are uploaded - repeated bytes report_bucket = 71; - - // the ID of the spreadsheet where test results are uploaded - repeated bytes report_spreadsheet_id = 72; - - // the GCS url where the latest test result is uploaded - repeated bytes report_persistent_url = 73; - - // the GCS url where the reference result is. - repeated bytes report_reference_url = 74; -} diff --git a/proto/TestScheduleConfigMessage_pb2.py b/proto/TestScheduleConfigMessage_pb2.py deleted file mode 100644 index 46dafac..0000000 --- a/proto/TestScheduleConfigMessage_pb2.py +++ /dev/null @@ -1,394 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: vti/test_serving/proto/TestScheduleConfigMessage.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf.internal import enum_type_wrapper -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -from google.protobuf import descriptor_pb2 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='vti/test_serving/proto/TestScheduleConfigMessage.proto', - package='android.test.lab', - syntax='proto2', - serialized_pb=_b('\n6vti/test_serving/proto/TestScheduleConfigMessage.proto\x12\x10\x61ndroid.test.lab\"\xe4\x01\n\x15ScheduleConfigMessage\x12\x17\n\x0fmanifest_branch\x18\x01 \x01(\x0c\x12\x16\n\x0epab_account_id\x18\x02 \x01(\x0c\x12V\n\x12\x62uild_storage_type\x18\x03 \x01(\x0e\x32\".android.test.lab.BuildStorageType:\x16\x42UILD_STORAGE_TYPE_PAB\x12\x42\n\x0c\x62uild_target\x18\x0b \x03(\x0b\x32,.android.test.lab.BuildScheduleConfigMessage\"\xd2\x01\n\x1a\x42uildScheduleConfigMessage\x12\x0c\n\x04name\x18\x01 \x01(\x0c\x12#\n\x1brequire_signed_device_build\x18\x02 \x01(\x08\x12 \n\x12has_bootloader_img\x18\x03 \x01(\x08:\x04true\x12\x1b\n\rhas_radio_img\x18\x04 \x01(\x08:\x04true\x12\x42\n\rtest_schedule\x18\x0b \x03(\x0b\x32+.android.test.lab.TestScheduleConfigMessage\"\xe4\x05\n\x19TestScheduleConfigMessage\x12\x11\n\ttest_name\x18\x01 \x01(\x0c\x12\x0e\n\x06period\x18\x02 \x01(\x05\x12\x10\n\x08priority\x18\x03 \x01(\x0c\x12\x0e\n\x06\x64\x65vice\x18\x04 \x03(\x0c\x12\x0e\n\x06shards\x18\x05 \x01(\x05\x12\x1f\n\x17required_host_equipment\x18\x06 \x03(\x0c\x12!\n\x19required_device_equipment\x18\x07 \x03(\x0c\x12\r\n\x05param\x18\x0b \x01(\x0c\x12T\n\x10gsi_storage_type\x18\x18 \x01(\x0e\x32\".android.test.lab.BuildStorageType:\x16\x42UILD_STORAGE_TYPE_PAB\x12\x12\n\ngsi_branch\x18\x15 \x01(\x0c\x12\x18\n\x10gsi_build_target\x18\x16 \x01(\x0c\x12\x1a\n\x12gsi_pab_account_id\x18\x17 \x01(\x0c\x12\x1a\n\x12gsi_vendor_version\x18\x19 \x01(\x0c\x12U\n\x11test_storage_type\x18\" \x01(\x0e\x32\".android.test.lab.BuildStorageType:\x16\x42UILD_STORAGE_TYPE_PAB\x12\x13\n\x0btest_branch\x18\x1f \x01(\x0c\x12\x19\n\x11test_build_target\x18 \x01(\x0c\x12\x1b\n\x13test_pab_account_id\x18! \x01(\x0c\x12\x13\n\x0bretry_count\x18) \x01(\x05\x12\x16\n\x07\x64isable\x18\x33 \x01(\x08:\x05\x66\x61lse\x12\x1f\n\x17image_package_repo_base\x18= \x01(\x0c\x12\x15\n\rreport_bucket\x18G \x03(\x0c\x12\x1d\n\x15report_spreadsheet_id\x18H \x03(\x0c\x12\x1d\n\x15report_persistent_url\x18I \x03(\x0c\x12\x1c\n\x14report_reference_url\x18J \x03(\x0c*j\n\x10\x42uildStorageType\x12\x1e\n\x1aUNKNOWN_BUILD_STORAGE_TYPE\x10\x00\x12\x1a\n\x16\x42UILD_STORAGE_TYPE_PAB\x10\x01\x12\x1a\n\x16\x42UILD_STORAGE_TYPE_GCS\x10\x02') -) -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -_BUILDSTORAGETYPE = _descriptor.EnumDescriptor( - name='BuildStorageType', - full_name='android.test.lab.BuildStorageType', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='UNKNOWN_BUILD_STORAGE_TYPE', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BUILD_STORAGE_TYPE_PAB', index=1, number=1, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BUILD_STORAGE_TYPE_GCS', index=2, number=2, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=1263, - serialized_end=1369, -) -_sym_db.RegisterEnumDescriptor(_BUILDSTORAGETYPE) - -BuildStorageType = enum_type_wrapper.EnumTypeWrapper(_BUILDSTORAGETYPE) -UNKNOWN_BUILD_STORAGE_TYPE = 0 -BUILD_STORAGE_TYPE_PAB = 1 -BUILD_STORAGE_TYPE_GCS = 2 - - - -_SCHEDULECONFIGMESSAGE = _descriptor.Descriptor( - name='ScheduleConfigMessage', - full_name='android.test.lab.ScheduleConfigMessage', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='manifest_branch', full_name='android.test.lab.ScheduleConfigMessage.manifest_branch', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='pab_account_id', full_name='android.test.lab.ScheduleConfigMessage.pab_account_id', index=1, - number=2, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='build_storage_type', full_name='android.test.lab.ScheduleConfigMessage.build_storage_type', index=2, - number=3, type=14, cpp_type=8, label=1, - has_default_value=True, default_value=1, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='build_target', full_name='android.test.lab.ScheduleConfigMessage.build_target', index=3, - number=11, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto2', - extension_ranges=[], - oneofs=[ - ], - serialized_start=77, - serialized_end=305, -) - - -_BUILDSCHEDULECONFIGMESSAGE = _descriptor.Descriptor( - name='BuildScheduleConfigMessage', - full_name='android.test.lab.BuildScheduleConfigMessage', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='name', full_name='android.test.lab.BuildScheduleConfigMessage.name', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='require_signed_device_build', full_name='android.test.lab.BuildScheduleConfigMessage.require_signed_device_build', index=1, - number=2, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='has_bootloader_img', full_name='android.test.lab.BuildScheduleConfigMessage.has_bootloader_img', index=2, - number=3, type=8, cpp_type=7, label=1, - has_default_value=True, default_value=True, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='has_radio_img', full_name='android.test.lab.BuildScheduleConfigMessage.has_radio_img', index=3, - number=4, type=8, cpp_type=7, label=1, - has_default_value=True, default_value=True, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='test_schedule', full_name='android.test.lab.BuildScheduleConfigMessage.test_schedule', index=4, - number=11, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto2', - extension_ranges=[], - oneofs=[ - ], - serialized_start=308, - serialized_end=518, -) - - -_TESTSCHEDULECONFIGMESSAGE = _descriptor.Descriptor( - name='TestScheduleConfigMessage', - full_name='android.test.lab.TestScheduleConfigMessage', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='test_name', full_name='android.test.lab.TestScheduleConfigMessage.test_name', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='period', full_name='android.test.lab.TestScheduleConfigMessage.period', index=1, - number=2, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='priority', full_name='android.test.lab.TestScheduleConfigMessage.priority', index=2, - number=3, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='device', full_name='android.test.lab.TestScheduleConfigMessage.device', index=3, - number=4, type=12, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='shards', full_name='android.test.lab.TestScheduleConfigMessage.shards', index=4, - number=5, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='required_host_equipment', full_name='android.test.lab.TestScheduleConfigMessage.required_host_equipment', index=5, - number=6, type=12, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='required_device_equipment', full_name='android.test.lab.TestScheduleConfigMessage.required_device_equipment', index=6, - number=7, type=12, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='param', full_name='android.test.lab.TestScheduleConfigMessage.param', index=7, - number=11, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='gsi_storage_type', full_name='android.test.lab.TestScheduleConfigMessage.gsi_storage_type', index=8, - number=24, type=14, cpp_type=8, label=1, - has_default_value=True, default_value=1, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='gsi_branch', full_name='android.test.lab.TestScheduleConfigMessage.gsi_branch', index=9, - number=21, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='gsi_build_target', full_name='android.test.lab.TestScheduleConfigMessage.gsi_build_target', index=10, - number=22, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='gsi_pab_account_id', full_name='android.test.lab.TestScheduleConfigMessage.gsi_pab_account_id', index=11, - number=23, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='gsi_vendor_version', full_name='android.test.lab.TestScheduleConfigMessage.gsi_vendor_version', index=12, - number=25, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='test_storage_type', full_name='android.test.lab.TestScheduleConfigMessage.test_storage_type', index=13, - number=34, type=14, cpp_type=8, label=1, - has_default_value=True, default_value=1, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='test_branch', full_name='android.test.lab.TestScheduleConfigMessage.test_branch', index=14, - number=31, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='test_build_target', full_name='android.test.lab.TestScheduleConfigMessage.test_build_target', index=15, - number=32, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='test_pab_account_id', full_name='android.test.lab.TestScheduleConfigMessage.test_pab_account_id', index=16, - number=33, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='retry_count', full_name='android.test.lab.TestScheduleConfigMessage.retry_count', index=17, - number=41, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='disable', full_name='android.test.lab.TestScheduleConfigMessage.disable', index=18, - number=51, type=8, cpp_type=7, label=1, - has_default_value=True, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='image_package_repo_base', full_name='android.test.lab.TestScheduleConfigMessage.image_package_repo_base', index=19, - number=61, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='report_bucket', full_name='android.test.lab.TestScheduleConfigMessage.report_bucket', index=20, - number=71, type=12, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='report_spreadsheet_id', full_name='android.test.lab.TestScheduleConfigMessage.report_spreadsheet_id', index=21, - number=72, type=12, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='report_persistent_url', full_name='android.test.lab.TestScheduleConfigMessage.report_persistent_url', index=22, - number=73, type=12, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='report_reference_url', full_name='android.test.lab.TestScheduleConfigMessage.report_reference_url', index=23, - number=74, type=12, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto2', - extension_ranges=[], - oneofs=[ - ], - serialized_start=521, - serialized_end=1261, -) - -_SCHEDULECONFIGMESSAGE.fields_by_name['build_storage_type'].enum_type = _BUILDSTORAGETYPE -_SCHEDULECONFIGMESSAGE.fields_by_name['build_target'].message_type = _BUILDSCHEDULECONFIGMESSAGE -_BUILDSCHEDULECONFIGMESSAGE.fields_by_name['test_schedule'].message_type = _TESTSCHEDULECONFIGMESSAGE -_TESTSCHEDULECONFIGMESSAGE.fields_by_name['gsi_storage_type'].enum_type = _BUILDSTORAGETYPE -_TESTSCHEDULECONFIGMESSAGE.fields_by_name['test_storage_type'].enum_type = _BUILDSTORAGETYPE -DESCRIPTOR.message_types_by_name['ScheduleConfigMessage'] = _SCHEDULECONFIGMESSAGE -DESCRIPTOR.message_types_by_name['BuildScheduleConfigMessage'] = _BUILDSCHEDULECONFIGMESSAGE -DESCRIPTOR.message_types_by_name['TestScheduleConfigMessage'] = _TESTSCHEDULECONFIGMESSAGE -DESCRIPTOR.enum_types_by_name['BuildStorageType'] = _BUILDSTORAGETYPE - -ScheduleConfigMessage = _reflection.GeneratedProtocolMessageType('ScheduleConfigMessage', (_message.Message,), dict( - DESCRIPTOR = _SCHEDULECONFIGMESSAGE, - __module__ = 'vti.test_serving.proto.TestScheduleConfigMessage_pb2' - # @@protoc_insertion_point(class_scope:android.test.lab.ScheduleConfigMessage) - )) -_sym_db.RegisterMessage(ScheduleConfigMessage) - -BuildScheduleConfigMessage = _reflection.GeneratedProtocolMessageType('BuildScheduleConfigMessage', (_message.Message,), dict( - DESCRIPTOR = _BUILDSCHEDULECONFIGMESSAGE, - __module__ = 'vti.test_serving.proto.TestScheduleConfigMessage_pb2' - # @@protoc_insertion_point(class_scope:android.test.lab.BuildScheduleConfigMessage) - )) -_sym_db.RegisterMessage(BuildScheduleConfigMessage) - -TestScheduleConfigMessage = _reflection.GeneratedProtocolMessageType('TestScheduleConfigMessage', (_message.Message,), dict( - DESCRIPTOR = _TESTSCHEDULECONFIGMESSAGE, - __module__ = 'vti.test_serving.proto.TestScheduleConfigMessage_pb2' - # @@protoc_insertion_point(class_scope:android.test.lab.TestScheduleConfigMessage) - )) -_sym_db.RegisterMessage(TestScheduleConfigMessage) - - -# @@protoc_insertion_point(module_scope) diff --git a/proto/__init__.py b/proto/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/proto/__init__.py +++ /dev/null diff --git a/script/build-python.sh b/script/build-python.sh deleted file mode 100755 index f8d2fb0..0000000 --- a/script/build-python.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -# -# Copyright 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Modifies any import statements (to remove subdir path) -pushd ${ANDROID_BUILD_TOP}/test - -protoc -I=. --python_out=. ./vti/test_serving/proto/TestLabConfigMessage.proto -protoc -I=. --python_out=. ./vti/test_serving/proto/TestScheduleConfigMessage.proto -protoc -I=. --python_out=. ./vti/test_serving/proto/GreenBuildScheduleConfigMessage.proto - -# Compiles all the python source codes. -python -m compileall . - -popd diff --git a/script/pack-gae.sh b/script/pack-gae.sh deleted file mode 100755 index 9917aef..0000000 --- a/script/pack-gae.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -# -# Copyright 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -if [ -d "$ANDROID_BUILD_TOP" ]; then - TEST_SERVING_DIR="$ANDROID_BUILD_TOP/test/vti/test_serving" -else - CURRENT_DIR_NAME="${PWD##*/}" - if [ "${CURRENT_DIR_NAME}" = "test_serving" ]; then - TEST_SERVING_DIR="${PWD}" - elif [ "${CURRENT_DIR_NAME}" = "script" ]; then - TEST_SERVING_DIR="${PWD}/.." - else - echo "Missing ANDROID_BUILD_TOP env variable. Run 'lunch' first." - exit 1 - fi -fi - -if [ ! -d "${TEST_SERVING_DIR}/gae" ]; then - echo "Please run this script in 'test_serving' directory." - exit 1 -fi - -pushd $TEST_SERVING_DIR/gae -echo "Removing unnecessary files in ${TEST_SERVING_DIR}/gae directory..." -git clean -f - -echo "Updating python libraries..." -rm -rf lib/ -./script/install-pip.sh -popd - -pushd $TEST_SERVING_DIR/ -zip vtslab-scheduler-$(git log -s -n 1 --format="%cd" --date=format:"%Y%m%d_%H%M%S")-$(git rev-parse --short HEAD).zip -r gae -x *.pyc "*/\.*" *.DS_Store* gae/frontend/node_modules**\* -popd diff --git a/script/run-unittest.sh b/script/run-unittest.sh deleted file mode 100755 index 6ce6949..0000000 --- a/script/run-unittest.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -# -# Copyright 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -if [ -z "$ANDROID_BUILD_TOP" ]; then - echo "Missing ANDROID_BUILD_TOP env variable. Run 'lunch' first." - exit 1 -fi - -# Runs all unit tests under test/vti/test_serving/gae using an e2e_test framework. -pushd $ANDROID_BUILD_TOP/test/vti/test_serving/gae -python testing/e2e_test.py -popd - |