├── .angular-cli.json
├── .dockerignore
├── .editorconfig
├── .eslintrc.json
├── .github
├── ISSUE_TEMPLATE.md
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .snyk
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE.md
├── README.md
├── TUTORIAL.md
├── VIDEOS.md
├── e2e
├── app.e2e-spec.ts
├── app.po.ts
└── tsconfig.e2e.json
├── karma.conf.js
├── package-lock.json
├── package.json
├── protractor.conf.js
├── src
├── client
│ ├── app
│ │ ├── app.component.ts
│ │ ├── app.module.ts
│ │ ├── hero.service.ts
│ │ ├── hero.ts
│ │ ├── heroes.component.html
│ │ └── heroes.component.ts
│ ├── assets
│ │ └── .gitkeep
│ ├── environments
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ ├── favicon.ico
│ ├── index.html
│ ├── main.ts
│ ├── polyfills.ts
│ ├── styles.scss
│ ├── test.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.spec.json
│ └── typings.d.ts
└── server
│ ├── env
│ └── example-environment.js
│ ├── hero.model.js
│ ├── hero.service.js
│ ├── index.js
│ ├── mongo.js
│ └── routes.js
├── tsconfig.json
└── tslint.json
/.angular-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "project": {
4 | "name": "angular-cosmosdb"
5 | },
6 | "apps": [
7 | {
8 | "root": "src/client",
9 | "outDir": "dist",
10 | "assets": ["assets", "favicon.ico"],
11 | "index": "index.html",
12 | "main": "main.ts",
13 | "polyfills": "polyfills.ts",
14 | "test": "test.ts",
15 | "tsconfig": "tsconfig.app.json",
16 | "testTsconfig": "tsconfig.spec.json",
17 | "prefix": "app",
18 | "styles": ["styles.scss"],
19 | "scripts": [],
20 | "environmentSource": "environments/environment.ts",
21 | "environments": {
22 | "dev": "environments/environment.ts",
23 | "prod": "environments/environment.prod.ts"
24 | }
25 | }
26 | ],
27 | "e2e": {
28 | "protractor": {
29 | "config": "./protractor.conf.js"
30 | }
31 | },
32 | "lint": [
33 | {
34 | "project": "src/client/tsconfig.app.json",
35 | "exclude": "**/node_modules/**"
36 | },
37 | {
38 | "project": "src/client/tsconfig.spec.json",
39 | "exclude": "**/node_modules/**"
40 | },
41 | {
42 | "project": "e2e/tsconfig.e2e.json",
43 | "exclude": "**/node_modules/**"
44 | }
45 | ],
46 | "test": {
47 | "karma": {
48 | "config": "./karma.conf.js"
49 | }
50 | },
51 | "defaults": {
52 | "styleExt": "scss",
53 | "component": {
54 | "spec": false,
55 | "inlineStyle": true,
56 | "inlineTemplate": true
57 | },
58 | "directive": {
59 | "spec": false
60 | },
61 | "class": {
62 | "spec": false
63 | },
64 | "guard": {
65 | "spec": false
66 | },
67 | "module": {
68 | "spec": false
69 | },
70 | "pipe": {
71 | "spec": false
72 | },
73 | "service": {
74 | "spec": false
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | Dockerfile*
4 | docker-compose*
5 | .dockerignore
6 | .git
7 | .gitignore
8 | README.md
9 | LICENSE
10 | .vscode
11 |
12 | dist/node_modules
13 | dist/npm-debug.log
14 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "commonjs": true,
5 | "es6": true,
6 | "node": true
7 | },
8 | "parserOptions": {
9 | "ecmaFeatures": {
10 | "jsx": true
11 | },
12 | "sourceType": "module"
13 | },
14 | "rules": {
15 | "arrow-body-style": ["warn", "as-needed"],
16 | "arrow-parens": ["warn", "as-needed"],
17 | "arrow-spacing": ["warn", { "before": true, "after": true }],
18 | "max-len": ["error", 100],
19 | "prefer-arrow-callback": [
20 | "warn",
21 | {
22 | "allowNamedFunctions": true
23 | }
24 | ],
25 | "quotes": ["error", "single"],
26 | "no-var": "warn",
27 | "prefer-const": "warn",
28 | "no-const-assign": "warn",
29 | "no-this-before-super": "warn",
30 | "no-undef": "warn",
31 | "no-unreachable": "warn",
32 | "no-unused-vars": "warn",
33 | "constructor-super": "warn",
34 | "valid-typeof": "warn"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
4 | > Please provide us with the following information:
5 | > ---------------------------------------------------------------
6 |
7 | ### This issue is for a: (mark with an `x`)
8 |
9 | - [ ] bug report -> please search issues before submitting
10 | - [ ] feature request
11 | - [ ] documentation issue or request
12 | - [ ] regression (a behavior that used to work and stopped in a new release)
13 |
14 |
15 | ### Minimal steps to reproduce
16 | >
17 |
18 | ### Any log messages given by the failure
19 | >
20 |
21 | ### Expected/desired behavior
22 | >
23 |
24 | ### OS and Version?
25 | > Windows 7, 8 or 10. Linux (which distribution). macOS (Yosemite? El Capitan? Sierra?)
26 |
27 | ### Versions
28 | >
29 |
30 | ### Mention any other details that might be useful
31 |
32 | > ---------------------------------------------------------------
33 | > Thanks! We'll be in touch soon.
34 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Purpose
2 |
3 | * ...
4 |
5 | ## Does this introduce a breaking change?
6 |
7 |
8 | - [ ] Yes
9 | - [ ] No
10 |
11 |
12 | ## Pull Request Type
13 | What kind of change does this Pull Request introduce?
14 |
15 |
16 |
17 | - [ ] Bugfix
18 | - [ ] Feature
19 | - [ ] Code style update (formatting, local variables)
20 | - [ ] Refactoring (no functional changes, no api changes)
21 | - [ ] Documentation content changes
22 | - [ ] Other... Please describe:
23 |
24 |
25 | ## How to Test
26 | * Get the code
27 |
28 | ```
29 | git clone [repo-address]
30 | cd [repo-name]
31 | git checkout [branch-name]
32 | npm install
33 | ```
34 |
35 | * Test the code
36 |
37 | ```
38 | ```
39 |
40 | ## What to Check
41 | Verify that the following are valid
42 | * ...
43 |
44 | ## Other Information
45 |
46 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # ignore server configuration
2 | src/server/env/environment.js
3 | src/server/env/development.js
4 |
5 | # Logs
6 | logs
7 | *.log
8 | npm-debug.log*
9 | yarn-debug.log*
10 | yarn-error.log*
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 |
24 | # nyc test coverage
25 | .nyc_output
26 |
27 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
28 | .grunt
29 |
30 | # Bower dependency directory (https://bower.io/)
31 | bower_components
32 |
33 | # node-waf configuration
34 | .lock-wscript
35 |
36 | # Compiled binary addons (http://nodejs.org/api/addons.html)
37 | build/Release
38 |
39 | # Dependency directories
40 | node_modules/
41 | jspm_packages/
42 |
43 | # Typescript v1 declaration files
44 | typings/
45 |
46 | # Optional npm cache directory
47 | .npm
48 |
49 | # Optional eslint cache
50 | .eslintcache
51 |
52 | # Optional REPL history
53 | .node_repl_history
54 |
55 | # Output of 'npm pack'
56 | *.tgz
57 |
58 | # Yarn Integrity file
59 | .yarn-integrity
60 |
61 | # dotenv environment variables file
62 | .env
63 |
64 | # See http://help.github.com/ignore-files/ for more about ignoring files.
65 |
66 | # compiled output
67 | /dist
68 | /tmp
69 | /out-tsc
70 |
71 | # IDEs and editors
72 | /.idea
73 | .project
74 | .classpath
75 | .c9/
76 | *.launch
77 | .settings/
78 | *.sublime-workspace
79 |
80 | # IDE - VSCode
81 | .vscode/*
82 | !.vscode/settings.json
83 | !.vscode/tasks.json
84 | !.vscode/launch.json
85 | !.vscode/extensions.json
86 |
87 | # misc
88 | /.sass-cache
89 | /connect.lock
90 | /coverage
91 | /libpeerconnection.log
92 | npm-debug.log
93 | testem.log
94 | /typings
95 |
96 | # e2e
97 | /e2e/*.js
98 | /e2e/*.map
99 |
100 | # System Files
101 | .DS_Store
102 | Thumbs.db
103 |
104 |
--------------------------------------------------------------------------------
/.snyk:
--------------------------------------------------------------------------------
1 | # Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
2 | version: v1.12.0
3 | ignore: {}
4 | patch: {}
5 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## angular-cosmosdb Changelog
2 |
3 |
4 | # 1.0.1 (2018-06-26)
5 |
6 | *Bug Fixes*
7 | * Fix [8436](https://github.com/MicrosoftDocs/azure-docs/issues/8436)
8 | - Error on insert due to duplicate key. Mongoose reserves the key `id` and we're attempting to use that within our Hero model. Changed our model to have a key of `uid` instead.
9 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to angular-cosmosdb
2 |
3 | This project welcomes contributions and suggestions. Most contributions require you to agree to a
4 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
5 | the rights to use your contribution. For details, visit https://cla.microsoft.com.
6 |
7 | When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
8 | a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
9 | provided by the bot. You will only need to do this once across all repos using our CLA.
10 |
11 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
12 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
13 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
14 |
15 | - [Code of Conduct](#coc)
16 | - [Issues and Bugs](#issue)
17 | - [Feature Requests](#feature)
18 | - [Submission Guidelines](#submit)
19 |
20 | ## Code of Conduct
21 | Help us keep this project open and inclusive. Please read and follow our [Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
22 |
23 | ## Found an Issue?
24 | If you find a bug in the source code or a mistake in the documentation, you can help us by
25 | [submitting an issue](#submit-issue) to the GitHub Repository. Even better, you can
26 | [submit a Pull Request](#submit-pr) with a fix.
27 |
28 | ## Want a Feature?
29 | You can *request* a new feature by [submitting an issue](#submit-issue) to the GitHub
30 | Repository. If you would like to *implement* a new feature, please submit an issue with
31 | a proposal for your work first, to be sure that we can use it.
32 |
33 | * **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
34 |
35 | ## Submission Guidelines
36 |
37 | ### Submitting an Issue
38 | Before you submit an issue, search the archive, maybe your question was already answered.
39 |
40 | If your issue appears to be a bug, and hasn't been reported, open a new issue.
41 | Help us to maximize the effort we can spend fixing issues and adding new
42 | features, by not reporting duplicate issues. Providing the following information will increase the
43 | chances of your issue being dealt with quickly:
44 |
45 | * **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps
46 | * **Version** - what version is affected (e.g. 0.1.2)
47 | * **Motivation for or Use Case** - explain what are you trying to do and why the current behavior is a bug for you
48 | * **Browsers and Operating System** - is this a problem with all browsers?
49 | * **Reproduce the Error** - provide a live example or a unambiguous set of steps
50 | * **Related Issues** - has a similar issue been reported before?
51 | * **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be
52 | causing the problem (line of code or commit)
53 |
54 | You can file new issues by providing the above information at the corresponding repository's issues link: https://github.com/[organization-name]/[repository-name]/issues/new].
55 |
56 | ### Submitting a Pull Request (PR)
57 | Before you submit your Pull Request (PR) consider the following guidelines:
58 |
59 | * Search the repository (https://github.com/[organization-name]/[repository-name]/pulls) for an open or closed PR
60 | that relates to your submission. You don't want to duplicate effort.
61 |
62 | * Make your changes in a new git fork:
63 |
64 | * Commit your changes using a descriptive commit message
65 | * Push your fork to GitHub:
66 | * In GitHub, create a pull request
67 | * If we suggest changes then:
68 | * Make the required updates.
69 | * Rebase your fork and force push to your GitHub repository (this will update your Pull Request):
70 |
71 | ```shell
72 | git rebase master -i
73 | git push -f
74 | ```
75 |
76 | That's it! Thank you for your contribution!
77 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:6-alpine
2 | # FROM mhart/alpine-node
3 |
4 | # Create app directory
5 | RUN mkdir -p /usr/src/app
6 | WORKDIR /usr/src/app
7 |
8 | # Install app dependencies
9 | COPY package.json /usr/src/app/
10 | RUN npm install
11 |
12 | # Bundle app source
13 | COPY dist/ /usr/src/app/dist
14 | COPY src/server/ /usr/src/app/
15 |
16 | ENV PORT 80
17 | # EXPOSE 3000
18 |
19 | CMD [ "node", "index.js" ]
20 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Microsoft Corporation. All rights reserved.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | page_type: sample
3 | languages:
4 | - typescript
5 | - javascript
6 | - html
7 | products:
8 | - azure
9 | description: "You can watch me build the app as part of my series here"
10 | urlFragment: angular-cosmosdb
11 | ---
12 |
13 | # Angular Cosmos DB
14 |
15 | by [John Papa](http://twitter.com/john_papa)
16 |
17 | You can [watch me build the app as part of my series here](https://johnpapa.net/angular-cosmosdb-1/)
18 |
19 | You can [view all videos together here](/VIDEOS.md)
20 |
21 | [Learn more about developing Node.js apps with Azure's cloud services here](https://docs.microsoft.com/en-us/nodejs/azure)
22 |
23 | ## Requirements
24 |
25 | 1. Install the Angular CLI
26 |
27 | ```bash
28 | npm install -g @angular/cli
29 | ```
30 |
31 | 2. Install the [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli)
32 |
33 | 3. Set up a Cosmos DB server with a MongoDB database. Make sure you note the name of the Azure Cosmos DB account, the name of the database, the primary password and the port. You can find all this information in the [Azure portal](https://portal.azure.com).
34 |
35 |
36 | ## Getting Started
37 |
38 | 1. Clone this repository
39 |
40 | ```bash
41 | git clone https://github.com/johnpapa/angular-cosmosdb.git
42 | cd angular-cosmosdb
43 | ```
44 |
45 | 2. Install the npm packages
46 |
47 | ```bash
48 | npm i
49 | ```
50 |
51 | 3. Configure Cosmos DB server settings
52 |
53 | Rename the `example-environment.js` file to `environment.js` in the `server/env/` folder and update it with your Cosmos DB settings. Replace the account, database name, key, and port with your specific configuration.
54 |
55 | ```javascript
56 | // server/env/environment.js
57 |
58 | module.exports = {
59 | accountName: 'your-cosmosdb-account-name-goes-here',
60 | databaseName: 'your-cosmosdb-database-name-goes-here',
61 | key: 'your-key-goes-here',
62 | port: 10255
63 | };
64 | ```
65 |
66 | ## Running the app
67 |
68 | 1. Build the Angular app
69 |
70 | ```bash
71 | ng build
72 | ```
73 |
74 | 2. Launch the server
75 |
76 | ```bash
77 | node src/server/index.js
78 | ```
79 |
80 | 3. Open the browser to http://localhost:3000
81 |
82 | ## Problems or Suggestions
83 |
84 | [Open an issue here](https://github.com/johnpapa/angular-cosmos/issues)
85 |
--------------------------------------------------------------------------------
/TUTORIAL.md:
--------------------------------------------------------------------------------
1 | # MEAN app with Cosmos DB
2 |
3 | ## Angular app
4 |
5 | 1. Install the Angular CLI
6 |
7 | ```bash
8 | npm install @angular/cli -g
9 | ```
10 |
11 | 1. Generate a new Angular app with routing. We indicate we want the Angular app to be placed in the `src/client` folder, `--minimal` indicates we should create inline styles and templates, and we don't want test files. `--style scss` indicates we will use SASS instead of CSS.
12 |
13 | ```bash
14 | ng new angular-cosmosdb -sd src/client --minimal --style scss
15 | cd angular-cosmosdb
16 | ```
17 |
18 | ## Node and Express app
19 |
20 | 1. Install Express, and body parser.
21 |
22 | ```bash
23 | npm install express body-parser --save
24 | ```
25 |
26 | 1. Open VS Code
27 |
28 | 1. Create a new folder and file for `src/server/index.js`
29 |
30 | 1. Type this code into `index.js`
31 |
32 | ```javascript
33 | const express = require('express');
34 | const path = require('path');
35 | const bodyParser = require('body-parser');
36 | //const routes = require('./routes');
37 |
38 | const root = './';
39 | const port = process.env.PORT || '3000';
40 | const app = express();
41 |
42 | app.use(bodyParser.json());
43 | app.use(bodyParser.urlencoded({ extended: false }));
44 | app.use(express.static(path.join(root, 'dist')));
45 | //app.use('/api', routes);
46 | app.get('*', (req, res) => {
47 | res.sendFile('dist/index.html', {root: root});
48 | });
49 |
50 | app.listen(port, () => console.log(`API running on localhost:${port}`));
51 | ```
52 |
53 | 1. Create `src/server/routes.js`
54 |
55 | 1. Create a `/api` route (link to routes.js)
56 |
57 | 1. Create a `/api/get` route
58 |
59 | ```javascript
60 | const express = require('express');
61 | const router = express.Router();
62 |
63 | // const heroService = require('./hero.service');
64 |
65 | router.get('/heroes', (req, res) => {
66 | //heroService.getHeroes(req, res);
67 | res.send(200, [
68 | {"id": 10,"name": "Starlord","saying": "oh yeah"}
69 | ]);
70 | });
71 |
72 | module.exports = router;
73 | ```
74 |
75 | 1. Create a VS Code debug configuration for Node, and press the green arrow to run it! (Press debug, press configure, select Node.js, press green button)
76 |
77 | 1. Change the launch config's launch program to our node server
78 |
79 | ```json
80 | "program": "${workspaceRoot}/src/server/index.js"
81 | ```
82 |
83 | 1. Open Postman and paste perform a *GET* http request on http://localhost:3000/api/heroes. You should see the following JSON results
84 |
85 | ## Adding the A in MEAN
86 |
87 | ### The Heroes UI
88 |
89 | 1. Add a component to list Heroes. The `--flat` flag adds the component without putting it in its own sub folder.
90 |
91 | ```bash
92 | ng g c heroes --flat
93 | ```
94 |
95 | 1. Copy this code into `heroes.component.html`
96 |
97 | 1. Copy the styles.scss code
98 |
99 | 1. Copy this code into `heroes.component.ts`
100 |
101 | 1. Add the hero component's selector to the `app.component.ts`
102 |
103 | ```javascript
104 | import { Component } from '@angular/core';
105 |
106 | @Component({
107 | selector: 'app-root',
108 | template: `
109 |
Heroes
110 |
111 | `
112 | })
113 | export class AppComponent {}
114 | ```
115 |
116 | 1. Import Forms and Http modules into the app
117 |
118 | ```javascript
119 | import { FormsModule } from '@angular/forms';
120 | import { HttpClientModule } from '@angular/common/http';
121 |
122 | // ...
123 |
124 | imports: [BrowserModule, FormsModule, HttpClientModule],
125 | ```
126 |
127 | 1. Create a model for our heroes, `hero.ts`
128 |
129 | ```bash
130 | ng g cl hero
131 | ```
132 |
133 | 1. Add a service to handle http interactions between Angular and Node
134 |
135 | ```bash
136 | ng g s hero -m app.module
137 | ```
138 |
139 | 1. Copy the code into `hero.service.ts`
140 | 1. Build the app
141 |
142 | ```bash
143 | ng build
144 | ```
145 |
146 | 1. Press the green arrow to run it!
147 | 1. Open the browser to http://localhost:3000
148 |
149 | ## Create a Cosmos DB account
150 |
151 | ### Cosmos DB setup
152 |
153 | 1. Install the Azure CLI
154 |
155 | 1. Login to Azure via the CLI
156 |
157 | ```bash
158 | # interactive login to Azure
159 | az login
160 | ```
161 |
162 | 1. Create a logical place for our Azure resources. This is a called a resource group.
163 |
164 | ```bash
165 | # Create a resource group (logical container for our Azure resources)
166 | az group create -n my-heroes-group -l "East US"
167 | ```
168 |
169 | 1. Create the Cosmos DB account and make sure it is of type MongoDB
170 |
171 | ```bash
172 | # Create our Cosmos DB
173 | az cosmosdb create -n my-heroes-db -g my-heroes-group --kind MongoDB
174 | ```
175 |
176 | ### Connecting to Mongo
177 |
178 | 1. Install the mongoose node package from npm
179 |
180 | ```bash
181 | npm install mongoose --save
182 | ```
183 |
184 | 1. Create `src/server/mongo.js` to handle mongo connections
185 |
186 | ```javascript
187 | const mongoose = require('mongoose');
188 | /**
189 | * Set to Node.js native promises
190 | * Per http://mongoosejs.com/docs/promises.html
191 | */
192 | mongoose.Promise = global.Promise;
193 |
194 | const env = require('./env/environment');
195 |
196 | // eslint-disable-next-line max-len
197 | const mongoUri = `mongodb://${env.dbName}:${env.key}@${env.dbName}.documents.azure.com:${env.cosmosPort}/?ssl=true`;
198 |
199 | function connect() {
200 | return mongoose.connect(mongoUri, { useMongoClient: true });
201 | }
202 |
203 | module.exports = {
204 | connect,
205 | mongoose
206 | };
207 |
208 | ```
209 |
210 | 1. Create the connection environment file `src/server/env/env.js`
211 |
212 | ```javascript
213 | const cosmosPort = 1234; // replace with your port
214 | const dbName = 'your-cosmos-db-name-goes-here';
215 | const key = 'your-key-goes-here';
216 |
217 | module.exports = {
218 | dbName,
219 | key,
220 | cosmosPort
221 | };
222 | ```
223 |
224 | 1. Enter the following Azure CLI commands to get the password and connection string. Copy these to your text editor
225 |
226 | ```bash
227 | # Get the key for the Cosmos DB
228 | az cosmosdb list-keys -n my-heroes-db -g my-heroes-group --query "primaryMasterKey"
229 |
230 | # Get the connection string
231 | az cosmosdb list-connection-strings -n my-cosmos-heroes -g my-heroes-db-group --query "connectionStrings[0].connectionString"
232 | ```
233 |
234 | 1. Replace your port, database name, and password/key for Cosmos DB. You can find these in the Azure portal for your Cosmos DB account.
235 |
236 | 1. Create the Hero model `src/server/hero.model.js`
237 |
238 | 1. Enter the following code to create the Hero model using a mongoose schema
239 |
240 | ```javascript
241 | const mongoose = require('mongoose');
242 | const Schema = mongoose.Schema;
243 | const heroSchema = new Schema(
244 | {
245 | id: { type: Number, required: true, unique: true },
246 | name: String,
247 | saying: String
248 | },
249 | {
250 | collection: 'heroes',
251 | read: 'nearest'
252 | }
253 | );
254 | const Hero = mongoose.model('Hero', heroSchema);
255 | module.exports = Hero;
256 | ```
257 |
258 | ### Create a service to handle heroes data using Mongo via Mongoose
259 |
260 | 1. Create `src/server/hero.service.js`
261 |
262 | ```javascript
263 | const Hero = require('./hero.model');
264 |
265 | require('./mongo').connect();
266 |
267 | function getHeroes(req, res) {
268 | const docquery = Hero.find({});
269 | docquery
270 | .exec()
271 | .then(heroes => {
272 | res.status(200).json(heroes);
273 | })
274 | .catch(error => {
275 | res.status(500).send(error);
276 | return;
277 | });
278 | }
279 |
280 | module.exports = {
281 | getHeroes
282 | };
283 | ```
284 |
285 | 1. Modify `src/server/routes.js` to use the new `hero.service.js`
286 |
287 | ```javascript
288 | const express = require('express');
289 | const router = express.Router();
290 |
291 | const heroService = require('./hero.service');
292 |
293 | router.get('/heroes', (req, res) => {
294 | heroService.getHeroes(req, res);
295 | //res.send(200, [
296 | // {"id": 10,"name": "Starlord","saying": "oh yeah"}
297 | //]);
298 | });
299 |
300 | module.exports = router;
301 | ```
302 |
303 | 1. Restart the node process in the VS Code debugger.
304 |
305 | 1. Open Postman and paste perform a *GET* http request on http://localhost:3000/api/heroes. You should see the following JSON results
306 |
307 | 1. Open the app at http://localhost:3000. You should see the app no longer has any heroes, because we are hitting the new database.
308 |
309 | ## Create the post, put and delete methods in the node server
310 |
311 | ### Create the Hero Post
312 |
313 | 1. In the `src/server/hero.service`, create the post method and the helper functions.
314 |
315 | ```javascript
316 | function postHero(req, res) {
317 | const originalHero = { id: req.body.id, name: req.body.name, saying: req.body.saying };
318 | const hero = new Hero(originalHero);
319 | hero.save(error => {
320 | if (checkServerError(res, error)) return;
321 | res.status(201).json(hero);
322 | console.log('Hero created successfully!');
323 | });
324 | }
325 |
326 | function checkServerError(res, error) {
327 | if (error) {
328 | res.status(500).send(error);
329 | return error;
330 | }
331 | }
332 |
333 | module.exports = {
334 | getHeroes,
335 | postHero
336 | };
337 | ```
338 |
339 | 1. In the `src/server/routes.js`, call the post function
340 |
341 | ```javascript
342 | router.post('/hero', (req, res) => {
343 | heroService.postHero(req, res);
344 | });
345 | ```
346 |
347 | 1. Restart the node process in the VS Code debugger.
348 |
349 | 1. Open the app at http://localhost:3000. Add a hero
350 |
351 | ### Create the PUT and DELETE
352 |
353 | 1. In the `src/server/hero.service`, create the post method and the helper functions.
354 |
355 | ```javascript
356 | function putHero(req, res) {
357 | const id = parseInt(req.params.id, 10);
358 | const updatedHero = {
359 | id: id,
360 | name: req.body.name,
361 | saying: req.body.saying
362 | };
363 | Hero.findOne({ id: id }, (error, hero) => {
364 | if (checkServerError(res, error)) return;
365 | if (!checkFound(res, hero)) return;
366 |
367 | hero.name = updatedHero.name;
368 | hero.saying = updatedHero.saying;
369 | hero.save(error => {
370 | if (checkServerError(res, error)) return;
371 | res.status(200).json(hero);
372 | console.log('Hero udpated successfully!');
373 | });
374 | });
375 | }
376 |
377 | function deleteHero(req, res) {
378 | const id = parseInt(req.params.id, 10);
379 | Hero.findOneAndRemove({ id: id })
380 | .then(hero => {
381 | if (!checkFound(res, hero)) return;
382 | res.status(200).json(hero);
383 | console.log('Hero deleted successfully!');
384 | })
385 | .catch(error => {
386 | if (checkServerError(res, error)) return;
387 | });
388 | }
389 |
390 | function checkFound(res, hero) {
391 | if (!hero) {
392 | res.status(404).send('Hero not found.');
393 | return;
394 | }
395 | return hero;
396 | }
397 |
398 | module.exports = {
399 | getHeroes,
400 | postHero,
401 | putHero,
402 | deleteHero
403 | };
404 | ```
405 |
406 | 1. In the `src/server/routes.js`, call the post function
407 |
408 | ```javascript
409 | router.post('/hero', (req, res) => {
410 | heroService.postHero(req, res);
411 | });
412 | ```
413 |
414 | 1. Restart the node process in the VS Code debugger.
415 |
416 | 1. Open the app at http://localhost:3000. Get, Add, Update, Delete !
417 |
--------------------------------------------------------------------------------
/VIDEOS.md:
--------------------------------------------------------------------------------
1 | # Learning to Build MEAN Apps with Cosmos DB
2 |
3 | ## Overview
4 |
5 | This video series shows how to build MEAN apps using Cosmos DB. Each video in the series will be a couple of minutes and show how each part of the application works.
6 |
7 | ## Videos
8 |
9 | ### Part 1 - Introduction
10 |
11 | Cosmos DB makes it easy to step right in where I used to use Mongo, because it lets me use the same exact APIs I used with Mongo. Are you using the mongo npm module? No problem. Do you prefer mongoose? That works too. I just love that I don't have to change how I work! This video takes a look at what we'll build.
12 |
13 | [](https://www.youtube.com/watch?v=vlZRP0mDabM)
14 |
15 | ### Part 2 - Node.js and Express
16 |
17 | One of the best ways to create a MEAN app is to start with the [Angular CLI](https://github.com/angular/angular-cli) to generate a starting point. We'll use this and extend it to include Node.js and Express.js, to serve our future APIs to the Angular app. Then we'll test out an API to make sure it works.
18 |
19 | [](https://www.youtube.com/watch?v=lIwJIYcGSUg)
20 |
21 | ### Part 3 - Angular and Express APIs
22 |
23 | I create the UI with Angular and connect to the Express APIs. I use the [Angular CLI](https://github.com/angular/angular-cli) to generate a the component and service the app needs. Then I build the app and show it in the browser.
24 |
25 | [](https://www.youtube.com/watch?v=MnxHuqcJVoM)
26 |
27 | ### Part 4 - Creating Cosmos DB
28 |
29 | Using the Azure CLI, I create the Cosmos DB account to represent a MongoDB model database and deploy it to Azure. Then I show how to view what we created in the Azure portal.
30 |
31 | [](https://www.youtube.com/watch?v=hfUM-AbOh94)
32 |
33 | ### Part 5 - Querying Cosmos DB
34 |
35 | This next video shows how to connect to the MongoDB database with Azure Cosmos DB, using Mongoose, and query it for data.
36 |
37 | [](https://www.youtube.com/watch?v=sI5hw6KPPXI)
38 |
39 | ### Part 6 - POST, PUT, and DELETE
40 |
41 | This next video shows how to POST, PUT, and DELETE to the MongoDB database with Azure Cosmos DB, using Mongoose.
42 |
43 | [](https://www.youtube.com/watch?v=Y5mdAlFGZjc)
44 |
--------------------------------------------------------------------------------
/e2e/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AngularCosmosdbPage } from './app.po';
2 |
3 | describe('angular-cosmosdb App', () => {
4 | let page: AngularCosmosdbPage;
5 |
6 | beforeEach(() => {
7 | page = new AngularCosmosdbPage();
8 | });
9 |
10 | it('should display welcome message', () => {
11 | page.navigateTo();
12 | expect(page.getParagraphText()).toEqual('Welcome to toh!!');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/e2e/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AngularCosmosdbPage {
4 | navigateTo() {
5 | return browser.get('/');
6 | }
7 |
8 | getParagraphText() {
9 | return element(by.css('app-root h1')).getText();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/e2e/tsconfig.e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/e2e",
5 | "module": "commonjs",
6 | "target": "es5",
7 | "types": [
8 | "jasmine",
9 | "jasminewd2",
10 | "node"
11 | ]
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/0.13/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular/cli'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular/cli/plugins/karma')
14 | ],
15 | client:{
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | reports: [ 'html', 'lcovonly' ],
20 | fixWebpackSourcePaths: true
21 | },
22 | angularCli: {
23 | environment: 'dev'
24 | },
25 | reporters: ['progress', 'kjhtml'],
26 | port: 9876,
27 | colors: true,
28 | logLevel: config.LOG_INFO,
29 | autoWatch: true,
30 | browsers: ['Chrome'],
31 | singleRun: false
32 | });
33 | };
34 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-cosmosdb",
3 | "version": "1.0.1",
4 | "license": "MIT",
5 | "scripts": {
6 | "ng": "ng",
7 | "start": "node src/server/index.js",
8 | "start-ng": "ng serve",
9 | "build": "ng build",
10 | "test": "ng test",
11 | "lint": "ng lint",
12 | "e2e": "ng e2e"
13 | },
14 | "private": true,
15 | "dependencies": {
16 | "@angular/animations": "^4.2.4",
17 | "@angular/common": "^4.2.4",
18 | "@angular/compiler": "^4.2.4",
19 | "@angular/core": "^4.2.4",
20 | "@angular/forms": "^4.2.4",
21 | "@angular/http": "^4.2.4",
22 | "@angular/platform-browser": "^4.2.4",
23 | "@angular/platform-browser-dynamic": "^4.2.4",
24 | "@angular/router": "^4.2.4",
25 | "core-js": "^2.4.1",
26 | "express": "^4.16.0",
27 | "mongoose": "^4.11.14",
28 | "rxjs": "^5.4.2",
29 | "zone.js": "^0.8.14"
30 | },
31 | "devDependencies": {
32 | "@angular/cli": "1.3.1",
33 | "@angular/compiler-cli": "^4.2.4",
34 | "@angular/language-service": "^4.2.4",
35 | "@types/jasmine": "~2.5.53",
36 | "@types/jasminewd2": "~2.0.2",
37 | "@types/node": "~6.0.60",
38 | "codelyzer": "~3.1.1",
39 | "jasmine-core": "~2.6.2",
40 | "jasmine-spec-reporter": "~4.1.0",
41 | "karma": "^2.0.2",
42 | "karma-chrome-launcher": "~2.1.1",
43 | "karma-cli": "~1.0.1",
44 | "karma-coverage-istanbul-reporter": "^1.2.1",
45 | "karma-jasmine": "~1.1.0",
46 | "karma-jasmine-html-reporter": "^0.2.2",
47 | "protractor": "^5.3.2",
48 | "ts-node": "~3.2.0",
49 | "tslint": "~5.3.2",
50 | "typescript": "~2.3.3"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // Protractor configuration file, see link for more information
2 | // https://github.com/angular/protractor/blob/master/lib/config.ts
3 |
4 | const { SpecReporter } = require('jasmine-spec-reporter');
5 |
6 | exports.config = {
7 | allScriptsTimeout: 11000,
8 | specs: [
9 | './e2e/**/*.e2e-spec.ts'
10 | ],
11 | capabilities: {
12 | 'browserName': 'chrome'
13 | },
14 | directConnect: true,
15 | baseUrl: 'http://localhost:4200/',
16 | framework: 'jasmine',
17 | jasmineNodeOpts: {
18 | showColors: true,
19 | defaultTimeoutInterval: 30000,
20 | print: function() {}
21 | },
22 | onPrepare() {
23 | require('ts-node').register({
24 | project: 'e2e/tsconfig.e2e.json'
25 | });
26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/src/client/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-root',
5 | template: `
6 |
7 | Angular Heroes
8 |
9 |
10 |
11 | `
12 | })
13 | export class AppComponent {}
14 |
--------------------------------------------------------------------------------
/src/client/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { NgModule } from '@angular/core';
3 | import { FormsModule } from '@angular/forms';
4 | import { HttpClientModule } from '@angular/common/http';
5 |
6 | import { AppComponent } from './app.component';
7 | import { HeroService } from './hero.service';
8 | import { HeroesComponent } from './heroes.component';
9 |
10 | @NgModule({
11 | declarations: [
12 | AppComponent,
13 | HeroesComponent
14 | ],
15 | imports: [
16 | BrowserModule,
17 | FormsModule,
18 | HttpClientModule
19 | ],
20 | providers: [HeroService],
21 | bootstrap: [AppComponent]
22 | })
23 | export class AppModule { }
24 |
--------------------------------------------------------------------------------
/src/client/app/hero.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { HttpClient } from '@angular/common/http';
3 |
4 | import { Hero } from './hero';
5 |
6 | const api = '/api';
7 |
8 | @Injectable()
9 | export class HeroService {
10 | constructor(private http: HttpClient) {}
11 |
12 | getHeroes() {
13 | return this.http.get>(`${api}/heroes`);
14 | }
15 |
16 | deleteHero(hero: Hero) {
17 | return this.http.delete(`${api}/hero/${hero.uid}`);
18 | }
19 |
20 | addHero(hero: Hero) {
21 | return this.http.post(`${api}/hero/`, hero);
22 | }
23 |
24 | updateHero(hero: Hero) {
25 | return this.http.put(`${api}/hero/${hero.uid}`, hero);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/client/app/hero.ts:
--------------------------------------------------------------------------------
1 | export class Hero {
2 | uid: number;
3 | name: string;
4 | saying: string;
5 | }
6 |
--------------------------------------------------------------------------------
/src/client/app/heroes.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
{{hero.uid}}
7 |
{{hero.name}}
8 |
{{hero.saying}}
9 |
10 |
11 |
12 |
13 |
14 |
15 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/client/app/heroes.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | import { Hero } from './hero';
4 | import { HeroService } from './hero.service';
5 |
6 | @Component({
7 | selector: 'app-heroes',
8 | templateUrl: './heroes.component.html'
9 | })
10 | export class HeroesComponent implements OnInit {
11 | addingHero = false;
12 | heroes: any = [];
13 | selectedHero: Hero;
14 |
15 | constructor(private heroService: HeroService) {}
16 |
17 | ngOnInit() {
18 | this.getHeroes();
19 | }
20 |
21 | cancel() {
22 | this.addingHero = false;
23 | this.selectedHero = null;
24 | }
25 |
26 | deleteHero(hero: Hero) {
27 | this.heroService.deleteHero(hero).subscribe(res => {
28 | this.heroes = this.heroes.filter(h => h !== hero);
29 | if (this.selectedHero === hero) {
30 | this.selectedHero = null;
31 | }
32 | });
33 | }
34 |
35 | getHeroes() {
36 | return this.heroService.getHeroes().subscribe(heroes => {
37 | this.heroes = heroes;
38 | });
39 | }
40 |
41 | enableAddMode() {
42 | this.addingHero = true;
43 | this.selectedHero = new Hero();
44 | }
45 |
46 | onSelect(hero: Hero) {
47 | this.addingHero = false;
48 | this.selectedHero = hero;
49 | }
50 |
51 | save() {
52 | if (this.addingHero) {
53 | this.heroService.addHero(this.selectedHero).subscribe(hero => {
54 | this.addingHero = false;
55 | this.selectedHero = null;
56 | this.heroes.push(hero);
57 | });
58 | } else {
59 | this.heroService.updateHero(this.selectedHero).subscribe(hero => {
60 | this.addingHero = false;
61 | this.selectedHero = null;
62 | });
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/client/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/angular-cosmosdb/73549bff17f095f36a6bb5288709852f7f8d68dc/src/client/assets/.gitkeep
--------------------------------------------------------------------------------
/src/client/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/src/client/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // The file contents for the current environment will overwrite these during build.
2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do
3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead.
4 | // The list of which env maps to which file can be found in `.angular-cli.json`.
5 |
6 | export const environment = {
7 | production: false
8 | };
9 |
--------------------------------------------------------------------------------
/src/client/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/angular-cosmosdb/73549bff17f095f36a6bb5288709852f7f8d68dc/src/client/favicon.ico
--------------------------------------------------------------------------------
/src/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | AngularCosmosdb
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/client/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic().bootstrapModule(AppModule);
12 |
--------------------------------------------------------------------------------
/src/client/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/
22 | // import 'core-js/es6/symbol';
23 | // import 'core-js/es6/object';
24 | // import 'core-js/es6/function';
25 | // import 'core-js/es6/parse-int';
26 | // import 'core-js/es6/parse-float';
27 | // import 'core-js/es6/number';
28 | // import 'core-js/es6/math';
29 | // import 'core-js/es6/string';
30 | // import 'core-js/es6/date';
31 | // import 'core-js/es6/array';
32 | // import 'core-js/es6/regexp';
33 | // import 'core-js/es6/map';
34 | // import 'core-js/es6/weak-map';
35 | // import 'core-js/es6/set';
36 |
37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */
38 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
39 |
40 | /** Evergreen browsers require these. **/
41 | import 'core-js/es6/reflect';
42 | import 'core-js/es7/reflect';
43 |
44 |
45 | /**
46 | * Required to support Web Animations `@angular/animation`.
47 | * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
48 | **/
49 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
50 |
51 |
52 |
53 | /***************************************************************************************************
54 | * Zone JS is required by Angular itself.
55 | */
56 | import 'zone.js/dist/zone'; // Included with Angular CLI.
57 |
58 |
59 |
60 | /***************************************************************************************************
61 | * APPLICATION IMPORTS
62 | */
63 |
64 | /**
65 | * Date, currency, decimal and percent pipes.
66 | * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10
67 | */
68 | // import 'intl'; // Run `npm install --save intl`.
69 | /**
70 | * Need to import at least one locale-data with intl.
71 | */
72 | // import 'intl/locale-data/jsonp/en';
73 |
--------------------------------------------------------------------------------
/src/client/styles.scss:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
3 | * {
4 | font-family: Arial;
5 | }
6 | h2 {
7 | color: #444;
8 | font-weight: lighter;
9 | }
10 | body {
11 | margin: 2em;
12 | }
13 |
14 | body,
15 | input[text],
16 | button {
17 | color: #888;
18 | // font-family: Cambria, Georgia;
19 | }
20 | button {
21 | font-size: 14px;
22 | font-family: Arial;
23 | background-color: #eee;
24 | border: none;
25 | padding: 5px 10px;
26 | border-radius: 4px;
27 | cursor: pointer;
28 | cursor: hand;
29 | &:hover {
30 | background-color: #cfd8dc;
31 | }
32 | &.delete-button {
33 | float: right;
34 | background-color: gray !important;
35 | background-color: rgb(216, 59, 1) !important;
36 | color: white;
37 | padding: 4px;
38 | position: relative;
39 | font-size: 12px;
40 | }
41 | }
42 | div {
43 | margin: .1em;
44 | }
45 |
46 | .selected {
47 | background-color: #cfd8dc !important;
48 | background-color: rgb(0, 120, 215) !important;
49 | color: white;
50 | }
51 |
52 | .heroes {
53 | float: left;
54 | margin: 0 0 2em 0;
55 | list-style-type: none;
56 | padding: 0;
57 | li {
58 | cursor: pointer;
59 | position: relative;
60 | left: 0;
61 | background-color: #eee;
62 | margin: .5em;
63 | padding: .5em;
64 | height: 3.0em;
65 | border-radius: 4px;
66 | width: 17em;
67 | &:hover {
68 | color: #607d8b;
69 | color: rgb(0, 120, 215);
70 | background-color: #ddd;
71 | left: .1em;
72 | }
73 | &.selected:hover {
74 | /*background-color: #BBD8DC !important;*/
75 | color: white;
76 | }
77 | }
78 | .text {
79 | position: relative;
80 | top: -3px;
81 | }
82 | .saying {
83 | margin: 5px 0;
84 | }
85 | .name {
86 | font-weight: bold;
87 | }
88 | .badge {
89 | /* display: inline-block; */
90 | float: left;
91 | font-size: small;
92 | color: white;
93 | padding: 0.7em 0.7em 0 0.5em;
94 | background-color: #607d8b;
95 | background-color: rgb(0, 120, 215);
96 | background-color:rgb(134, 183, 221);
97 | line-height: 1em;
98 | position: relative;
99 | left: -1px;
100 | top: -4px;
101 | height: 3.0em;
102 | margin-right: .8em;
103 | border-radius: 4px 0 0 4px;
104 | width: 1.2em;
105 | }
106 | }
107 |
108 | .header-bar {
109 | background-color: rgb(0, 120, 215);
110 | height: 4px;
111 | margin-top: 10px;
112 | margin-bottom: 10px;
113 | }
114 |
115 | label {
116 | display: inline-block;
117 | width: 4em;
118 | margin: .5em 0;
119 | color: #888;
120 | &.value {
121 | margin-left: 10px;
122 | font-size: 14px;
123 | }
124 | }
125 |
126 | input {
127 | height: 2em;
128 | font-size: 1em;
129 | padding-left: .4em;
130 | &::placeholder {
131 | color: lightgray;
132 | font-weight: normal;
133 | font-size: 12px;
134 | letter-spacing: 3px;
135 | }
136 | }
137 |
138 | .editarea {
139 | float: left;
140 | input {
141 | margin: 4px;
142 | height: 20px;
143 | color: rgb(0, 120, 215);
144 | }
145 | button {
146 | margin: 8px;
147 | }
148 | .editfields {
149 | margin-left: 12px;
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/src/client/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/long-stack-trace-zone';
4 | import 'zone.js/dist/proxy.js';
5 | import 'zone.js/dist/sync-test';
6 | import 'zone.js/dist/jasmine-patch';
7 | import 'zone.js/dist/async-test';
8 | import 'zone.js/dist/fake-async-test';
9 | import { getTestBed } from '@angular/core/testing';
10 | import {
11 | BrowserDynamicTestingModule,
12 | platformBrowserDynamicTesting
13 | } from '@angular/platform-browser-dynamic/testing';
14 |
15 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
16 | declare const __karma__: any;
17 | declare const require: any;
18 |
19 | // Prevent Karma from running prematurely.
20 | __karma__.loaded = function () {};
21 |
22 | // First, initialize the Angular testing environment.
23 | getTestBed().initTestEnvironment(
24 | BrowserDynamicTestingModule,
25 | platformBrowserDynamicTesting()
26 | );
27 | // Then we find all the tests.
28 | const context = require.context('./', true, /\.spec\.ts$/);
29 | // And load the modules.
30 | context.keys().map(context);
31 | // Finally, start Karma to run the tests.
32 | __karma__.start();
33 |
--------------------------------------------------------------------------------
/src/client/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/app",
5 | "module": "es2015",
6 | "baseUrl": "",
7 | "types": []
8 | },
9 | "exclude": [
10 | "test.ts",
11 | "**/*.spec.ts"
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/src/client/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/spec",
5 | "module": "commonjs",
6 | "target": "es5",
7 | "baseUrl": "",
8 | "types": [
9 | "jasmine",
10 | "node"
11 | ]
12 | },
13 | "files": [
14 | "test.ts"
15 | ],
16 | "include": [
17 | "**/*.spec.ts",
18 | "**/*.d.ts"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/src/client/typings.d.ts:
--------------------------------------------------------------------------------
1 | /* SystemJS module definition */
2 | declare var module: NodeModule;
3 | interface NodeModule {
4 | id: string;
5 | }
6 |
--------------------------------------------------------------------------------
/src/server/env/example-environment.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | accountName: 'your-cosmosdb-account-name-goes-here',
3 | databaseName: 'admin',
4 | key: 'your-key-goes-here',
5 | port: 10255
6 | };
7 |
--------------------------------------------------------------------------------
/src/server/hero.model.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 |
3 | const Schema = mongoose.Schema;
4 |
5 | const heroSchema = new Schema(
6 | {
7 | uid: { type: Number, required: true, unique: true },
8 | name: String,
9 | saying: String
10 | },
11 | {
12 | collection: 'heroes',
13 | read: 'nearest'
14 | }
15 | );
16 |
17 | const Hero = mongoose.model('Hero', heroSchema);
18 |
19 | module.exports = Hero;
20 |
--------------------------------------------------------------------------------
/src/server/hero.service.js:
--------------------------------------------------------------------------------
1 | const Hero = require('./hero.model');
2 | const ReadPreference = require('mongodb').ReadPreference;
3 |
4 | require('./mongo').connect();
5 |
6 | function getHeroes(req, res) {
7 | const docquery = Hero.find({}).read(ReadPreference.NEAREST);
8 | docquery
9 | .exec()
10 | .then(heroes => {
11 | res.status(200).json(heroes);
12 | })
13 | .catch(error => {
14 | res.status(500).send(error);
15 | return;
16 | });
17 | }
18 |
19 | function postHero(req, res) {
20 | const originalHero = { uid: req.body.uid, name: req.body.name, saying: req.body.saying };
21 | const hero = new Hero(originalHero);
22 | hero.save(error => {
23 | if (checkServerError(res, error)) return;
24 | res.status(201).json(hero);
25 | console.log('Hero created successfully!');
26 | });
27 | }
28 |
29 | function putHero(req, res) {
30 | const originalHero = {
31 | uid: parseInt(req.params.uid, 10),
32 | name: req.body.name,
33 | saying: req.body.saying
34 | };
35 | Hero.findOne({ uid: originalHero.uid }, (error, hero) => {
36 | if (checkServerError(res, error)) return;
37 | if (!checkFound(res, hero)) return;
38 |
39 | hero.name = originalHero.name;
40 | hero.saying = originalHero.saying;
41 | hero.save(error => {
42 | if (checkServerError(res, error)) return;
43 | res.status(200).json(hero);
44 | console.log('Hero updated successfully!');
45 | });
46 | });
47 | }
48 |
49 | function deleteHero(req, res) {
50 | const uid = parseInt(req.params.uid, 10);
51 | Hero.findOneAndRemove({ uid: uid })
52 | .then(hero => {
53 | if (!checkFound(res, hero)) return;
54 | res.status(200).json(hero);
55 | console.log('Hero deleted successfully!');
56 | })
57 | .catch(error => {
58 | if (checkServerError(res, error)) return;
59 | });
60 | }
61 |
62 | function checkServerError(res, error) {
63 | if (error) {
64 | res.status(500).send(error);
65 | return error;
66 | }
67 | }
68 |
69 | function checkFound(res, hero) {
70 | if (!hero) {
71 | res.status(404).send('Hero not found.');
72 | return;
73 | }
74 | return hero;
75 | }
76 |
77 | module.exports = {
78 | getHeroes,
79 | postHero,
80 | putHero,
81 | deleteHero
82 | };
83 |
--------------------------------------------------------------------------------
/src/server/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const path = require('path');
3 | const bodyParser = require('body-parser');
4 | const routes = require('./routes');
5 |
6 | const root = './';
7 | const app = express();
8 |
9 | app.use(bodyParser.json());
10 | app.use(bodyParser.urlencoded({ extended: false }));
11 | app.use(express.static(path.join(root, 'dist')));
12 | app.use('/api', routes);
13 | app.get('*', (req, res) => {
14 | res.sendFile('dist/index.html', {root: root});
15 | });
16 |
17 | const port = process.env.PORT || '3000';
18 | app.listen(port, () => console.log(`API running on localhost:${port}`));
19 |
--------------------------------------------------------------------------------
/src/server/mongo.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 | /**
3 | * Set to Node.js native promises
4 | * Per http://mongoosejs.com/docs/promises.html
5 | */
6 | mongoose.Promise = global.Promise;
7 |
8 | const env = require('./env/environment');
9 |
10 | // eslint-disable-next-line max-len
11 | const mongoUri = `mongodb://${env.accountName}:${env.key}@${env.accountName}.documents.azure.com:${env.port}/${
12 | env.databaseName
13 | }?ssl=true`;
14 |
15 | function connect() {
16 | mongoose.set('debug', true);
17 | return mongoose.connect(
18 | mongoUri,
19 | {
20 | useMongoClient: true
21 | }
22 | );
23 | }
24 |
25 | module.exports = {
26 | connect,
27 | mongoose
28 | };
29 |
--------------------------------------------------------------------------------
/src/server/routes.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const router = express.Router();
3 |
4 | const heroService = require('./hero.service');
5 |
6 | router.get('/heroes', (req, res) => {
7 | heroService.getHeroes(req, res);
8 | });
9 |
10 | router.post('/hero', (req, res) => {
11 | heroService.postHero(req, res);
12 | });
13 |
14 | router.put('/hero/:uid', (req, res) => {
15 | heroService.putHero(req, res);
16 | });
17 |
18 | router.delete('/hero/:uid', (req, res) => {
19 | heroService.deleteHero(req, res);
20 | });
21 |
22 | module.exports = router;
23 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "outDir": "./dist/out-tsc",
5 | "baseUrl": "src",
6 | "sourceMap": true,
7 | "declaration": false,
8 | "moduleResolution": "node",
9 | "emitDecoratorMetadata": true,
10 | "experimentalDecorators": true,
11 | "target": "es5",
12 | "typeRoots": [
13 | "node_modules/@types"
14 | ],
15 | "lib": [
16 | "es2016",
17 | "dom"
18 | ]
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "rules": {
6 | "arrow-return-shorthand": true,
7 | "callable-types": true,
8 | "class-name": true,
9 | "comment-format": [
10 | true,
11 | "check-space"
12 | ],
13 | "curly": true,
14 | "eofline": true,
15 | "forin": true,
16 | "import-blacklist": [
17 | true,
18 | "rxjs"
19 | ],
20 | "import-spacing": true,
21 | "indent": [
22 | true,
23 | "spaces"
24 | ],
25 | "interface-over-type-literal": true,
26 | "label-position": true,
27 | "max-line-length": [
28 | true,
29 | 140
30 | ],
31 | "member-access": false,
32 | "member-ordering": [
33 | true,
34 | "static-before-instance",
35 | "variables-before-functions"
36 | ],
37 | "no-arg": true,
38 | "no-bitwise": true,
39 | "no-console": [
40 | true,
41 | "debug",
42 | "info",
43 | "time",
44 | "timeEnd",
45 | "trace"
46 | ],
47 | "no-construct": true,
48 | "no-debugger": true,
49 | "no-duplicate-super": true,
50 | "no-empty": false,
51 | "no-empty-interface": true,
52 | "no-eval": true,
53 | "no-inferrable-types": [
54 | true,
55 | "ignore-params"
56 | ],
57 | "no-misused-new": true,
58 | "no-non-null-assertion": true,
59 | "no-shadowed-variable": true,
60 | "no-string-literal": false,
61 | "no-string-throw": true,
62 | "no-switch-case-fall-through": true,
63 | "no-trailing-whitespace": true,
64 | "no-unnecessary-initializer": true,
65 | "no-unused-expression": true,
66 | "no-use-before-declare": true,
67 | "no-var-keyword": true,
68 | "object-literal-sort-keys": false,
69 | "one-line": [
70 | true,
71 | "check-open-brace",
72 | "check-catch",
73 | "check-else",
74 | "check-whitespace"
75 | ],
76 | "prefer-const": true,
77 | "quotemark": [
78 | true,
79 | "single"
80 | ],
81 | "radix": true,
82 | "semicolon": [
83 | "always"
84 | ],
85 | "triple-equals": [
86 | true,
87 | "allow-null-check"
88 | ],
89 | "typedef-whitespace": [
90 | true,
91 | {
92 | "call-signature": "nospace",
93 | "index-signature": "nospace",
94 | "parameter": "nospace",
95 | "property-declaration": "nospace",
96 | "variable-declaration": "nospace"
97 | }
98 | ],
99 | "typeof-compare": true,
100 | "unified-signatures": true,
101 | "variable-name": false,
102 | "whitespace": [
103 | true,
104 | "check-branch",
105 | "check-decl",
106 | "check-operator",
107 | "check-separator",
108 | "check-type"
109 | ],
110 | "directive-selector": [
111 | true,
112 | "attribute",
113 | "app",
114 | "camelCase"
115 | ],
116 | "component-selector": [
117 | true,
118 | "element",
119 | "app",
120 | "kebab-case"
121 | ],
122 | "use-input-property-decorator": true,
123 | "use-output-property-decorator": true,
124 | "use-host-property-decorator": true,
125 | "no-input-rename": true,
126 | "no-output-rename": true,
127 | "use-life-cycle-interface": true,
128 | "use-pipe-transform-interface": true,
129 | "component-class-suffix": true,
130 | "directive-class-suffix": true,
131 | "no-access-missing-member": true,
132 | "templates-use-public": true,
133 | "invoke-injectable": true
134 | }
135 | }
136 |
--------------------------------------------------------------------------------