Merge pull request #20 from MycroftAI/master

down-merging master to dev
pull/21/head
Chris Veilleux 2018-11-14 18:58:55 -06:00 committed by GitHub
commit 336b1f2ee4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
259 changed files with 6323 additions and 14940 deletions

View File

@ -2,7 +2,7 @@
# The selene-shared parent image contains all the common Docker configs for
# all Selene apps and services see the "shared" directory in this repository.
FROM docker.mycroft.ai/selene-shared:latest
FROM docker.mycroft.ai/selene-shared:2018.4
LABEL description="Run the API for the Mycroft marketplace"
# Use pipenv to install the package's dependencies in the container

View File

@ -4,14 +4,14 @@ verify_ssl = true
name = "pypi"
[packages]
flask-restful = "*"
flask = "*"
requests = "*"
certifi = "*"
flask-restful = "*"
pyjwt = "*"
uwsgi = "*"
markdown = "*"
[dev-packages]
selene-util = {path = "./../../../../shared"}
[requires]
python_version = "3.7"

View File

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "7cf1dde24d5a966645f3e49d93dde93dad42c6b6fa62f9b254b79d6b58e93e06"
"sha256": "b4d53000e056d8f0a03cde9f3a8a2d4a96d19309908aee9020357cc3800c52cc"
},
"pipfile-spec": 6,
"requires": {
@ -18,18 +18,17 @@
"default": {
"aniso8601": {
"hashes": [
"sha256:7849749cf00ae0680ad2bdfe4419c7a662bef19c03691a19e008c8b9a5267802",
"sha256:94f90871fcd314a458a3d4eca1c84448efbd200e86f55fe4c733c7a40149ef50"
"sha256:547e7bc88c19742e519fb4ca39f4b8113fdfb8fca322e325f16a8bfc6cfc553c",
"sha256:e7560de91bf00baa712b2550a2fdebf0188c5fce2fcd1162fbac75c19bb29c95"
],
"version": "==3.0.2"
"version": "==4.0.1"
},
"certifi": {
"hashes": [
"sha256:376690d6f16d32f9d1fe8932551d80b23e9d393a8578c5633a2ed39a64861638",
"sha256:456048c7e371c089d0a77a5212fb37a2c2dce1e24146e3b7e0261736aaeaa22a"
"sha256:339dc09518b07e2fa7eda5450740925974815557727d6bd35d319c1524a04a4c",
"sha256:6d58c986d22b038c8c0df30d639f23a3e6d172a05c3583e766f4c0b785c0986a"
],
"index": "pypi",
"version": "==2018.8.24"
"version": "==2018.10.15"
},
"chardet": {
"hashes": [
@ -43,7 +42,6 @@
"sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
"sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
],
"markers": "python_version >= '2.7' and python_version != '3.3.*' and python_version != '3.0.*' and python_version != '3.2.*' and python_version != '3.1.*'",
"version": "==7.0"
},
"flask": {
@ -71,9 +69,10 @@
},
"itsdangerous": {
"hashes": [
"sha256:cbb3fcf8d3e33df861709ecaf89d9e6629cff0a217bc2848f1b41cd30d360519"
"sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19",
"sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"
],
"version": "==0.24"
"version": "==1.1.0"
},
"jinja2": {
"hashes": [
@ -82,6 +81,14 @@
],
"version": "==2.10"
},
"markdown": {
"hashes": [
"sha256:c00429bd503a47ec88d5e30a751e147dcb4c6889663cd3e2ba0afe858e009baa",
"sha256:d02e0f9b04c500cde6637c11ad7c72671f359b87b9fe924b2383649d8841db7c"
],
"index": "pypi",
"version": "==3.0.1"
},
"markupsafe": {
"hashes": [
"sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665"
@ -98,18 +105,18 @@
},
"pytz": {
"hashes": [
"sha256:a061aa0a9e06881eb8b3b2b43f05b9439d6583c206d0a6c340ff72a7b6669053",
"sha256:ffb9ef1de172603304d9d2819af6f5ece76f2e85ec10692a524dd876e72bf277"
"sha256:31cb35c89bd7d333cd32c5f278fca91b523b0834369e757f4c5641ea252236ca",
"sha256:8e0f8568c118d3077b46be7d654cc8167fa916092e28320cde048e54bfc9f1e6"
],
"version": "==2018.5"
"version": "==2018.7"
},
"requests": {
"hashes": [
"sha256:63b52e3c866428a224f97cab011de738c36aec0185aa91cfacd418b5d58911d1",
"sha256:ec22d826a36ed72a7358ff3fe56cbd4ba69dd7a6718ffd450ff0e9df7a47ce6a"
"sha256:99dcfdaaeb17caf6e526f32b6a7b780461512ab3f1d992187801694cba42770c",
"sha256:a84b8c9ab6239b578f22d1c21d51b696dcfe004032bb80ea832398d6909d7279"
],
"index": "pypi",
"version": "==2.19.1"
"version": "==2.20.0"
},
"six": {
"hashes": [
@ -120,11 +127,10 @@
},
"urllib3": {
"hashes": [
"sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf",
"sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5"
"sha256:41c3db2fc01e5b907288010dec72f9d0a74e37d6994e6eb56849f59fea2265ae",
"sha256:8819bba37a02d143296a4d032373c4dd4aca11f6d4c9973335ca75f9c8475f59"
],
"markers": "python_version >= '2.6' and python_version != '3.1.*' and python_version != '3.3.*' and python_version < '4' and python_version != '3.0.*' and python_version != '3.2.*'",
"version": "==1.23"
"version": "==1.24"
},
"uwsgi": {
"hashes": [
@ -141,9 +147,5 @@
"version": "==0.14.1"
}
},
"develop": {
"selene-util": {
"path": "./../../../../shared"
}
}
"develop": {}
}

View File

@ -12,19 +12,19 @@ from market_api.endpoints import (
)
# Define the Flask application
marketplace = Flask(__name__)
marketplace.config.from_object(get_config_location())
market = Flask(__name__)
market.config.from_object(get_config_location())
# Define the API and its endpoints.
marketplace_api = Api(marketplace)
marketplace_api.add_resource(AvailableSkillsEndpoint, '/api/skill/available')
marketplace_api.add_resource(
market_api = Api(market)
market_api.add_resource(AvailableSkillsEndpoint, '/api/skill/available')
market_api.add_resource(
SkillDetailEndpoint,
'/api/skill/detail/<skill_name>'
)
marketplace_api.add_resource(SkillInstallEndpoint, '/api/skill/install')
marketplace_api.add_resource(
market_api.add_resource(SkillInstallEndpoint, '/api/skill/install')
market_api.add_resource(
SkillInstallationsEndpoint,
'/api/skill/installations'
)
marketplace_api.add_resource(UserEndpoint, '/api/user')
market_api.add_resource(UserEndpoint, '/api/user')

View File

@ -10,7 +10,7 @@ class BaseConfig:
"""Base configuration."""
DEBUG = False
SECRET_KEY = os.environ['JWT_SECRET']
SELENE_BASE_URL = os.environ['SELENE_BASE_URL']
SERVICE_BASE_URL = os.environ['SERVICE_BASE_URL']
TARTARUS_BASE_URL = os.environ['TARTARUS_BASE_URL']

View File

@ -40,7 +40,7 @@ class AvailableSkillsEndpoint(SeleneEndpoint):
repository. The JSON object contains metadata about each skill.
"""
skill_service_response = service_request.get(
self.config['SELENE_BASE_URL'] + '/skill/all'
self.config['SERVICE_BASE_URL'] + '/skill/all'
)
if skill_service_response.status_code != HTTPStatus.OK:
self._check_for_service_errors(skill_service_response)

View File

@ -34,7 +34,7 @@ class SkillDetailEndpoint(SeleneEndpoint):
def _get_skill_details(self) -> RepositorySkill:
"""Build the data to include in the response."""
skill_service_response = service_request.get(
self.config['SELENE_BASE_URL'] + '/skill/name/' + self.skill_name
self.config['SERVICE_BASE_URL'] + '/skill/name/' + self.skill_name
)
self._check_for_service_errors(skill_service_response)

View File

@ -0,0 +1,228 @@
swagger: '2.0'
info:
description: >-
The marketplace is where users can access the skills available to install on
their devices. In the future, other products like voices and hardware will
be available through the store.
version: '2018.3'
title: Mycroft Marketplace
host: market.mycroft.ai
basePath: /api
tags:
- name: skill
description: >-
Browse information about available skills and manage skills on your
devices.
schemes:
- https
paths:
/skill/available:
get:
tags:
- skill
summary: Retrieve all available skills for devices using Mycroft.
description: >-
The data retrieved is based on the skill metadata found in the
mycroft-skills-data Github repository.
parameters:
- name: search
in: query
description: >-
Filter skills by comparing the value of this parameter to a skill's
title, summary, description, categories and tags. All skills are
returned when this parameter is omitted.
required: false
type: string
produces:
- application/json
responses:
'200':
description: successful operation
schema:
type: array
items:
$ref: '#/definitions/SkillSummary'
'/skill/detail/{skillName}':
get:
tags:
- skill
summary: Retrieve more detailed information about a selected skill.
description: >-
This endpoint provides more information about a skill than is provided
in the available skills endpoint.
parameters:
- name: skillName
in: path
description: Unique name of the skill to return.
required: true
type: string
produces:
- application/json
responses:
'200':
description: successful operation
schema:
type: array
items:
$ref: '#/definitions/SkillDetail'
'/skill/installations':
get:
tags:
- skill
summary: Retrieve the installation status of skills for a user.
description: >-
When a user is logged in, this endpoint will return the skills known by that user's device(s). It will communicate the installation status of each skill as it relates to a user's devices.
produces:
- application/json
responses:
'200':
description: successful operation
schema:
type: array
items:
$ref: '#/definitions/Installations'
definitions:
SkillSummary:
description: >-
Subset of the skill metadata used for generating a list of available
skills.
type: object
properties:
icon:
$ref: '#/definitions/Icon'
iconImage:
description: >-
Url to an icon image. When provided it will take precedence over the
value of the icon field.
format: uri
type: string
isMycroftMade:
description: Denotes a skill that is written by someone on the Mycroft team.
type: boolean
isSystemSkill:
description: >-
System skills are treated differently than others in that they are all
installed on a device by default and cannot be installed. Volume
control is an example.
type: boolean
marketCategory:
description: >-
A skill may have many categories. The first category defined is used
when displaying the list of available skills.
example: Music
type: string
name:
description: >-
uniquely identifying name of skill that is the same as the skill's
submodule name in the mycroft-skills Github repository
example: spotify
type: string
summary:
description: A short phrase describing the skill's function
example: Listen to music from your Spotify account
type: string
title:
example: Spotify
type: string
trigger:
description: Example of a voice command that triggers the skill.
example: Play Rush on Spotify
type: string
SkillDetail:
type: object
properties:
categories:
description: All categories related to the skill
example:
- Music
- Entertainment
type: array
items:
type: string
credits:
$ref: '#/definitions/Credits'
description:
description: Detailed description of the skill and its capabilities.
type: string
icon:
$ref: '#/definitions/Icon'
iconImage:
description: >-
Url to an icon image. When provided it will take precedence over the
value of the icon field.
format: uri
type: string
isSystemSkill:
description: >-
System skills are treated differently than others in that they are all
installed on a device by default and cannot be installed. Volume
control is an example.
type: boolean
marketCategory:
description: >-
A skill may have many categories. The first category defined is used
when displaying the list of available skills.
example: Music
type: string
name:
description: >-
uniquely identifying name of skill that is the same as the skill's
submodule name in the mycroft-skills Github repository
example: Spotify
type: string
platforms:
description: Lists the platforms this skill can run on. Defaults to "any"
example:
- Mark I
- Mark II
type: array
items:
type: string
repositoryUrl:
description: A URL representing the Github page for that skill.
summary:
description: A short phrase describing the skill's function
example: Listen to music from your Spotify account
type: string
title:
example: Spotify
type: string
triggers:
description: Examples of a voice commands that trigger the skill.
example:
- Play Rush on Spotify
- Play Discover Weekly
type: array
items:
type: string
Credits:
type: object
properties:
name:
example: Mycroft AI
type: string
githubId:
type: string
Icon:
description: >-
Identifies a FontAwesome icon representing the skill and the color of the
icon
type: object
properties:
name:
description: Name of the icon as defined in the FontAwesome library
example: music
type: string
color:
description: The color that will be applied to the icon in the UI.
example: '#22a7f0'
type: string
format: hex
Installations:
description: >-
Install status of all skills on a user's device(s) and the reason for installation failures, if there are any.
properties:
installStatuses:
type: object
failureReasons:
type: object

View File

@ -1,5 +1,5 @@
[uwsgi]
master = true
module = market_api.api:marketplace
module = market_api.api:market
processes = 4
socket = :7101

View File

@ -2,7 +2,7 @@
# The selene-shared parent image contains all the common Docker configs for
# all Selene apps and services see the "shared" directory in this repository.
FROM docker.mycroft.ai/selene-shared:latest
FROM docker.mycroft.ai/selene-shared:2018.4
LABEL description="Run the API for the Mycroft login screen"
# Use pipenv to install the package's dependencies in the container
@ -16,7 +16,7 @@ RUN rm Pipfile
RUN rm Pipfile.lock
# Load the skill service application to the image
COPY login_api /opt/selene/login_api
COPY sso_api /opt/selene/sso_api
WORKDIR /opt/selene/
EXPOSE 7102

View File

@ -12,7 +12,6 @@ certifi = "*"
uwsgi = "*"
[dev-packages]
selene-util = {path = "./../../../../shared"}
[requires]
python_version = "3.7"

171
backend/api/sso/Pipfile.lock generated Normal file
View File

@ -0,0 +1,171 @@
{
"_meta": {
"hash": {
"sha256": "a9e06c9e1f71a037b615d9ad628b9fe9954e7fa97e0e0460626c8aa59ecb0ccf"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.7"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"aniso8601": {
"hashes": [
"sha256:547e7bc88c19742e519fb4ca39f4b8113fdfb8fca322e325f16a8bfc6cfc553c",
"sha256:e7560de91bf00baa712b2550a2fdebf0188c5fce2fcd1162fbac75c19bb29c95"
],
"version": "==4.0.1"
},
"certifi": {
"hashes": [
"sha256:339dc09518b07e2fa7eda5450740925974815557727d6bd35d319c1524a04a4c",
"sha256:6d58c986d22b038c8c0df30d639f23a3e6d172a05c3583e766f4c0b785c0986a"
],
"index": "pypi",
"version": "==2018.10.15"
},
"chardet": {
"hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
],
"version": "==3.0.4"
},
"click": {
"hashes": [
"sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
"sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
],
"version": "==7.0"
},
"flask": {
"hashes": [
"sha256:2271c0070dbcb5275fad4a82e29f23ab92682dc45f9dfbc22c02ba9b9322ce48",
"sha256:a080b744b7e345ccfcbc77954861cb05b3c63786e93f2b3875e0913d44b43f05"
],
"index": "pypi",
"version": "==1.0.2"
},
"flask-restful": {
"hashes": [
"sha256:5795519501347e108c436b693ff9a4d7b373a3ac9069627d64e4001c05dd3407",
"sha256:e2f1b8063de3944b94c7f8be5cee4d2161db0267c54c5b757d875295061776fa"
],
"index": "pypi",
"version": "==0.3.6"
},
"idna": {
"hashes": [
"sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e",
"sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16"
],
"version": "==2.7"
},
"itsdangerous": {
"hashes": [
"sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19",
"sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"
],
"version": "==1.1.0"
},
"jinja2": {
"hashes": [
"sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd",
"sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"
],
"version": "==2.10"
},
"markupsafe": {
"hashes": [
"sha256:048ef924c1623740e70204aa7143ec592504045ae4429b59c30054cb31e3c432",
"sha256:130f844e7f5bdd8e9f3f42e7102ef1d49b2e6fdf0d7526df3f87281a532d8c8b",
"sha256:19f637c2ac5ae9da8bfd98cef74d64b7e1bb8a63038a3505cd182c3fac5eb4d9",
"sha256:1b8a7a87ad1b92bd887568ce54b23565f3fd7018c4180136e1cf412b405a47af",
"sha256:1c25694ca680b6919de53a4bb3bdd0602beafc63ff001fea2f2fc16ec3a11834",
"sha256:1f19ef5d3908110e1e891deefb5586aae1b49a7440db952454b4e281b41620cd",
"sha256:1fa6058938190ebe8290e5cae6c351e14e7bb44505c4a7624555ce57fbbeba0d",
"sha256:31cbb1359e8c25f9f48e156e59e2eaad51cd5242c05ed18a8de6dbe85184e4b7",
"sha256:3e835d8841ae7863f64e40e19477f7eb398674da6a47f09871673742531e6f4b",
"sha256:4e97332c9ce444b0c2c38dd22ddc61c743eb208d916e4265a2a3b575bdccb1d3",
"sha256:525396ee324ee2da82919f2ee9c9e73b012f23e7640131dd1b53a90206a0f09c",
"sha256:52b07fbc32032c21ad4ab060fec137b76eb804c4b9a1c7c7dc562549306afad2",
"sha256:52ccb45e77a1085ec5461cde794e1aa037df79f473cbc69b974e73940655c8d7",
"sha256:5c3fbebd7de20ce93103cb3183b47671f2885307df4a17a0ad56a1dd51273d36",
"sha256:5e5851969aea17660e55f6a3be00037a25b96a9b44d2083651812c99d53b14d1",
"sha256:5edfa27b2d3eefa2210fb2f5d539fbed81722b49f083b2c6566455eb7422fd7e",
"sha256:7d263e5770efddf465a9e31b78362d84d015cc894ca2c131901a4445eaa61ee1",
"sha256:83381342bfc22b3c8c06f2dd93a505413888694302de25add756254beee8449c",
"sha256:857eebb2c1dc60e4219ec8e98dfa19553dae33608237e107db9c6078b1167856",
"sha256:98e439297f78fca3a6169fd330fbe88d78b3bb72f967ad9961bcac0d7fdd1550",
"sha256:bf54103892a83c64db58125b3f2a43df6d2cb2d28889f14c78519394feb41492",
"sha256:d9ac82be533394d341b41d78aca7ed0e0f4ba5a2231602e2f05aa87f25c51672",
"sha256:e982fe07ede9fada6ff6705af70514a52beb1b2c3d25d4e873e82114cf3c5401",
"sha256:edce2ea7f3dfc981c4ddc97add8a61381d9642dc3273737e756517cc03e84dd6",
"sha256:efdc45ef1afc238db84cb4963aa689c0408912a0239b0721cb172b4016eb31d6",
"sha256:f137c02498f8b935892d5c0172560d7ab54bc45039de8805075e19079c639a9c",
"sha256:f82e347a72f955b7017a39708a3667f106e6ad4d10b25f237396a7115d8ed5fd",
"sha256:fb7c206e01ad85ce57feeaaa0bf784b97fa3cad0d4a5737bc5295785f5c613a1"
],
"version": "==1.1.0"
},
"pyjwt": {
"hashes": [
"sha256:30b1380ff43b55441283cc2b2676b755cca45693ae3097325dea01f3d110628c",
"sha256:4ee413b357d53fd3fb44704577afac88e72e878716116270d722723d65b42176"
],
"index": "pypi",
"version": "==1.6.4"
},
"pytz": {
"hashes": [
"sha256:31cb35c89bd7d333cd32c5f278fca91b523b0834369e757f4c5641ea252236ca",
"sha256:8e0f8568c118d3077b46be7d654cc8167fa916092e28320cde048e54bfc9f1e6"
],
"version": "==2018.7"
},
"requests": {
"hashes": [
"sha256:65b3a120e4329e33c9889db89c80976c5272f56ea92d3e74da8a463992e3ff54",
"sha256:ea881206e59f41dbd0bd445437d792e43906703fff75ca8ff43ccdb11f33f263"
],
"index": "pypi",
"version": "==2.20.1"
},
"six": {
"hashes": [
"sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
"sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
],
"version": "==1.11.0"
},
"urllib3": {
"hashes": [
"sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39",
"sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22"
],
"version": "==1.24.1"
},
"uwsgi": {
"hashes": [
"sha256:d2318235c74665a60021a4fc7770e9c2756f9fc07de7b8c22805efe85b5ab277"
],
"index": "pypi",
"version": "==2.0.17.1"
},
"werkzeug": {
"hashes": [
"sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c",
"sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b"
],
"version": "==0.14.1"
}
},
"develop": {}
}

View File

@ -11,17 +11,17 @@ from .endpoints import (
)
from .config import get_config_location
# Initialize the Flask application and the Flask Restful API
login = Flask(__name__)
login.config.from_object(get_config_location())
login_api = Api(login, catch_all_404s=True)
sso = Flask(__name__)
sso.config.from_object(get_config_location())
sso_api = Api(sso, catch_all_404s=True)
# Define the endpoints
login_api.add_resource(AuthenticateAntisocialEndpoint, '/api/antisocial')
login_api.add_resource(AuthorizeFacebookEndpoint, '/api/social/facebook')
login_api.add_resource(AuthorizeGithubEndpoint, '/api/social/github')
login_api.add_resource(AuthorizeGoogleEndpoint, '/api/social/google')
login_api.add_resource(SocialLoginTokensEndpoint, '/api/social/tokens')
login_api.add_resource(LogoutEndpoint, '/api/logout')
sso_api.add_resource(AuthenticateAntisocialEndpoint, '/api/antisocial')
sso_api.add_resource(AuthorizeFacebookEndpoint, '/api/social/facebook')
sso_api.add_resource(AuthorizeGithubEndpoint, '/api/social/github')
sso_api.add_resource(AuthorizeGoogleEndpoint, '/api/social/google')
sso_api.add_resource(SocialLoginTokensEndpoint, '/api/social/tokens')
sso_api.add_resource(LogoutEndpoint, '/api/logout')
def add_cors_headers(response):
@ -38,4 +38,4 @@ def add_cors_headers(response):
return response
login.after_request(add_cors_headers)
sso.after_request(add_cors_headers)

View File

@ -8,9 +8,9 @@ class LoginConfigException(Exception):
class BaseConfig:
"""Base configuration."""
DEBUG = False
LOGIN_BASE_URL = os.environ['LOGIN_BASE_URL']
SECRET_KEY = os.environ['JWT_SECRET']
SELENE_BASE_URL = os.environ['SELENE_BASE_URL']
SERVICE_BASE_URL = os.environ['SERVICE_BASE_URL']
SSO_BASE_URL = os.environ['SSO_BASE_URL']
TARTARUS_BASE_URL = os.environ['TARTARUS_BASE_URL']
@ -29,7 +29,7 @@ class ProdConfig(BaseConfig):
def get_config_location():
environment_configs = dict(
dev='login_api.config.DevelopmentConfig',
dev=DevelopmentConfig,
test=TestConfig,
prod=ProdConfig
)

View File

@ -11,7 +11,7 @@ class AuthorizeFacebookEndpoint(SeleneEndpoint):
'{tartarus_url}/social/auth/facebook'
'?clientUri={login_url}&path=/social/login'.format(
tartarus_url=self.config['TARTARUS_BASE_URL'],
login_url=self.config['LOGIN_BASE_URL']
login_url=self.config['SSO_BASE_URL']
)
)
return redirect(tartarus_auth_endpoint)

View File

@ -12,7 +12,7 @@ class AuthorizeGithubEndpoint(SeleneEndpoint):
'{tartarus_url}/social/auth/github'
'?clientUri={login_url}&path=/social/login'.format(
tartarus_url=self.config['TARTARUS_BASE_URL'],
login_url=self.config['LOGIN_BASE_URL']
login_url=self.config['SSO_BASE_URL']
)
)
return redirect(tartarus_auth_endpoint)

View File

@ -11,7 +11,7 @@ class AuthorizeGoogleEndpoint(SeleneEndpoint):
tartarus_auth_endpoint = (
'{tartarus_url}/social/auth/google'
'?clientUri={login_url}&path=/social/login'.format(
login_url=self.config['LOGIN_BASE_URL'],
login_url=self.config['SSO_BASE_URL'],
tartarus_url=self.config['TARTARUS_BASE_URL']
)
)

View File

@ -1,5 +1,5 @@
[uwsgi]
master = true
module = login_api.api:login
module = sso_api.api:sso
processes = 4
socket = :7102

View File

@ -2,7 +2,7 @@
# The selene-shared parent image contains all the common Docker configs for
# all Selene apps and services see the "shared" directory in this repository.
FROM docker.mycroft.ai/selene-shared:latest
FROM docker.mycroft.ai/selene-shared:2018.4
LABEL description="Selene Skill Service API"
# Use pipenv to install the package's dependencies in the container
@ -16,7 +16,7 @@ RUN rm Pipfile
RUN rm Pipfile.lock
# Load the skill service application to the image
COPY skill_service /opt/selene/skill_service
COPY skill/skill_service /opt/selene/skill_service
WORKDIR /opt/selene/
EXPOSE 7100

View File

@ -9,8 +9,5 @@ mongoengine = "*"
pygithub = "*"
uwsgi = "*"
[dev-packages]
selene-util = {path = "./../../../shared"}
[requires]
python_version = "3.7"

234
backend/service/Pipfile.lock generated Normal file
View File

@ -0,0 +1,234 @@
{
"_meta": {
"hash": {
"sha256": "0249828591e0b2cebfad9c9ff2d00f8e931f5228061b151f12666add5cbd2d81"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.7"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"aniso8601": {
"hashes": [
"sha256:547e7bc88c19742e519fb4ca39f4b8113fdfb8fca322e325f16a8bfc6cfc553c",
"sha256:e7560de91bf00baa712b2550a2fdebf0188c5fce2fcd1162fbac75c19bb29c95"
],
"version": "==4.0.1"
},
"certifi": {
"hashes": [
"sha256:339dc09518b07e2fa7eda5450740925974815557727d6bd35d319c1524a04a4c",
"sha256:6d58c986d22b038c8c0df30d639f23a3e6d172a05c3583e766f4c0b785c0986a"
],
"version": "==2018.10.15"
},
"chardet": {
"hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
],
"version": "==3.0.4"
},
"click": {
"hashes": [
"sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
"sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
],
"version": "==7.0"
},
"deprecated": {
"hashes": [
"sha256:8bfeba6e630abf42b5d111b68a05f7fe3d6de7004391b3cd614947594f87a4ff",
"sha256:b784e0ca85a8c1e694d77e545c10827bd99772392e79d5f5442e761515a1246e"
],
"version": "==1.2.4"
},
"flask": {
"hashes": [
"sha256:2271c0070dbcb5275fad4a82e29f23ab92682dc45f9dfbc22c02ba9b9322ce48",
"sha256:a080b744b7e345ccfcbc77954861cb05b3c63786e93f2b3875e0913d44b43f05"
],
"version": "==1.0.2"
},
"flask-restful": {
"hashes": [
"sha256:5795519501347e108c436b693ff9a4d7b373a3ac9069627d64e4001c05dd3407",
"sha256:e2f1b8063de3944b94c7f8be5cee4d2161db0267c54c5b757d875295061776fa"
],
"index": "pypi",
"version": "==0.3.6"
},
"idna": {
"hashes": [
"sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e",
"sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16"
],
"version": "==2.7"
},
"itsdangerous": {
"hashes": [
"sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19",
"sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"
],
"version": "==1.1.0"
},
"jinja2": {
"hashes": [
"sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd",
"sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"
],
"version": "==2.10"
},
"markupsafe": {
"hashes": [
"sha256:048ef924c1623740e70204aa7143ec592504045ae4429b59c30054cb31e3c432",
"sha256:130f844e7f5bdd8e9f3f42e7102ef1d49b2e6fdf0d7526df3f87281a532d8c8b",
"sha256:19f637c2ac5ae9da8bfd98cef74d64b7e1bb8a63038a3505cd182c3fac5eb4d9",
"sha256:1b8a7a87ad1b92bd887568ce54b23565f3fd7018c4180136e1cf412b405a47af",
"sha256:1c25694ca680b6919de53a4bb3bdd0602beafc63ff001fea2f2fc16ec3a11834",
"sha256:1f19ef5d3908110e1e891deefb5586aae1b49a7440db952454b4e281b41620cd",
"sha256:1fa6058938190ebe8290e5cae6c351e14e7bb44505c4a7624555ce57fbbeba0d",
"sha256:31cbb1359e8c25f9f48e156e59e2eaad51cd5242c05ed18a8de6dbe85184e4b7",
"sha256:3e835d8841ae7863f64e40e19477f7eb398674da6a47f09871673742531e6f4b",
"sha256:4e97332c9ce444b0c2c38dd22ddc61c743eb208d916e4265a2a3b575bdccb1d3",
"sha256:525396ee324ee2da82919f2ee9c9e73b012f23e7640131dd1b53a90206a0f09c",
"sha256:52b07fbc32032c21ad4ab060fec137b76eb804c4b9a1c7c7dc562549306afad2",
"sha256:52ccb45e77a1085ec5461cde794e1aa037df79f473cbc69b974e73940655c8d7",
"sha256:5c3fbebd7de20ce93103cb3183b47671f2885307df4a17a0ad56a1dd51273d36",
"sha256:5e5851969aea17660e55f6a3be00037a25b96a9b44d2083651812c99d53b14d1",
"sha256:5edfa27b2d3eefa2210fb2f5d539fbed81722b49f083b2c6566455eb7422fd7e",
"sha256:7d263e5770efddf465a9e31b78362d84d015cc894ca2c131901a4445eaa61ee1",
"sha256:83381342bfc22b3c8c06f2dd93a505413888694302de25add756254beee8449c",
"sha256:857eebb2c1dc60e4219ec8e98dfa19553dae33608237e107db9c6078b1167856",
"sha256:98e439297f78fca3a6169fd330fbe88d78b3bb72f967ad9961bcac0d7fdd1550",
"sha256:bf54103892a83c64db58125b3f2a43df6d2cb2d28889f14c78519394feb41492",
"sha256:d9ac82be533394d341b41d78aca7ed0e0f4ba5a2231602e2f05aa87f25c51672",
"sha256:e982fe07ede9fada6ff6705af70514a52beb1b2c3d25d4e873e82114cf3c5401",
"sha256:edce2ea7f3dfc981c4ddc97add8a61381d9642dc3273737e756517cc03e84dd6",
"sha256:efdc45ef1afc238db84cb4963aa689c0408912a0239b0721cb172b4016eb31d6",
"sha256:f137c02498f8b935892d5c0172560d7ab54bc45039de8805075e19079c639a9c",
"sha256:f82e347a72f955b7017a39708a3667f106e6ad4d10b25f237396a7115d8ed5fd",
"sha256:fb7c206e01ad85ce57feeaaa0bf784b97fa3cad0d4a5737bc5295785f5c613a1"
],
"version": "==1.1.0"
},
"mongoengine": {
"hashes": [
"sha256:1c375c8bbae89fc3df672c619a5835a2bd15148f9e8939f214a0b398cf12525f",
"sha256:e8ed6d9d7f648205f4adcd773bb25ce626fdc6cd74b2959451e425441fb0e6d3"
],
"index": "pypi",
"version": "==0.16.0"
},
"pygithub": {
"hashes": [
"sha256:70d90139f61a3d88417ff15eaca6150d0b3ba7ef0dc59589ea3719c3ce518ef6"
],
"index": "pypi",
"version": "==1.43.3"
},
"pyjwt": {
"hashes": [
"sha256:30b1380ff43b55441283cc2b2676b755cca45693ae3097325dea01f3d110628c",
"sha256:4ee413b357d53fd3fb44704577afac88e72e878716116270d722723d65b42176"
],
"version": "==1.6.4"
},
"pymongo": {
"hashes": [
"sha256:025f94fc1e1364f00e50badc88c47f98af20012f23317234e51a11333ef986e6",
"sha256:02aa7fb282606331aefbc0586e2cf540e9dbe5e343493295e7f390936ad2738e",
"sha256:057210e831573e932702cf332012ed39da78edf0f02d24a3f0b213264a87a397",
"sha256:0d946b79c56187fe139276d4c8ed612a27a616966c8b9779d6b79e2053587c8b",
"sha256:104790893b928d310aae8a955e0bdbaa442fb0ac0a33d1bbb0741c791a407778",
"sha256:15527ef218d95a8717486106553b0d54ff2641e795b65668754e17ab9ca6e381",
"sha256:1826527a0b032f6e20e7ac7f72d7c26dd476a5e5aa82c04aa1c7088a59fded7d",
"sha256:22e3aa4ce1c3eebc7f70f9ca7fd4ce1ea33e8bdb7b61996806cd312f08f84a3a",
"sha256:244e1101e9a48615b9a16cbd194f73c115fdfefc96894803158608115f703b26",
"sha256:24b8c04fdb633a84829d03909752c385faef249c06114cc8d8e1700b95aae5c8",
"sha256:2c276696350785d3104412cbe3ac70ab1e3a10c408e7b20599ee41403a3ed630",
"sha256:2d8474dc833b1182b651b184ace997a7bd83de0f51244de988d3c30e49f07de3",
"sha256:3119b57fe1d964781e91a53e81532c85ed1701baaddec592e22f6b77a9fdf3df",
"sha256:3bee8e7e0709b0fcdaa498a3e513bde9ffc7cd09dbceb11e425bd91c89dbd5b6",
"sha256:436c071e01a464753d30dbfc8768dd93aecf2a8e378e5314d130b95e77b4d612",
"sha256:46635e3f19ad04d5a7d7cf23d232388ddbfccf46d9a3b7436b6abadda4e84813",
"sha256:4772e0b679717e7ac4608d996f57b6f380748a919b457cb05bb941467b888b22",
"sha256:4e2cd80e16f481a62c3175b607373200e714ed29025f21559ebf7524f295689f",
"sha256:52732960efa0e003ca1c092dc0a3c65276e897681287a788a01ca78dda3b41f0",
"sha256:55a7de51ec7d1731b2431886d0349146645f2816e5b8eb982d7c49f89472c9f3",
"sha256:5f8ed5934197a2d4b2087646e98de3e099a237099dcf498b9e38dd3465f74ef4",
"sha256:64b064124fcbc8eb04a155117dc4d9a336e3cda3f069958fbc44fe70c3c3d1e9",
"sha256:65958b8e4319f992e85dad59d8081888b97fcdbde5f0d14bc28f2848b92d3ef1",
"sha256:7683428862e20c6a790c19e64f8ccf487f613fbc83d47e3d532df9c81668d451",
"sha256:78566d5570c75a127c2491e343dc006798a384f06be588fe9b0cbe5595711559",
"sha256:7d1cb00c093dbf1d0b16ccf123e79dee3b82608e4a2a88947695f0460eef13ff",
"sha256:8c74e2a9b594f7962c62cef7680a4cb92a96b4e6e3c2f970790da67cc0213a7e",
"sha256:8e60aa7699170f55f4b0f56ee6f8415229777ac7e4b4b1aa41fc61eec08c1f1d",
"sha256:9447b561529576d89d3bf973e5241a88cf76e45bd101963f5236888713dea774",
"sha256:970055bfeb0be373f2f5299a3db8432444bad3bc2f198753ee6c2a3a781e0959",
"sha256:a6344b8542e584e140dc3c651d68bde51270e79490aa9320f9e708f9b2c39bd5",
"sha256:ce309ca470d747b02ba6069d286a17b7df8e9c94d10d727d9cf3a64e51d85184",
"sha256:cfbd86ed4c2b2ac71bbdbcea6669bf295def7152e3722ddd9dda94ac7981f33d",
"sha256:d7929c513732dff093481f4a0954ed5ff16816365842136b17caa0b4992e49d3"
],
"version": "==3.7.2"
},
"pytz": {
"hashes": [
"sha256:31cb35c89bd7d333cd32c5f278fca91b523b0834369e757f4c5641ea252236ca",
"sha256:8e0f8568c118d3077b46be7d654cc8167fa916092e28320cde048e54bfc9f1e6"
],
"version": "==2018.7"
},
"requests": {
"hashes": [
"sha256:65b3a120e4329e33c9889db89c80976c5272f56ea92d3e74da8a463992e3ff54",
"sha256:ea881206e59f41dbd0bd445437d792e43906703fff75ca8ff43ccdb11f33f263"
],
"version": "==2.20.1"
},
"six": {
"hashes": [
"sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
"sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
],
"version": "==1.11.0"
},
"urllib3": {
"hashes": [
"sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39",
"sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22"
],
"version": "==1.24.1"
},
"uwsgi": {
"hashes": [
"sha256:d2318235c74665a60021a4fc7770e9c2756f9fc07de7b8c22805efe85b5ab277"
],
"index": "pypi",
"version": "==2.0.17.1"
},
"werkzeug": {
"hashes": [
"sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c",
"sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b"
],
"version": "==0.14.1"
},
"wrapt": {
"hashes": [
"sha256:d4d560d479f2c21e1b5443bbd15fe7ec4b37fe7e53d335d3b9b0a7b1226fe3c6"
],
"version": "==1.10.11"
}
},
"develop": {}
}

View File

@ -3,7 +3,7 @@
from flask import Flask
from flask_restful import Api
from skill_service.api.endpoints import AllSkillsEndpoint, SkillDetailEndpoint
from .endpoints import AllSkillsEndpoint, SkillDetailEndpoint
# from .config import get_config_location

View File

@ -9,10 +9,6 @@ LABEL maintainer="Mycroft AI <devops@mycroft.ai>"
# Install the software required for this image
RUN pip install pipenv
# Copy the applicaction code to the image
COPY selene_util /opt/selene/selene_util
WORKDIR /opt/selene/
# Use pipenv to install the dependencies for selene-util
COPY Pipfile Pipfile
COPY Pipfile.lock Pipfile.lock
@ -22,5 +18,9 @@ RUN pipenv install --system
# the Pipfile can be removed from the container. This makes way for the
# pepenv to use these files to install dependencies for the Selene services
# or applications that will use this Docker config
RUN rm /opt/selene/Pipfile
RUN rm /opt/selene/Pipfile.lock
RUN rm Pipfile
RUN rm Pipfile.lock
# Copy the applicaction code to the image
COPY selene_util /opt/selene/selene_util
WORKDIR /opt/selene/

View File

@ -0,0 +1,2 @@
dist
node_modules

View File

@ -0,0 +1,13 @@
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false

39
frontend/internet/.gitignore vendored Normal file
View File

@ -0,0 +1,39 @@
# 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

View File

@ -7,8 +7,10 @@ COPY package*.json ./
RUN npm install
COPY . .
ARG selene_env
RUN npm run build-${selene_env}
ARG application_name
RUN npm run build -- --project=globalnav
RUN npm run build-${selene_env} -- --project=${application_name}
# STAGE TWO: build the web server and copy the compiled angular app to it.
FROM nginx:latest
COPY --from=build /usr/src/app/dist/mycroft-marketplace /usr/share/nginx/html
COPY --from=build /usr/src/app/dist/${application_name} /usr/share/nginx/html

View File

@ -0,0 +1,27 @@
# Internet
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 7.0.3.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).

View File

@ -0,0 +1,506 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"internet": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"prefix": "app",
"schematics": {
"@schematics/angular:component": {
"styleext": "scss"
}
},
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/internet",
"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,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "internet:build"
},
"configurations": {
"production": {
"browserTarget": "internet:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "internet: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/**"
]
}
}
}
},
"internet-e2e": {
"root": "e2e/",
"projectType": "application",
"prefix": "",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "internet:serve"
},
"configurations": {
"production": {
"devServerTarget": "internet:serve:production"
}
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": "e2e/tsconfig.e2e.json",
"exclude": [
"**/node_modules/**"
]
}
}
}
},
"market": {
"root": "projects/market/",
"sourceRoot": "projects/market/src",
"projectType": "application",
"prefix": "market",
"schematics": {
"@schematics/angular:component": {
"styleext": "scss",
"spec": false
},
"@schematics/angular:class": {
"spec": false
},
"@schematics/angular:directive": {
"spec": false
},
"@schematics/angular:guard": {
"spec": false
},
"@schematics/angular:module": {
"spec": false
},
"@schematics/angular:pipe": {
"spec": false
},
"@schematics/angular:service": {
"spec": false
}
},
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/market",
"index": "projects/market/src/index.html",
"main": "projects/market/src/main.ts",
"polyfills": "projects/market/src/polyfills.ts",
"tsConfig": "projects/market/tsconfig.app.json",
"assets": [
"projects/market/src/favicon.ico",
"projects/market/src/assets"
],
"styles": [
"projects/market/src/styles.scss"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "projects/market/src/environments/environment.ts",
"with": "projects/market/src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
}
]
},
"test": {
"fileReplacements": [
{
"replace": "projects/market/src/environments/environment.ts",
"with": "projects/market/src/environments/environment.test.ts"
}
]
},
"development": {
"fileReplacements": [
{
"replace": "projects/market/src/environments/environment.ts",
"with": "projects/market/src/environments/environment.dev.ts"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "market:build"
},
"configurations": {
"production": {
"browserTarget": "market:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "market:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/market/src/test.ts",
"polyfills": "projects/market/src/polyfills.ts",
"tsConfig": "projects/market/tsconfig.spec.json",
"karmaConfig": "projects/market/karma.conf.js",
"styles": [
"projects/market/src/styles.scss"
],
"scripts": [],
"assets": [
"projects/market/src/favicon.ico",
"projects/market/src/assets"
]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"projects/market/tsconfig.app.json",
"projects/market/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
},
"market-e2e": {
"root": "projects/market-e2e/",
"projectType": "application",
"prefix": "",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "projects/market-e2e/protractor.conf.js",
"devServerTarget": "market:serve"
},
"configurations": {
"production": {
"devServerTarget": "market:serve:production"
}
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": "projects/market-e2e/tsconfig.e2e.json",
"exclude": [
"**/node_modules/**"
]
}
}
}
},
"sso": {
"root": "projects/sso/",
"sourceRoot": "projects/sso/src",
"projectType": "application",
"prefix": "sso",
"schematics": {
"@schematics/angular:component": {
"styleext": "scss",
"spec": false
},
"@schematics/angular:class": {
"spec": false
},
"@schematics/angular:directive": {
"spec": false
},
"@schematics/angular:guard": {
"spec": false
},
"@schematics/angular:module": {
"spec": false
},
"@schematics/angular:pipe": {
"spec": false
},
"@schematics/angular:service": {
"spec": false
}
},
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/sso",
"index": "projects/sso/src/index.html",
"main": "projects/sso/src/main.ts",
"polyfills": "projects/sso/src/polyfills.ts",
"tsConfig": "projects/sso/tsconfig.app.json",
"assets": [
"projects/sso/src/favicon.ico",
"projects/sso/src/assets"
],
"styles": [
"projects/sso/src/styles.scss"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "projects/sso/src/environments/environment.ts",
"with": "projects/sso/src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
}
]
},
"test": {
"fileReplacements": [
{
"replace": "projects/market/src/environments/environment.ts",
"with": "projects/market/src/environments/environment.test.ts"
}
]
},
"development": {
"fileReplacements": [
{
"replace": "projects/market/src/environments/environment.ts",
"with": "projects/market/src/environments/environment.dev.ts"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "sso:build"
},
"configurations": {
"production": {
"browserTarget": "sso:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "sso:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/sso/src/test.ts",
"polyfills": "projects/sso/src/polyfills.ts",
"tsConfig": "projects/sso/tsconfig.spec.json",
"karmaConfig": "projects/sso/karma.conf.js",
"styles": [
"projects/sso/src/styles.scss"
],
"scripts": [],
"assets": [
"projects/sso/src/favicon.ico",
"projects/sso/src/assets"
]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"projects/sso/tsconfig.app.json",
"projects/sso/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
},
"sso-e2e": {
"root": "projects/sso-e2e/",
"projectType": "application",
"prefix": "",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "projects/sso-e2e/protractor.conf.js",
"devServerTarget": "sso:serve"
},
"configurations": {
"production": {
"devServerTarget": "sso:serve:production"
}
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": "projects/sso-e2e/tsconfig.e2e.json",
"exclude": [
"**/node_modules/**"
]
}
}
}
},
"globalnav": {
"root": "projects/globalnav",
"sourceRoot": "projects/globalnav/src",
"projectType": "library",
"prefix": "globalnav",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "projects/globalnav/tsconfig.lib.json",
"project": "projects/globalnav/ng-package.json"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/globalnav/src/test.ts",
"tsConfig": "projects/globalnav/tsconfig.spec.json",
"karmaConfig": "projects/globalnav/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"projects/globalnav/tsconfig.lib.json",
"projects/globalnav/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
}
},
"defaultProject": "internet"
}

View File

@ -9,6 +9,6 @@ describe('workspace-project App', () => {
it('should display welcome message', () => {
page.navigateTo();
expect(page.getParagraphText()).toEqual('Welcome to frontend!');
expect(page.getParagraphText()).toEqual('Welcome to internet!');
});
});

View File

@ -0,0 +1,64 @@
{
"name": "internet",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"build-dev": "ng build --configuration=development",
"build-test": "ng build --configuration=test",
"build-prod": "ng build --prod",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "~7.0.0",
"@angular/cdk": "^7.0.1",
"@angular/common": "~7.0.0",
"@angular/compiler": "~7.0.0",
"@angular/core": "~7.0.0",
"@angular/flex-layout": "^7.0.0-beta.19",
"@angular/forms": "~7.0.0",
"@angular/http": "~7.0.0",
"@angular/material": "^7.0.1",
"@angular/platform-browser": "~7.0.0",
"@angular/platform-browser-dynamic": "~7.0.0",
"@angular/router": "~7.0.0",
"@fortawesome/angular-fontawesome": "^0.3.0",
"@fortawesome/fontawesome-svg-core": "^1.2.7",
"@fortawesome/free-brands-svg-icons": "^5.4.2",
"@fortawesome/free-solid-svg-icons": "^5.4.2",
"angular-font-awesome": "^3.1.2",
"core-js": "^2.5.4",
"font-awesome": "^4.7.0",
"rxjs": "~6.3.3",
"zone.js": "~0.8.26"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.10.0",
"@angular-devkit/build-ng-packagr": "~0.10.0",
"@angular/cli": "~7.0.3",
"@angular/compiler-cli": "~7.0.0",
"@angular/language-service": "~7.0.0",
"@types/node": "~8.9.4",
"@types/jasmine": "~2.8.8",
"@types/jasminewd2": "~2.0.3",
"codelyzer": "~4.5.0",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~3.0.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~1.1.2",
"karma-jasmine-html-reporter": "^0.2.2",
"ng-packagr": "^4.2.0",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tsickle": ">=0.29.0",
"tslib": "^1.9.0",
"tslint": "~5.11.0",
"typescript": "~3.1.1"
}
}

View File

@ -0,0 +1,31 @@
// 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
});
};

View File

@ -0,0 +1,13 @@
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/globalnav",
"lib": {
"entryFile": "src/public_api.ts",
"cssUrl": "inline",
"umdModuleIds": {
"@fortawesome/angular-fontawesome": "angularFontawesome",
"@fortawesome/free-brands-svg-icons": "freeBrandsSvgIcons",
"@fortawesome/free-solid-svg-icons": "freeSolidSvgIcons"
}
}
}

View File

@ -0,0 +1,10 @@
{
"name": "globalnav",
"version": "0.0.1",
"peerDependencies": {
"@angular/common": "^7.0.0",
"@angular/core": "^7.0.0",
"@angular/flex-layout": "^7.0.0-beta.19"
}
}

View File

@ -0,0 +1,26 @@
<div fxFlex="nogrow" class="nav-footer">
<mat-divider></mat-divider>
<div fxLayout="row wrap" fxLayoutAlign="start" class="social-icons">
<button
*ngFor="let media of socialMediaIcons"
mat-icon-button
class="social-icon-button"
(click)="navigateToSocialMedia(media.url)"
>
<fa-icon [icon]="media.icon"></fa-icon>
</button>
</div>
<div>
<button mat-flat-button (click)="navigateToContactUs()" class="footer-text">Contact Us</button>
</div>
<div>
<button mat-flat-button (click)="navigateToMediaKit()" class="footer-text">Media Kit</button>
</div>
<div>
<button mat-flat-button (click)="navigateToTermsOfUse()" class="footer-text">Terms of Use</button>
</div>
<div>
<button mat-flat-button (click)="navigateToPrivacyPolicy()" class="footer-text">Privacy Policy</button>
</div>
<div class="mat-body-2">&copy; Mycroft AI, Inc.</div>
</div>

View File

@ -0,0 +1,30 @@
.nav-footer {
//bottom: 0;
padding: 16px;
//position: fixed;
}
.social-icons {
width: 180px;
margin-left: -10px;
}
.social-icon-button {
color: #6c7a89;
font-size: 20px;
}
.footer-text {
color: #6c7a89;
font-weight: 400;
line-height: 20px;
padding: 0;
}
.mat-body-2 {
color: #6c7a89;
margin-top: 5px;
}
.mat-divider.mat-divider-vertical {
height: 30px;
}

View File

@ -0,0 +1,59 @@
import { Component, Input, OnInit } from '@angular/core';
import {
faFacebook,
faInstagram,
faLinkedin,
faMedium,
faReddit,
faTelegram,
faTwitter,
faYoutube,
} from '@fortawesome/free-brands-svg-icons';
@Component({
selector: 'globalnav-footer',
templateUrl: './footer.component.html',
styleUrls: ['./footer.component.scss']
})
export class FooterComponent implements OnInit {
@Input() contactUsUrl: string;
@Input() mediaKitUrl: string;
@Input() privacyPolicyUrl: string;
public socialMediaIcons = [
{icon: faTwitter, url: 'https://twitter.com/mycroft_ai'},
{icon: faFacebook, url: 'https://www.facebook.com/aiforeveryone/'},
{icon: faInstagram, url: 'https://www.instagram.com/mycroft_ai/'},
{icon: faYoutube, url: 'https://www.youtube.com/channel/UC1dlmB1lup9RwFQBSGnhA-g'},
{icon: faTelegram, url: 'https://t.me/mycroft_ai'},
{icon: faReddit, url: 'https://www.reddit.com/r/Mycroftai/'},
{icon: faLinkedin, url: 'https://www.linkedin.com/company/mycroft-a.i./'},
{icon: faMedium, url: 'https://medium.com/@mycroftai'}
];
@Input() termsOfUseUrl: string;
constructor() { }
ngOnInit() {
}
navigateToSocialMedia(url) {
window.location.assign(url);
}
navigateToTermsOfUse() {
window.location.assign(this.termsOfUseUrl);
}
navigateToPrivacyPolicy() {
window.location.assign(this.privacyPolicyUrl);
}
navigateToContactUs() {
window.location.assign(this.contactUsUrl);
}
navigateToMediaKit() {
window.location.assign(this.mediaKitUrl);
}
}

View File

@ -0,0 +1,47 @@
<div class="app">
<mat-toolbar color="primary">
<button mat-icon-button (click)="snav.toggle()">
<fa-icon [icon]="menuIcon"></fa-icon>
</button>
<a href="{{environment.wordpressUrl}}">
<div class="globalnav-logo"></div>
</a>
</mat-toolbar>
<mat-sidenav-container>
<mat-sidenav
#snav
[mode]="mobileQuery.matches ? 'over' : 'side'"
[opened]="!mobileQuery.matches"
[fixedInViewport]="true"
>
<div fxFill fxLayout="column">
<div fxFlex="initial">
<a href="{{environment.wordpressUrl}}">
<div class="globalnav-logo"></div>
</a>
<mat-divider></mat-divider>
</div>
<div fxFlex>
<mat-nav-list [disableRipple]="true">
<globalnav-primary-nav-item
*ngFor="let nav of navigationItems"
[primaryNavItem]="nav"
>
</globalnav-primary-nav-item>
</mat-nav-list>
</div>
<globalnav-footer
[contactUsUrl]="contactUsUrl"
[mediaKitUrl]="mediaKitUrl"
[privacyPolicyUrl]="privacyPolicyUrl"
[termsOfUseUrl]="termsOfUseUrl"
>
</globalnav-footer>
</div>
</mat-sidenav>
<mat-sidenav-content>
<ng-content select="[appBody]"></ng-content>
</mat-sidenav-content>
</mat-sidenav-container>
</div>

View File

@ -0,0 +1,49 @@
.app-container {
display: flex;
flex-direction: column;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
// Use inline css to display the logo at top of sidebar because Angular
// libraries do not support static assets yet.
mat-toolbar {
height: 50px;
position: fixed;
z-index: 2;
button {
margin-right: 20px;
fa-icon {
color: white;
font-size: 20px;
}
}
.globalnav-logo {
background: url("toolbar-logo.svg") no-repeat;
height: 25px;
margin-top: -5px;
width: 168px;
}
}
mat-sidenav-container {
mat-sidenav {
border: none;
margin-top: 50px;
mat-divider {
width: 200px;
margin-left: 10px;
}
mat-nav-list {
overflow-y: auto;
}
}
mat-sidenav-content {
margin-top: 50px;
background-color: #f1f3f4;
}
}

View File

@ -0,0 +1,134 @@
import { Component, Input, OnInit } from '@angular/core';
import { MediaMatcher } from '@angular/cdk/layout';
import { PrimaryNavItem } from './globalnav.service';
import {
faBars,
faLightbulb,
faRobot,
faRocket,
faRss,
faStore,
faUserCircle,
faUsers
} from '@fortawesome/free-solid-svg-icons';
@Component({
selector: 'globalnav-sidenav',
templateUrl: './globalnav.component.html',
styleUrls: ['./globalnav.component.scss']
})
export class GlobalnavComponent implements OnInit {
@Input() environment: any;
public contactUsUrl: string;
public isLoggedIn: boolean;
public mediaKitUrl: string;
public menuIcon = faBars;
public mobileQuery: MediaQueryList;
public navigationItems: PrimaryNavItem[];
public privacyPolicyUrl: string;
public termsOfUseUrl: string;
constructor(media: MediaMatcher) {
this.mobileQuery = media.matchMedia('(max-width: 600px)');
}
ngOnInit() {
this.buildNavigationItems();
this.setLoginStatus();
this.buildAccountNav();
}
buildNavigationItems(): void {
const aboutMycroftNav: PrimaryNavItem = {
children: [
{text: 'Team', url: this.environment.wordpressUrl + '/team'},
{text: 'Careers', url: this.environment.wordpressUrl + '/careers'}
],
icon: faRobot,
text: 'About Mycroft'
};
const blogNav: PrimaryNavItem = {
icon: faRss,
text: 'Blog',
url: this.environment.wordpressUrl + '/blog'
};
const communityNav: PrimaryNavItem = {
children: [
{text: 'Chat', url: this.environment.chatUrl},
{text: 'Forum', url: this.environment.forumUrl}
],
icon: faUsers,
text: 'Community'
};
const contributeNav: PrimaryNavItem = {
children: [
{text: 'GitHub', url: 'https://github.com/MycroftAI'},
{text: 'Translate', url: this.environment.translateUrl},
{text: 'Wake Words', url: this.environment.accountUrl + '/#/precise'},
{text: 'Text to Speech', url: this.environment.accountUrl + '/#/deepspeech'}
],
icon: faLightbulb,
text: 'Contribute'
};
const getStartedNav: PrimaryNavItem = {
children: [
{text: 'Get Mycroft', url: this.environment.wordpressUrl + '/download'},
{text: 'Documentation', url: this.environment.wordpressUrl + '/documentation'}
],
icon: faRocket,
text: 'Get Started'
};
const marketplaceNav: PrimaryNavItem = {
children: [
{text: 'Skills', url: this.environment.marketplaceUrl + '/skills'},
{text: 'Hardware', url: this.environment.wordpressUrl + '/shop'}
],
icon: faStore,
text: 'Marketplace'
};
this.navigationItems = [
aboutMycroftNav,
getStartedNav,
blogNav,
communityNav,
contributeNav,
marketplaceNav,
];
this.contactUsUrl = this.environment.wordpressUrl + '/contact';
this.mediaKitUrl = this.environment.wordpressUrl + '/media';
this.privacyPolicyUrl = this.environment.accountUrl + '/#/privacy-policy';
this.termsOfUseUrl = this.environment.accountUrl + '/#/terms-of-use';
}
setLoginStatus(): void {
const cookies = document.cookie;
const seleneTokenExists = cookies.includes('seleneToken');
const seleneTokenEmpty = cookies.includes('seleneToken=""');
this.isLoggedIn = seleneTokenExists && !seleneTokenEmpty;
}
buildAccountNav() {
const accountNav: PrimaryNavItem = {
icon: faUserCircle,
text: 'My Account'
};
if (this.isLoggedIn) {
accountNav.children = [
{text: 'Devices', url: this.environment.accountUrl + '/#/device'},
{text: 'Profile', url: this.environment.accountUrl + '/#/profile'},
{text: 'Skill Settings', url: this.environment.accountUrl + '/#/skill'},
{text: 'Subscription', url: this.environment.accountUrl + '/#/account'},
{text: 'User Settings', url: this.environment.accountUrl + '/#/setting/basic'},
{text: 'Logout', url: this.environment.singleSignOnUrl + '/logout?redirect=' + window.location.href}
];
} else {
accountNav.children = [
{text: 'Log In', url: this.environment.singleSignOnUrl + '/login?redirect=' + window.location.href},
{text: 'Sign Up', url: this.environment.singleSignOnUrl + '/signup'}
];
}
this.navigationItems.push(accountNav);
}
}

View File

@ -0,0 +1,44 @@
import { CommonModule } from '@angular/common';
import { FlexLayoutModule } from '@angular/flex-layout';
import { NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatDividerModule } from '@angular/material/divider';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatListModule } from '@angular/material';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatToolbarModule } from '@angular/material/toolbar';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { GlobalnavComponent } from './globalnav.component';
import { GlobalnavService } from './globalnav.service';
import { NavItemComponent } from './nav-item/nav-item.component';
import { PrimaryNavItemComponent } from './primary-nav-item/primary-nav-item.component';
import { FooterComponent } from './footer/footer.component';
@NgModule({
imports: [
CommonModule,
FlexLayoutModule,
FontAwesomeModule,
MatButtonModule,
MatDividerModule,
MatExpansionModule,
MatListModule,
MatSidenavModule,
MatToolbarModule,
],
declarations: [
GlobalnavComponent,
NavItemComponent,
PrimaryNavItemComponent,
FooterComponent
],
exports: [
GlobalnavComponent
],
providers: [
GlobalnavService
]
})
export class GlobalnavModule { }

View File

@ -0,0 +1,25 @@
import { Injectable } from '@angular/core';
import { IconDefinition } from '@fortawesome/free-solid-svg-icons';
export interface NavItem {
text: string;
url: string;
}
export interface PrimaryNavItem {
children?: NavItem[];
icon: IconDefinition;
text: string;
url?: string;
}
@Injectable({
providedIn: 'root'
})
export class GlobalnavService {
constructor() {
}
}

View File

@ -0,0 +1,4 @@
<mat-list-item (click)="navigateToUrl()">
<div style="width: 40px"></div>
<span class="mat-body-1" [ngStyle]="navItemStyle">{{item.text}}</span>
</mat-list-item>

View File

@ -0,0 +1,8 @@
.mat-list-item {
height: 20px;
}
.mat-body-1 {
color: #6c7a89;
}

View File

@ -0,0 +1,30 @@
import { Component, Input, OnInit } from '@angular/core';
import { NavItem } from '../globalnav.service';
@Component({
selector: 'globalnav-nav-item',
templateUrl: './nav-item.component.html',
styleUrls: ['./nav-item.component.scss']
})
export class NavItemComponent implements OnInit {
@Input() item: NavItem;
public navItemStyle: object;
constructor() {
}
ngOnInit() {
this.buildNavItemStyle();
}
buildNavItemStyle() {
if (window.location.href.includes(this.item.url)) {
this.navItemStyle = {'color': '#22a7f0'};
}
}
navigateToUrl() {
window.location.assign(this.item.url);
}
}

View File

@ -0,0 +1,18 @@
<mat-list-item (click)="onItemSelected()" class="nav-item">
<div fxLayout="row" style="width: 100%;">
<!-- Not all of the icons are the same width so center them -->
<span style="text-align: center; width: 20px; margin-right: 20px">
<fa-icon [icon]="primaryNavItem.icon" style="color: #22a7f0"></fa-icon>
</span>
<span>
{{primaryNavItem.text}}
</span>
</div>
</mat-list-item>
<div *ngIf="expanded">
<globalnav-nav-item
*ngFor="let child of primaryNavItem.children"
[item]="child"
>
</globalnav-nav-item>
</div>

View File

@ -0,0 +1,7 @@
.nav-item {
color: #2c3e50;
height: 30px;
}
.nav-item:hover {
background-color: #e4f1fe;
}

View File

@ -0,0 +1,39 @@
import { Component, Input, OnInit } from '@angular/core';
import { PrimaryNavItem } from '../globalnav.service';
@Component({
selector: 'globalnav-primary-nav-item',
templateUrl: './primary-nav-item.component.html',
styleUrls: ['./primary-nav-item.component.scss']
})
export class PrimaryNavItemComponent implements OnInit {
public expanded = false;
@Input() primaryNavItem: PrimaryNavItem;
constructor() { }
ngOnInit() {
this.expandCurrentLocation();
}
expandCurrentLocation() {
if (this.primaryNavItem.children && this.primaryNavItem.children.length) {
this.primaryNavItem.children.forEach(
(navItem) => {
if (window.location.href.includes(navItem.url)) {
this.expanded = true;
}
}
);
}
}
onItemSelected() {
if (this.primaryNavItem.children && this.primaryNavItem.children.length) {
this.expanded = !this.expanded;
} else {
window.location.assign(this.primaryNavItem.url);
}
}
}

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -0,0 +1,7 @@
/*
* Public API Surface of globalnav
*/
export * from './lib/globalnav.service';
export * from './lib/globalnav.component';
export * from './lib/globalnav.module';

View File

@ -0,0 +1,22 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'core-js/es7/reflect';
import 'zone.js/dist/zone';
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);

View File

@ -0,0 +1,32 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"target": "es2015",
"module": "es2015",
"moduleResolution": "node",
"declaration": true,
"sourceMap": true,
"inlineSources": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": true,
"types": [],
"lib": [
"dom",
"es2018"
]
},
"angularCompilerOptions": {
"annotateForClosureCompiler": true,
"skipTemplateCodegen": true,
"strictMetadataEmit": true,
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true,
"enableResourceInlining": true
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

View File

@ -1,16 +1,14 @@
{
"extends": "../tsconfig.json",
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/spec",
"module": "commonjs",
"outDir": "../../out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"test.ts",
"polyfills.ts"
"src/test.ts"
],
"include": [
"**/*.spec.ts",

View File

@ -0,0 +1,17 @@
{
"extends": "../../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"globalnav",
"camelCase"
],
"component-selector": [
true,
"element",
"globalnav",
"kebab-case"
]
}
}

View File

@ -9,6 +9,6 @@ describe('workspace-project App', () => {
it('should display welcome message', () => {
page.navigateTo();
expect(page.getParagraphText()).toEqual('Welcome to mycroft-marketplace!');
expect(page.getParagraphText()).toEqual('Welcome to market!');
});
});

View File

@ -0,0 +1,11 @@
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get('/');
}
getParagraphText() {
return element(by.css('market-root h1')).getText();
}
}

View File

@ -1,7 +1,7 @@
{
"extends": "../tsconfig.json",
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"outDir": "../../out-tsc/app",
"module": "commonjs",
"target": "es5",
"types": [

View File

@ -1,9 +1,11 @@
# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For IE 9-11 support, please uncomment the last line of the file and adjust as needed
#
# For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed
> 0.5%
last 2 versions
Firefox ESR
not dead
# IE 9-11
not IE 9-11

View File

@ -16,7 +16,7 @@ module.exports = function (config) {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../coverage'),
dir: require('path').join(__dirname, '../../coverage'),
reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
},

View File

@ -1,7 +1,7 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { PageNotFoundComponent } from "./page-not-found/page-not-found.component";
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
const routes: Routes = [
{ path: '', redirectTo: '/skills', pathMatch: 'full' },

View File

@ -0,0 +1,9 @@
<globalnav-sidenav [environment]="environment">
<!--
Inject the marketplace app into the <mat-sidenav-content> component
of the global navigation menu library.
-->
<div appBody class="app-body">
<router-outlet></router-outlet>
</div>
</globalnav-sidenav>

View File

@ -5,3 +5,8 @@
margin-right: 3vw;
margin-top: 30px;
}
img {
height: 20px;
margin-top: -7px;
}

Some files were not shown because too many files have changed in this diff Show More