From 219903814314543ff201587c8cd7d465fb8ccec7 Mon Sep 17 00:00:00 2001 From: Thomas Schaffter Date: Thu, 22 Aug 2024 14:11:32 -0700 Subject: [PATCH] feat(agora): migrate Agora to spec-first development workflow (#2767) undefined --- .devcontainer/devcontainer.json | 2 +- apps/agora/apex/.env.example | 6 + apps/agora/apex/Caddyfile | 14 + apps/agora/apex/Dockerfile | 1 + apps/agora/apex/project.json | 68 ++ apps/agora/{server => api}/.env.example | 0 apps/agora/{server => api}/.eslintrc.json | 0 apps/agora/{server => api}/Dockerfile | 6 +- .../{server => api}/docker-entrypoint.sh | 0 apps/agora/{server => api}/jest.config.ts | 4 +- apps/agora/{server => api}/project.json | 65 +- apps/agora/{server => api}/src/api.ts | 0 .../agora/{server => api}/src/assets/.gitkeep | 0 .../src/components/dataversion.ts | 0 apps/agora/{server => api}/src/helpers.ts | 0 apps/agora/{server => api}/src/main.ts | 4 +- .../{server => api}/src/models/dataversion.ts | 6 +- apps/agora/{server => api}/tsconfig.app.json | 0 apps/agora/{server => api}/tsconfig.json | 2 +- apps/agora/{server => api}/tsconfig.spec.json | 0 apps/agora/{server => api}/webpack.config.js | 0 apps/agora/app/.env.example | 7 + apps/agora/app/.eslintrc.json | 4 +- apps/agora/app/Dockerfile | 4 +- .../10-envsubst-on-app-config-template.sh | 4 +- apps/agora/app/docker-entrypoint.sh | 2 +- apps/agora/app/project.json | 99 +- apps/agora/app/proxy.conf.json | 6 - apps/agora/app/server.ts | 2 +- apps/agora/app/src/_app-theme.scss | 48 + apps/agora/app/src/app/app.component.html | 10 +- .../app/src/app/app.component.spec.ts.off | 36 +- apps/agora/app/src/app/app.component.ts | 24 +- apps/agora/app/src/app/app.config.server.ts | 12 +- apps/agora/app/src/app/app.config.ts | 57 +- apps/agora/app/src/app/app.routes.ts | 14 +- .../agora/app/src/app/nx-welcome.component.ts | 984 ------------------ apps/agora/app/src/config/config.json | 9 + .../agora/app/src/config/config.json.template | 9 + apps/agora/app/src/humans.txt | 6 + apps/agora/app/src/index.html | 4 +- apps/agora/app/src/robots.txt | 1 + apps/agora/app/src/styles.scss | 3 + apps/agora/app/tsconfig.app.json | 21 +- apps/agora/app/tsconfig.json | 4 + apps/agora/app/tsconfig.server.json | 14 +- docker/agora/serve-detach.sh | 3 +- docker/agora/services/apex.yml | 24 + docker/agora/services/{server.yml => api.yml} | 8 +- docker/agora/services/app.yml | 4 +- libs/agora/api-client-angular/.eslintrc.json | 16 +- libs/agora/api-client-angular/ng-package.json | 2 +- .../api-client-angular/openapitools.json | 19 + libs/agora/api-client-angular/package.json | 2 +- libs/agora/api-client-angular/project.json | 38 +- .../api-client-angular/src/lib/.gitignore | 4 + .../src/lib/.openapi-generator-ignore | 23 + .../src/lib/.openapi-generator/FILES | 14 + .../src/lib/.openapi-generator/VERSION | 1 + .../api-client-angular/src/lib/README.md | 226 ++++ .../api-client-angular/src/lib/api.module.ts | 31 + .../api-client-angular/src/lib/api/api.ts | 4 +- .../src/lib/api/constants.ts | 9 - .../src/lib/api/dataversion.service.ts | 162 ++- .../src/lib/configuration.ts | 166 +++ .../api-client-angular/src/lib/encoder.ts | 20 + .../api-client-angular/src/lib/git_push.sh | 57 + .../agora/api-client-angular/src/lib/index.ts | 4 + .../src/lib/model/basicError.ts | 35 + .../src/lib/model/dataversion.ts | 25 +- .../src/lib/model/models.ts | 1 + .../agora/api-client-angular/src/lib/param.ts | 69 ++ .../api-client-angular/src/lib/variables.ts | 9 + .../templates/api.module.mustache | 37 + .../api-client-angular/templates/config.yaml | 3 + .../templates/v6.1.x/api.module.mustache | 37 + .../tools/pull-templates.sh | 9 + libs/agora/config/.eslintrc.json | 50 + libs/agora/config/README.md | 7 + libs/agora/config/jest.config.ts | 23 + libs/agora/config/project.json | 33 + libs/agora/config/src/index.ts | 3 + libs/agora/config/src/lib/app.config.ts | 24 + libs/agora/config/src/lib/config.factory.ts | 5 + libs/agora/config/src/lib/config.service.ts | 48 + libs/agora/config/src/test-setup.ts | 1 + libs/agora/config/tsconfig.json | 27 + libs/agora/config/tsconfig.lib.json | 12 + libs/agora/config/tsconfig.spec.json | 10 + libs/agora/not-found/.eslintrc.json | 50 + libs/agora/not-found/README.md | 7 + libs/agora/not-found/jest.config.ts | 23 + libs/agora/not-found/project.json | 33 + libs/agora/not-found/src/_lib-theme.scss | 5 + libs/agora/not-found/src/index.ts | 1 + .../not-found/src/lib/_not-found-theme.scss | 36 + .../src/lib/not-found.component.html | 32 + .../src/lib/not-found.component.scss | 22 + .../src/lib/not-found.component.spec.ts | 30 + .../not-found/src/lib/not-found.component.ts | 56 + .../not-found/src/lib/not-found.routes.ts | 4 + libs/agora/not-found/src/lib/seo-data.ts | 8 + libs/agora/not-found/src/test-setup.ts | 1 + libs/agora/not-found/tsconfig.json | 27 + libs/agora/not-found/tsconfig.lib.json | 12 + libs/agora/not-found/tsconfig.spec.json | 10 + libs/agora/styles/README.md | 1 + libs/agora/styles/project.json | 13 + libs/agora/styles/src/_index.scss | 4 + libs/agora/styles/src/lib/_constants.scss | 39 + libs/agora/styles/src/lib/_general.scss | 432 ++++++++ libs/agora/themes/README.md | 1 + libs/agora/themes/project.json | 14 + libs/agora/themes/src/_fonts.scss | 15 + libs/agora/themes/src/_index.scss | 7 + libs/agora/themes/src/_palettes.scss | 135 +++ libs/agora/ui/.eslintrc.json | 50 + libs/agora/ui/README.md | 7 + libs/agora/ui/jest.config.ts | 23 + libs/agora/ui/project.json | 27 + libs/agora/ui/src/_lib-theme.scss | 5 + libs/agora/ui/src/index.ts | 1 + .../ui/src/lib/footer/_footer-theme.scss | 60 ++ .../ui/src/lib/footer/footer.component.html | 41 + .../ui/src/lib/footer/footer.component.scss | 71 ++ .../src/lib/footer/footer.component.spec.ts | 25 + .../ui/src/lib/footer/footer.component.ts | 18 + libs/agora/ui/src/test-setup.ts | 1 + libs/agora/ui/tsconfig.json | 27 + libs/agora/ui/tsconfig.lib.json | 12 + libs/agora/ui/tsconfig.spec.json | 10 + libs/agora/util/.eslintrc.json | 50 + libs/agora/util/README.md | 7 + libs/agora/util/jest.config.ts | 22 + libs/agora/util/project.json | 27 + libs/agora/util/src/index.ts | 1 + libs/agora/util/src/lib/default-seo-data.ts | 13 + libs/agora/util/src/test-setup.ts | 1 + libs/agora/util/tsconfig.json | 26 + libs/agora/util/tsconfig.lib.json | 12 + libs/agora/util/tsconfig.spec.json | 10 + tools/configure-hostnames.sh | 2 +- tsconfig.base.json | 112 +- 143 files changed, 3324 insertions(+), 1222 deletions(-) create mode 100644 apps/agora/apex/.env.example create mode 100644 apps/agora/apex/Caddyfile create mode 100644 apps/agora/apex/Dockerfile create mode 100644 apps/agora/apex/project.json rename apps/agora/{server => api}/.env.example (100%) rename apps/agora/{server => api}/.eslintrc.json (100%) rename apps/agora/{server => api}/Dockerfile (75%) rename apps/agora/{server => api}/docker-entrypoint.sh (100%) rename apps/agora/{server => api}/jest.config.ts (73%) rename apps/agora/{server => api}/project.json (56%) rename apps/agora/{server => api}/src/api.ts (100%) rename apps/agora/{server => api}/src/assets/.gitkeep (100%) rename apps/agora/{server => api}/src/components/dataversion.ts (100%) rename apps/agora/{server => api}/src/helpers.ts (100%) rename apps/agora/{server => api}/src/main.ts (86%) rename apps/agora/{server => api}/src/models/dataversion.ts (64%) rename apps/agora/{server => api}/tsconfig.app.json (100%) rename apps/agora/{server => api}/tsconfig.json (99%) rename apps/agora/{server => api}/tsconfig.spec.json (100%) rename apps/agora/{server => api}/webpack.config.js (100%) delete mode 100644 apps/agora/app/proxy.conf.json create mode 100644 apps/agora/app/src/_app-theme.scss delete mode 100644 apps/agora/app/src/app/nx-welcome.component.ts create mode 100644 apps/agora/app/src/config/config.json create mode 100644 apps/agora/app/src/config/config.json.template create mode 100644 apps/agora/app/src/humans.txt create mode 100644 apps/agora/app/src/robots.txt create mode 100644 docker/agora/services/apex.yml rename docker/agora/services/{server.yml => api.yml} (55%) create mode 100644 libs/agora/api-client-angular/openapitools.json create mode 100644 libs/agora/api-client-angular/src/lib/.gitignore create mode 100644 libs/agora/api-client-angular/src/lib/.openapi-generator-ignore create mode 100644 libs/agora/api-client-angular/src/lib/.openapi-generator/FILES create mode 100644 libs/agora/api-client-angular/src/lib/.openapi-generator/VERSION create mode 100644 libs/agora/api-client-angular/src/lib/README.md create mode 100644 libs/agora/api-client-angular/src/lib/api.module.ts delete mode 100644 libs/agora/api-client-angular/src/lib/api/constants.ts create mode 100644 libs/agora/api-client-angular/src/lib/configuration.ts create mode 100644 libs/agora/api-client-angular/src/lib/encoder.ts create mode 100644 libs/agora/api-client-angular/src/lib/git_push.sh create mode 100644 libs/agora/api-client-angular/src/lib/model/basicError.ts create mode 100644 libs/agora/api-client-angular/src/lib/param.ts create mode 100644 libs/agora/api-client-angular/src/lib/variables.ts create mode 100644 libs/agora/api-client-angular/templates/api.module.mustache create mode 100644 libs/agora/api-client-angular/templates/config.yaml create mode 100644 libs/agora/api-client-angular/templates/v6.1.x/api.module.mustache create mode 100755 libs/agora/api-client-angular/tools/pull-templates.sh create mode 100644 libs/agora/config/.eslintrc.json create mode 100644 libs/agora/config/README.md create mode 100644 libs/agora/config/jest.config.ts create mode 100644 libs/agora/config/project.json create mode 100644 libs/agora/config/src/index.ts create mode 100644 libs/agora/config/src/lib/app.config.ts create mode 100644 libs/agora/config/src/lib/config.factory.ts create mode 100644 libs/agora/config/src/lib/config.service.ts create mode 100644 libs/agora/config/src/test-setup.ts create mode 100644 libs/agora/config/tsconfig.json create mode 100644 libs/agora/config/tsconfig.lib.json create mode 100644 libs/agora/config/tsconfig.spec.json create mode 100644 libs/agora/not-found/.eslintrc.json create mode 100644 libs/agora/not-found/README.md create mode 100644 libs/agora/not-found/jest.config.ts create mode 100644 libs/agora/not-found/project.json create mode 100644 libs/agora/not-found/src/_lib-theme.scss create mode 100644 libs/agora/not-found/src/index.ts create mode 100644 libs/agora/not-found/src/lib/_not-found-theme.scss create mode 100644 libs/agora/not-found/src/lib/not-found.component.html create mode 100644 libs/agora/not-found/src/lib/not-found.component.scss create mode 100644 libs/agora/not-found/src/lib/not-found.component.spec.ts create mode 100644 libs/agora/not-found/src/lib/not-found.component.ts create mode 100644 libs/agora/not-found/src/lib/not-found.routes.ts create mode 100644 libs/agora/not-found/src/lib/seo-data.ts create mode 100644 libs/agora/not-found/src/test-setup.ts create mode 100644 libs/agora/not-found/tsconfig.json create mode 100644 libs/agora/not-found/tsconfig.lib.json create mode 100644 libs/agora/not-found/tsconfig.spec.json create mode 100644 libs/agora/styles/README.md create mode 100644 libs/agora/styles/project.json create mode 100644 libs/agora/styles/src/_index.scss create mode 100644 libs/agora/styles/src/lib/_constants.scss create mode 100644 libs/agora/styles/src/lib/_general.scss create mode 100644 libs/agora/themes/README.md create mode 100644 libs/agora/themes/project.json create mode 100644 libs/agora/themes/src/_fonts.scss create mode 100644 libs/agora/themes/src/_index.scss create mode 100644 libs/agora/themes/src/_palettes.scss create mode 100644 libs/agora/ui/.eslintrc.json create mode 100644 libs/agora/ui/README.md create mode 100644 libs/agora/ui/jest.config.ts create mode 100644 libs/agora/ui/project.json create mode 100644 libs/agora/ui/src/_lib-theme.scss create mode 100644 libs/agora/ui/src/index.ts create mode 100644 libs/agora/ui/src/lib/footer/_footer-theme.scss create mode 100644 libs/agora/ui/src/lib/footer/footer.component.html create mode 100644 libs/agora/ui/src/lib/footer/footer.component.scss create mode 100644 libs/agora/ui/src/lib/footer/footer.component.spec.ts create mode 100644 libs/agora/ui/src/lib/footer/footer.component.ts create mode 100644 libs/agora/ui/src/test-setup.ts create mode 100644 libs/agora/ui/tsconfig.json create mode 100644 libs/agora/ui/tsconfig.lib.json create mode 100644 libs/agora/ui/tsconfig.spec.json create mode 100644 libs/agora/util/.eslintrc.json create mode 100644 libs/agora/util/README.md create mode 100644 libs/agora/util/jest.config.ts create mode 100644 libs/agora/util/project.json create mode 100644 libs/agora/util/src/index.ts create mode 100644 libs/agora/util/src/lib/default-seo-data.ts create mode 100644 libs/agora/util/src/test-setup.ts create mode 100644 libs/agora/util/tsconfig.json create mode 100644 libs/agora/util/tsconfig.lib.json create mode 100644 libs/agora/util/tsconfig.spec.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index a593ec4d5b..1352fba0e2 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -118,7 +118,7 @@ "onAutoForward": "silent" }, "3333": { - "label": "agora-server", + "label": "agora-api", "onAutoForward": "silent" }, "4200": { diff --git a/apps/agora/apex/.env.example b/apps/agora/apex/.env.example new file mode 100644 index 0000000000..44bf6bd1b8 --- /dev/null +++ b/apps/agora/apex/.env.example @@ -0,0 +1,6 @@ +API_DOCS_HOST=agora-api-docs +API_DOCS_PORT=8010 +API_HOST=agora-api +API_PORT=3333 +APP_HOST=agora-app +APP_PORT=4200 \ No newline at end of file diff --git a/apps/agora/apex/Caddyfile b/apps/agora/apex/Caddyfile new file mode 100644 index 0000000000..9961cec392 --- /dev/null +++ b/apps/agora/apex/Caddyfile @@ -0,0 +1,14 @@ +:80 { + handle_path /api-docs* { + reverse_proxy {env.API_DOCS_HOST}:{env.API_DOCS_PORT} + } + + # Serve the API, stripping the /api prefix + handle_path /api/* { + reverse_proxy {env.API_HOST}:{env.API_PORT} + } + + handle { + reverse_proxy {env.APP_HOST}:{env.APP_PORT} + } +} \ No newline at end of file diff --git a/apps/agora/apex/Dockerfile b/apps/agora/apex/Dockerfile new file mode 100644 index 0000000000..d088b5de77 --- /dev/null +++ b/apps/agora/apex/Dockerfile @@ -0,0 +1 @@ +FROM caddy:2.8.4 \ No newline at end of file diff --git a/apps/agora/apex/project.json b/apps/agora/apex/project.json new file mode 100644 index 0000000000..e0b3231b41 --- /dev/null +++ b/apps/agora/apex/project.json @@ -0,0 +1,68 @@ +{ + "name": "agora-apex", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "targets": { + "create-config": { + "executor": "nx:run-commands", + "options": { + "command": "cp -n .env.example .env", + "cwd": "{projectRoot}" + } + }, + "serve-detach": { + "executor": "nx:run-commands", + "options": { + "command": "docker/agora/serve-detach.sh agora-apex" + } + }, + "build-image": { + "executor": "@nx-tools/nx-container:build", + "options": { + "context": "apps/agora/apex", + "metadata": { + "images": [ + "ghcr.io/sage-bionetworks/agora-apex" + ], + "tags": [ + "type=edge,branch=main", + "type=raw,value=local", + "type=sha" + ] + }, + "push": false + } + }, + "publish-image": { + "executor": "@nx-tools/nx-container:build", + "options": { + "context": "apps/agora/apex", + "metadata": { + "images": [ + "ghcr.io/sage-bionetworks/agora-apex" + ], + "tags": [ + "type=edge,branch=main", + "type=sha" + ] + }, + "push": true + }, + "dependsOn": [ + "build-image" + ] + }, + "scan-image": { + "executor": "nx:run-commands", + "options": { + "command": "trivy image ghcr.io/sage-bionetworks/agora-apex:local --quiet", + "color": true + } + } + }, + "tags": [ + "type:service", + "scope:backend" + ], + "implicitDependencies": [] +} \ No newline at end of file diff --git a/apps/agora/server/.env.example b/apps/agora/api/.env.example similarity index 100% rename from apps/agora/server/.env.example rename to apps/agora/api/.env.example diff --git a/apps/agora/server/.eslintrc.json b/apps/agora/api/.eslintrc.json similarity index 100% rename from apps/agora/server/.eslintrc.json rename to apps/agora/api/.eslintrc.json diff --git a/apps/agora/server/Dockerfile b/apps/agora/api/Dockerfile similarity index 75% rename from apps/agora/server/Dockerfile rename to apps/agora/api/Dockerfile index eb46523941..315aaa0ec6 100644 --- a/apps/agora/server/Dockerfile +++ b/apps/agora/api/Dockerfile @@ -5,11 +5,11 @@ ENV APP_DIR=/app RUN apk add --no-cache curl jq su-exec WORKDIR / -COPY apps/agora/server/docker-entrypoint.sh . -RUN chmod +x docker-entrypoint.sh +COPY apps/agora/api/docker-entrypoint.sh . +RUN chmod +x docker-entrypoint.sh WORKDIR ${APP_DIR} -COPY dist/apps/agora/server ${APP_DIR} +COPY dist/apps/agora/api ${APP_DIR} RUN npm install HEALTHCHECK --interval=2s --timeout=3s --retries=20 --start-period=5s \ diff --git a/apps/agora/server/docker-entrypoint.sh b/apps/agora/api/docker-entrypoint.sh similarity index 100% rename from apps/agora/server/docker-entrypoint.sh rename to apps/agora/api/docker-entrypoint.sh diff --git a/apps/agora/server/jest.config.ts b/apps/agora/api/jest.config.ts similarity index 73% rename from apps/agora/server/jest.config.ts rename to apps/agora/api/jest.config.ts index 82c474421f..728fc45a94 100644 --- a/apps/agora/server/jest.config.ts +++ b/apps/agora/api/jest.config.ts @@ -1,11 +1,11 @@ /* eslint-disable */ export default { - displayName: 'agora-server', + displayName: 'agora-api', preset: '../../../jest.preset.js', testEnvironment: 'node', transform: { '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], }, moduleFileExtensions: ['ts', 'js', 'html'], - coverageDirectory: '../../../coverage/apps/agora/server', + coverageDirectory: '../../../coverage/apps/agora/api', }; diff --git a/apps/agora/server/project.json b/apps/agora/api/project.json similarity index 56% rename from apps/agora/server/project.json rename to apps/agora/api/project.json index 762d786551..6eaf92cbf2 100644 --- a/apps/agora/server/project.json +++ b/apps/agora/api/project.json @@ -1,7 +1,7 @@ { - "name": "agora-server", + "name": "agora-api", "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "apps/agora/server/src", + "sourceRoot": "apps/agora/api/src", "projectType": "application", "targets": { "create-config": { @@ -13,16 +13,20 @@ }, "build": { "executor": "@nx/webpack:webpack", - "outputs": ["{options.outputPath}"], + "outputs": [ + "{options.outputPath}" + ], "defaultConfiguration": "production", "options": { "target": "node", "compiler": "tsc", - "outputPath": "dist/apps/agora/server", - "main": "apps/agora/server/src/main.ts", - "tsConfig": "apps/agora/server/tsconfig.app.json", - "assets": ["apps/agora/server/src/assets"], - "webpackConfig": "apps/agora/server/webpack.config.js", + "outputPath": "dist/apps/agora/api", + "main": "apps/agora/api/src/main.ts", + "tsConfig": "apps/agora/api/tsconfig.app.json", + "assets": [ + "apps/agora/api/src/assets" + ], + "webpackConfig": "apps/agora/api/webpack.config.js", "generatePackageJson": true }, "configurations": { @@ -34,21 +38,21 @@ "executor": "@nx/js:node", "defaultConfiguration": "development", "options": { - "buildTarget": "agora-server:build" + "buildTarget": "agora-api:build" }, "configurations": { "development": { - "buildTarget": "agora-server:build:development" + "buildTarget": "agora-api:build:development" }, "production": { - "buildTarget": "agora-server:build:production" + "buildTarget": "agora-api:build:production" } } }, "serve-detach": { "executor": "nx:run-commands", "options": { - "command": "docker/agora/serve-detach.sh agora-server" + "command": "docker/agora/serve-detach.sh agora-api" } }, "lint": { @@ -65,39 +69,56 @@ "options": { "context": ".", "metadata": { - "images": ["ghcr.io/sage-bionetworks/agora-server"], - "tags": ["type=edge,branch=main", "type=raw,value=local", "type=sha"] + "images": [ + "ghcr.io/sage-bionetworks/agora-api" + ], + "tags": [ + "type=edge,branch=main", + "type=raw,value=local", + "type=sha" + ] }, "push": false }, - "dependsOn": ["build"] + "dependsOn": [ + "build" + ] }, "publish-image": { "executor": "@nx-tools/nx-container:build", "options": { "context": ".", "metadata": { - "images": ["ghcr.io/sage-bionetworks/agora-server"], - "tags": ["type=edge,branch=main", "type=sha"] + "images": [ + "ghcr.io/sage-bionetworks/agora-api" + ], + "tags": [ + "type=edge,branch=main", + "type=sha" + ] }, "push": true }, - "dependsOn": ["build-image"] + "dependsOn": [ + "build-image" + ] }, "scan-image": { "executor": "nx:run-commands", "options": { - "command": "trivy image ghcr.io/sage-bionetworks/agora-server:local --quiet", + "command": "trivy image ghcr.io/sage-bionetworks/agora-api:local --quiet", "color": true } }, "test": { "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "outputs": [ + "{workspaceRoot}/coverage/{projectRoot}" + ], "options": { - "jestConfig": "apps/agora/server/jest.config.ts" + "jestConfig": "apps/agora/api/jest.config.ts" } } }, "tags": [] -} +} \ No newline at end of file diff --git a/apps/agora/server/src/api.ts b/apps/agora/api/src/api.ts similarity index 100% rename from apps/agora/server/src/api.ts rename to apps/agora/api/src/api.ts diff --git a/apps/agora/server/src/assets/.gitkeep b/apps/agora/api/src/assets/.gitkeep similarity index 100% rename from apps/agora/server/src/assets/.gitkeep rename to apps/agora/api/src/assets/.gitkeep diff --git a/apps/agora/server/src/components/dataversion.ts b/apps/agora/api/src/components/dataversion.ts similarity index 100% rename from apps/agora/server/src/components/dataversion.ts rename to apps/agora/api/src/components/dataversion.ts diff --git a/apps/agora/server/src/helpers.ts b/apps/agora/api/src/helpers.ts similarity index 100% rename from apps/agora/server/src/helpers.ts rename to apps/agora/api/src/helpers.ts diff --git a/apps/agora/server/src/main.ts b/apps/agora/api/src/main.ts similarity index 86% rename from apps/agora/server/src/main.ts rename to apps/agora/api/src/main.ts index 38963a2dea..a4781e9563 100644 --- a/apps/agora/server/src/main.ts +++ b/apps/agora/api/src/main.ts @@ -10,13 +10,13 @@ import api from './api'; const app = express(); app.use('/assets', express.static(path.join(__dirname, 'assets'))); -app.use('/api', api); +app.use('/v1', api); // Health endpoint used by the container app.get('/health', (_req, res) => res.status(200).json({ status: 'UP' })); const port = process.env.PORT ?? 3333; const server = app.listen(port, () => { - console.log(`Listening at http://localhost:${port}/api`); + console.log(`Listening at http://localhost:${port}`); }); server.on('error', console.error); diff --git a/apps/agora/server/src/models/dataversion.ts b/apps/agora/api/src/models/dataversion.ts similarity index 64% rename from apps/agora/server/src/models/dataversion.ts rename to apps/agora/api/src/models/dataversion.ts index 73364326f8..bb2327bfee 100644 --- a/apps/agora/server/src/models/dataversion.ts +++ b/apps/agora/api/src/models/dataversion.ts @@ -1,7 +1,7 @@ -import { DataVersion } from '@sagebionetworks/agora/api-client-angular'; +import { Dataversion } from '@sagebionetworks/agora/api-client-angular'; import { Schema, model } from 'mongoose'; -const DataVersionSchema = new Schema( +const DataVersionSchema = new Schema( { data_file: { type: String, required: true }, data_version: { type: String, required: true }, @@ -12,7 +12,7 @@ const DataVersionSchema = new Schema( }, ); -export const DataVersionCollection = model( +export const DataVersionCollection = model( 'DataVersionCollection', DataVersionSchema, ); diff --git a/apps/agora/server/tsconfig.app.json b/apps/agora/api/tsconfig.app.json similarity index 100% rename from apps/agora/server/tsconfig.app.json rename to apps/agora/api/tsconfig.app.json diff --git a/apps/agora/server/tsconfig.json b/apps/agora/api/tsconfig.json similarity index 99% rename from apps/agora/server/tsconfig.json rename to apps/agora/api/tsconfig.json index fdfa691bda..8c2ad81017 100644 --- a/apps/agora/server/tsconfig.json +++ b/apps/agora/api/tsconfig.json @@ -13,4 +13,4 @@ "compilerOptions": { "esModuleInterop": true } -} +} \ No newline at end of file diff --git a/apps/agora/server/tsconfig.spec.json b/apps/agora/api/tsconfig.spec.json similarity index 100% rename from apps/agora/server/tsconfig.spec.json rename to apps/agora/api/tsconfig.spec.json diff --git a/apps/agora/server/webpack.config.js b/apps/agora/api/webpack.config.js similarity index 100% rename from apps/agora/server/webpack.config.js rename to apps/agora/api/webpack.config.js diff --git a/apps/agora/app/.env.example b/apps/agora/app/.env.example index e69de29bb2..030a9adbe1 100644 --- a/apps/agora/app/.env.example +++ b/apps/agora/app/.env.example @@ -0,0 +1,7 @@ +API_DOCS_URL="http://localhost:8000/api-docs" +APP_VERSION="1.0.0-alpha" +CSR_API_URL="http://localhost:8000/api/v1" +DATA_UPDATED_ON="2023-09-26" +ENVIRONMENT="production" +GOOGLE_TAG_MANAGER_ID="" +SSR_API_URL="http://agora-api:8000/api/v1" \ No newline at end of file diff --git a/apps/agora/app/.eslintrc.json b/apps/agora/app/.eslintrc.json index 9d4d8b5718..bcce474e6f 100644 --- a/apps/agora/app/.eslintrc.json +++ b/apps/agora/app/.eslintrc.json @@ -19,7 +19,7 @@ "error", { "type": "attribute", - "prefix": "agora", + "prefix": "app", "style": "camelCase" } ], @@ -27,7 +27,7 @@ "error", { "type": "element", - "prefix": "agora", + "prefix": "app", "style": "kebab-case" } ] diff --git a/apps/agora/app/Dockerfile b/apps/agora/app/Dockerfile index d1875b4725..d4cfa7f56d 100644 --- a/apps/agora/app/Dockerfile +++ b/apps/agora/app/Dockerfile @@ -15,9 +15,9 @@ COPY dist/apps/agora/app/browser/server ${APP_DIR} COPY dist/apps/agora/app/browser/browser ${APP_DIR}/dist/apps/agora/app/browser/browser HEALTHCHECK --interval=2s --timeout=3s --retries=20 --start-period=5s \ - CMD curl --fail --silent "localhost:5200/health" | jq '.status' | grep UP || exit 1 + CMD curl --fail --silent "localhost:4200/health" | jq '.status' | grep UP || exit 1 -EXPOSE 5200 +EXPOSE 4200 ENTRYPOINT ["/docker-entrypoint.sh"] diff --git a/apps/agora/app/docker-entrypoint.d/10-envsubst-on-app-config-template.sh b/apps/agora/app/docker-entrypoint.d/10-envsubst-on-app-config-template.sh index 44ec27535b..7a2f26b445 100644 --- a/apps/agora/app/docker-entrypoint.d/10-envsubst-on-app-config-template.sh +++ b/apps/agora/app/docker-entrypoint.d/10-envsubst-on-app-config-template.sh @@ -1,4 +1,4 @@ #!/usr/bin/env sh -# cd "${APP_DIR}/dist/apps/agora/app/browser/browser/config" -# envsubst < config.json.template > config.json +cd "${APP_DIR}/dist/apps/agora/app/browser/browser/config" +envsubst < config.json.template > config.json diff --git a/apps/agora/app/docker-entrypoint.sh b/apps/agora/app/docker-entrypoint.sh index e61de35c17..fc31ae5aa4 100644 --- a/apps/agora/app/docker-entrypoint.sh +++ b/apps/agora/app/docker-entrypoint.sh @@ -1,7 +1,7 @@ #!/usr/bin/env sh set -e -# /docker-entrypoint.d/10-envsubst-on-app-config-template.sh +/docker-entrypoint.d/10-envsubst-on-app-config-template.sh if [ "$1" = 'node' ]; then cd ${APP_DIR} diff --git a/apps/agora/app/project.json b/apps/agora/app/project.json index 12c35c8dd4..b1fcdaeec5 100644 --- a/apps/agora/app/project.json +++ b/apps/agora/app/project.json @@ -15,16 +15,35 @@ }, "build": { "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], + "outputs": [ + "{options.outputPath}" + ], "options": { "outputPath": "dist/apps/agora/app/browser/browser", "index": "apps/agora/app/src/index.html", "main": "apps/agora/app/src/main.ts", - "polyfills": ["zone.js"], + "polyfills": [ + "zone.js" + ], "tsConfig": "apps/agora/app/tsconfig.app.json", "inlineStyleLanguage": "scss", - "assets": ["apps/agora/app/src/favicon.ico", "apps/agora/app/src/assets"], - "styles": ["apps/agora/app/src/styles.scss"], + "assets": [ + "apps/agora/app/src/assets", + "apps/agora/app/src/config", + "apps/agora/app/src/humans.txt", + "apps/agora/app/src/robots.txt", + { + "input": "libs/shared/typescript/assets/src/assets", + "glob": "**/*", + "output": "assets" + } + ], + "styles": [ + "apps/agora/app/src/styles.scss", + "node_modules/primeicons/primeicons.css", + "node_modules/primeng/resources/themes/lara-light-blue/theme.css", + "node_modules/primeng/resources/primeng.min.css" + ], "scripts": [] }, "configurations": { @@ -32,13 +51,13 @@ "budgets": [ { "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" + "maximumWarning": "1mb", + "maximumError": "2mb" }, { "type": "anyComponentStyle", "maximumWarning": "2kb", - "maximumError": "4kb" + "maximumError": "10kb" } ], "outputHashing": "all" @@ -64,10 +83,7 @@ "buildTarget": "agora-app:build:development" } }, - "defaultConfiguration": "development", - "options": { - "proxyConfig": "apps/agora/app/proxy.conf.json" - } + "defaultConfiguration": "development" }, "serve-detach": { "executor": "nx:run-commands", @@ -92,13 +108,17 @@ }, "test": { "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "outputs": [ + "{workspaceRoot}/coverage/{projectRoot}" + ], "options": { "jestConfig": "apps/agora/app/jest.config.ts" } }, "server": { - "dependsOn": ["build"], + "dependsOn": [ + "build" + ], "executor": "@angular-devkit/build-angular:server", "options": { "outputPath": "dist/apps/agora/app/browser/server", @@ -134,46 +154,44 @@ }, "defaultConfiguration": "development" }, - "prerender": { - "executor": "@angular-devkit/build-angular:prerender", - "options": { - "routes": ["/"] - }, - "configurations": { - "development": { - "browserTarget": "agora-app:build:development", - "serverTarget": "agora-app:server:development" - }, - "production": { - "browserTarget": "agora-app:build:production", - "serverTarget": "agora-app:server:production" - } - }, - "defaultConfiguration": "production" - }, "build-image": { "executor": "@nx-tools/nx-container:build", "options": { "context": ".", "metadata": { - "images": ["ghcr.io/sage-bionetworks/agora-app"], - "tags": ["type=edge,branch=main", "type=raw,value=local", "type=sha"] + "images": [ + "ghcr.io/sage-bionetworks/agora-app" + ], + "tags": [ + "type=edge,branch=main", + "type=raw,value=local", + "type=sha" + ] }, "push": false }, - "dependsOn": ["server"] + "dependsOn": [ + "server" + ] }, "publish-image": { "executor": "@nx-tools/nx-container:build", "options": { "context": ".", "metadata": { - "images": ["ghcr.io/sage-bionetworks/agora-app"], - "tags": ["type=edge,branch=main", "type=sha"] + "images": [ + "ghcr.io/sage-bionetworks/agora-app" + ], + "tags": [ + "type=edge,branch=main", + "type=sha" + ] }, "push": true }, - "dependsOn": ["build-image"] + "dependsOn": [ + "build-image" + ] }, "scan-image": { "executor": "nx:run-commands", @@ -182,5 +200,10 @@ "color": true } } - } -} + }, + "implicitDependencies": [ + "agora-styles", + "agora-themes", + "shared-typescript-assets" + ] +} \ No newline at end of file diff --git a/apps/agora/app/proxy.conf.json b/apps/agora/app/proxy.conf.json deleted file mode 100644 index f7ec326306..0000000000 --- a/apps/agora/app/proxy.conf.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "/api": { - "target": "http://agora-server:3333", - "secure": false - } -} diff --git a/apps/agora/app/server.ts b/apps/agora/app/server.ts index b73672fbf7..0f44c67540 100644 --- a/apps/agora/app/server.ts +++ b/apps/agora/app/server.ts @@ -7,7 +7,7 @@ import { existsSync } from 'node:fs'; import { join } from 'node:path'; import bootstrap from './src/main.server'; -const PORT = process.env['PORT'] || '5200'; +const PORT = process.env['PORT'] || '4200'; console.log(`server.ts: ${PORT}`); // The Express app is exported so that it can be used by serverless Functions. diff --git a/apps/agora/app/src/_app-theme.scss b/apps/agora/app/src/_app-theme.scss new file mode 100644 index 0000000000..2a721a9e1a --- /dev/null +++ b/apps/agora/app/src/_app-theme.scss @@ -0,0 +1,48 @@ +@use 'sass:map'; +@use '@angular/material' as mat; +@use 'libs/agora/themes/src/fonts' as fonts; +@use 'libs/agora/themes/src/palettes' as palettes; +@use 'libs/agora/themes/src/index' as agora; + +@include mat.typography-hierarchy(fonts.$lato); +@include mat.core(); + +$primary: mat.m2-define-palette(palettes.$dark-blue-palette, 600); +$accent: mat.m2-define-palette(palettes.$accent-purple-palette, 400); + +$theme: mat.m2-define-light-theme( + ( + color: ( + primary: $primary, + accent: $accent, + ), + typography: fonts.$lato, + density: 0, + is-dark: false, + ) +); + +// Add custom palettes used in figma to the theme +$theme: map.deep-merge( + $theme, + ( + color: ( + figma: palettes.$figma-collection, + ), + ) +); + +// Emit theme-dependent styles for common features used across multiple components. +@include mat.core-theme($theme); + +// Emit styles for MatButton based on `$theme`. +@include mat.button-theme($theme); + +// Include the theme mixins for other components you use here. +@include agora.theme($theme); + +:root { + --color-btn-primary: #39bde7; + --color-btn-disabled: #ebebe4; + --color-btn-shadow: rgba(196, 196, 196, 1); +} diff --git a/apps/agora/app/src/app/app.component.html b/apps/agora/app/src/app/app.component.html index 86454cd555..ec0f9fa004 100644 --- a/apps/agora/app/src/app/app.component.html +++ b/apps/agora/app/src/app/app.component.html @@ -1,9 +1,3 @@ -
-

Data Release Info

-
    -
  • Data File: {{ dataVersion.data_file }}
  • -
  • Data Version: {{ dataVersion.data_version }}
  • -
  • Team Images Id: {{ dataVersion.team_images_id }}
  • -
+
+
- diff --git a/apps/agora/app/src/app/app.component.spec.ts.off b/apps/agora/app/src/app/app.component.spec.ts.off index 980735e4ec..0cf77c15bb 100644 --- a/apps/agora/app/src/app/app.component.spec.ts.off +++ b/apps/agora/app/src/app/app.component.spec.ts.off @@ -3,23 +3,23 @@ import { AppComponent } from './app.component'; import { NxWelcomeComponent } from './nx-welcome.component'; import { RouterTestingModule } from '@angular/router/testing'; -// describe('AppComponent', () => { -// beforeEach(async () => { -// await TestBed.configureTestingModule({ -// imports: [AppComponent, NxWelcomeComponent, RouterTestingModule], -// }).compileComponents(); -// }); +describe('AppComponent', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AppComponent, NxWelcomeComponent, RouterTestingModule], + }).compileComponents(); + }); -// it('should render title', () => { -// const fixture = TestBed.createComponent(AppComponent); -// fixture.detectChanges(); -// const compiled = fixture.nativeElement as HTMLElement; -// expect(compiled.querySelector('h1')?.textContent).toContain('Welcome agora-app'); -// }); + it('should render title', () => { + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const compiled = fixture.nativeElement as HTMLElement; + expect(compiled.querySelector('h1')?.textContent).toContain('Welcome agora-app'); + }); -// it(`should have as title 'agora-app'`, () => { -// const fixture = TestBed.createComponent(AppComponent); -// const app = fixture.componentInstance; -// expect(app.title).toEqual('agora-app'); -// }); -// }); + it(`should have as title 'agora-app'`, () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + expect(app.title).toEqual('agora-app'); + }); +}); diff --git a/apps/agora/app/src/app/app.component.ts b/apps/agora/app/src/app/app.component.ts index 94a5707dc1..1c7b079e5b 100644 --- a/apps/agora/app/src/app/app.component.ts +++ b/apps/agora/app/src/app/app.component.ts @@ -1,28 +1,14 @@ -import { CommonModule } from '@angular/common'; -import { Component, OnInit } from '@angular/core'; +import { Component } from '@angular/core'; import { RouterModule } from '@angular/router'; -import { - DataVersion, - DataVersionService, -} from '@sagebionetworks/agora/api-client-angular'; -import { Observable } from 'rxjs'; -import { NxWelcomeComponent } from './nx-welcome.component'; +import { FooterComponent } from '@sagebionetworks/agora/ui'; @Component({ standalone: true, - imports: [CommonModule, NxWelcomeComponent, RouterModule], - selector: 'agora-root', + imports: [RouterModule, FooterComponent], + selector: 'app-root', templateUrl: './app.component.html', styleUrl: './app.component.scss', }) -export class AppComponent implements OnInit { +export class AppComponent { title = 'agora-app'; - - dataVersion$!: Observable; - - constructor(private dataVersionService: DataVersionService) {} - - ngOnInit(): void { - this.dataVersion$ = this.dataVersionService.getDataVersion(); - } } diff --git a/apps/agora/app/src/app/app.config.server.ts b/apps/agora/app/src/app/app.config.server.ts index 1980cfe118..73eeb34f58 100644 --- a/apps/agora/app/src/app/app.config.server.ts +++ b/apps/agora/app/src/app/app.config.server.ts @@ -1,9 +1,17 @@ import { mergeApplicationConfig, ApplicationConfig } from '@angular/core'; import { provideServerRendering } from '@angular/platform-server'; -import { appConfig } from './app.config'; +import { APP_BASE_URL_PROVIDER_INDEX, appConfig } from './app.config'; +import { provideClientHydration } from '@angular/platform-browser'; const serverConfig: ApplicationConfig = { - providers: [provideServerRendering()], + providers: [provideServerRendering(), provideClientHydration()], }; +// The file server.ts defines a provider that specifies 'APP_BASE_URL' based on the request protocol +// and host. If this provider could be defined in serverConfig above, there would be no need to +// manually remove the provider that specifies 'APP_BASE_URL' from appConfig used for client-side +// rendering. Also removing based on an index should be avoided: I would have preferred to remove it +// based on a property value but couldn't. +appConfig.providers.splice(APP_BASE_URL_PROVIDER_INDEX, 1); + export const config = mergeApplicationConfig(appConfig, serverConfig); diff --git a/apps/agora/app/src/app/app.config.ts b/apps/agora/app/src/app/app.config.ts index ca337e5bbb..632dd42227 100644 --- a/apps/agora/app/src/app/app.config.ts +++ b/apps/agora/app/src/app/app.config.ts @@ -1,13 +1,54 @@ -import { provideHttpClient } from '@angular/common/http'; -import { ApplicationConfig } from '@angular/core'; -import { provideClientHydration } from '@angular/platform-browser'; -import { provideRouter } from '@angular/router'; -import { appRoutes } from './app.routes'; +import { ApplicationConfig, APP_INITIALIZER, APP_ID } from '@angular/core'; +import { + provideRouter, + withEnabledBlockingInitialNavigation, + withInMemoryScrolling, +} from '@angular/router'; +import { + withInterceptorsFromDi, + provideHttpClient, +} from '@angular/common/http'; +import { provideAnimations } from '@angular/platform-browser/animations'; +import { BASE_PATH as API_CLIENT_BASE_PATH } from '@sagebionetworks/agora/api-client-angular'; +import { configFactory, ConfigService } from '@sagebionetworks/agora/config'; + +import { routes } from './app.routes'; + +// This index is used to remove the corresponding provider in app.config.server.ts. +// TODO: This index could be out of sync if we are not careful. Find a more elegant way. +export const APP_BASE_URL_PROVIDER_INDEX = 1; export const appConfig: ApplicationConfig = { providers: [ - provideClientHydration(), - provideHttpClient(), - provideRouter(appRoutes), + { provide: APP_ID, useValue: 'agora-app' }, + { + // This provider must be specified at the index defined by APP_BASE_URL_PROVIDER_INDEX. + provide: 'APP_BASE_URL', + useFactory: () => '.', + deps: [], + }, + { + provide: APP_INITIALIZER, + useFactory: configFactory, + deps: [ConfigService], + multi: true, + }, + { + provide: API_CLIENT_BASE_PATH, + useFactory: (configService: ConfigService) => + configService.config.isPlatformServer + ? configService.config.ssrApiUrl + : configService.config.csrApiUrl, + deps: [ConfigService], + }, + provideAnimations(), + provideHttpClient(withInterceptorsFromDi()), + provideRouter( + routes, + withEnabledBlockingInitialNavigation(), + withInMemoryScrolling({ + scrollPositionRestoration: 'enabled', + }), + ), ], }; diff --git a/apps/agora/app/src/app/app.routes.ts b/apps/agora/app/src/app/app.routes.ts index 8762dfe2c6..0a8907e8fc 100644 --- a/apps/agora/app/src/app/app.routes.ts +++ b/apps/agora/app/src/app/app.routes.ts @@ -1,3 +1,15 @@ import { Route } from '@angular/router'; -export const appRoutes: Route[] = []; +export const routes: Route[] = [ + { + path: 'not-found', + loadChildren: () => + import('@sagebionetworks/agora/not-found').then( + (routes) => routes.routes, + ), + }, + { + path: '**', + redirectTo: '/not-found', + }, +]; diff --git a/apps/agora/app/src/app/nx-welcome.component.ts b/apps/agora/app/src/app/nx-welcome.component.ts deleted file mode 100644 index 9938fb0a29..0000000000 --- a/apps/agora/app/src/app/nx-welcome.component.ts +++ /dev/null @@ -1,984 +0,0 @@ -import { Component, ViewEncapsulation } from '@angular/core'; -import { CommonModule } from '@angular/common'; - -@Component({ - selector: 'agora-nx-welcome', - standalone: true, - imports: [CommonModule], - template: ` - - -
-
- -
-

- Hello there, - Welcome agora-app 👋 -

-
- -
-
-

- - - - You're up and running -

- What's next? -
-
- - - -
-
- - - -
-

Next steps

-

Here are some things you can do with Nx:

-
- - - - - Add UI library - -
# Generate UI lib
-nx g @nx/angular:lib ui
-# Add a component
-nx g @nx/angular:component ui/src/lib/button
-
-
- - - - - View project details - -
nx show project agora-app --web
-
-
- - - - - View interactive project graph - -
nx graph
-
-
- - - - - Run affected commands - -
# see what's been affected by changes
-nx affected:graph
-# run tests for current changes
-nx affected:test
-# run e2e tests for current changes
-nx affected:e2e
-
-
-

- Carefully crafted with - - - -

-
-
- `, - styles: [], - encapsulation: ViewEncapsulation.None, -}) -export class NxWelcomeComponent {} diff --git a/apps/agora/app/src/config/config.json b/apps/agora/app/src/config/config.json new file mode 100644 index 0000000000..a84716e7ab --- /dev/null +++ b/apps/agora/app/src/config/config.json @@ -0,0 +1,9 @@ +{ + "apiDocsUrl": "http://localhost:8000/api-docs", + "appVersion": "1.0.0-beta", + "csrApiUrl": "http://localhost:3333/v1", + "dataUpdatedOn": "yyyy-mm-dd", + "environment": "development", + "googleTagManagerId": "", + "ssrApiUrl": "http://agora-api:3333/v1" +} \ No newline at end of file diff --git a/apps/agora/app/src/config/config.json.template b/apps/agora/app/src/config/config.json.template new file mode 100644 index 0000000000..e00d068ea2 --- /dev/null +++ b/apps/agora/app/src/config/config.json.template @@ -0,0 +1,9 @@ +{ + "apiDocsUrl": "${API_DOCS_URL}", + "appVersion": "${APP_VERSION}", + "csrApiUrl": "${CSR_API_URL}", + "dataUpdatedOn": "${DATA_UPDATED_ON}", + "environment": "${ENVIRONMENT}", + "googleTagManagerId": "${GOOGLE_TAG_MANAGER_ID}", + "ssrApiUrl": "${SSR_API_URL}" +} \ No newline at end of file diff --git a/apps/agora/app/src/humans.txt b/apps/agora/app/src/humans.txt new file mode 100644 index 0000000000..9093f0f578 --- /dev/null +++ b/apps/agora/app/src/humans.txt @@ -0,0 +1,6 @@ +/* TEAM */ + +/* THANKS */ + +/* SITE */ +Last update: 2024/05/29 diff --git a/apps/agora/app/src/index.html b/apps/agora/app/src/index.html index 517b6c3130..243afeddf2 100644 --- a/apps/agora/app/src/index.html +++ b/apps/agora/app/src/index.html @@ -1,4 +1,4 @@ - + @@ -8,6 +8,6 @@ - + diff --git a/apps/agora/app/src/robots.txt b/apps/agora/app/src/robots.txt new file mode 100644 index 0000000000..4f9540ba35 --- /dev/null +++ b/apps/agora/app/src/robots.txt @@ -0,0 +1 @@ +User-agent: * \ No newline at end of file diff --git a/apps/agora/app/src/styles.scss b/apps/agora/app/src/styles.scss index 90d4ee0072..1e413426a0 100644 --- a/apps/agora/app/src/styles.scss +++ b/apps/agora/app/src/styles.scss @@ -1 +1,4 @@ /* You can add global styles to this file, and also import other style files */ +@use 'libs/agora/styles/src/index'; + +@use 'app-theme'; diff --git a/apps/agora/app/tsconfig.app.json b/apps/agora/app/tsconfig.app.json index 58220429a4..a979b5683d 100644 --- a/apps/agora/app/tsconfig.app.json +++ b/apps/agora/app/tsconfig.app.json @@ -2,9 +2,20 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../dist/out-tsc", - "types": [] + "types": [ + "node" + ] }, - "files": ["src/main.ts"], - "include": ["src/**/*.d.ts"], - "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] -} + "files": [ + "src/main.ts", + "src/main.server.ts", + ], + "include": [ + "src/**/*.d.ts" + ], + "exclude": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + ] +} \ No newline at end of file diff --git a/apps/agora/app/tsconfig.json b/apps/agora/app/tsconfig.json index e19e8a0e14..f12e9f7034 100644 --- a/apps/agora/app/tsconfig.json +++ b/apps/agora/app/tsconfig.json @@ -2,6 +2,7 @@ "compilerOptions": { "target": "es2022", "useDefineForClassFields": false, + "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "noImplicitOverride": true, @@ -20,6 +21,9 @@ }, { "path": "./tsconfig.spec.json" + }, + { + "path": "./tsconfig.server.json" } ], "extends": "../../../tsconfig.base.json", diff --git a/apps/agora/app/tsconfig.server.json b/apps/agora/app/tsconfig.server.json index 6f9cb4965c..6edb093e23 100644 --- a/apps/agora/app/tsconfig.server.json +++ b/apps/agora/app/tsconfig.server.json @@ -3,8 +3,14 @@ "extends": "./tsconfig.app.json", "compilerOptions": { "outDir": "../../out-tsc/server", - "target": "es2019", - "types": ["node"] + "target": "ES2022", + "types": [ + "node" + ], + "esModuleInterop": false, }, - "files": ["src/main.server.ts", "server.ts"] -} + "files": [ + "src/main.server.ts", + "server.ts" + ] +} \ No newline at end of file diff --git a/docker/agora/serve-detach.sh b/docker/agora/serve-detach.sh index 978e2256f4..7e80994c1a 100755 --- a/docker/agora/serve-detach.sh +++ b/docker/agora/serve-detach.sh @@ -2,11 +2,12 @@ args=( # List of services in alphanumeric order + --file docker/agora/services/apex.yml --file docker/agora/services/api-docs.yml + --file docker/agora/services/api.yml --file docker/agora/services/app.yml --file docker/agora/services/data.yml --file docker/agora/services/mongo.yml - --file docker/agora/services/server.yml --file docker/agora/networks.yml --file docker/agora/volumes.yml diff --git a/docker/agora/services/apex.yml b/docker/agora/services/apex.yml new file mode 100644 index 0000000000..680c27f14d --- /dev/null +++ b/docker/agora/services/apex.yml @@ -0,0 +1,24 @@ +services: + agora-apex: + image: ghcr.io/sage-bionetworks/agora-apex:${AGORA_VERSION:-local} + container_name: agora-apex + restart: always + env_file: + - ../../../apps/agora/apex/.env + volumes: + - ../../../apps/agora/apex/Caddyfile:/etc/caddy/Caddyfile + networks: + - agora + ports: + - '8000:80' + depends_on: + agora-api-docs: + condition: service_healthy + agora-api: + condition: service_healthy + agora-app: + condition: service_healthy + deploy: + resources: + limits: + memory: 200M diff --git a/docker/agora/services/server.yml b/docker/agora/services/api.yml similarity index 55% rename from docker/agora/services/server.yml rename to docker/agora/services/api.yml index 5d87b8a8c6..6fabf9e198 100644 --- a/docker/agora/services/server.yml +++ b/docker/agora/services/api.yml @@ -1,10 +1,10 @@ services: - agora-server: - image: ghcr.io/sage-bionetworks/agora-server:${AGORA_VERSION:-local} - container_name: agora-server + agora-api: + image: ghcr.io/sage-bionetworks/agora-api:${AGORA_VERSION:-local} + container_name: agora-api restart: always env_file: - - ../../../apps/agora/server/.env + - ../../../apps/agora/api/.env networks: - agora ports: diff --git a/docker/agora/services/app.yml b/docker/agora/services/app.yml index b27132aea6..f4c424df17 100644 --- a/docker/agora/services/app.yml +++ b/docker/agora/services/app.yml @@ -6,7 +6,7 @@ services: networks: - agora ports: - - '5200:5200' + - '4200:4200' env_file: - ../../../apps/agora/app/.env deploy: @@ -16,5 +16,5 @@ services: depends_on: agora-data: condition: service_completed_successfully - agora-server: + agora-api: condition: service_healthy diff --git a/libs/agora/api-client-angular/.eslintrc.json b/libs/agora/api-client-angular/.eslintrc.json index 3456be9b90..6de4e28d19 100644 --- a/libs/agora/api-client-angular/.eslintrc.json +++ b/libs/agora/api-client-angular/.eslintrc.json @@ -1,18 +1,4 @@ { "extends": ["../../../.eslintrc.json"], - "ignorePatterns": ["!**/*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], - "rules": {} - }, - { - "files": ["*.ts", "*.tsx"], - "rules": {} - }, - { - "files": ["*.js", "*.jsx"], - "rules": {} - } - ] + "ignorePatterns": ["src/**/*.ts"] } diff --git a/libs/agora/api-client-angular/ng-package.json b/libs/agora/api-client-angular/ng-package.json index 717d235439..665cba24cc 100644 --- a/libs/agora/api-client-angular/ng-package.json +++ b/libs/agora/api-client-angular/ng-package.json @@ -4,4 +4,4 @@ "lib": { "entryFile": "src/index.ts" } -} +} \ No newline at end of file diff --git a/libs/agora/api-client-angular/openapitools.json b/libs/agora/api-client-angular/openapitools.json new file mode 100644 index 0000000000..379204f1c4 --- /dev/null +++ b/libs/agora/api-client-angular/openapitools.json @@ -0,0 +1,19 @@ +{ + "$schema": "../../../node_modules/@openapitools/openapi-generator-cli/config.schema.json", + "spaces": 2, + "generator-cli": { + "version": "6.1.0", + "generators": { + "api-client-angular": { + "config": "templates/config.yaml", + "generatorName": "typescript-angular", + "inputSpec": "#{cwd}/../api-description/build/openapi.yaml", + "output": "#{cwd}/src/lib/", + "templateDir": "templates", + "additionalProperties": { + "ngVersion": "14.2.11" + } + } + } + } +} diff --git a/libs/agora/api-client-angular/package.json b/libs/agora/api-client-angular/package.json index a041e786ed..f0f674b180 100644 --- a/libs/agora/api-client-angular/package.json +++ b/libs/agora/api-client-angular/package.json @@ -8,4 +8,4 @@ "dependencies": { "tslib": "2.4.1" } -} +} \ No newline at end of file diff --git a/libs/agora/api-client-angular/project.json b/libs/agora/api-client-angular/project.json index 58459bd1cd..c1925004d7 100644 --- a/libs/agora/api-client-angular/project.json +++ b/libs/agora/api-client-angular/project.json @@ -7,7 +7,9 @@ "targets": { "build": { "executor": "@nx/angular:ng-packagr-lite", - "outputs": ["{workspaceRoot}/dist/libs/agora/api-client-angular"], + "outputs": [ + "{workspaceRoot}/dist/libs/agora/api-client-angular" + ], "options": { "project": "libs/agora/api-client-angular/ng-package.json" }, @@ -23,14 +25,38 @@ }, "test": { "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/libs/agora/api-client-angular"], + "outputs": [ + "{workspaceRoot}/coverage/libs/agora/api-client-angular" + ], "options": { "jestConfig": "libs/agora/api-client-angular/jest.config.ts" } }, - "lint": { - "executor": "@nx/eslint:lint" + "lint-fix": { + "executor": "@nx/eslint:lint", + "options": { + "fix": true + } + }, + "generate": { + "executor": "nx:run-commands", + "options": { + "commands": [ + "rm -fr src/lib/*", + "openapi-generator-cli generate" + ], + "cwd": "{projectRoot}", + "parallel": false + }, + "dependsOn": [ + "^build" + ] } }, - "tags": ["language:typescript"] -} + "tags": [ + "language:typescript" + ], + "implicitDependencies": [ + "agora-api-description" + ] +} \ No newline at end of file diff --git a/libs/agora/api-client-angular/src/lib/.gitignore b/libs/agora/api-client-angular/src/lib/.gitignore new file mode 100644 index 0000000000..149b576547 --- /dev/null +++ b/libs/agora/api-client-angular/src/lib/.gitignore @@ -0,0 +1,4 @@ +wwwroot/*.js +node_modules +typings +dist diff --git a/libs/agora/api-client-angular/src/lib/.openapi-generator-ignore b/libs/agora/api-client-angular/src/lib/.openapi-generator-ignore new file mode 100644 index 0000000000..7484ee590a --- /dev/null +++ b/libs/agora/api-client-angular/src/lib/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/libs/agora/api-client-angular/src/lib/.openapi-generator/FILES b/libs/agora/api-client-angular/src/lib/.openapi-generator/FILES new file mode 100644 index 0000000000..1811232afb --- /dev/null +++ b/libs/agora/api-client-angular/src/lib/.openapi-generator/FILES @@ -0,0 +1,14 @@ +.gitignore +README.md +api.module.ts +api/api.ts +api/dataversion.service.ts +configuration.ts +encoder.ts +git_push.sh +index.ts +model/basicError.ts +model/dataversion.ts +model/models.ts +param.ts +variables.ts diff --git a/libs/agora/api-client-angular/src/lib/.openapi-generator/VERSION b/libs/agora/api-client-angular/src/lib/.openapi-generator/VERSION new file mode 100644 index 0000000000..358e78e607 --- /dev/null +++ b/libs/agora/api-client-angular/src/lib/.openapi-generator/VERSION @@ -0,0 +1 @@ +6.1.0 \ No newline at end of file diff --git a/libs/agora/api-client-angular/src/lib/README.md b/libs/agora/api-client-angular/src/lib/README.md new file mode 100644 index 0000000000..de16f95a8b --- /dev/null +++ b/libs/agora/api-client-angular/src/lib/README.md @@ -0,0 +1,226 @@ +## @ + +### Building + +To install the required dependencies and to build the typescript sources run: +``` +npm install +npm run build +``` + +### publishing + +First build the package then run ```npm publish dist``` (don't forget to specify the `dist` folder!) + +### consuming + +Navigate to the folder of your consuming project and run one of next commands. + +_published:_ + +``` +npm install @ --save +``` + +_without publishing (not recommended):_ + +``` +npm install PATH_TO_GENERATED_PACKAGE/dist.tgz --save +``` + +_It's important to take the tgz file, otherwise you'll get trouble with links on windows_ + +_using `npm link`:_ + +In PATH_TO_GENERATED_PACKAGE/dist: +``` +npm link +``` + +In your project: +``` +npm link +``` + +__Note for Windows users:__ The Angular CLI has troubles to use linked npm packages. +Please refer to this issue https://github.com/angular/angular-cli/issues/8284 for a solution / workaround. +Published packages are not effected by this issue. + + +#### General usage + +In your Angular project: + + +``` +// without configuring providers +import { ApiModule } from ''; +import { HttpClientModule } from '@angular/common/http'; + +@NgModule({ + imports: [ + ApiModule, + // make sure to import the HttpClientModule in the AppModule only, + // see https://github.com/angular/angular/issues/20575 + HttpClientModule + ], + declarations: [ AppComponent ], + providers: [], + bootstrap: [ AppComponent ] +}) +export class AppModule {} +``` + +``` +// configuring providers +import { ApiModule, Configuration, ConfigurationParameters } from ''; + +export function apiConfigFactory (): Configuration { + const params: ConfigurationParameters = { + // set configuration parameters here. + } + return new Configuration(params); +} + +@NgModule({ + imports: [ ApiModule.forRoot(apiConfigFactory) ], + declarations: [ AppComponent ], + providers: [], + bootstrap: [ AppComponent ] +}) +export class AppModule {} +``` + +``` +// configuring providers with an authentication service that manages your access tokens +import { ApiModule, Configuration } from ''; + +@NgModule({ + imports: [ ApiModule ], + declarations: [ AppComponent ], + providers: [ + { + provide: Configuration, + useFactory: (authService: AuthService) => new Configuration( + { + basePath: environment.apiUrl, + accessToken: authService.getAccessToken.bind(authService) + } + ), + deps: [AuthService], + multi: false + } + ], + bootstrap: [ AppComponent ] +}) +export class AppModule {} +``` + +``` +import { DefaultApi } from ''; + +export class AppComponent { + constructor(private apiGateway: DefaultApi) { } +} +``` + +Note: The ApiModule is restricted to being instantiated once app wide. +This is to ensure that all services are treated as singletons. + +#### Using multiple OpenAPI files / APIs / ApiModules +In order to use multiple `ApiModules` generated from different OpenAPI files, +you can create an alias name when importing the modules +in order to avoid naming conflicts: +``` +import { ApiModule } from 'my-api-path'; +import { ApiModule as OtherApiModule } from 'my-other-api-path'; +import { HttpClientModule } from '@angular/common/http'; + +@NgModule({ + imports: [ + ApiModule, + OtherApiModule, + // make sure to import the HttpClientModule in the AppModule only, + // see https://github.com/angular/angular/issues/20575 + HttpClientModule + ] +}) +export class AppModule { + +} +``` + + +### Set service base path +If different than the generated base path, during app bootstrap, you can provide the base path to your service. + +``` +import { BASE_PATH } from ''; + +bootstrap(AppComponent, [ + { provide: BASE_PATH, useValue: 'https://your-web-service.com' }, +]); +``` +or + +``` +import { BASE_PATH } from ''; + +@NgModule({ + imports: [], + declarations: [ AppComponent ], + providers: [ provide: BASE_PATH, useValue: 'https://your-web-service.com' ], + bootstrap: [ AppComponent ] +}) +export class AppModule {} +``` + + +#### Using @angular/cli +First extend your `src/environments/*.ts` files by adding the corresponding base path: + +``` +export const environment = { + production: false, + API_BASE_PATH: 'http://127.0.0.1:8080' +}; +``` + +In the src/app/app.module.ts: +``` +import { BASE_PATH } from ''; +import { environment } from '../environments/environment'; + +@NgModule({ + declarations: [ + AppComponent + ], + imports: [ ], + providers: [{ provide: BASE_PATH, useValue: environment.API_BASE_PATH }], + bootstrap: [ AppComponent ] +}) +export class AppModule { } +``` + +### Customizing path parameter encoding + +Without further customization, only [path-parameters][parameter-locations-url] of [style][style-values-url] 'simple' +and Dates for format 'date-time' are encoded correctly. + +Other styles (e.g. "matrix") are not that easy to encode +and thus are best delegated to other libraries (e.g.: [@honoluluhenk/http-param-expander]). + +To implement your own parameter encoding (or call another library), +pass an arrow-function or method-reference to the `encodeParam` property of the Configuration-object +(see [General Usage](#general-usage) above). + +Example value for use in your Configuration-Provider: +```typescript +new Configuration({ + encodeParam: (param: Param) => myFancyParamEncoder(param), +}) +``` + +[parameter-locations-url]: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#parameter-locations +[style-values-url]: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#style-values +[@honoluluhenk/http-param-expander]: https://www.npmjs.com/package/@honoluluhenk/http-param-expander diff --git a/libs/agora/api-client-angular/src/lib/api.module.ts b/libs/agora/api-client-angular/src/lib/api.module.ts new file mode 100644 index 0000000000..7ee93d08ea --- /dev/null +++ b/libs/agora/api-client-angular/src/lib/api.module.ts @@ -0,0 +1,31 @@ +import { NgModule, ModuleWithProviders, SkipSelf, Optional } from '@angular/core'; +import { Configuration } from './configuration'; +import { HttpClient } from '@angular/common/http'; + +import { DataversionService } from './api/dataversion.service'; + +@NgModule({ + imports: [], + declarations: [], + exports: [], + providers: [] +}) +export class ApiModule { + public static forRoot(configurationFactory: () => Configuration): ModuleWithProviders { + return { + ngModule: ApiModule, + providers: [ { provide: Configuration, useFactory: configurationFactory } ] + }; + } + + constructor( @Optional() @SkipSelf() parentModule: ApiModule, + @Optional() http: HttpClient) { + if (parentModule) { + throw new Error('ApiModule is already loaded. Import in your base AppModule only.'); + } + if (!http) { + throw new Error('You need to import the HttpClientModule in your AppModule! \n' + + 'See also https://github.com/angular/angular/issues/20575'); + } + } +} diff --git a/libs/agora/api-client-angular/src/lib/api/api.ts b/libs/agora/api-client-angular/src/lib/api/api.ts index 3cc8670a91..f06156f44d 100644 --- a/libs/agora/api-client-angular/src/lib/api/api.ts +++ b/libs/agora/api-client-angular/src/lib/api/api.ts @@ -1,3 +1,3 @@ -import { DataVersionService } from './dataversion.service'; export * from './dataversion.service'; -export const APIS = [DataVersionService]; +import { DataversionService } from './dataversion.service'; +export const APIS = [DataversionService]; diff --git a/libs/agora/api-client-angular/src/lib/api/constants.ts b/libs/agora/api-client-angular/src/lib/api/constants.ts deleted file mode 100644 index 84aa7fed98..0000000000 --- a/libs/agora/api-client-angular/src/lib/api/constants.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const BASE_URL = 'http://agora-server:3333/api'; - -export const DEFAULT_HEADERS = { - 'Content-Type': 'application/json', - 'Cache-Control': - 'no-cache, no-store, must-revalidate, post-check=0, pre-check=0', - Pragma: 'no-cache', - Expires: '0', -}; diff --git a/libs/agora/api-client-angular/src/lib/api/dataversion.service.ts b/libs/agora/api-client-angular/src/lib/api/dataversion.service.ts index e10f638cf2..edb892117e 100644 --- a/libs/agora/api-client-angular/src/lib/api/dataversion.service.ts +++ b/libs/agora/api-client-angular/src/lib/api/dataversion.service.ts @@ -1,18 +1,152 @@ -import { HttpClient, HttpHeaders } from '@angular/common/http'; -import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs'; -import { DataVersion } from '../model/dataversion'; -import { BASE_URL, DEFAULT_HEADERS } from './constants'; +/** + * Agora REST API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +/* tslint:disable:no-unused-variable member-ordering */ + +import { Inject, Injectable, Optional } from '@angular/core'; +import { HttpClient, HttpHeaders, HttpParams, + HttpResponse, HttpEvent, HttpParameterCodec, HttpContext + } from '@angular/common/http'; +import { CustomHttpParameterCodec } from '../encoder'; +import { Observable } from 'rxjs'; + +// @ts-ignore +import { BasicError } from '../model/basicError'; +// @ts-ignore +import { Dataversion } from '../model/dataversion'; + +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; +import { Configuration } from '../configuration'; + + @Injectable({ - providedIn: 'root', + providedIn: 'root' }) -export class DataVersionService { - constructor(private http: HttpClient) {} - - getDataVersion(): Observable { - return this.http.get(BASE_URL + '/dataversion', { - headers: new HttpHeaders(DEFAULT_HEADERS), - }); - } +export class DataversionService { + + protected basePath = 'http://localhost/v1'; + public defaultHeaders = new HttpHeaders(); + public configuration = new Configuration(); + public encoder: HttpParameterCodec; + + constructor(protected httpClient: HttpClient, @Optional()@Inject(BASE_PATH) basePath: string|string[], @Optional() configuration: Configuration) { + if (configuration) { + this.configuration = configuration; + } + if (typeof this.configuration.basePath !== 'string') { + if (Array.isArray(basePath) && basePath.length > 0) { + basePath = basePath[0]; + } + + if (typeof basePath !== 'string') { + basePath = this.basePath; + } + this.configuration.basePath = basePath; + } + this.encoder = this.configuration.encoder || new CustomHttpParameterCodec(); + } + + + // @ts-ignore + private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { + if (typeof value === "object" && value instanceof Date === false) { + httpParams = this.addToHttpParamsRecursive(httpParams, value); + } else { + httpParams = this.addToHttpParamsRecursive(httpParams, value, key); + } + return httpParams; + } + + private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { + if (value == null) { + return httpParams; + } + + if (typeof value === "object") { + if (Array.isArray(value)) { + (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); + } else if (value instanceof Date) { + if (key != null) { + httpParams = httpParams.append(key, (value as Date).toISOString().substr(0, 10)); + } else { + throw Error("key may not be null if value is Date"); + } + } else { + Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( + httpParams, value[k], key != null ? `${key}[${k}]` : k)); + } + } else if (key != null) { + httpParams = httpParams.append(key, value); + } else { + throw Error("key may not be null if value is not object or array"); + } + return httpParams; + } + + /** + * Get dataversion + * Get dataversion + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public getDataversion(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext}): Observable; + public getDataversion(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext}): Observable>; + public getDataversion(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext}): Observable>; + public getDataversion(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext}): Observable { + + let localVarHeaders = this.defaultHeaders; + + let localVarHttpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (localVarHttpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json', + 'application/problem+json' + ]; + localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (localVarHttpHeaderAcceptSelected !== undefined) { + localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected); + } + + let localVarHttpContext: HttpContext | undefined = options && options.context; + if (localVarHttpContext === undefined) { + localVarHttpContext = new HttpContext(); + } + + + let responseType_: 'text' | 'json' | 'blob' = 'json'; + if (localVarHttpHeaderAcceptSelected) { + if (localVarHttpHeaderAcceptSelected.startsWith('text')) { + responseType_ = 'text'; + } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) { + responseType_ = 'json'; + } else { + responseType_ = 'blob'; + } + } + + let localVarPath = `/dataversion`; + return this.httpClient.get(`${this.configuration.basePath}${localVarPath}`, + { + context: localVarHttpContext, + responseType: responseType_, + withCredentials: this.configuration.withCredentials, + headers: localVarHeaders, + observe: observe, + reportProgress: reportProgress + } + ); + } + } diff --git a/libs/agora/api-client-angular/src/lib/configuration.ts b/libs/agora/api-client-angular/src/lib/configuration.ts new file mode 100644 index 0000000000..d38a4c153f --- /dev/null +++ b/libs/agora/api-client-angular/src/lib/configuration.ts @@ -0,0 +1,166 @@ +import { HttpParameterCodec } from '@angular/common/http'; +import { Param } from './param'; + +export interface ConfigurationParameters { + /** + * @deprecated Since 5.0. Use credentials instead + */ + apiKeys?: {[ key: string ]: string}; + username?: string; + password?: string; + /** + * @deprecated Since 5.0. Use credentials instead + */ + accessToken?: string | (() => string); + basePath?: string; + withCredentials?: boolean; + /** + * Takes care of encoding query- and form-parameters. + */ + encoder?: HttpParameterCodec; + /** + * Override the default method for encoding path parameters in various + * styles. + *

+ * See {@link README.md} for more details + *

+ */ + encodeParam?: (param: Param) => string; + /** + * The keys are the names in the securitySchemes section of the OpenAPI + * document. They should map to the value used for authentication + * minus any standard prefixes such as 'Basic' or 'Bearer'. + */ + credentials?: {[ key: string ]: string | (() => string | undefined)}; +} + +export class Configuration { + /** + * @deprecated Since 5.0. Use credentials instead + */ + apiKeys?: {[ key: string ]: string}; + username?: string; + password?: string; + /** + * @deprecated Since 5.0. Use credentials instead + */ + accessToken?: string | (() => string); + basePath?: string; + withCredentials?: boolean; + /** + * Takes care of encoding query- and form-parameters. + */ + encoder?: HttpParameterCodec; + /** + * Encoding of various path parameter + * styles. + *

+ * See {@link README.md} for more details + *

+ */ + encodeParam: (param: Param) => string; + /** + * The keys are the names in the securitySchemes section of the OpenAPI + * document. They should map to the value used for authentication + * minus any standard prefixes such as 'Basic' or 'Bearer'. + */ + credentials: {[ key: string ]: string | (() => string | undefined)}; + + constructor(configurationParameters: ConfigurationParameters = {}) { + this.apiKeys = configurationParameters.apiKeys; + this.username = configurationParameters.username; + this.password = configurationParameters.password; + this.accessToken = configurationParameters.accessToken; + this.basePath = configurationParameters.basePath; + this.withCredentials = configurationParameters.withCredentials; + this.encoder = configurationParameters.encoder; + if (configurationParameters.encodeParam) { + this.encodeParam = configurationParameters.encodeParam; + } + else { + this.encodeParam = param => this.defaultEncodeParam(param); + } + if (configurationParameters.credentials) { + this.credentials = configurationParameters.credentials; + } + else { + this.credentials = {}; + } + } + + /** + * Select the correct content-type to use for a request. + * Uses {@link Configuration#isJsonMime} to determine the correct content-type. + * If no content type is found return the first found type if the contentTypes is not empty + * @param contentTypes - the array of content types that are available for selection + * @returns the selected content-type or undefined if no selection could be made. + */ + public selectHeaderContentType (contentTypes: string[]): string | undefined { + if (contentTypes.length === 0) { + return undefined; + } + + const type = contentTypes.find((x: string) => this.isJsonMime(x)); + if (type === undefined) { + return contentTypes[0]; + } + return type; + } + + /** + * Select the correct accept content-type to use for a request. + * Uses {@link Configuration#isJsonMime} to determine the correct accept content-type. + * If no content type is found return the first found type if the contentTypes is not empty + * @param accepts - the array of content types that are available for selection. + * @returns the selected content-type or undefined if no selection could be made. + */ + public selectHeaderAccept(accepts: string[]): string | undefined { + if (accepts.length === 0) { + return undefined; + } + + const type = accepts.find((x: string) => this.isJsonMime(x)); + if (type === undefined) { + return accepts[0]; + } + return type; + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * @param mime - MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + public isJsonMime(mime: string): boolean { + const jsonMime: RegExp = new RegExp('^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$', 'i'); + return mime !== null && (jsonMime.test(mime) || mime.toLowerCase() === 'application/json-patch+json'); + } + + public lookupCredential(key: string): string | undefined { + const value = this.credentials[key]; + return typeof value === 'function' + ? value() + : value; + } + + private defaultEncodeParam(param: Param): string { + // This implementation exists as fallback for missing configuration + // and for backwards compatibility to older typescript-angular generator versions. + // It only works for the 'simple' parameter style. + // Date-handling only works for the 'date-time' format. + // All other styles and Date-formats are probably handled incorrectly. + // + // But: if that's all you need (i.e.: the most common use-case): no need for customization! + + const value = param.dataFormat === 'date-time' + ? (param.value as Date).toISOString() + : param.value; + + return encodeURIComponent(String(value)); + } +} diff --git a/libs/agora/api-client-angular/src/lib/encoder.ts b/libs/agora/api-client-angular/src/lib/encoder.ts new file mode 100644 index 0000000000..138c4d5cf2 --- /dev/null +++ b/libs/agora/api-client-angular/src/lib/encoder.ts @@ -0,0 +1,20 @@ +import { HttpParameterCodec } from '@angular/common/http'; + +/** + * Custom HttpParameterCodec + * Workaround for https://github.com/angular/angular/issues/18261 + */ +export class CustomHttpParameterCodec implements HttpParameterCodec { + encodeKey(k: string): string { + return encodeURIComponent(k); + } + encodeValue(v: string): string { + return encodeURIComponent(v); + } + decodeKey(k: string): string { + return decodeURIComponent(k); + } + decodeValue(v: string): string { + return decodeURIComponent(v); + } +} diff --git a/libs/agora/api-client-angular/src/lib/git_push.sh b/libs/agora/api-client-angular/src/lib/git_push.sh new file mode 100644 index 0000000000..f53a75d4fa --- /dev/null +++ b/libs/agora/api-client-angular/src/lib/git_push.sh @@ -0,0 +1,57 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 +git_host=$4 + +if [ "$git_host" = "" ]; then + git_host="github.com" + echo "[INFO] No command line input provided. Set \$git_host to $git_host" +fi + +if [ "$git_user_id" = "" ]; then + git_user_id="GIT_USER_ID" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="GIT_REPO_ID" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="Minor update" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=$(git remote) +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' diff --git a/libs/agora/api-client-angular/src/lib/index.ts b/libs/agora/api-client-angular/src/lib/index.ts index cdfea183ad..104dd3d21e 100644 --- a/libs/agora/api-client-angular/src/lib/index.ts +++ b/libs/agora/api-client-angular/src/lib/index.ts @@ -1,2 +1,6 @@ export * from './api/api'; export * from './model/models'; +export * from './variables'; +export * from './configuration'; +export * from './api.module'; +export * from './param'; diff --git a/libs/agora/api-client-angular/src/lib/model/basicError.ts b/libs/agora/api-client-angular/src/lib/model/basicError.ts new file mode 100644 index 0000000000..1ddf4cdf01 --- /dev/null +++ b/libs/agora/api-client-angular/src/lib/model/basicError.ts @@ -0,0 +1,35 @@ +/** + * Agora REST API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +/** + * Problem details (tools.ietf.org/html/rfc7807) + */ +export interface BasicError { + /** + * A human readable documentation for the problem type + */ + title: string; + /** + * The HTTP status code + */ + status: number; + /** + * A human readable explanation specific to this occurrence of the problem + */ + detail?: string; + /** + * An absolute URI that identifies the problem type + */ + type?: string; +} + diff --git a/libs/agora/api-client-angular/src/lib/model/dataversion.ts b/libs/agora/api-client-angular/src/lib/model/dataversion.ts index cbf824ac3b..e8b2f990db 100644 --- a/libs/agora/api-client-angular/src/lib/model/dataversion.ts +++ b/libs/agora/api-client-angular/src/lib/model/dataversion.ts @@ -1,5 +1,22 @@ -export interface DataVersion { - data_file: string; - data_version: string; - team_images_id: string; +/** + * Agora REST API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +/** + * A dataversion + */ +export interface Dataversion { + data_file: string; + data_version: string; + team_images_id: string; } + diff --git a/libs/agora/api-client-angular/src/lib/model/models.ts b/libs/agora/api-client-angular/src/lib/model/models.ts index dd4ad22a68..f918055fc9 100644 --- a/libs/agora/api-client-angular/src/lib/model/models.ts +++ b/libs/agora/api-client-angular/src/lib/model/models.ts @@ -1 +1,2 @@ +export * from './basicError'; export * from './dataversion'; diff --git a/libs/agora/api-client-angular/src/lib/param.ts b/libs/agora/api-client-angular/src/lib/param.ts new file mode 100644 index 0000000000..78a2d20a64 --- /dev/null +++ b/libs/agora/api-client-angular/src/lib/param.ts @@ -0,0 +1,69 @@ +/** + * Standard parameter styles defined by OpenAPI spec + */ +export type StandardParamStyle = + | 'matrix' + | 'label' + | 'form' + | 'simple' + | 'spaceDelimited' + | 'pipeDelimited' + | 'deepObject' + ; + +/** + * The OpenAPI standard {@link StandardParamStyle}s may be extended by custom styles by the user. + */ +export type ParamStyle = StandardParamStyle | string; + +/** + * Standard parameter locations defined by OpenAPI spec + */ +export type ParamLocation = 'query' | 'header' | 'path' | 'cookie'; + +/** + * Standard types as defined in OpenAPI Specification: Data Types + */ +export type StandardDataType = + | "integer" + | "number" + | "boolean" + | "string" + | "object" + | "array" + ; + +/** + * Standard {@link DataType}s plus your own types/classes. + */ +export type DataType = StandardDataType | string; + +/** + * Standard formats as defined in OpenAPI Specification: Data Types + */ +export type StandardDataFormat = + | "int32" + | "int64" + | "float" + | "double" + | "byte" + | "binary" + | "date" + | "date-time" + | "password" + ; + +export type DataFormat = StandardDataFormat | string; + +/** + * The parameter to encode. + */ +export interface Param { + name: string; + value: unknown; + in: ParamLocation; + style: ParamStyle, + explode: boolean; + dataType: DataType; + dataFormat: DataFormat | undefined; +} diff --git a/libs/agora/api-client-angular/src/lib/variables.ts b/libs/agora/api-client-angular/src/lib/variables.ts new file mode 100644 index 0000000000..6fe58549f3 --- /dev/null +++ b/libs/agora/api-client-angular/src/lib/variables.ts @@ -0,0 +1,9 @@ +import { InjectionToken } from '@angular/core'; + +export const BASE_PATH = new InjectionToken('basePath'); +export const COLLECTION_FORMATS = { + 'csv': ',', + 'tsv': ' ', + 'ssv': ' ', + 'pipes': '|' +} diff --git a/libs/agora/api-client-angular/templates/api.module.mustache b/libs/agora/api-client-angular/templates/api.module.mustache new file mode 100644 index 0000000000..428af5e48b --- /dev/null +++ b/libs/agora/api-client-angular/templates/api.module.mustache @@ -0,0 +1,37 @@ +import { NgModule, ModuleWithProviders, SkipSelf, Optional } from '@angular/core'; +import { {{configurationClassName}} } from './configuration'; +import { HttpClient } from '@angular/common/http'; + +{{#apiInfo}} +{{#apis}} +import { {{classname}} } from './{{importPath}}'; +{{/apis}} +{{/apiInfo}} + +@NgModule({ + imports: [], + declarations: [], + exports: [], + providers: [{{#isProvidedInNone}} + {{#apiInfo}}{{#apis}}{{classname}}{{^-last}}, + {{/-last}}{{/apis}}{{/apiInfo}} {{/isProvidedInNone}}] +}) +export class {{apiModuleClassName}} { + public static forRoot(configurationFactory: () => {{configurationClassName}}): ModuleWithProviders{{#enforceGenericModuleWithProviders}}<{{apiModuleClassName}}>{{/enforceGenericModuleWithProviders}} { + return { + ngModule: {{apiModuleClassName}}, + providers: [ { provide: {{configurationClassName}}, useFactory: configurationFactory } ] + }; + } + + constructor( @Optional() @SkipSelf() parentModule: {{apiModuleClassName}}, + @Optional() http: HttpClient) { + if (parentModule) { + throw new Error('{{apiModuleClassName}} is already loaded. Import in your base AppModule only.'); + } + if (!http) { + throw new Error('You need to import the HttpClientModule in your AppModule! \n' + + 'See also https://github.com/angular/angular/issues/20575'); + } + } +} diff --git a/libs/agora/api-client-angular/templates/config.yaml b/libs/agora/api-client-angular/templates/config.yaml new file mode 100644 index 0000000000..756c6c3f01 --- /dev/null +++ b/libs/agora/api-client-angular/templates/config.yaml @@ -0,0 +1,3 @@ +templateDir: templates +additionalProperties: + queryParamObjectFormat: key diff --git a/libs/agora/api-client-angular/templates/v6.1.x/api.module.mustache b/libs/agora/api-client-angular/templates/v6.1.x/api.module.mustache new file mode 100644 index 0000000000..428af5e48b --- /dev/null +++ b/libs/agora/api-client-angular/templates/v6.1.x/api.module.mustache @@ -0,0 +1,37 @@ +import { NgModule, ModuleWithProviders, SkipSelf, Optional } from '@angular/core'; +import { {{configurationClassName}} } from './configuration'; +import { HttpClient } from '@angular/common/http'; + +{{#apiInfo}} +{{#apis}} +import { {{classname}} } from './{{importPath}}'; +{{/apis}} +{{/apiInfo}} + +@NgModule({ + imports: [], + declarations: [], + exports: [], + providers: [{{#isProvidedInNone}} + {{#apiInfo}}{{#apis}}{{classname}}{{^-last}}, + {{/-last}}{{/apis}}{{/apiInfo}} {{/isProvidedInNone}}] +}) +export class {{apiModuleClassName}} { + public static forRoot(configurationFactory: () => {{configurationClassName}}): ModuleWithProviders{{#enforceGenericModuleWithProviders}}<{{apiModuleClassName}}>{{/enforceGenericModuleWithProviders}} { + return { + ngModule: {{apiModuleClassName}}, + providers: [ { provide: {{configurationClassName}}, useFactory: configurationFactory } ] + }; + } + + constructor( @Optional() @SkipSelf() parentModule: {{apiModuleClassName}}, + @Optional() http: HttpClient) { + if (parentModule) { + throw new Error('{{apiModuleClassName}} is already loaded. Import in your base AppModule only.'); + } + if (!http) { + throw new Error('You need to import the HttpClientModule in your AppModule! \n' + + 'See also https://github.com/angular/angular/issues/20575'); + } + } +} diff --git a/libs/agora/api-client-angular/tools/pull-templates.sh b/libs/agora/api-client-angular/tools/pull-templates.sh new file mode 100755 index 0000000000..84cde04f6b --- /dev/null +++ b/libs/agora/api-client-angular/tools/pull-templates.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +openapiGeneratorVersion="6.1.x" +destinationFolder="${PWD}/templates/v${openapiGeneratorVersion}" + +mkdir -p "${destinationFolder}" + +curl -O --output-dir "${destinationFolder}" \ + "https://raw.githubusercontent.com/OpenAPITools/openapi-generator/${openapiGeneratorVersion}/modules/openapi-generator/src/main/resources/typescript-angular/api.module.mustache" \ No newline at end of file diff --git a/libs/agora/config/.eslintrc.json b/libs/agora/config/.eslintrc.json new file mode 100644 index 0000000000..10af9fa3d4 --- /dev/null +++ b/libs/agora/config/.eslintrc.json @@ -0,0 +1,50 @@ +{ + "extends": [ + "../../../.eslintrc.json" + ], + "ignorePatterns": [ + "!**/*" + ], + "env": { + "jest": true + }, + "overrides": [ + { + "files": [ + "*.ts" + ], + "extends": [ + "plugin:@nx/angular", + "plugin:@angular-eslint/template/process-inline-templates", + "plugin:jest/recommended" + ], + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "modelAd", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "agora", + "style": "kebab-case" + } + ] + } + }, + { + "files": [ + "*.html" + ], + "extends": [ + "plugin:@nx/angular-template" + ], + "rules": {} + } + ] +} \ No newline at end of file diff --git a/libs/agora/config/README.md b/libs/agora/config/README.md new file mode 100644 index 0000000000..d78c0d335c --- /dev/null +++ b/libs/agora/config/README.md @@ -0,0 +1,7 @@ +# agora-config + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test agora-config` to execute the unit tests. diff --git a/libs/agora/config/jest.config.ts b/libs/agora/config/jest.config.ts new file mode 100644 index 0000000000..68532bfc67 --- /dev/null +++ b/libs/agora/config/jest.config.ts @@ -0,0 +1,23 @@ +/* eslint-disable */ +export default { + displayName: 'agora-config', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + globals: {}, + coverageDirectory: '../../../coverage/libs/agora/config', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/libs/agora/config/project.json b/libs/agora/config/project.json new file mode 100644 index 0000000000..f7da50b7ce --- /dev/null +++ b/libs/agora/config/project.json @@ -0,0 +1,33 @@ +{ + "name": "agora-config", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "library", + "sourceRoot": "libs/agora/config/src", + "prefix": "agora", + "targets": { + "test": { + "executor": "@nx/jest:jest", + "outputs": [ + "{workspaceRoot}/coverage/libs/agora/config" + ], + "options": { + "jestConfig": "libs/agora/config/jest.config.ts" + } + }, + "lint": { + "executor": "@nx/eslint:lint" + }, + "lint-fix": { + "executor": "@nx/eslint:lint", + "options": { + "fix": true + } + } + }, + "tags": [ + "type:feature", + "scope:agora", + "language:typescript" + ], + "implicitDependencies": [] +} \ No newline at end of file diff --git a/libs/agora/config/src/index.ts b/libs/agora/config/src/index.ts new file mode 100644 index 0000000000..8585927f3f --- /dev/null +++ b/libs/agora/config/src/index.ts @@ -0,0 +1,3 @@ +export * from './lib/app.config'; +export * from './lib/config.factory'; +export * from './lib/config.service'; diff --git a/libs/agora/config/src/lib/app.config.ts b/libs/agora/config/src/lib/app.config.ts new file mode 100644 index 0000000000..f733ae5d8f --- /dev/null +++ b/libs/agora/config/src/lib/app.config.ts @@ -0,0 +1,24 @@ +/* eslint-disable no-unused-vars */ +export enum Environment { + Production = 'prod', + Staging = 'staging', + Test = 'test', + Development = 'dev', + Local = 'local', +} +/* eslint-enable no-unused-vars */ + +export interface AppConfig { + appVersion: string; + csrApiUrl: string; + dataUpdatedOn: string; + environment: Environment; + googleTagManagerId: string; + isPlatformServer: boolean; + privacyPolicyUrl: string; + ssrApiUrl: string; + termsOfUseUrl: string; + apiDocsUrl: string; +} + +export const EMPTY_APP_CONFIG = {} as AppConfig; diff --git a/libs/agora/config/src/lib/config.factory.ts b/libs/agora/config/src/lib/config.factory.ts new file mode 100644 index 0000000000..2116e1ddef --- /dev/null +++ b/libs/agora/config/src/lib/config.factory.ts @@ -0,0 +1,5 @@ +import { ConfigService } from './config.service'; + +export const configFactory = (configService: ConfigService) => { + return () => configService.loadConfig(); +}; diff --git a/libs/agora/config/src/lib/config.service.ts b/libs/agora/config/src/lib/config.service.ts new file mode 100644 index 0000000000..c76ee78706 --- /dev/null +++ b/libs/agora/config/src/lib/config.service.ts @@ -0,0 +1,48 @@ +import { isPlatformServer } from '@angular/common'; +import { HttpClient } from '@angular/common/http'; +import { Inject, Injectable, Optional, PLATFORM_ID } from '@angular/core'; +import { lastValueFrom } from 'rxjs'; +import { AppConfig, EMPTY_APP_CONFIG } from './app.config'; + +@Injectable({ + providedIn: 'root', +}) +export class ConfigService { + config: AppConfig = EMPTY_APP_CONFIG; + + constructor( + private http: HttpClient, + @Inject(PLATFORM_ID) private platformId: string, + @Inject('APP_PORT') @Optional() private readonly port: string, + ) {} + + async loadConfig(): Promise { + // The location of the browser folder, which includes the config folder, depends on whether the + // present code is run in the browser and by the node server. We could use the request received + // by the node server (SSR) and get the host and port, but this does not work when the port is + // different from the Angular or node port. Note that `localhost` works inside the container + // (production) as well as outside (dev server). APP_BASE_URL could be used when running the dev + // server and accessing it directly (e.g. APP_BASE_URL = 'http://localhost:37677') but not when + // accessing the production server in the container from apex (APP_BASE_URL = + // 'http://localhost:8000', which is invalid inside the container). + const browserRoot = isPlatformServer(this.platformId) + ? `http://localhost:${this.port}` + : '.'; + + const appConfig$ = this.http.get( + `${browserRoot}/config/config.json`, + ); + try { + const config = await lastValueFrom(appConfig$); + this.config = config; + this.config.isPlatformServer = isPlatformServer(this.platformId); + this.config.privacyPolicyUrl = + 'https://sagebionetworks.jira.com/wiki/spaces/OA/pages/2948530178/OpenChallenges+Privacy+Policy'; + this.config.termsOfUseUrl = + 'https://sagebionetworks.jira.com/wiki/spaces/OA/pages/2948333575/OpenChallenges+Terms+of+Use'; + } catch (err) { + console.error('Unable to load the config file: ', err); + return await Promise.resolve(); + } + } +} diff --git a/libs/agora/config/src/test-setup.ts b/libs/agora/config/src/test-setup.ts new file mode 100644 index 0000000000..1100b3e8a6 --- /dev/null +++ b/libs/agora/config/src/test-setup.ts @@ -0,0 +1 @@ +import 'jest-preset-angular/setup-jest'; diff --git a/libs/agora/config/tsconfig.json b/libs/agora/config/tsconfig.json new file mode 100644 index 0000000000..f96ad85d2e --- /dev/null +++ b/libs/agora/config/tsconfig.json @@ -0,0 +1,27 @@ +{ + "extends": "../../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "compilerOptions": { + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "target": "es2020" + }, + "angularCompilerOptions": { + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/libs/agora/config/tsconfig.lib.json b/libs/agora/config/tsconfig.lib.json new file mode 100644 index 0000000000..b228a1a081 --- /dev/null +++ b/libs/agora/config/tsconfig.lib.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [] + }, + "exclude": ["src/test-setup.ts", "**/*.spec.ts", "**/*.test.ts", "jest.config.ts"], + "include": ["**/*.ts"] +} diff --git a/libs/agora/config/tsconfig.spec.json b/libs/agora/config/tsconfig.spec.json new file mode 100644 index 0000000000..d3889a9881 --- /dev/null +++ b/libs/agora/config/tsconfig.spec.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "files": ["src/test-setup.ts"], + "include": ["**/*.test.ts", "**/*.spec.ts", "**/*.d.ts", "jest.config.ts"] +} diff --git a/libs/agora/not-found/.eslintrc.json b/libs/agora/not-found/.eslintrc.json new file mode 100644 index 0000000000..10af9fa3d4 --- /dev/null +++ b/libs/agora/not-found/.eslintrc.json @@ -0,0 +1,50 @@ +{ + "extends": [ + "../../../.eslintrc.json" + ], + "ignorePatterns": [ + "!**/*" + ], + "env": { + "jest": true + }, + "overrides": [ + { + "files": [ + "*.ts" + ], + "extends": [ + "plugin:@nx/angular", + "plugin:@angular-eslint/template/process-inline-templates", + "plugin:jest/recommended" + ], + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "modelAd", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "agora", + "style": "kebab-case" + } + ] + } + }, + { + "files": [ + "*.html" + ], + "extends": [ + "plugin:@nx/angular-template" + ], + "rules": {} + } + ] +} \ No newline at end of file diff --git a/libs/agora/not-found/README.md b/libs/agora/not-found/README.md new file mode 100644 index 0000000000..66869d5de9 --- /dev/null +++ b/libs/agora/not-found/README.md @@ -0,0 +1,7 @@ +# web-not-found + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test web-not-found` to execute the unit tests. diff --git a/libs/agora/not-found/jest.config.ts b/libs/agora/not-found/jest.config.ts new file mode 100644 index 0000000000..b3b521c2b8 --- /dev/null +++ b/libs/agora/not-found/jest.config.ts @@ -0,0 +1,23 @@ +/* eslint-disable */ +export default { + displayName: 'web-not-found', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + globals: {}, + coverageDirectory: '../../../coverage/libs/agora/not-found', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/libs/agora/not-found/project.json b/libs/agora/not-found/project.json new file mode 100644 index 0000000000..2f915cac8b --- /dev/null +++ b/libs/agora/not-found/project.json @@ -0,0 +1,33 @@ +{ + "name": "agora-not-found", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "library", + "sourceRoot": "libs/agora/not-found/src", + "prefix": "agora", + "targets": { + "test": { + "executor": "@nx/jest:jest", + "outputs": [ + "{workspaceRoot}/coverage/libs/agora/not-found" + ], + "options": { + "jestConfig": "libs/agora/not-found/jest.config.ts" + } + }, + "lint": { + "executor": "@nx/eslint:lint" + }, + "lint-fix": { + "executor": "@nx/eslint:lint", + "options": { + "fix": true + } + } + }, + "tags": [ + "type:feature", + "scope:agora", + "language:typescript" + ], + "implicitDependencies": [] +} \ No newline at end of file diff --git a/libs/agora/not-found/src/_lib-theme.scss b/libs/agora/not-found/src/_lib-theme.scss new file mode 100644 index 0000000000..ba34b73259 --- /dev/null +++ b/libs/agora/not-found/src/_lib-theme.scss @@ -0,0 +1,5 @@ +@use './lib/not-found-theme'; + +@mixin theme($theme) { + @include not-found-theme.theme($theme); +} diff --git a/libs/agora/not-found/src/index.ts b/libs/agora/not-found/src/index.ts new file mode 100644 index 0000000000..072d58c8cb --- /dev/null +++ b/libs/agora/not-found/src/index.ts @@ -0,0 +1 @@ +export * from './lib/not-found.routes'; diff --git a/libs/agora/not-found/src/lib/_not-found-theme.scss b/libs/agora/not-found/src/lib/_not-found-theme.scss new file mode 100644 index 0000000000..64d928f1f2 --- /dev/null +++ b/libs/agora/not-found/src/lib/_not-found-theme.scss @@ -0,0 +1,36 @@ +@use 'sass:map'; +@use '@angular/material' as mat; + +@mixin color($theme) { + $config: mat.m2-get-color-config($theme); + $primary: map.get($config, primary); + $accent: map.get($config, accent); + + .home-btn { + background-color: mat.m2-get-color-from-palette($accent) !important; + color: white !important; + } + .home-btn:hover { + background-color: mat.m2-get-color-from-palette($accent, 500); + } +} + +@mixin typography($theme) { + $config: mat.m2-get-typography-config($theme); + + .err-code { + font-size: 160px; + } +} + +@mixin theme($theme) { + $color-config: mat.m2-get-color-config($theme); + @if $color-config != null { + @include color($theme); + } + + $typography-config: mat.m2-get-typography-config($theme); + @if $typography-config != null { + @include typography($theme); + } +} diff --git a/libs/agora/not-found/src/lib/not-found.component.html b/libs/agora/not-found/src/lib/not-found.component.html new file mode 100644 index 0000000000..4fb7f923fd --- /dev/null +++ b/libs/agora/not-found/src/lib/not-found.component.html @@ -0,0 +1,32 @@ +
+
+ 404b +

Page Not Found

+

Oops! The page you are looking for does not exist. It might have been moved or deleted.

+
+
+

Data Release Info

+
    +
  • Data File: {{ dataVersion.data_file }}
  • +
  • Data Version: {{ dataVersion.data_version }}
  • +
  • Team Images Id: {{ dataVersion.team_images_id }}
  • +
+
+ +
+ + diff --git a/libs/agora/not-found/src/lib/not-found.component.scss b/libs/agora/not-found/src/lib/not-found.component.scss new file mode 100644 index 0000000000..05d81c0e7d --- /dev/null +++ b/libs/agora/not-found/src/lib/not-found.component.scss @@ -0,0 +1,22 @@ +@use 'libs/agora/styles/src/lib/constants'; + +#error { + background: url('/agora-assets/images/banner-sm.svg'); + background-size: contain; + background-repeat: no-repeat; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + min-height: calc(100vh - constants.$navbar-height); +} +.err-message { + padding-top: 100px; +} +.btn-group { + margin-top: 48px; + + a { + padding: 26px 40px; + } +} diff --git a/libs/agora/not-found/src/lib/not-found.component.spec.ts b/libs/agora/not-found/src/lib/not-found.component.spec.ts new file mode 100644 index 0000000000..6f91fafe37 --- /dev/null +++ b/libs/agora/not-found/src/lib/not-found.component.spec.ts @@ -0,0 +1,30 @@ +import { HttpClientModule } from '@angular/common/http'; +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +// import { ConfigService } from '@sagebionetworks/openchallenges/config'; + +import { NotFoundComponent } from './not-found.component'; + +describe('NotFoundComponent', () => { + let component: NotFoundComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [HttpClientModule, RouterTestingModule, NotFoundComponent], + providers: [], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(NotFoundComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/libs/agora/not-found/src/lib/not-found.component.ts b/libs/agora/not-found/src/lib/not-found.component.ts new file mode 100644 index 0000000000..7a2920f132 --- /dev/null +++ b/libs/agora/not-found/src/lib/not-found.component.ts @@ -0,0 +1,56 @@ +import { CommonModule } from '@angular/common'; +import { Component, OnInit, Renderer2 } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { RouterModule } from '@angular/router'; +import { MatCardModule } from '@angular/material/card'; +import { FooterComponent } from '@sagebionetworks/agora/ui'; +import { ConfigService } from '@sagebionetworks/agora/config'; +import { SeoService } from '@sagebionetworks/shared/util'; +import { + DataversionService, + Dataversion, +} from '@sagebionetworks/agora/api-client-angular'; +import { getSeoData } from './seo-data'; +import { Observable } from 'rxjs'; + +@Component({ + selector: 'agora-not-found', + standalone: true, + imports: [ + CommonModule, + RouterModule, + MatCardModule, + MatButtonModule, + FooterComponent, + ], + templateUrl: './not-found.component.html', + styleUrls: ['./not-found.component.scss'], +}) +export class NotFoundComponent implements OnInit { + public appVersion: string; + public dataUpdatedOn: string; + public privacyPolicyUrl: string; + public termsOfUseUrl: string; + public apiDocsUrl: string; + + dataversion$!: Observable; + + constructor( + private readonly configService: ConfigService, + private dataversionService: DataversionService, + private seoService: SeoService, + private renderer2: Renderer2, + ) { + this.appVersion = this.configService.config.appVersion; + this.dataUpdatedOn = this.configService.config.dataUpdatedOn; + this.privacyPolicyUrl = this.configService.config.privacyPolicyUrl; + this.termsOfUseUrl = this.configService.config.termsOfUseUrl; + this.apiDocsUrl = this.configService.config.apiDocsUrl; + + this.seoService.setData(getSeoData(), this.renderer2); + } + + ngOnInit(): void { + this.dataversion$ = this.dataversionService.getDataversion(); + } +} diff --git a/libs/agora/not-found/src/lib/not-found.routes.ts b/libs/agora/not-found/src/lib/not-found.routes.ts new file mode 100644 index 0000000000..6d3f611fda --- /dev/null +++ b/libs/agora/not-found/src/lib/not-found.routes.ts @@ -0,0 +1,4 @@ +import { Routes } from '@angular/router'; +import { NotFoundComponent } from './not-found.component'; + +export const routes: Routes = [{ path: '', component: NotFoundComponent }]; diff --git a/libs/agora/not-found/src/lib/seo-data.ts b/libs/agora/not-found/src/lib/seo-data.ts new file mode 100644 index 0000000000..ce58298e6b --- /dev/null +++ b/libs/agora/not-found/src/lib/seo-data.ts @@ -0,0 +1,8 @@ +import { SeoData } from '@sagebionetworks/shared/util'; +import { getDefaultSeoData } from '@sagebionetworks/agora/util'; + +export const getSeoData = (): SeoData => { + return Object.assign(getDefaultSeoData(), { + title: 'Not Found | Agora', + } as SeoData); +}; diff --git a/libs/agora/not-found/src/test-setup.ts b/libs/agora/not-found/src/test-setup.ts new file mode 100644 index 0000000000..1100b3e8a6 --- /dev/null +++ b/libs/agora/not-found/src/test-setup.ts @@ -0,0 +1 @@ +import 'jest-preset-angular/setup-jest'; diff --git a/libs/agora/not-found/tsconfig.json b/libs/agora/not-found/tsconfig.json new file mode 100644 index 0000000000..b1efdf54a6 --- /dev/null +++ b/libs/agora/not-found/tsconfig.json @@ -0,0 +1,27 @@ +{ + "extends": "../../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "compilerOptions": { + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "target": "es2020" + }, + "angularCompilerOptions": { + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} \ No newline at end of file diff --git a/libs/agora/not-found/tsconfig.lib.json b/libs/agora/not-found/tsconfig.lib.json new file mode 100644 index 0000000000..b228a1a081 --- /dev/null +++ b/libs/agora/not-found/tsconfig.lib.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [] + }, + "exclude": ["src/test-setup.ts", "**/*.spec.ts", "**/*.test.ts", "jest.config.ts"], + "include": ["**/*.ts"] +} diff --git a/libs/agora/not-found/tsconfig.spec.json b/libs/agora/not-found/tsconfig.spec.json new file mode 100644 index 0000000000..d3889a9881 --- /dev/null +++ b/libs/agora/not-found/tsconfig.spec.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "files": ["src/test-setup.ts"], + "include": ["**/*.test.ts", "**/*.spec.ts", "**/*.d.ts", "jest.config.ts"] +} diff --git a/libs/agora/styles/README.md b/libs/agora/styles/README.md new file mode 100644 index 0000000000..dbe32c57e5 --- /dev/null +++ b/libs/agora/styles/README.md @@ -0,0 +1 @@ +# agora-styles diff --git a/libs/agora/styles/project.json b/libs/agora/styles/project.json new file mode 100644 index 0000000000..d2c794a326 --- /dev/null +++ b/libs/agora/styles/project.json @@ -0,0 +1,13 @@ +{ + "name": "agora-styles", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/agora/styles/src", + "projectType": "library", + "generators": {}, + "targets": {}, + "tags": [ + "type:styles", + "scope:agora", + "language:typescript" + ] +} \ No newline at end of file diff --git a/libs/agora/styles/src/_index.scss b/libs/agora/styles/src/_index.scss new file mode 100644 index 0000000000..144ee1f14c --- /dev/null +++ b/libs/agora/styles/src/_index.scss @@ -0,0 +1,4 @@ +@use 'libs/shared/typescript/styles/src/index'; + +@use './lib/constants'; +@use './lib/general'; diff --git a/libs/agora/styles/src/lib/_constants.scss b/libs/agora/styles/src/lib/_constants.scss new file mode 100644 index 0000000000..ceb3628f29 --- /dev/null +++ b/libs/agora/styles/src/lib/_constants.scss @@ -0,0 +1,39 @@ +// See https://github.com/angular/components/blob/master/src/material/core/style/_variables.scss + +// Global constants +$pi: 3.14159264; +$padding-test: 20px; + +// Figma variables +$dl-size-size-large: 144px; +$dl-size-size-small: 48px; +$dl-size-size-medium: 96px; +$dl-size-size-xlarge: 192px; +$dl-size-size-xsmall: 16px; +$dl-space-space-unit: 16px; +$dl-size-size-xxlarge: 288px; +$dl-size-size-maxwidth: 1400px; +$dl-radius-radius-round: 50%; +$dl-space-space-halfunit: 8px; +$dl-space-space-sixunits: 96px; +$dl-space-space-twounits: 32px; +$dl-radius-radius-radius2: 2px; +$dl-radius-radius-radius4: 4px; +$dl-radius-radius-radius8: 8px; +$dl-space-space-fiveunits: 80px; +$dl-space-space-fourunits: 64px; +$dl-radius-radius-radius16: 16px; +$dl-space-space-threeunits: 48px; +$dl-space-space-oneandhalfunits: 24px; + +// Breakpoints from https://getbootstrap.com/docs/5.0/layout/breakpoints/ +$sm-breakpoint: 576px; +$md-breakpoint: 768px; +$lg-breakpoint: 992px; +$xl-breakpoint: 1200px; +$xxl-breakpoint: 1400px; + +// Dimensions of component +$navbar-height: 68px; +$navbar-height-tall: 240px; +$footer-height: 259px; diff --git a/libs/agora/styles/src/lib/_general.scss b/libs/agora/styles/src/lib/_general.scss new file mode 100644 index 0000000000..000f26cc9e --- /dev/null +++ b/libs/agora/styles/src/lib/_general.scss @@ -0,0 +1,432 @@ +@use 'libs/agora/styles/src/lib/constants'; + +html { + margin-top: constants.$navbar-height; +} +body { + max-height: 100vh; + margin: 0; +} +em { + color: #00b1e5; + font-style: normal; + font-weight: 700; +} + +// GRID +.base { + width: 100%; + min-height: calc(100vh - constants.$navbar-height - constants.$footer-height); +} +.row { + display: flex; + flex-direction: row; + flex-wrap: wrap; + width: 100%; +} +.col { + display: flex; + flex-direction: column; + flex-basis: 100%; +} +.fill-empty { + max-width: 920px; +} +.content { + @extend .row; + max-width: 2000px; + margin: auto; + padding: 48px 32px; + gap: 36px; +} + +// TEXT +.text-left { + text-align: left; +} +.text-center { + text-align: center; +} +.text-right { + text-align: right; +} +.text-grey, +span.text-grey a { + color: rgba(black, 0.38); +} +.text-right { + font-weight: 600; +} + +// LOGO & DESIGN +.logo-icon { + width: 140px; + height: 140px; +} +.top-design { + background: url('/agora-assets/images/banner-sm.svg'); + background-size: cover; + background-position: bottom; + padding-top: 108px; + text-align: center; + + .section-inner { + margin-bottom: 40px; + + h2 { + margin: 8px 0; + } + } +} + +// BUTTONS +.btn { + padding: 6px 32px !important; + text-transform: uppercase; + text-decoration: none; + transition: background 0.15s ease !important; +} +.btn-block { + width: 100%; + padding: 16px 32px !important; + text-transform: uppercase; + text-decoration: none; +} +.btn-group { + display: flex; + flex-flow: column wrap; + align-content: center; + + a { + text-decoration: none; + } +} +.btn-group > *:first-child { + align-self: stretch; +} +.btn-group button, +.btn-group a { + margin: 5px; + padding: 21px 32px !important; +} + +// SEARCH PAGES +#search-top { + padding: 0 32px 80px; +} +.search-sort-container { + gap: 24px; + justify-content: space-between; +} +.search-field { + position: relative; + flex-grow: 1; + margin: auto; +} +.sort-field { + display: flex; + gap: 16px; + align-items: center; +} +.sort-title { + white-space: nowrap; +} +.facets { + padding: 24px 16px; + height: 100%; + min-width: 280px; + display: flex; + align-items: flex-start; + flex-direction: column; + border-style: solid; + border-width: 2px; + border-radius: 8px; + + & > * { + width: 100%; + } +} + +// PROFILE PAGES +#profile-top, +#profile-bottom { + min-height: 320px; + justify-content: center; +} +#profile-top { + padding-top: 60px; +} +#profile-bottom { + padding-top: 32px; +} +#profile-top > div { + align-self: center; + flex: 1 0 auto; + padding: 22px; +} +.profile-pic, +.organization-card-banner { + // width: 40px; + // object-fit: cover; + margin: 8px; + align-self: center; + flex: 0 0 auto; + box-sizing: border-box; + border-radius: constants.$dl-radius-radius-round; + box-shadow: 1px 5px 18px 0px #d4d4d4; + + div.avatar-content { + height: 100%; + display: flex; + align-items: center; + justify-content: center; + } +} +#profile-details { + max-width: 100%; + text-align: center; +} +.profile-activity-card { + text-align: right; +} +.profile-nav-group { + margin: 18px; + padding: 48px 18px; + display: flex; + align-items: flex-start; + flex-direction: column; + border-style: solid; + border-width: 2px; + border-radius: 8px; +} +.profile-nav-item { + width: 100%; + height: 54px; + margin: 0 0 18px 0; + padding: 13px 18px; + box-sizing: border-box; + transition: 0.3s; + align-items: flex-start; + flex-shrink: 1; + text-align: left; + text-decoration: none; +} +.profile-type { + width: 100%; + max-width: 148px; + margin: 8px 0; + padding: 5px; + border-radius: 32px; + text-align: center; + align-self: center; +} +.stats-group { + display: flex; + flex-direction: row; + flex-wrap: wrap; + width: 100%; + justify-content: center; +} +.stat-item { + flex-direction: column; + flex: 1; + max-width: 120px; + padding: 18px 5px 8px; + border-style: solid; + border-width: 1px; + text-align: center; + border-color: var(--color-btn-shadow); + background-color: white; + border-right-width: 0; + + h3 { + margin-bottom: 2px !important; + } +} +.action-btn, +.disabled-btn { + height: 100%; + min-width: 260px; + max-width: 280px; + padding: 13px 5px; + display: inline-flex; + flex-direction: row; + justify-content: center; + align-items: center; +} +.action-btn { + background-color: var(--color-btn-primary); + color: white; + border-color: transparent; + cursor: pointer; +} +.disabled-btn { + background-color: var(--color-btn-disabled); + border-right-width: 1px; +} +.stats-card-icon { + margin-right: 4px; +} +table { + border-spacing: 11px; +} +#bio div a mat-icon { + font-size: 15px; + vertical-align: middle; +} +.created-updated-dates, +.created-updated-dates a, +.read-more { + color: #1f3b8f; +} +.created-updated-dates { + font-style: italic !important; +} +.read-more { + text-underline-offset: 3px; +} + +// CARDS +@mixin line-clamp($max-lines) { + display: -webkit-box; + overflow: hidden; + text-overflow: ellipsis; + -webkit-box-orient: vertical; + -webkit-line-clamp: $max-lines; +} +.card-group { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); + gap: 24px; + padding: 16px 0; +} +.card-banner { + width: 100%; + height: 130px; + display: flex; + align-items: flex-start; +} +.card-icon { + margin: 0 3px; + padding-right: 24px; +} +.card-title { + margin: 15px 0 !important; + font-weight: 700 !important; + line-height: 24px !important; + min-height: 48px; + @include line-clamp(2); +} +.mat-caption { + @include line-clamp(2); +} +.card-body { + width: 100%; + margin: 8px; + display: flex; + flex: 1; + align-items: center; + flex-direction: column; + justify-content: flex-start; + + & > p { + text-align: center; + } +} +.card-footer { + display: flex; + flex-direction: row; + height: 50px; + width: 100%; + border-top: 1px solid #c4c4c4; + justify-content: center; + align-items: center; + gap: 6px; +} +.star-btn { + position: absolute; + top: 18px; + right: 18px; + // height: 32px; + padding: 0 8px; + display: flex; + justify-content: center; + align-items: center; + border-radius: constants.$dl-radius-radius-radius16; + cursor: pointer; +} + +// MEDIA QUERIES +@media screen and (max-width: constants.$sm-breakpoint) { + .profile-activity-card { + text-align: center; + } +} +@media screen and (max-width: constants.$md-breakpoint) { + .content { + flex-direction: column; + } +} +@media screen and (min-width: constants.$md-breakpoint) { + .col, + .col-1 { + flex: 1; + } + .col-2 { + flex: 2; + } + .col-3 { + flex: 3; + } + .col-4 { + flex: 4; + } + .col-5 { + flex: 5; + } + .col-6 { + flex: 6; + } + .col-7 { + flex: 7; + } + .col-8 { + flex: 8; + } + .col-9 { + flex: 9; + } + .col-10 { + flex: 10; + } + .col-11 { + flex: 11; + } + .col-12 { + flex: 12; + } + #search-top { + padding: 0 132px 80px; + } + #profile-details { + // max-width: 760px; + max-width: 1060px; + text-align: left; + } + .profile-type { + align-self: auto; + } + .profile-sidenav { + max-width: 320px; + } +} +@media only screen and (max-width: constants.$lg-breakpoint) { + html { + margin-top: constants.$navbar-height-tall; + } + + .card-group { + grid-template-columns: 60%; + justify-content: center; + } +} diff --git a/libs/agora/themes/README.md b/libs/agora/themes/README.md new file mode 100644 index 0000000000..d4575df045 --- /dev/null +++ b/libs/agora/themes/README.md @@ -0,0 +1 @@ +# agora-themes \ No newline at end of file diff --git a/libs/agora/themes/project.json b/libs/agora/themes/project.json new file mode 100644 index 0000000000..4dbbaadc4c --- /dev/null +++ b/libs/agora/themes/project.json @@ -0,0 +1,14 @@ +{ + "name": "agora-themes", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/agora/themes/src", + "projectType": "library", + "generators": {}, + "targets": {}, + "tags": [ + "type:themes", + "scope:agora", + "language:typescript" + ], + "implicitDependencies": [] +} \ No newline at end of file diff --git a/libs/agora/themes/src/_fonts.scss b/libs/agora/themes/src/_fonts.scss new file mode 100644 index 0000000000..a7909beff7 --- /dev/null +++ b/libs/agora/themes/src/_fonts.scss @@ -0,0 +1,15 @@ +@use '@angular/material' as mat; + +@import url('https://fonts.googleapis.com/css2?family=Lato:wght@400;700&display=swap'); + +$lato: mat.m2-define-typography-config( + $font-family: "'Lato', sans-serif", + $headline-5: mat.m2-define-typography-level(46px, 56px, 700), + $headline-6: mat.m2-define-typography-level(40px, 50px, 700), + $subtitle-1: mat.m2-define-typography-level(28px, 38px, 700, $letter-spacing: -0.1px), + $subtitle-2: mat.m2-define-typography-level(20px, 32px, 400, $letter-spacing: -0.1px), + $body-1: mat.m2-define-typography-level(21px, 36px, 400, $letter-spacing: -0.1px), + $body-2: mat.m2-define-typography-level(16px, 26px, 400), + $caption: mat.m2-define-typography-level(14px, 21px, 400), + $button: mat.m2-define-typography-level(16px, 18px, 700), +); diff --git a/libs/agora/themes/src/_index.scss b/libs/agora/themes/src/_index.scss new file mode 100644 index 0000000000..05080c7fc5 --- /dev/null +++ b/libs/agora/themes/src/_index.scss @@ -0,0 +1,7 @@ +@use 'libs/agora/not-found/src/lib-theme' as agora-not-found; +@use 'libs/agora/ui/src/lib-theme' as agora-ui; + +@mixin theme($theme) { + @include agora-not-found.theme($theme); + @include agora-ui.theme($theme); +} diff --git a/libs/agora/themes/src/_palettes.scss b/libs/agora/themes/src/_palettes.scss new file mode 100644 index 0000000000..815a96839d --- /dev/null +++ b/libs/agora/themes/src/_palettes.scss @@ -0,0 +1,135 @@ +@use 'sass:map'; + +// Color variables +$dark-primary-text: rgba(black, 0.87); +$dark-secondary-text: rgba(black, 0.54); +$dark-disabled-text: rgba(black, 0.38); +$dark-dividers: rgba(black, 0.12); +$dark-focused: rgba(black, 0.12); +$light-primary-text: white; +$light-secondary-text: rgba(white, 0.7); +$light-disabled-text: rgba(white, 0.5); +$light-dividers: rgba(white, 0.12); +$light-focused: rgba(white, 0.12); + +$dark-blue-palette: ( + 50: #e8ebf5, + 100: #c4cce7, + 200: #9dabd7, + 300: #768ac7, + 400: #586fbc, + 500: #3756b1, + 600: #314fa7, + 700: #27459b, + 800: #1f3b8f, + 900: #102979, + contrast: ( + 50: $dark-primary-text, + 100: $dark-primary-text, + 200: $dark-primary-text, + 300: $dark-primary-text, + 400: $light-primary-text, + 500: $light-primary-text, + 600: $light-primary-text, + 700: $light-primary-text, + 800: $light-primary-text, + 900: $light-primary-text, + ), +); + +$light-blue-palette: ( + 50: #dff4fb, + 100: #ade3f4, + 200: #76d1ed, + 300: #39bde7, + 400: #00b1e5, + 500: #00a4e3, + 600: #0096d5, + 700: #0084c3, + 800: #0073b0, + 900: #005390, + contrast: ( + 50: $dark-primary-text, + 100: $dark-primary-text, + 200: $dark-primary-text, + 300: $dark-primary-text, + 400: $dark-primary-text, + 500: $dark-primary-text, + 600: $dark-primary-text, + 700: $dark-primary-text, + 800: $light-primary-text, + 900: $light-primary-text, + ), +); + +$light-green-palette: ( + 50: #e8fefb, + 100: #c6fdf3, + 200: #a5fdec, + 300: #88f9e3, + 400: #76f2d9, + 500: #71ecd0, + 600: #6adbbf, + 700: #61c8ad, + 800: #5bb79d, + 900: #50977e, + contrast: ( + 50: $dark-primary-text, + 100: $dark-primary-text, + 200: $dark-primary-text, + 300: $dark-primary-text, + 400: $dark-primary-text, + 500: $dark-primary-text, + 600: $dark-primary-text, + 700: $dark-primary-text, + 800: $dark-primary-text, + 900: $dark-primary-text, + ), +); + +$light-purple-palette: ( + 50: #ede8ff, + 100: #d0c6fd, + 200: #afa0fe, + 300: #8a78ff, + 400: #6859ff, + 500: #594AFC, + 600: #2e3aee, + 700: #0032e5, + 800: #002ddc, + 900: #0023ce, + contrast: ( + 50: $dark-primary-text, + 100: $dark-primary-text, + 200: $dark-primary-text, + 300: $dark-primary-text, + 400: $light-primary-text, + 500: $light-primary-text, + 600: $light-primary-text, + 700: $light-primary-text, + 800: $light-primary-text, + 900: $light-primary-text, + ), +); + +// Alias for "accent" usage +$accent-green-palette: $light-green-palette; +$accent-purple-palette: $light-purple-palette; + +// Figma palettes variables +$figma-collection: ( + dl-color-default-navbar: map.get($dark-blue-palette, 600), + dl-color-default-accent1: map.get($accent-green-palette, 100), + dl-color-default-accent2: map.get($accent-purple-palette, 50), + dl-color-default-primary1: map.get($dark-blue-palette, 400), + dl-color-default-primary2: $dark-focused, + dl-color-default-navbardark: map.get($dark-blue-palette, 800), + dl-color-default-secondary1: map.get($light-blue-palette, 300), + dl-color-default-secondary2: map.get($accent-purple-palette, 300), + dl-color-default-darkaccent1: map.get($accent-green-palette, 700), + dl-color-default-darkaccent2: map.get($accent-purple-palette, 200), + dl-color-gray-black: #000000, + dl-color-gray-white: #ffffff, + dl-color-default-hover1: rgba(249, 249, 249, 1), + dl-color-default-hover2: rgba(229, 229, 229, 1), +); diff --git a/libs/agora/ui/.eslintrc.json b/libs/agora/ui/.eslintrc.json new file mode 100644 index 0000000000..6019e3266f --- /dev/null +++ b/libs/agora/ui/.eslintrc.json @@ -0,0 +1,50 @@ +{ + "extends": [ + "../../../.eslintrc.json" + ], + "ignorePatterns": [ + "!**/*" + ], + "env": { + "jest": true + }, + "overrides": [ + { + "files": [ + "*.ts" + ], + "extends": [ + "plugin:@nx/angular", + "plugin:@angular-eslint/template/process-inline-templates", + "plugin:jest/recommended" + ], + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "agora", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "agora", + "style": "kebab-case" + } + ] + } + }, + { + "files": [ + "*.html" + ], + "extends": [ + "plugin:@nx/angular-template" + ], + "rules": {} + } + ] +} \ No newline at end of file diff --git a/libs/agora/ui/README.md b/libs/agora/ui/README.md new file mode 100644 index 0000000000..5744d12600 --- /dev/null +++ b/libs/agora/ui/README.md @@ -0,0 +1,7 @@ +# agora-ui + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test agora-ui` to execute the unit tests. diff --git a/libs/agora/ui/jest.config.ts b/libs/agora/ui/jest.config.ts new file mode 100644 index 0000000000..5cb94af2b9 --- /dev/null +++ b/libs/agora/ui/jest.config.ts @@ -0,0 +1,23 @@ +/* eslint-disable */ +export default { + displayName: 'agora-ui', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + globals: {}, + coverageDirectory: '../../../coverage/libs/agora/ui', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/libs/agora/ui/project.json b/libs/agora/ui/project.json new file mode 100644 index 0000000000..7da04593cc --- /dev/null +++ b/libs/agora/ui/project.json @@ -0,0 +1,27 @@ +{ + "name": "agora-ui", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "library", + "sourceRoot": "libs/agora/ui/src", + "prefix": "agora", + "targets": { + "test": { + "executor": "@nx/jest:jest", + "outputs": [ + "{workspaceRoot}/coverage/libs/agora/ui" + ], + "options": { + "jestConfig": "libs/agora/ui/jest.config.ts" + } + }, + "lint": { + "executor": "@nx/eslint:lint" + } + }, + "tags": [ + "type:feature", + "scope:agora", + "language:typescript" + ], + "implicitDependencies": [] +} \ No newline at end of file diff --git a/libs/agora/ui/src/_lib-theme.scss b/libs/agora/ui/src/_lib-theme.scss new file mode 100644 index 0000000000..f5d492663f --- /dev/null +++ b/libs/agora/ui/src/_lib-theme.scss @@ -0,0 +1,5 @@ +@use './lib/footer/footer-theme' as footer; + +@mixin theme($theme) { + @include footer.theme($theme); +} diff --git a/libs/agora/ui/src/index.ts b/libs/agora/ui/src/index.ts new file mode 100644 index 0000000000..e70ebd86fe --- /dev/null +++ b/libs/agora/ui/src/index.ts @@ -0,0 +1 @@ +export * from './lib/footer/footer.component'; diff --git a/libs/agora/ui/src/lib/footer/_footer-theme.scss b/libs/agora/ui/src/lib/footer/_footer-theme.scss new file mode 100644 index 0000000000..2f538fc13c --- /dev/null +++ b/libs/agora/ui/src/lib/footer/_footer-theme.scss @@ -0,0 +1,60 @@ +@use 'sass:map'; +@use '@angular/material' as mat; +@use 'libs/agora/styles/src/lib/constants'; + +@mixin color($theme) { + $config: mat.m2-get-color-config($theme); + $primary: map.get($config, 'primary'); + $accent: map.get($config, 'accent'); + $warn: map.get($config, 'warn'); + + footer { + background-color: mat.m2-get-color-from-palette($primary, 600); + color: #fff; + } + .footer-link-group a, + .footer-links a { + color: #fff; + } + .footer-bottom { + background-color: mat.m2-get-color-from-palette($primary, 800); + } +} + +@mixin typography($theme) { + $config: mat.m2-get-typography-config($theme); + + footer { + font-weight: 500; + line-height: normal; + } + .app-info { + font-size: 14px; + line-height: 21px; + } + .footer-link-group { + font-size: 16px; + } + .footer-subtext, + .footer-links, + .footer-links a { + font-size: 16px; + } + @media only screen and (max-width: constants.$md-breakpoint) { + .footer-link-group { + font-size: 14px !important; + } + } +} + +@mixin theme($theme) { + $color-config: mat.m2-get-color-config($theme); + @if $color-config != null { + @include color($theme); + } + + $typography-config: mat.m2-get-typography-config($theme); + @if $typography-config != null { + @include typography($theme); + } +} diff --git a/libs/agora/ui/src/lib/footer/footer.component.html b/libs/agora/ui/src/lib/footer/footer.component.html new file mode 100644 index 0000000000..7c69debde2 --- /dev/null +++ b/libs/agora/ui/src/lib/footer/footer.component.html @@ -0,0 +1,41 @@ + diff --git a/libs/agora/ui/src/lib/footer/footer.component.scss b/libs/agora/ui/src/lib/footer/footer.component.scss new file mode 100644 index 0000000000..7f195038bc --- /dev/null +++ b/libs/agora/ui/src/lib/footer/footer.component.scss @@ -0,0 +1,71 @@ +@use 'libs/agora/styles/src/lib/constants'; + +footer { + width: 100%; + height: constants.$footer-height; + position: relative; + padding: 42px 52px; + display: flex; + flex-direction: column; + align-items: flex-start; + box-sizing: border-box; +} +.footer-link-group { + width: 420px; + height: 30px; + padding: 0; + display: flex; + justify-content: space-between; + list-style: none; +} +.footer-link-group li { + flex: 0 0 auto; +} +.app-info { + text-align: right; + align-self: center; +} +.app-info ul { + list-style-type: none; + padding: 0; + margin: 0; +} +.footer-bottom { + bottom: 0; + left: 0px; + width: 100%; + height: 70px; + display: flex; + position: absolute; + align-items: center; + justify-content: center; +} +.footer-links, +.footer-links a { + text-decoration: none; +} +.footer-links a:hover { + text-decoration: underline; +} +.logo { + height: 56px; +} + +@media only screen and (max-width: constants.$md-breakpoint) { + footer { + height: 410px; + } + .about-oc, + .app-info, + .footer-link-group { + width: 100%; + text-align: center; + justify-content: space-around; + } + .footer-link-group li { + flex: 1; + } + .app-info { + margin-top: 45px; + } +} diff --git a/libs/agora/ui/src/lib/footer/footer.component.spec.ts b/libs/agora/ui/src/lib/footer/footer.component.spec.ts new file mode 100644 index 0000000000..48a1b8798f --- /dev/null +++ b/libs/agora/ui/src/lib/footer/footer.component.spec.ts @@ -0,0 +1,25 @@ +import { HttpClientModule } from '@angular/common/http'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { FooterComponent } from './footer.component'; + +describe('FooterComponent', () => { + let component: FooterComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [HttpClientModule, RouterTestingModule], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(FooterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/libs/agora/ui/src/lib/footer/footer.component.ts b/libs/agora/ui/src/lib/footer/footer.component.ts new file mode 100644 index 0000000000..3b72ae37f9 --- /dev/null +++ b/libs/agora/ui/src/lib/footer/footer.component.ts @@ -0,0 +1,18 @@ +import { Component, Input } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule } from '@angular/router'; + +@Component({ + selector: 'agora-footer', + standalone: true, + imports: [CommonModule, RouterModule], + templateUrl: './footer.component.html', + styleUrls: ['./footer.component.scss'], +}) +export class FooterComponent { + @Input({ required: true }) appVersion = ''; + @Input({ required: true }) dataUpdatedOn = ''; + @Input({ required: true }) privacyPolicyUrl = ''; + @Input({ required: true }) termsOfUseUrl = ''; + @Input({ required: true }) apiDocsUrl = ''; +} diff --git a/libs/agora/ui/src/test-setup.ts b/libs/agora/ui/src/test-setup.ts new file mode 100644 index 0000000000..1100b3e8a6 --- /dev/null +++ b/libs/agora/ui/src/test-setup.ts @@ -0,0 +1 @@ +import 'jest-preset-angular/setup-jest'; diff --git a/libs/agora/ui/tsconfig.json b/libs/agora/ui/tsconfig.json new file mode 100644 index 0000000000..b1efdf54a6 --- /dev/null +++ b/libs/agora/ui/tsconfig.json @@ -0,0 +1,27 @@ +{ + "extends": "../../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "compilerOptions": { + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "target": "es2020" + }, + "angularCompilerOptions": { + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} \ No newline at end of file diff --git a/libs/agora/ui/tsconfig.lib.json b/libs/agora/ui/tsconfig.lib.json new file mode 100644 index 0000000000..b228a1a081 --- /dev/null +++ b/libs/agora/ui/tsconfig.lib.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [] + }, + "exclude": ["src/test-setup.ts", "**/*.spec.ts", "**/*.test.ts", "jest.config.ts"], + "include": ["**/*.ts"] +} diff --git a/libs/agora/ui/tsconfig.spec.json b/libs/agora/ui/tsconfig.spec.json new file mode 100644 index 0000000000..d3889a9881 --- /dev/null +++ b/libs/agora/ui/tsconfig.spec.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "files": ["src/test-setup.ts"], + "include": ["**/*.test.ts", "**/*.spec.ts", "**/*.d.ts", "jest.config.ts"] +} diff --git a/libs/agora/util/.eslintrc.json b/libs/agora/util/.eslintrc.json new file mode 100644 index 0000000000..10af9fa3d4 --- /dev/null +++ b/libs/agora/util/.eslintrc.json @@ -0,0 +1,50 @@ +{ + "extends": [ + "../../../.eslintrc.json" + ], + "ignorePatterns": [ + "!**/*" + ], + "env": { + "jest": true + }, + "overrides": [ + { + "files": [ + "*.ts" + ], + "extends": [ + "plugin:@nx/angular", + "plugin:@angular-eslint/template/process-inline-templates", + "plugin:jest/recommended" + ], + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "modelAd", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "agora", + "style": "kebab-case" + } + ] + } + }, + { + "files": [ + "*.html" + ], + "extends": [ + "plugin:@nx/angular-template" + ], + "rules": {} + } + ] +} \ No newline at end of file diff --git a/libs/agora/util/README.md b/libs/agora/util/README.md new file mode 100644 index 0000000000..f8a0534b33 --- /dev/null +++ b/libs/agora/util/README.md @@ -0,0 +1,7 @@ +# web-util + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test web-util` to execute the unit tests. diff --git a/libs/agora/util/jest.config.ts b/libs/agora/util/jest.config.ts new file mode 100644 index 0000000000..c85b82c00c --- /dev/null +++ b/libs/agora/util/jest.config.ts @@ -0,0 +1,22 @@ +module.exports = { + displayName: 'web-util', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + globals: {}, + coverageDirectory: '../../../coverage/libs/agora/util', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/libs/agora/util/project.json b/libs/agora/util/project.json new file mode 100644 index 0000000000..b81c50b22f --- /dev/null +++ b/libs/agora/util/project.json @@ -0,0 +1,27 @@ +{ + "name": "agora-util", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "library", + "sourceRoot": "libs/agora/util/src", + "prefix": "agora", + "targets": { + "test": { + "executor": "@nx/jest:jest", + "outputs": [ + "{workspaceRoot}/coverage/libs/agora/util" + ], + "options": { + "jestConfig": "libs/agora/util/jest.config.ts" + } + }, + "lint": { + "executor": "@nx/eslint:lint" + } + }, + "tags": [ + "type:util", + "scope:agora", + "language:typescript" + ], + "implicitDependencies": [] +} \ No newline at end of file diff --git a/libs/agora/util/src/index.ts b/libs/agora/util/src/index.ts new file mode 100644 index 0000000000..80502a42e6 --- /dev/null +++ b/libs/agora/util/src/index.ts @@ -0,0 +1 @@ +export * from './lib/default-seo-data'; diff --git a/libs/agora/util/src/lib/default-seo-data.ts b/libs/agora/util/src/lib/default-seo-data.ts new file mode 100644 index 0000000000..8f77913f10 --- /dev/null +++ b/libs/agora/util/src/lib/default-seo-data.ts @@ -0,0 +1,13 @@ +import { SeoData } from '@sagebionetworks/shared/util'; + +export const getDefaultSeoData = (): SeoData => { + return new SeoData({ + title: 'Agora', + description: 'A description', + url: '', + imageUrl: '', + imageAlt: 'Agora logo', + publishDate: '2023-09-20T00:00:00Z', + jsonLds: [], + }); +}; diff --git a/libs/agora/util/src/test-setup.ts b/libs/agora/util/src/test-setup.ts new file mode 100644 index 0000000000..1100b3e8a6 --- /dev/null +++ b/libs/agora/util/src/test-setup.ts @@ -0,0 +1 @@ +import 'jest-preset-angular/setup-jest'; diff --git a/libs/agora/util/tsconfig.json b/libs/agora/util/tsconfig.json new file mode 100644 index 0000000000..857a3df1ef --- /dev/null +++ b/libs/agora/util/tsconfig.json @@ -0,0 +1,26 @@ +{ + "extends": "../../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "compilerOptions": { + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "angularCompilerOptions": { + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} \ No newline at end of file diff --git a/libs/agora/util/tsconfig.lib.json b/libs/agora/util/tsconfig.lib.json new file mode 100644 index 0000000000..f9941fa47e --- /dev/null +++ b/libs/agora/util/tsconfig.lib.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [] + }, + "exclude": ["src/test-setup.ts", "**/*.spec.ts", "**/*.test.ts"], + "include": ["**/*.ts"] +} diff --git a/libs/agora/util/tsconfig.spec.json b/libs/agora/util/tsconfig.spec.json new file mode 100644 index 0000000000..81bff2d560 --- /dev/null +++ b/libs/agora/util/tsconfig.spec.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "files": ["src/test-setup.ts"], + "include": ["**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"] +} diff --git a/tools/configure-hostnames.sh b/tools/configure-hostnames.sh index ed808a6677..23828901de 100755 --- a/tools/configure-hostnames.sh +++ b/tools/configure-hostnames.sh @@ -4,8 +4,8 @@ # list of hostnames (defined in alphabetical order) declare -a hostnames=( + "127.0.0.1 agora-api" "127.0.0.1 agora-mongo" - "127.0.0.1 agora-server" "127.0.0.1 iatlas-postgres" "127.0.0.1 model-ad-api" "127.0.0.1 model-ad-mongo" diff --git a/tsconfig.base.json b/tsconfig.base.json index 97ca6746ee..70bb5fd39b 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -10,7 +10,10 @@ "importHelpers": true, "target": "es2015", "module": "esnext", - "lib": ["es2017", "dom"], + "lib": [ + "es2017", + "dom" + ], "skipLibCheck": true, "skipDefaultLibCheck": true, "forceConsistentCasingInFileNames": true, @@ -20,50 +23,109 @@ "@sagebionetworks/agora/api-client-angular": [ "libs/agora/api-client-angular/src/index.ts" ], + "@sagebionetworks/agora/config": [ + "libs/agora/config/src/index.ts" + ], + "@sagebionetworks/agora/not-found": [ + "libs/agora/not-found/src/index.ts" + ], + "@sagebionetworks/agora/ui": [ + "libs/agora/ui/src/index.ts" + ], + "@sagebionetworks/agora/util": [ + "libs/agora/util/src/index.ts" + ], "@sagebionetworks/model-ad/api-client-angular": [ "libs/model-ad/api-client-angular/src/index.ts" ], - "@sagebionetworks/model-ad/config": ["libs/model-ad/config/src/index.ts"], - "@sagebionetworks/model-ad/not-found": ["libs/model-ad/not-found/src/index.ts"], - "@sagebionetworks/model-ad/ui": ["libs/model-ad/ui/src/index.ts"], - "@sagebionetworks/model-ad/util": ["libs/model-ad/util/src/index.ts"], - "@sagebionetworks/openchallenges/about": ["libs/openchallenges/about/src/index.ts"], + "@sagebionetworks/model-ad/config": [ + "libs/model-ad/config/src/index.ts" + ], + "@sagebionetworks/model-ad/not-found": [ + "libs/model-ad/not-found/src/index.ts" + ], + "@sagebionetworks/model-ad/ui": [ + "libs/model-ad/ui/src/index.ts" + ], + "@sagebionetworks/model-ad/util": [ + "libs/model-ad/util/src/index.ts" + ], + "@sagebionetworks/openchallenges/about": [ + "libs/openchallenges/about/src/index.ts" + ], "@sagebionetworks/openchallenges/api-client-angular": [ "libs/openchallenges/api-client-angular/src/index.ts" ], - "@sagebionetworks/openchallenges/assets": ["libs/openchallenges/assets/src/index.ts"], - "@sagebionetworks/openchallenges/auth": ["libs/openchallenges/auth/src/index.ts"], - "@sagebionetworks/openchallenges/challenge": ["libs/openchallenges/challenge/src/index.ts"], + "@sagebionetworks/openchallenges/assets": [ + "libs/openchallenges/assets/src/index.ts" + ], + "@sagebionetworks/openchallenges/auth": [ + "libs/openchallenges/auth/src/index.ts" + ], + "@sagebionetworks/openchallenges/challenge": [ + "libs/openchallenges/challenge/src/index.ts" + ], "@sagebionetworks/openchallenges/challenge-search": [ "libs/openchallenges/challenge-search/src/index.ts" ], - "@sagebionetworks/openchallenges/config": ["libs/openchallenges/config/src/index.ts"], - "@sagebionetworks/openchallenges/home": ["libs/openchallenges/home/src/index.ts"], - "@sagebionetworks/openchallenges/login": ["libs/openchallenges/login/src/index.ts"], - "@sagebionetworks/openchallenges/not-found": ["libs/openchallenges/not-found/src/index.ts"], + "@sagebionetworks/openchallenges/config": [ + "libs/openchallenges/config/src/index.ts" + ], + "@sagebionetworks/openchallenges/home": [ + "libs/openchallenges/home/src/index.ts" + ], + "@sagebionetworks/openchallenges/login": [ + "libs/openchallenges/login/src/index.ts" + ], + "@sagebionetworks/openchallenges/not-found": [ + "libs/openchallenges/not-found/src/index.ts" + ], "@sagebionetworks/openchallenges/org-profile": [ "libs/openchallenges/org-profile/src/index.ts" ], - "@sagebionetworks/openchallenges/org-search": ["libs/openchallenges/org-search/src/index.ts"], - "@sagebionetworks/openchallenges/pages": ["libs/openchallenges/pages/src/index.ts"], - "@sagebionetworks/openchallenges/signup": ["libs/openchallenges/signup/src/index.ts"], - "@sagebionetworks/openchallenges/styles": ["libs/openchallenges/styles/src/index.ts"], - "@sagebionetworks/openchallenges/team": ["libs/openchallenges/team/src/index.ts"], - "@sagebionetworks/openchallenges/themes": ["libs/openchallenges/themes/src/index.ts"], - "@sagebionetworks/openchallenges/ui": ["libs/openchallenges/ui/src/index.ts"], - "@sagebionetworks/openchallenges/util": ["libs/openchallenges/util/src/index.ts"], + "@sagebionetworks/openchallenges/org-search": [ + "libs/openchallenges/org-search/src/index.ts" + ], + "@sagebionetworks/openchallenges/pages": [ + "libs/openchallenges/pages/src/index.ts" + ], + "@sagebionetworks/openchallenges/signup": [ + "libs/openchallenges/signup/src/index.ts" + ], + "@sagebionetworks/openchallenges/styles": [ + "libs/openchallenges/styles/src/index.ts" + ], + "@sagebionetworks/openchallenges/team": [ + "libs/openchallenges/team/src/index.ts" + ], + "@sagebionetworks/openchallenges/themes": [ + "libs/openchallenges/themes/src/index.ts" + ], + "@sagebionetworks/openchallenges/ui": [ + "libs/openchallenges/ui/src/index.ts" + ], + "@sagebionetworks/openchallenges/util": [ + "libs/openchallenges/util/src/index.ts" + ], "@sagebionetworks/results-visualization-framework/ui": [ "libs/results-visualization-framework/ui/src/index.ts" ], - "@sagebionetworks/shared/charts": ["libs/shared/typescript/charts/src/index.ts"], + "@sagebionetworks/shared/charts": [ + "libs/shared/typescript/charts/src/index.ts" + ], "@sagebionetworks/shared/charts-angular": [ "libs/shared/typescript/charts-angular/src/index.ts" ], - "@sagebionetworks/shared/util": ["libs/shared/typescript/util/src/index.ts"], + "@sagebionetworks/shared/util": [ + "libs/shared/typescript/util/src/index.ts" + ], "@sagebionetworks/shared/web-components": [ "libs/shared/typescript/web-components/src/index.ts" ] } }, - "exclude": ["node_modules", "tmp"] -} + "exclude": [ + "node_modules", + "tmp" + ] +} \ No newline at end of file