Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Forum sample rework #2455

Merged
merged 29 commits into from
Apr 14, 2021
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f8b2d62
forum sample: fix demo script & other cleanup
letmaik Apr 9, 2021
da55875
wip, still broken
letmaik Apr 9, 2021
70f76f4
add missing "type": "module"
letmaik Apr 9, 2021
f8a7c7d
add missing file extensions in ccf-app pkg
letmaik Apr 10, 2021
bc057ba
Merge branch 'letmaik/js-ccf-app-imports' into letmaik/forum-unit-tests
letmaik Apr 10, 2021
97cff31
add .js exts
letmaik Apr 10, 2021
ba510f5
extend polyfill test
letmaik Apr 10, 2021
e78f4d8
docs
letmaik Apr 10, 2021
e33d2d5
wip
letmaik Apr 10, 2021
e8b247f
add dev notes
letmaik Apr 11, 2021
624bd37
formatting
letmaik Apr 11, 2021
2062e54
Merge branch 'letmaik/js-ccf-app-imports' into letmaik/forum-unit-tests
letmaik Apr 11, 2021
0c80344
wip
letmaik Apr 11, 2021
8c8320f
.
letmaik Apr 11, 2021
ab8b712
Merge branch 'letmaik/forum-sample-fix' into letmaik/forum-unit-tests
letmaik Apr 11, 2021
c54f8f9
cleanup
letmaik Apr 11, 2021
360d11c
readme
letmaik Apr 11, 2021
2b99e88
final round
letmaik Apr 12, 2021
ea9ec0b
readme
letmaik Apr 12, 2021
37052d9
Merge remote-tracking branch 'origin/main' into letmaik/forum-unit-tests
letmaik Apr 12, 2021
02f4e2b
docs
letmaik Apr 12, 2021
cb32bca
Merge remote-tracking branch 'origin/main' into letmaik/forum-unit-tests
letmaik Apr 12, 2021
3601fef
diff
letmaik Apr 12, 2021
4dda02f
fix e2e test
letmaik Apr 12, 2021
c712e1c
Merge branch 'main' into letmaik/forum-unit-tests
achamayou Apr 12, 2021
4ace408
Merge branch 'main' into letmaik/forum-unit-tests
achamayou Apr 13, 2021
a04a49c
Merge branch 'main' into letmaik/forum-unit-tests
achamayou Apr 14, 2021
287d092
Merge branch 'main' into letmaik/forum-unit-tests
achamayou Apr 14, 2021
b95265f
Merge branch 'main' into letmaik/forum-unit-tests
achamayou Apr 14, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions doc/build_apps/js_app_tsoa.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,13 @@ The sample app has the following folder layout:
│ │ └── site.ts
│ ├── models
│ │ └── poll.ts
│ ├── services
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are CCF/enclave apps called services? If not, I would suggest to change services into e.g. enclaveApp. Services is confusing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This term comes from tsoa's docs: https://tsoa-community.github.io/docs/getting-started.html#defining-our-first-model. I've seen this before outside tsoa, another term would be *Manager but I'd like to stick to tsoa's docs here. The whole npm app runs inside CCF, not just the services.

│ │ ├── csv.ts
│ │ └── poll.ts
│ ├── authentication.ts
│ ├── constants.ts
│ └── error_handler.ts
├── tsoa-support
│ ├── entry.ts
│ ├── postprocess.js
│ └── routes.ts.tmpl
├── app.tmpl.json
Expand All @@ -56,9 +59,11 @@ The sample app has the following folder layout:
It contains these files:

- ``src/controllers/*.ts``: :ref:`build_apps/js_app_tsoa:Controllers`.
- ``src/models/*.ts``: Data models shared between endpoint handlers.
- ``src/models/*.ts``: Data models shared between different app components.
- ``src/services/*.ts``: Business logic, called by controllers.
- ``src/authentication.ts``: `authentication module <https://tsoa-community.github.io/docs/authentication.html>`_.
See also :ref:`build_apps/auth/jwt_ms_example:JWT Authentication example using Microsoft Identity Platform`.
- ``src/constants.ts``: app-wide constants.
- ``src/error_handler.ts``: global error handler.
- ``tsoa-support/*``: Supporting scripts used during :ref:`build_apps/js_app_tsoa:Conversion to an app bundle`.
- ``app.tmpl.json``: :ref:`App metadata <build_apps/js_app_tsoa:Metadata>`.
Expand Down
3 changes: 2 additions & 1 deletion samples/apps/forum/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ build/
.venv_ccf_sandbox/
.workspace_ccf/
*_opinions.csv
*.jwt
*.jwt
*.pem
51 changes: 44 additions & 7 deletions samples/apps/forum/README.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,68 @@
# Confidential Forum sample app

NOTE: This sample is a work-in-progress.
See also the [TypeScript Application using tsoa](https://microsoft.github.io/CCF/main/build_apps/js_app_tsoa.html) documentation page for further details on how this sample is built using the tsoa framework.

Install dependencies:
## Getting started

When running this sample against a CCF release, open `package.json` and replace the `file:` reference of `ccf-app` with a reference to a published version (adjust the version number accordingly):

```
"@microsoft/ccf-app": "~1.0.0",
```

Now you can continue with installing all dependencies:

```sh
npm install
```

To run the demo and end-to-end tests, define the following environment variable:

```sh
export CCF_BINARY_DIR=/opt/ccf-x.y.z/bin
```

If not defined, it assumes you built CCF from source and defaults to `CCF_BINARY_DIR=<repo_root>/build`.

## Demo

Start the sandbox:

```sh
npm start
```

Open your browser at https://127.0.0.1:8000/app/site
(Use `VERBOSE=1 npm start` for verbose output)

Generate opinions, user identities and submit:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These edits are really a great addition

Open your browser at https://127.0.0.1:8000/app/site and create the sample polls and opinions.
The statistics will still be empty since the opinion threshold has not been reached yet.

Now, generate more opinions, user identities and submit:

```sh
python3.8 test/demo/generate-opinions.py test/demo/polls.csv 9
npm run ts test/demo/generate-jwts.ts . 9
npm run ts test/demo/submit-opinions.ts .
mkdir demo/data
python3.8 demo/generate-opinions.py demo/data demo/polls.csv 9
npm run ts demo/generate-jwts.ts demo/data 9
npm run ts demo/submit-opinions.ts demo/data
```

Return to the website and view the statistics which should be visible now.

## Tests & debugging

Run tests:

```sh
npm test
# or:
npm run test:unit # unit tests
npm run test:e2e # end-to-end tests
```

Unit tests run outside CCF and end-to-end tests run against a single CCF sandbox node.

The unit tests make use of the [`ccf-app` polyfill](https://microsoft.github.io/CCF/main/js/ccf-app/modules/polyfill.html) and can be easily debugged in VS Code.
The simplest workflow is using the [JavaScript Debug Terminal](https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_javascript-debug-terminal) in VS Code. Set a break point and run `npm run test:unit` inside the JavaScript Debug Terminal. The debugger will attach and stop automatically.

Debugging of JavaScript code running in CCF is currently not possible.
However, all uncaught exceptions and output from `console.log()` are dumped to the node's log files.
6 changes: 3 additions & 3 deletions samples/apps/forum/app.tmpl.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@
},
"/csv": {
"get": {
"js_module": "CSVControllerProxy.js",
"js_function": "getOpinionsAsCSV",
"js_module": "CsvControllerProxy.js",
"js_function": "getOpinionsAsCsv",
"forwarding_required": "always",
"execute_outside_consensus": "never",
"authn_policies": ["jwt"],
Expand All @@ -112,7 +112,7 @@
}
},
"post": {
"js_module": "CSVControllerProxy.js",
"js_module": "CsvControllerProxy.js",
"js_function": "submitOpinionsFromCSV",
"forwarding_required": "always",
"execute_outside_consensus": "never",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as fs from "fs";
import * as path from "path";
import jwt from "jsonwebtoken";

const demoJwtKeyPath = "test/jwt_demo_key.pem";
const demoJwtKeyPath = "demo/jwt_demo_key.pem";

function main() {
const args = process.argv.slice(2);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the Apache 2.0 License.

import os
import sys
import csv
import random
Expand Down Expand Up @@ -42,15 +43,15 @@ def spread(topic):
assert False


def main(polls_path, user_count):
def main(out_dir, polls_path, user_count):
entries = []

with open(polls_path, "r") as pp:
polls = csv.DictReader(pp)
entries = [poll for poll in polls]

for user in range(user_count):
with open(f"user{user}_opinions.csv", "w") as uf:
with open(os.path.join(out_dir, f"user{user}_opinions.csv"), "w") as uf:
header = ["Topic", "Opinion"]
writer = csv.DictWriter(uf, header)
writer.writeheader()
Expand All @@ -68,4 +69,4 @@ def push(opinion):


if __name__ == "__main__":
main(sys.argv[1], int(sys.argv[2]))
main(sys.argv[1], sys.argv[2], int(sys.argv[3]))
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@
import { spawnSync } from "child_process";
import * as fs from "fs";
import * as crypto from "crypto";
import selfsigned from "selfsigned";
import tmp from "tmp";
import bent from "bent";
import forge from "node-forge";
import * as util from "./util";
import * as util from "../test/e2e/util";

const demoJwtKeyPath = "test/jwt_demo_key.pem";
const demoJwtCertPath = "test/jwt_demo_cert.pem";
const demoJwtKeyPath = "demo/jwt_demo_key.pem";
const demoJwtCertPath = "demo/jwt_demo_cert.pem";

async function main() {
tmp.setGracefulCleanup();
Expand Down Expand Up @@ -71,13 +70,20 @@ function generateKeyPair(keyPath: string, certPath: string) {
format: "pem",
},
});
const certPem = selfsigned.generate(null, {
algorithm: "sha256",
keyPair: {
privateKey: keys.privateKey,
publicKey: keys.publicKey,
const cert = forge.pki.createCertificate();
const attrs = [
{
name: "commonName",
value: "Test",
},
}).cert;
];
cert.setIssuer(attrs);
cert.publicKey = forge.pki.publicKeyFromPem(keys.publicKey);
cert.sign(
forge.pki.privateKeyFromPem(keys.privateKey),
forge.md.sha256.create()
);
const certPem = forge.pki.certificateToPem(cert);
fs.writeFileSync(keyPath, keys.privateKey);
fs.writeFileSync(certPath, certPem);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ import * as fs from "fs";
import * as path from "path";
import glob from "glob";
import bent from "bent";
import { parse } from "papaparse";
import { NODE_ADDR } from "../util";
import { SubmitOpinionsRequest } from "../../src/controllers/poll";
import papa from "papaparse";
import { NODE_ADDR } from "../test/e2e/util";
import { SubmitOpinionsRequest } from "../src/controllers/poll";

const ENDPOINT_URL = `${NODE_ADDR}/app/polls`;

function getAuth(jwt: string) {
// See src/util.ts.
return {
authorization: `Bearer ${jwt}'`,
authorization: `Bearer ${jwt}`,
};
}

Expand All @@ -36,7 +36,8 @@ async function main() {
const jwtPath = path.join(folder, user + ".jwt");
const jwt = fs.readFileSync(jwtPath, "utf8");
const csv = fs.readFileSync(csvPath, "utf8");
const rows = parse(csv, { header: true }).data as CSVRow[];
const rows = papa.parse(csv, { header: true, skipEmptyLines: true })
.data as CSVRow[];

const req: SubmitOpinionsRequest = { opinions: {} };
for (const row of rows) {
Expand All @@ -45,6 +46,7 @@ async function main() {
};
}
console.log("Submitting opinions for user " + user);
console.log(req);
try {
await bent("PUT", 204)(`${ENDPOINT_URL}`, req, getAuth(jwt));
} catch (e) {
Expand Down
22 changes: 11 additions & 11 deletions samples/apps/forum/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,26 @@
"private": true,
"scripts": {
"build": "del-cli -f dist/ build/ && tsoa spec-and-routes && node tsoa-support/postprocess.js && rollup --config && del-cli dist/src/build/endpoints.js",
"start": "npm run build && npm run ts test/start.ts",
"test": "npm run build && mocha -r esm -r ts-node/register test/**/*.test.ts",
"ts": "node --no-warnings --experimental-specifier-resolution=node --loader ts-node/esm"
"start": "npm run build && npm run ts demo/start.ts",
"test": "npm run test:unit && npm run test:e2e",
"test:unit": "mocha --loader=ts-node/esm --experimental-specifier-resolution=node --file=test/unit/setup.ts test/unit/*.test.ts",
"test:e2e": "npm run build && mocha --loader=ts-node/esm --experimental-specifier-resolution=node test/e2e/*.test.ts",
"ts": "node --no-warnings --loader=ts-node/esm --experimental-specifier-resolution=node"
},
"type": "module",
"dependencies": {
"@microsoft/ccf-app": "file:../../../js/ccf-app",
"@tsoa/runtime": "^3.3.0",
"@tsoa/runtime": "^3.6.1",
"lodash-es": "^4.17.15",
"mathjs": "^7.5.1",
"mathjs": "9.3.0 || ^9.3.2",
"papaparse": "^5.3.0"
},
"devDependencies": {
"@apidevtools/swagger-parser": "^10.0.2",
"@rollup/plugin-commonjs": "^14.0.0",
"@rollup/plugin-node-resolve": "^8.4.0",
"@rollup/plugin-typescript": "^5.0.2",
"@tsoa/cli": "^3.3.0",
"@tsoa/cli": "^3.6.1",
"@types/bent": "^7.3.1",
"@types/chai": "^4.2.13",
"@types/jsonwebtoken": "^8.5.0",
Expand All @@ -33,18 +35,16 @@
"bent": "^7.3.12",
"chai": "^4.2.0",
"del-cli": "^3.0.1",
"esm": "^3.2.25",
"glob": "^7.1.6",
"http-server": "^0.12.3",
"json-merge-patch": "^1.0.1",
"jsonwebtoken": "^8.5.1",
"mocha": "^8.1.3",
"mocha": "^8.3.2",
"node-forge": "^0.10.0",
"rollup": "^2.23.0",
"selfsigned": "^1.10.8",
"tmp": "^0.2.1",
"ts-node": "^9.1.0",
"tslib": "^2.0.1",
"typescript": "^4.0.2"
"tslib": "^2.2.0",
"typescript": "^4.2.4"
}
}
1 change: 1 addition & 0 deletions samples/apps/forum/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const MINIMUM_OPINION_THRESHOLD = 10;
Loading