-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loading status checks…
Merge pull request #28 from xeptagondev/main
Phase 2 Release
Showing
650 changed files
with
54,712 additions
and
5,374 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
name: Carbon Transparency Test Deployment | ||
on: | ||
workflow_dispatch: | ||
push: | ||
branches: | ||
- "staging" | ||
paths: | ||
- backend/** | ||
- web/** | ||
- .github/workflows/deployment* | ||
|
||
env: | ||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} | ||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | ||
AWS_DEFAULT_REGION: us-east-1 | ||
|
||
jobs: | ||
changes: | ||
name: Carbon Transparency Deploy Pre | ||
runs-on: ubuntu-latest | ||
outputs: | ||
backend-changes: ${{ steps.changes.outputs.backend-changes }} | ||
workflows-changes: ${{ steps.changes.outputs.workflows-changes }} | ||
frontend-changes: ${{ steps.changes.outputs.frontend-changes }} | ||
steps: | ||
- uses: actions/checkout@v3 | ||
with: | ||
fetch-depth: 0 | ||
- name: Use Node.js ${{ matrix.node-version }} | ||
uses: actions/setup-node@v3 | ||
with: | ||
node-version: ${{ matrix.node-version }} | ||
- name: Determine changed services | ||
id: changes | ||
run: | | ||
CHANGED_FILES=$(git diff --name-only HEAD^ HEAD) | ||
if echo "$CHANGED_FILES" | grep -q "backend/"; then | ||
echo "Backend changes detected." | ||
echo "backend-changes=true" >> $GITHUB_OUTPUT | ||
else | ||
echo "No Backend changes detected." | ||
echo "backend-changes=false" >> $GITHUB_OUTPUT | ||
fi | ||
if echo echo "$CHANGED_FILES" | grep -q ".github/workflows/"; then | ||
echo "Workflow changes detected." | ||
echo "workflows-changes=true" >> $GITHUB_OUTPUT | ||
else | ||
echo "No Workflow changes detected." | ||
echo "workflows-changes=false" >> $GITHUB_OUTPUT | ||
fi | ||
if echo "$CHANGED_FILES" | grep -q "web/"; then | ||
echo "Frontend changes detected." | ||
echo "frontend-changes=true" >> $GITHUB_OUTPUT | ||
else | ||
echo "No Frontend changes detected." | ||
echo "frontend-changes=false" >> $GITHUB_OUTPUT | ||
fi | ||
backend-deploy: | ||
needs: changes | ||
if: needs.changes.outputs.backend-changes == 'true' || needs.changes.outputs.workflows-changes == 'true' | ||
name: Carbon Transparency Backend Deploy | ||
runs-on: ubuntu-latest | ||
concurrency: | ||
group: ${{ github.workflow }}-${{ github.ref }}-'backend' | ||
cancel-in-progress: true | ||
steps: | ||
- uses: actions/checkout@v3 | ||
with: | ||
fetch-depth: 0 | ||
- name: Use Node.js ${{ matrix.node-version }} | ||
uses: actions/setup-node@v3 | ||
with: | ||
node-version: ${{ matrix.node-version }} | ||
- name: Configure AWS credentials | ||
uses: aws-actions/configure-aws-credentials@v1 | ||
with: | ||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | ||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | ||
aws-region: us-east-1 | ||
- name: Login to Amazon ECR | ||
id: login-ecr | ||
uses: aws-actions/amazon-ecr-login@v1 | ||
- name: Build, tag, and push the backend images to Amazon ECR | ||
id: build-backend-image | ||
env: | ||
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} | ||
ECR_REPOSITORY: transparency-services | ||
IMAGE_TAG: ${{ github.head_ref || github.ref_name }} | ||
run: | | ||
# Build a docker container and push it to ECR | ||
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -f backend/services/Dockerfile . | ||
echo "Pushing image to ECR..." | ||
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG | ||
echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" | ||
- name: Deploy backend images to Amazon EC2 | ||
id: deploy-backend | ||
env: | ||
PRIVATE_KEY: ${{ secrets.AWS_SSH_KEY_PRIVATE }} | ||
HOSTNAME: ${{secrets.HOST_IP }} | ||
USER_NAME: ec2-user | ||
run: | | ||
echo "$PRIVATE_KEY" > private_key && chmod 600 private_key | ||
ssh -o StrictHostKeyChecking=no -i private_key ${USER_NAME}@${HOSTNAME} ' | ||
repos/carbon-transparency/mrv_backend_deploy.sh ' | ||
frontend-deploy: | ||
needs: changes | ||
if: needs.changes.outputs.frontend-changes == 'true' || needs.changes.outputs.workflows-changes == 'true' | ||
name: Carbon Transparency Frontend Deploy | ||
runs-on: ubuntu-latest | ||
concurrency: | ||
group: ${{ github.workflow }}-${{ github.ref }}-'web' | ||
cancel-in-progress: true | ||
steps: | ||
- uses: actions/checkout@v3 | ||
with: | ||
fetch-depth: 0 | ||
- name: Use Node.js ${{ matrix.node-version }} | ||
uses: actions/setup-node@v3 | ||
with: | ||
node-version: ${{ matrix.node-version }} | ||
- name: Configure AWS credentials | ||
uses: aws-actions/configure-aws-credentials@v1 | ||
with: | ||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | ||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | ||
aws-region: us-east-1 | ||
- name: Login to Amazon ECR | ||
id: login-ecr | ||
uses: aws-actions/amazon-ecr-login@v1 | ||
- name: Build, tag, and push the frontend images to Amazon ECR | ||
id: build-frontend-image | ||
env: | ||
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} | ||
ECR_REPOSITORY: transparency-web | ||
IMAGE_TAG: ${{ github.head_ref || github.ref_name }} | ||
run: | | ||
# Build a docker container and push it to ECR | ||
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -f web/Dockerfile . --build-arg PORT=3030 --build-arg REACT_APP_BACKEND=http://localhost:9000 --build-arg REACT_APP_STAT_URL=http://localhost:9100 --build-arg COUNTRY_NAME="CountryX" --build-arg COUNTRY_FLAG_URL="https://carbon-common-dev.s3.amazonaws.com/flag.png" --build-arg COUNTRY_CODE="NG" --build-arg REACT_APP_MAP_TYPE="Mapbox" --build-arg REACT_APP_GOVERNMENT_MINISTRY:"Ministry Of Environment" --build-arg REACT_APP_MAPBOXGL_ACCESS_TOKEN=${{ secrets.MAPBOXGL_ACCESS_TOKEN }} | ||
echo "Pushing image to ECR..." | ||
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG | ||
echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" | ||
- name: Deploy frontend images to Amazon EC2 | ||
id: deploy-frontend | ||
env: | ||
PRIVATE_KEY: ${{ secrets.AWS_SSH_KEY_PRIVATE }} | ||
HOSTNAME: ${{secrets.HOST_IP }} | ||
USER_NAME: ec2-user | ||
run: | | ||
echo "$PRIVATE_KEY" > private_key && chmod 600 private_key | ||
ssh -o StrictHostKeyChecking=no -i private_key ${USER_NAME}@${HOSTNAME} ' | ||
repos/carbon-transparency/mrv_frontend_deploy.sh ' |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
name: Carbon Transparency Demo Deployment | ||
on: | ||
workflow_dispatch: | ||
push: | ||
branches: | ||
- "main" | ||
paths: | ||
- backend/** | ||
- web/** | ||
- .github/workflows/deployment* | ||
|
||
env: | ||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} | ||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | ||
AWS_DEFAULT_REGION: us-east-1 | ||
|
||
jobs: | ||
changes: | ||
name: Carbon Transparency Deploy Pre | ||
runs-on: ubuntu-latest | ||
outputs: | ||
backend-changes: ${{ steps.changes.outputs.backend-changes }} | ||
workflows-changes: ${{ steps.changes.outputs.workflows-changes }} | ||
frontend-changes: ${{ steps.changes.outputs.frontend-changes }} | ||
steps: | ||
- uses: actions/checkout@v3 | ||
with: | ||
fetch-depth: 0 | ||
- name: Use Node.js ${{ matrix.node-version }} | ||
uses: actions/setup-node@v3 | ||
with: | ||
node-version: ${{ matrix.node-version }} | ||
- name: Determine changed services | ||
id: changes | ||
run: | | ||
CHANGED_FILES=$(git diff --name-only HEAD^ HEAD) | ||
if echo "$CHANGED_FILES" | grep -q "backend/"; then | ||
echo "Backend changes detected." | ||
echo "backend-changes=true" >> $GITHUB_OUTPUT | ||
else | ||
echo "No Backend changes detected." | ||
echo "backend-changes=false" >> $GITHUB_OUTPUT | ||
fi | ||
if echo echo "$CHANGED_FILES" | grep -q ".github/workflows/"; then | ||
echo "Workflow changes detected." | ||
echo "workflows-changes=true" >> $GITHUB_OUTPUT | ||
else | ||
echo "No Workflow changes detected." | ||
echo "workflows-changes=false" >> $GITHUB_OUTPUT | ||
fi | ||
if echo "$CHANGED_FILES" | grep -q "web/"; then | ||
echo "Frontend changes detected." | ||
echo "frontend-changes=true" >> $GITHUB_OUTPUT | ||
else | ||
echo "No Frontend changes detected." | ||
echo "frontend-changes=false" >> $GITHUB_OUTPUT | ||
fi | ||
backend-deploy: | ||
needs: changes | ||
if: needs.changes.outputs.backend-changes == 'true' || needs.changes.outputs.workflows-changes == 'true' | ||
name: Carbon Registry Backend Deploy | ||
runs-on: ubuntu-latest | ||
concurrency: | ||
group: ${{ github.workflow }}-${{ github.ref }}-'backend' | ||
cancel-in-progress: true | ||
steps: | ||
- uses: actions/checkout@v3 | ||
with: | ||
fetch-depth: 0 | ||
- name: Use Node.js ${{ matrix.node-version }} | ||
uses: actions/setup-node@v3 | ||
with: | ||
node-version: ${{ matrix.node-version }} | ||
- name: Configure AWS credentials | ||
uses: aws-actions/configure-aws-credentials@v1 | ||
with: | ||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | ||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | ||
aws-region: us-east-1 | ||
- name: Login to Amazon ECR | ||
id: login-ecr | ||
uses: aws-actions/amazon-ecr-login@v1 | ||
- name: Build, tag, and push the backend images to Amazon ECR | ||
id: build-backend-image | ||
env: | ||
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} | ||
ECR_REPOSITORY: transparency-services | ||
IMAGE_TAG: ${{ github.head_ref || github.ref_name }} | ||
run: | | ||
# Build a docker container and push it to ECR | ||
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -f backend/services/Dockerfile . | ||
echo "Pushing image to ECR..." | ||
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG | ||
echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" | ||
- name: Deploy backend images to Amazon EC2 | ||
id: deploy-backend | ||
env: | ||
PRIVATE_KEY: ${{ secrets.AWS_SSH_KEY_PRIVATE_DEMO }} | ||
HOSTNAME: ${{secrets.HOST_IP_DEMO }} | ||
USER_NAME: ec2-user | ||
run: | | ||
echo "$PRIVATE_KEY" > private_key && chmod 600 private_key | ||
ssh -o StrictHostKeyChecking=no -i private_key ${USER_NAME}@${HOSTNAME} ' | ||
repos/carbon-transparency/prod_backend_deploy.sh ' | ||
frontend-deploy: | ||
needs: changes | ||
if: needs.changes.outputs.frontend-changes == 'true' || needs.changes.outputs.workflows-changes == 'true' | ||
name: Carbon Registry Frontend Deploy | ||
runs-on: ubuntu-latest | ||
concurrency: | ||
group: ${{ github.workflow }}-${{ github.ref }}-'web' | ||
cancel-in-progress: true | ||
steps: | ||
- uses: actions/checkout@v3 | ||
with: | ||
fetch-depth: 0 | ||
- name: Use Node.js ${{ matrix.node-version }} | ||
uses: actions/setup-node@v3 | ||
with: | ||
node-version: ${{ matrix.node-version }} | ||
- name: Configure AWS credentials | ||
uses: aws-actions/configure-aws-credentials@v1 | ||
with: | ||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | ||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | ||
aws-region: us-east-1 | ||
- name: Login to Amazon ECR | ||
id: login-ecr | ||
uses: aws-actions/amazon-ecr-login@v1 | ||
- name: Build, tag, and push the frontend images to Amazon ECR | ||
id: build-frontend-image | ||
env: | ||
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} | ||
ECR_REPOSITORY: transparency-web | ||
IMAGE_TAG: ${{ github.head_ref || github.ref_name }} | ||
run: | | ||
# Build a docker container and push it to ECR | ||
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -f web/Dockerfile . --build-arg PORT=3030 --build-arg REACT_APP_BACKEND=http://localhost:9000 --build-arg REACT_APP_STAT_URL=http://localhost:9100 --build-arg COUNTRY_NAME="CountryX" --build-arg COUNTRY_FLAG_URL="https://carbon-common-dev.s3.amazonaws.com/flag.png" --build-arg COUNTRY_CODE="NG" --build-arg REACT_APP_MAP_TYPE="Mapbox" --build-arg REACT_APP_GOVERNMENT_MINISTRY:"Ministry Of Environment" --build-arg REACT_APP_MAPBOXGL_ACCESS_TOKEN=${{ secrets.MAPBOXGL_ACCESS_TOKEN }} | ||
echo "Pushing image to ECR..." | ||
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG | ||
echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" | ||
- name: Deploy frontend images to Amazon EC2 | ||
id: deploy-frontend | ||
env: | ||
PRIVATE_KEY: ${{ secrets.AWS_SSH_KEY_PRIVATE_DEMO }} | ||
HOSTNAME: ${{secrets.HOST_IP_DEMO }} | ||
USER_NAME: ec2-user | ||
run: | | ||
echo "$PRIVATE_KEY" > private_key && chmod 600 private_key | ||
ssh -o StrictHostKeyChecking=no -i private_key ${USER_NAME}@${HOSTNAME} ' | ||
repos/carbon-transparency/prod_frontend_deploy.sh ' |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,20 @@ | ||
{ | ||
"cSpell.words": [ | ||
"antd", | ||
"Appstore", | ||
"centered", | ||
"cmpt", | ||
"donut", | ||
"Kpis", | ||
"Popconfirm", | ||
"Sider" | ||
"Popover", | ||
"Popups", | ||
"Sider", | ||
"typeorm", | ||
"UNNEST", | ||
"unvalidate", | ||
"vars" | ||
], | ||
"compile-hero.disable-compile-files-on-did-save-code": true | ||
"compile-hero.disable-compile-files-on-did-save-code": true, | ||
"cSpell.language": "en-GB" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { forwardRef, Module } from '@nestjs/common'; | ||
import { ActionService } from './action.service'; | ||
import { ConfigModule } from '@nestjs/config'; | ||
import configuration from '../configuration'; | ||
import { TypeOrmModule } from '@nestjs/typeorm'; | ||
import { TypeOrmConfigService } from '../typeorm.config.service'; | ||
import { ActionEntity } from '../entities/action.entity'; | ||
import { KpiEntity } from '../entities/kpi.entity'; | ||
import { LogEntity } from '../entities/log.entity'; | ||
import { ProgrammeEntity } from '../entities/programme.entity'; | ||
import { ProjectEntity } from '../entities/project.entity'; | ||
import { AchievementEntity } from '../entities/achievement.entity'; | ||
import { ActivityEntity } from '../entities/activity.entity'; | ||
import { SupportEntity } from '../entities/support.entity'; | ||
import { UtilModule } from '../util/util.module'; | ||
import { FileHandlerModule } from '../file-handler/filehandler.module'; | ||
import { ValidationModule } from '../validation/validation.module'; | ||
import { ActionViewEntity } from '../entities/action.view.entity'; | ||
import { KpiModule } from '../kpi/kpi.module'; | ||
|
||
@Module({ | ||
imports: [ | ||
ConfigModule.forRoot({ | ||
isGlobal: true, | ||
load: [configuration], | ||
envFilePath: [`.env.${process.env.NODE_ENV}`, `.env`], | ||
}), | ||
TypeOrmModule.forRootAsync({ | ||
useClass: TypeOrmConfigService, | ||
imports: undefined, | ||
}), | ||
TypeOrmModule.forFeature([ | ||
ActionEntity, | ||
KpiEntity, | ||
LogEntity, | ||
ProgrammeEntity, | ||
ProjectEntity, | ||
AchievementEntity, | ||
ActivityEntity, | ||
SupportEntity, | ||
ActionViewEntity, | ||
]), | ||
UtilModule, | ||
FileHandlerModule, | ||
ValidationModule, | ||
forwardRef(() => KpiModule) | ||
], | ||
providers: [ | ||
ActionService | ||
], | ||
exports: [ActionService], | ||
}) | ||
export class ActionModule { } |
1,016 changes: 1,016 additions & 0 deletions
1,016
backend/services/src/action/action.service.spec.ts
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { Module } from '@nestjs/common'; | ||
import { ConfigModule } from '@nestjs/config'; | ||
import configuration from '../configuration'; | ||
import { TypeOrmModule } from '@nestjs/typeorm'; | ||
import { TypeOrmConfigService } from '../typeorm.config.service'; | ||
import { ActionEntity } from '../entities/action.entity'; | ||
import { KpiEntity } from '../entities/kpi.entity'; | ||
import { LogEntity } from '../entities/log.entity'; | ||
import { ProgrammeEntity } from '../entities/programme.entity'; | ||
import { ProjectEntity } from '../entities/project.entity'; | ||
import { AchievementEntity } from '../entities/achievement.entity'; | ||
import { ActivityEntity } from '../entities/activity.entity'; | ||
import { SupportEntity } from '../entities/support.entity'; | ||
import { UtilModule } from '../util/util.module'; | ||
import { FileHandlerModule } from '../file-handler/filehandler.module'; | ||
import { ValidationModule } from '../validation/validation.module'; | ||
import { ActionViewEntity } from '../entities/action.view.entity'; | ||
import { ActivityService } from './activity.service'; | ||
import { ActionModule } from 'src/action/action.module'; | ||
import { ProgrammeModule } from 'src/programme/programme.module'; | ||
import { ProjectModule } from 'src/project/project.module'; | ||
import { ProgrammeViewEntity } from 'src/entities/programme.view.entity'; | ||
import { KpiModule } from 'src/kpi/kpi.module'; | ||
|
||
@Module({ | ||
imports: [ | ||
ConfigModule.forRoot({ | ||
isGlobal: true, | ||
load: [configuration], | ||
envFilePath: [`.env.${process.env.NODE_ENV}`, `.env`], | ||
}), | ||
TypeOrmModule.forRootAsync({ | ||
useClass: TypeOrmConfigService, | ||
imports: undefined, | ||
}), | ||
TypeOrmModule.forFeature([ | ||
ActionEntity, | ||
KpiEntity, | ||
LogEntity, | ||
ProgrammeEntity, | ||
ProjectEntity, | ||
AchievementEntity, | ||
ActivityEntity, | ||
SupportEntity, | ||
ActionViewEntity, | ||
ProgrammeViewEntity | ||
]), | ||
ActionModule, | ||
ProgrammeModule, | ||
ProjectModule, | ||
UtilModule, | ||
FileHandlerModule, | ||
ValidationModule, | ||
KpiModule | ||
], | ||
providers: [ | ||
ActivityService | ||
], | ||
exports: [ActivityService], | ||
}) | ||
export class ActivityModule { } |
Large diffs are not rendered by default.
Oops, something went wrong.
1,468 changes: 1,468 additions & 0 deletions
1,468
backend/services/src/activity/activity.service.ts
Large diffs are not rendered by default.
Oops, something went wrong.
54 changes: 54 additions & 0 deletions
54
backend/services/src/analytics-api/analytics.api.controller.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { | ||
Controller, | ||
UseGuards, | ||
Get, | ||
Param, | ||
} from "@nestjs/common"; | ||
import { ApiBearerAuth, ApiTags } from "@nestjs/swagger"; | ||
import { AnalyticsService } from "./analytics.api.service"; | ||
import { JwtAuthGuard } from "../auth/guards/jwt-auth.guard"; | ||
|
||
@ApiTags("Analytics") | ||
@ApiBearerAuth('api_key') | ||
@Controller("analytics") | ||
export class AnalyticsController { | ||
constructor( | ||
private analyticsService: AnalyticsService, | ||
) {} | ||
|
||
@UseGuards(JwtAuthGuard) | ||
@Get('/actionsSummery') | ||
getClimateActionChart() { | ||
return this.analyticsService.getClimateActionChart(); | ||
} | ||
|
||
@UseGuards(JwtAuthGuard) | ||
@Get('/projectSummary') | ||
getProjectSummaryChart() { | ||
return this.analyticsService.getProjectSummaryChart(); | ||
} | ||
|
||
@UseGuards(JwtAuthGuard) | ||
@Get('/supportSummary') | ||
getSupportChart() { | ||
return this.analyticsService.getActivitiesSupported(); | ||
} | ||
|
||
@UseGuards(JwtAuthGuard) | ||
@Get('/supportFinanceSummary') | ||
getSupportFinanceChart() { | ||
return this.analyticsService.getActivitiesFinance(); | ||
} | ||
|
||
@UseGuards(JwtAuthGuard) | ||
@Get('/ghgMitigationSummaryForYear/:year') | ||
getGhgMitigationForYear(@Param('year') year: number) { | ||
return this.analyticsService.getGhgMitigationForYear(year); | ||
} | ||
|
||
@UseGuards(JwtAuthGuard) | ||
@Get('/getGhgMitigationSummary') | ||
getGhgMitigationForRecentYear() { | ||
return this.analyticsService.getGhgMitigationForRecentYear(); | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
backend/services/src/analytics-api/analytics.api.module.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { Logger, Module } from "@nestjs/common"; | ||
import { ConfigModule } from "@nestjs/config"; | ||
import { TypeOrmModule } from "@nestjs/typeorm"; | ||
import configuration from "../configuration"; | ||
import { Organisation } from "../entities/organisation.entity"; | ||
import { TypeOrmConfigService } from "../typeorm.config.service"; | ||
import { AnalyticsController } from "./analytics.api.controller"; | ||
import { AnalyticsService } from "./analytics.api.service"; | ||
import { AuthModule } from "../auth/auth.module"; | ||
import { CaslModule } from "../casl/casl.module"; | ||
import { ActivityEntity } from "../entities/activity.entity"; | ||
import { UtilModule } from "../util/util.module"; | ||
|
||
@Module({ | ||
imports: [ | ||
ConfigModule.forRoot({ | ||
isGlobal: true, | ||
load: [configuration], | ||
envFilePath: [`.env.${process.env.NODE_ENV}`, `.env`], | ||
}), | ||
TypeOrmModule.forRootAsync({ | ||
useClass: TypeOrmConfigService, | ||
imports: undefined, | ||
}), | ||
TypeOrmModule.forFeature([ | ||
ActivityEntity | ||
]), | ||
AuthModule, | ||
CaslModule, | ||
UtilModule | ||
], | ||
controllers: [AnalyticsController], | ||
providers: [Logger, AnalyticsService], | ||
}) | ||
export class AnalyticsAPIModule { } |
241 changes: 241 additions & 0 deletions
241
backend/services/src/analytics-api/analytics.api.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,241 @@ | ||
import { HttpException, HttpStatus, Injectable } from "@nestjs/common"; | ||
import { DataCountResponseDto } from "../dtos/data.count.response"; | ||
import { ActionEntity } from "../entities/action.entity"; | ||
import { EntityManager, Repository } from 'typeorm'; | ||
import { InjectEntityManager, InjectRepository } from "@nestjs/typeorm"; | ||
import { ProjectEntity } from "../entities/project.entity"; | ||
import { ActivityEntity } from "../entities/activity.entity"; | ||
import { FinanceNature, SupportDirection } from "../enums/support.enum"; | ||
import { HelperService } from "../util/helpers.service"; | ||
|
||
@Injectable() | ||
export class AnalyticsService { | ||
|
||
constructor( | ||
@InjectEntityManager() private entityManager: EntityManager, | ||
@InjectRepository(ActivityEntity) private activityRepo: Repository<ActivityEntity>, | ||
private helperService: HelperService | ||
) { } | ||
|
||
async getClimateActionChart(): Promise<DataCountResponseDto> { | ||
try { | ||
const queryBuilder = this.entityManager.createQueryBuilder() | ||
.select('sector, COUNT("actionId") as count, MAX(action.updatedTime) as "latestTime"') | ||
.from(ActionEntity, 'action') | ||
.groupBy('sector') | ||
.orderBy('MAX(action.updatedTime)', 'DESC'); | ||
|
||
const result = await queryBuilder.getRawMany(); | ||
|
||
// Extract sectors and counts into separate arrays | ||
const sectors = result.map(row => row.sector); | ||
const counts = result.map(row => row.count); | ||
|
||
// Get the latest time from the first row if result is not empty | ||
const latestTime = result.length ? new Date(result[0].latestTime) : null; | ||
|
||
// Convert latestTime to epoch if it's not null | ||
const latestEpoch = latestTime ? Math.floor(latestTime.getTime() / 1000) : 0; | ||
|
||
return new DataCountResponseDto({ sectors, counts }, latestEpoch); | ||
} catch (err) { | ||
console.log(err); | ||
throw new HttpException( | ||
this.helperService.formatReqMessagesString( | ||
"common.unableToGetStats", | ||
[] | ||
), | ||
HttpStatus.INTERNAL_SERVER_ERROR | ||
); | ||
} | ||
|
||
} | ||
|
||
async getProjectSummaryChart(): Promise<DataCountResponseDto> { | ||
try { | ||
const queryBuilder = this.entityManager.createQueryBuilder() | ||
.select('sector, COUNT("projectId") as count, MAX(project.updatedTime) as "latestTime"') | ||
.from(ProjectEntity, 'project') | ||
.groupBy('sector') | ||
.orderBy('MAX(project.updatedTime)', 'DESC'); | ||
|
||
const result = await queryBuilder.getRawMany(); | ||
|
||
// Extract sectors and counts into separate arrays | ||
const sectors = result.map(row => row.sector); | ||
const counts = result.map(row => row.count); | ||
|
||
// Get the latest time from the first row if result is not empty | ||
const latestTime = result.length ? new Date(result[0].latestTime) : null; | ||
|
||
// Convert latestTime to epoch if it's not null | ||
const latestEpoch = latestTime ? Math.floor(latestTime.getTime() / 1000) : 0; | ||
|
||
|
||
return new DataCountResponseDto({ sectors, counts }, latestEpoch); | ||
} catch (err) { | ||
console.log(err); | ||
throw new HttpException( | ||
this.helperService.formatReqMessagesString( | ||
"common.unableToGetStats", | ||
[] | ||
), | ||
HttpStatus.INTERNAL_SERVER_ERROR | ||
); | ||
} | ||
} | ||
|
||
async getActivitiesSupported() { | ||
try { | ||
const results = await this.activityRepo.createQueryBuilder('activity') | ||
.leftJoin('activity.support', 'support') | ||
.select([ | ||
'COUNT(DISTINCT activity.activityId) as "totalActivities"', | ||
'COUNT(DISTINCT CASE WHEN support.financeNature = :financeNature AND support.direction = :directionReceived THEN activity.activityId END) as "supportReceivedActivities"', | ||
'GREATEST(MAX(activity."updatedTime"), MAX(support."updatedTime")) as "latestTime"' | ||
]) | ||
.setParameter('financeNature', FinanceNature.INTERNATIONAL) | ||
.setParameter('directionReceived', SupportDirection.RECEIVED) | ||
.getRawOne(); | ||
|
||
const totalActivities = results.totalActivities ? parseInt(results.totalActivities) : 0; | ||
const supportReceivedActivities = results.supportReceivedActivities ? parseInt(results.supportReceivedActivities) : 0; | ||
const supportNeededActivities = totalActivities - supportReceivedActivities; | ||
|
||
const latestTime = results.latestTime ? new Date(results.latestTime).getTime() / 1000 : 0; | ||
|
||
return new DataCountResponseDto({ supportReceivedActivities, supportNeededActivities }, latestTime); | ||
} catch (err) { | ||
console.log(err); | ||
throw new HttpException( | ||
this.helperService.formatReqMessagesString( | ||
"common.unableToGetStats", | ||
[] | ||
), | ||
HttpStatus.INTERNAL_SERVER_ERROR | ||
); | ||
} | ||
} | ||
|
||
async getActivitiesFinance() { | ||
try { | ||
const results = await this.activityRepo.createQueryBuilder('activity') | ||
.leftJoin('activity.support', 'support') | ||
.select([ | ||
'sum(support."receivedAmount") as "supportReceived"', 'sum(support."requiredAmount") as "supportNeeded"', | ||
'GREATEST(MAX(activity."updatedTime"), MAX(support."updatedTime")) as "latestTime"' | ||
]) | ||
.getRawOne(); | ||
|
||
const supportReceived = results.supportReceived ? parseFloat(results.supportReceived) : 0; | ||
const supportNeeded = results.supportNeeded ? parseFloat(results.supportNeeded) : 0; | ||
|
||
const latestTime = results.latestTime ? new Date(results.latestTime).getTime() / 1000 : 0; | ||
|
||
return new DataCountResponseDto({ supportReceived, supportNeeded }, latestTime); | ||
|
||
} catch (err) { | ||
console.log(err); | ||
throw new HttpException( | ||
this.helperService.formatReqMessagesString( | ||
"common.unableToGetStats", | ||
[] | ||
), | ||
HttpStatus.INTERNAL_SERVER_ERROR | ||
); | ||
} | ||
} | ||
|
||
async getGhgMitigationForYear(year: number) { | ||
try { | ||
const query = ` | ||
SELECT | ||
activity.sector, | ||
SUM((activity."mitigationTimeline"->'expected'->'expectedEmissionReductWithM'->>(${year} - (activity."mitigationTimeline" ->> 'startYear')::int))::numeric) AS total, | ||
Max(activity."updatedTime") as "latestTime" | ||
FROM | ||
activity | ||
WHERE activity."mitigationTimeline" IS NOT NULL | ||
AND (activity."mitigationTimeline" ->> 'startYear')::numeric <= ${year} | ||
GROUP BY | ||
activity.sector | ||
HAVING | ||
SUM((activity."mitigationTimeline" -> 'expected' -> 'expectedEmissionReductWithM' ->> (${year} - (activity."mitigationTimeline" ->> 'startYear')::int))::numeric) != 0 | ||
ORDER BY | ||
"latestTime" DESC; | ||
`; | ||
|
||
const result = await this.entityManager.query(query); | ||
// Extract sectors and counts into separate arrays | ||
const sectors = result.map(row => row.sector); | ||
const totals = result.map(row => row.total); | ||
|
||
// Get the latest time from the first row if result is not empty | ||
const latestTime = result.length ? new Date(result[0].latestTime) : null; | ||
|
||
// Convert latestTime to epoch if it's not null | ||
const latestEpoch = latestTime ? Math.floor(latestTime.getTime() / 1000) : 0; | ||
|
||
return new DataCountResponseDto({ sectors, totals }, latestEpoch); | ||
} catch (err) { | ||
console.log(err); | ||
throw new HttpException( | ||
this.helperService.formatReqMessagesString( | ||
"common.unableToGetStats", | ||
[] | ||
), | ||
HttpStatus.INTERNAL_SERVER_ERROR | ||
); | ||
} | ||
} | ||
|
||
async getGhgMitigationForRecentYear() { | ||
|
||
// Get the current year | ||
const currentYear = new Date().getFullYear(); | ||
|
||
// Calculate the previous year | ||
const previousYear = currentYear - 1; | ||
|
||
try { | ||
const query = ` | ||
SELECT | ||
activity.sector, | ||
SUM((activity."mitigationTimeline"->'actual'->'actualEmissionReduct'->>(${previousYear} - (activity."mitigationTimeline" ->> 'startYear')::int))::numeric) AS total, | ||
Max(activity."updatedTime") as "latestTime" | ||
FROM | ||
activity | ||
WHERE activity."mitigationTimeline" IS NOT NULL | ||
AND (activity."mitigationTimeline" ->> 'startYear')::numeric <= ${previousYear} | ||
GROUP BY | ||
activity.sector | ||
HAVING | ||
SUM((activity."mitigationTimeline" -> 'actual' -> 'actualEmissionReduct' ->> (${previousYear} - (activity."mitigationTimeline" ->> 'startYear')::int))::numeric) != 0 | ||
ORDER BY | ||
"latestTime" DESC; | ||
`; | ||
|
||
const result = await this.entityManager.query(query); | ||
const sectors = result.map(row => row.sector); | ||
const totals = result.map(row => row.total); | ||
|
||
// Get the latest time from the first row if result is not empty | ||
const latestTime = result.length ? new Date(result[0].latestTime) : null; | ||
|
||
// Convert latestTime to epoch if it's not null | ||
const latestEpoch = latestTime ? Math.floor(latestTime.getTime() / 1000) : 0; | ||
|
||
return new DataCountResponseDto({ sectors, totals }, latestEpoch); | ||
} catch (err) { | ||
console.log(err); | ||
throw new HttpException( | ||
this.helperService.formatReqMessagesString( | ||
"common.unableToGetStats", | ||
[] | ||
), | ||
HttpStatus.INTERNAL_SERVER_ERROR | ||
); | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
79 changes: 79 additions & 0 deletions
79
backend/services/src/async-operations-handler/async-operations-database-handler.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { Injectable, Logger } from '@nestjs/common'; | ||
import { InjectRepository } from '@nestjs/typeorm'; | ||
import { Repository } from 'typeorm'; | ||
import { AsyncOperationsHandlerInterface } from './async-operations-handler-interface.service'; | ||
import { AsyncOperationsHandlerService } from './async-operations-handler.service'; | ||
import { AsyncActionEntity } from '../entities/async.action.entity'; | ||
import { Counter } from '../entities/counter.entity'; | ||
import { CounterType } from '../enums/counter.type.enum'; | ||
|
||
@Injectable() | ||
export class AsyncOperationsDatabaseHandlerService | ||
implements AsyncOperationsHandlerInterface | ||
{ | ||
constructor( | ||
private logger: Logger, | ||
@InjectRepository(Counter) private counterRepo: Repository<Counter>, | ||
@InjectRepository(AsyncActionEntity) | ||
private asyncActionRepo: Repository<AsyncActionEntity>, | ||
private asyncOperationsHandlerService: AsyncOperationsHandlerService, | ||
) {} | ||
|
||
async asyncHandler(event: any): Promise<any> { | ||
this.logger.log('database asyncHandler started', JSON.stringify(event)); | ||
|
||
const seqObj = await this.counterRepo.findOneBy({ | ||
id: CounterType.ASYNC_OPERATIONS, | ||
}); | ||
let lastSeq = 0; | ||
if (seqObj) { | ||
lastSeq = seqObj.counter; | ||
} | ||
let retryCount = 0; | ||
const retryLimit = 50; | ||
const baseDelay = 5000; // Initial delay for exponential backoff | ||
|
||
const doActions = async () => { | ||
console.log('lastSeq', lastSeq, 'retryCount', retryCount); | ||
const notExecutedActions = await this.asyncActionRepo | ||
.createQueryBuilder('asyncAction') | ||
.where('asyncAction.actionId > :lastExecuted', { lastExecuted: lastSeq }) | ||
.orderBy('"actionId"', 'ASC') | ||
.select(['"actionId"', '"actionType"', '"actionProps"']) | ||
.getRawMany(); | ||
|
||
if (notExecutedActions.length !== 0) { | ||
try { | ||
for (const action of notExecutedActions) { | ||
console.log('Action start', action.actionType, action.actionId); | ||
await this.asyncOperationsHandlerService.handler( | ||
action.actionType, | ||
JSON.parse(action.actionProps), | ||
); | ||
lastSeq = action.actionId; | ||
await this.counterRepo.save({ | ||
id: CounterType.ASYNC_OPERATIONS, | ||
counter: lastSeq, | ||
}); | ||
retryCount = 0; // Reset retry count after a successful execution | ||
} | ||
} catch (exception) { | ||
this.logger.log('database asyncHandler failed', exception); | ||
if (retryCount >= retryLimit) { | ||
this.logger.log('database asyncHandler terminated'); | ||
return; | ||
} | ||
const delay = baseDelay * Math.pow(2, retryCount); // Exponential backoff | ||
console.log(`Retrying in ${delay}ms...`); | ||
await new Promise((resolve) => setTimeout(resolve, delay)); | ||
retryCount++; | ||
return doActions(); // Retry the operation | ||
} | ||
} | ||
// Schedule the next execution after a fixed delay regardless of success or failure | ||
setTimeout(doActions, baseDelay); | ||
}; | ||
|
||
await doActions(); | ||
} | ||
} |
6 changes: 6 additions & 0 deletions
6
backend/services/src/async-operations-handler/async-operations-handler-interface.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { Injectable } from "@nestjs/common"; | ||
|
||
@Injectable() | ||
export abstract class AsyncOperationsHandlerInterface { | ||
abstract asyncHandler(event): Promise<any>; | ||
} |
59 changes: 59 additions & 0 deletions
59
backend/services/src/async-operations-handler/async-operations-handler.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import { Injectable, Logger } from "@nestjs/common"; | ||
import { EmailService } from "../email/email.service"; | ||
import { AsyncActionType } from "../enums/async.action.type.enum"; | ||
// import { RegistryClientService } from "../shared/registry-client/registry-client.service"; | ||
// import { CadtApiService } from "../shared/cadt/cadt.api.service"; | ||
|
||
@Injectable() | ||
export class AsyncOperationsHandlerService { | ||
constructor( | ||
private emailService: EmailService, | ||
// private registryClient: RegistryClientService, | ||
// private cadtService: CadtApiService, | ||
private logger: Logger | ||
) {} | ||
|
||
async handler(actionType: any, dataObject: any) { | ||
|
||
this.logger.log("AsyncOperationsHandlerService started", actionType.toString()); | ||
if (actionType) { | ||
switch (actionType.toString()) { | ||
case AsyncActionType.Email.toString(): | ||
return await this.emailService.sendEmail(dataObject); | ||
// case AsyncActionType.RegistryCompanyCreate.toString(): | ||
// return await this.registryClient.createCompany(dataObject); | ||
// case AsyncActionType.CompanyUpdate.toString(): | ||
// return await this.registryClient.CompanyUpdate(dataObject); | ||
// case AsyncActionType.AuthProgramme.toString(): | ||
// return await this.registryClient.authProgramme(dataObject); | ||
// case AsyncActionType.IssueCredit.toString(): | ||
// return await this.registryClient.issueCredit(dataObject); | ||
// case AsyncActionType.RejectProgramme.toString(): | ||
// return await this.registryClient.rejectProgramme(dataObject); | ||
|
||
// case AsyncActionType.ProgrammeCreate.toString(): | ||
// return this.registryClient.createProgramme(dataObject); | ||
// case AsyncActionType.ProgrammeAccept.toString(): | ||
// return this.registryClient.programmeAccept(dataObject); | ||
// case AsyncActionType.DocumentUpload.toString(): | ||
// return this.registryClient.addDocument(dataObject); | ||
// case AsyncActionType.OwnershipUpdate.toString(): | ||
// return this.registryClient.updateOwnership(dataObject); | ||
// case AsyncActionType.AddMitigation.toString(): | ||
// return this.registryClient.addMitigation(dataObject); | ||
// case AsyncActionType.NationalInvestment.toString(): | ||
// return this.registryClient.addNationalInvestment(dataObject) | ||
|
||
// case AsyncActionType.CADTProgrammeCreate.toString(): | ||
// return this.cadtService.createProgramme(dataObject) | ||
// case AsyncActionType.CADTUpdateProgramme.toString(): | ||
// return this.cadtService.updateProgramme(dataObject.programme); | ||
// case AsyncActionType.CADTCreditIssue.toString(): | ||
// return this.cadtService.issueCredit(dataObject.programme, dataObject.amount); | ||
// case AsyncActionType.CADTTransferCredit.toString(): | ||
// return this.cadtService.transferCredit(dataObject.programme, dataObject.transfer); | ||
|
||
} | ||
} | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
backend/services/src/async-operations-handler/async-operations-queue-handler.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { Injectable, Logger } from "@nestjs/common"; | ||
import { AsyncOperationsHandlerInterface } from "./async-operations-handler-interface.service"; | ||
import { SQSEvent } from "aws-lambda"; | ||
import { AsyncOperationsHandlerService } from "./async-operations-handler.service"; | ||
import { AsyncActionType } from "../enums/async.action.type.enum"; | ||
|
||
type Response = { batchItemFailures: { itemIdentifier: string }[] }; | ||
|
||
@Injectable() | ||
export class AsyncOperationsQueueHandlerService | ||
implements AsyncOperationsHandlerInterface | ||
{ | ||
constructor( | ||
private asyncOperationsHandlerService: AsyncOperationsHandlerService, | ||
private logger: Logger | ||
) {} | ||
|
||
async asyncHandler(event: SQSEvent): Promise<Response> { | ||
this.logger.log("Queue asyncHandler started"); | ||
const response: Response = { batchItemFailures: [] }; | ||
|
||
for (const record of event.Records) { | ||
const actionType = record.messageAttributes?.actionType?.stringValue; | ||
try { | ||
|
||
await this.asyncOperationsHandlerService.handler( | ||
actionType, | ||
JSON.parse(record.body) | ||
); | ||
} catch (exception) { | ||
this.logger.log("queue asyncHandler failed", exception); | ||
if (actionType?.toString() === AsyncActionType.Email.toString()) { | ||
response.batchItemFailures.push({ itemIdentifier: record.messageId }); | ||
} else { | ||
throw exception; | ||
} | ||
} | ||
} | ||
return response; | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
backend/services/src/async-operations-handler/async-operations.module.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { Logger, Module } from "@nestjs/common"; | ||
import { ConfigModule } from "@nestjs/config"; | ||
import { TypeOrmModule } from "@nestjs/typeorm"; | ||
import { AsyncOperationsDatabaseHandlerService } from "./async-operations-database-handler.service"; | ||
import { AsyncOperationsHandlerInterface } from "./async-operations-handler-interface.service"; | ||
import { AsyncOperationsQueueHandlerService } from "./async-operations-queue-handler.service"; | ||
import { AsyncOperationsHandlerService } from "./async-operations-handler.service"; | ||
import configuration from "../configuration"; | ||
import { EmailModule } from "../email/email.module"; | ||
import { AsyncActionEntity } from "../entities/async.action.entity"; | ||
import { Counter } from "../entities/counter.entity"; | ||
import { AsyncOperationType } from "../enums/async.operation.type.enum"; | ||
import { TypeOrmConfigService } from "../typeorm.config.service"; | ||
|
||
|
||
@Module({ | ||
imports: [ | ||
ConfigModule.forRoot({ | ||
isGlobal: true, | ||
load: [configuration], | ||
envFilePath: [`.env.${process.env.NODE_ENV}`, `.env`], | ||
}), | ||
TypeOrmModule.forRootAsync({ | ||
useClass: TypeOrmConfigService, | ||
}), | ||
TypeOrmModule.forFeature([AsyncActionEntity, Counter]), | ||
// RegistryClientModule, | ||
EmailModule, | ||
// CadtModule, | ||
], | ||
providers: [ | ||
{ | ||
provide: AsyncOperationsHandlerInterface, | ||
useClass: | ||
process.env.ASYNC_OPERATIONS_TYPE === AsyncOperationType.Queue | ||
? AsyncOperationsQueueHandlerService | ||
: AsyncOperationsDatabaseHandlerService, | ||
}, | ||
Logger, | ||
AsyncOperationsHandlerService, | ||
], | ||
}) | ||
export class AsyncOperationsModuleMain {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
68 changes: 68 additions & 0 deletions
68
backend/services/src/async-operations/async-operations-database.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import { HttpException, HttpStatus, Injectable, Logger } from "@nestjs/common"; | ||
import { ConfigService } from "@nestjs/config"; | ||
import { InjectRepository } from "@nestjs/typeorm"; | ||
import { Repository } from "typeorm"; | ||
import { AsyncActionEntity } from "../entities/async.action.entity"; | ||
import { AsyncActionType } from "../enums/async.action.type.enum"; | ||
import { HelperService } from "../util/helpers.service"; | ||
import { | ||
AsyncAction, | ||
AsyncOperationsInterface, | ||
} from "./async-operations.interface"; | ||
|
||
@Injectable() | ||
export class AsyncOperationsDatabaseService | ||
implements AsyncOperationsInterface | ||
{ | ||
private emailDisabled: boolean; | ||
|
||
constructor( | ||
private configService: ConfigService, | ||
private logger: Logger, | ||
@InjectRepository(AsyncActionEntity) | ||
private asyncActionRepo: Repository<AsyncActionEntity>, | ||
private helperService: HelperService | ||
) { | ||
this.emailDisabled = this.configService.get<boolean>("email.disabled"); | ||
} | ||
|
||
public tx:AsyncAction[]=[] | ||
|
||
public async flushTx(): Promise<boolean>{ | ||
for (var action of this.tx){ | ||
//execute action | ||
} | ||
this.tx=[] | ||
return true | ||
} | ||
|
||
public async AddAction(action: AsyncAction): Promise<boolean> { | ||
|
||
if ([AsyncActionType.DocumentUpload, AsyncActionType.IssueCredit, AsyncActionType.RegistryCompanyCreate, AsyncActionType.RejectProgramme,AsyncActionType.AddMitigation,AsyncActionType.ProgrammeAccept,AsyncActionType.ProgrammeCreate,AsyncActionType.OwnershipUpdate, AsyncActionType.CompanyUpdate].includes(action.actionType) && !this.configService.get("registry.syncEnable")) { | ||
this.logger.log(`Dropping sync event ${action.actionType} due to sync disabled`) | ||
return false; | ||
} | ||
|
||
if (action.actionType === AsyncActionType.Email) { | ||
if (this.emailDisabled) return false; | ||
} | ||
|
||
let asyncActionEntity: AsyncActionEntity = {} as AsyncActionEntity; | ||
asyncActionEntity.actionType = action.actionType; | ||
asyncActionEntity.actionProps = JSON.stringify(action.actionProps); | ||
await this.asyncActionRepo.save(asyncActionEntity).catch((err: any) => { | ||
this.logger.error("error", err); | ||
throw new HttpException( | ||
this.helperService.formatReqMessagesString( | ||
"common.addAsyncActionDatabaseFailed", | ||
["Email"] | ||
), | ||
HttpStatus.INTERNAL_SERVER_ERROR | ||
); | ||
}); | ||
|
||
this.logger.log("Successfully added to the AsyncAction table", action); | ||
|
||
return true; | ||
} | ||
} |
93 changes: 93 additions & 0 deletions
93
backend/services/src/async-operations/async-operations-queue.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import { HttpException, HttpStatus, Injectable, Logger } from "@nestjs/common"; | ||
import { ConfigService } from "@nestjs/config"; | ||
import { AsyncActionType } from "../enums/async.action.type.enum"; | ||
import { HelperService } from "../util/helpers.service"; | ||
import { | ||
AsyncAction, | ||
AsyncOperationsInterface, | ||
} from "./async-operations.interface"; | ||
|
||
import { SQSClient, SendMessageCommand } from "@aws-sdk/client-sqs" | ||
|
||
@Injectable() | ||
export class AsyncOperationsQueueService implements AsyncOperationsInterface { | ||
private emailDisabled: boolean; | ||
private sqs = new SQSClient({}); | ||
|
||
constructor( | ||
private configService: ConfigService, | ||
private logger: Logger, | ||
private helperService: HelperService | ||
) { | ||
this.emailDisabled = this.configService.get<boolean>("email.disabled"); | ||
} | ||
public tx:AsyncAction[]=[] | ||
|
||
public async flushTx(): Promise<boolean>{ | ||
for (var action of this.tx){ | ||
//execute action | ||
} | ||
this.tx=[] | ||
return true | ||
} | ||
|
||
public async AddAction(action: AsyncAction): Promise<boolean> { | ||
// var params = {}; | ||
|
||
if ([AsyncActionType.DocumentUpload, AsyncActionType.IssueCredit, AsyncActionType.RegistryCompanyCreate, AsyncActionType.RejectProgramme,AsyncActionType.AddMitigation,AsyncActionType.ProgrammeAccept,AsyncActionType.ProgrammeCreate,AsyncActionType.OwnershipUpdate].includes(action.actionType) && !this.configService.get("registry.syncEnable")) { | ||
this.logger.log(`Dropping sync event ${action.actionType} due to sync disabled`) | ||
return false; | ||
} | ||
|
||
if (action.actionType === AsyncActionType.Email) { | ||
if (this.emailDisabled) { | ||
return false; | ||
} | ||
} | ||
|
||
if ([AsyncActionType.CADTProgrammeCreate, AsyncActionType.CADTCertify, AsyncActionType.CADTCreditIssue, AsyncActionType.CADTTransferCredit, AsyncActionType.CADTUpdateProgramme].includes(action.actionType) && !this.configService.get('cadTrust.enable')) { | ||
return false; | ||
} | ||
|
||
// params = { | ||
// MessageAttributes: { | ||
// actionType: { | ||
// DataType: "Number", | ||
// StringValue: action.actionType.toString(), | ||
// }, | ||
// }, | ||
// MessageBody: JSON.stringify(action.actionProps), | ||
// MessageGroupId: action.actionType.toString() + new Date().getTime(), | ||
// QueueUrl: this.configService.get("asyncQueueName"), | ||
// }; | ||
|
||
const params = new SendMessageCommand({ | ||
QueueUrl: this.configService.get("asyncQueueName"), | ||
MessageAttributes: { | ||
actionType: { | ||
DataType: "Number", | ||
StringValue: action.actionType.toString(), | ||
}, | ||
}, | ||
MessageBody: JSON.stringify(action.actionProps), | ||
MessageGroupId: this.configService.get("systemType") | ||
}); | ||
|
||
try { | ||
await this.sqs.send(params); | ||
this.logger.log("Successfully added to the queue", action.actionType); | ||
} catch (error) { | ||
this.logger.error("Failed when adding to queue", action.actionType); | ||
this.logger.error("Error",error) | ||
throw new HttpException( | ||
this.helperService.formatReqMessagesString( | ||
"common.addAsyncActionQueueFailed", | ||
["Email"] | ||
), | ||
HttpStatus.INTERNAL_SERVER_ERROR | ||
); | ||
} | ||
|
||
return true; | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
backend/services/src/async-operations/async-operations.interface.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { Injectable } from "@nestjs/common"; | ||
|
||
export interface AsyncAction { | ||
actionType: number; | ||
actionProps: any; | ||
} | ||
|
||
@Injectable() | ||
export abstract class AsyncOperationsInterface { | ||
public abstract tx:AsyncAction[] | ||
public abstract flushTx(): Promise<boolean>; | ||
public abstract AddAction(action: AsyncAction): Promise<boolean>; | ||
} |
38 changes: 38 additions & 0 deletions
38
backend/services/src/async-operations/async-operations.module.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { forwardRef, Logger, Module } from "@nestjs/common"; | ||
import { AsyncOperationsInterface } from "./async-operations.interface"; | ||
import { AsyncOperationsQueueService } from "./async-operations-queue.service"; | ||
import configuration from "../configuration"; | ||
import { ConfigModule } from "@nestjs/config"; | ||
import { AsyncOperationType } from "../enums/async.operation.type.enum"; | ||
import { AsyncOperationsDatabaseService } from "./async-operations-database.service"; | ||
import { TypeOrmModule } from "@nestjs/typeorm"; | ||
import { AsyncActionEntity } from "../entities/async.action.entity"; | ||
import { TypeOrmConfigService } from "../typeorm.config.service"; | ||
import { UtilModule } from "../util/util.module"; | ||
|
||
@Module({ | ||
exports: [AsyncOperationsInterface], | ||
imports: [ | ||
ConfigModule.forRoot({ | ||
isGlobal: true, | ||
load: [configuration], | ||
envFilePath: [`.env.${process.env.NODE_ENV}`, `.env`], | ||
}), | ||
TypeOrmModule.forRootAsync({ | ||
useClass: TypeOrmConfigService, | ||
}), | ||
TypeOrmModule.forFeature([AsyncActionEntity]), | ||
forwardRef(() => UtilModule) | ||
], | ||
providers: [ | ||
Logger, | ||
{ | ||
provide: AsyncOperationsInterface, | ||
useClass: | ||
process.env.ASYNC_OPERATIONS_TYPE === AsyncOperationType.Queue | ||
? AsyncOperationsQueueService | ||
: AsyncOperationsDatabaseService, | ||
} | ||
] | ||
}) | ||
export class AsyncOperationsModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { Logger, Module } from "@nestjs/common"; | ||
import { ConfigService } from "@nestjs/config"; | ||
import { JwtModule } from "@nestjs/jwt"; | ||
import { PassportModule } from "@nestjs/passport"; | ||
import { AuthService } from "./auth.service"; | ||
import { JwtStrategy } from "./strategies/jwt.strategy"; | ||
import { LocalStrategy } from "./strategies/local.strategy"; | ||
import { CaslModule } from "../casl/casl.module"; | ||
import { ApiKeyStrategy } from "./strategies/apikey.strategy"; | ||
import { UserModule } from "../user/user.module"; | ||
import { UtilModule } from "../util/util.module"; | ||
import { AsyncOperationsModule } from "../async-operations/async-operations.module"; | ||
import { PasswordReset } from "../entities/userPasswordResetToken.entity"; | ||
|
||
|
||
@Module({ | ||
imports: [ | ||
UserModule, | ||
PassportModule, | ||
UtilModule, | ||
JwtModule.registerAsync({ | ||
useFactory: async (configService: ConfigService) => ({ | ||
secretOrPrivateKey: configService.get<string>("jwt.userSecret"), | ||
signOptions: { | ||
expiresIn: parseInt(configService.get<string>("jwt.expiresIn")), | ||
}, | ||
}), | ||
inject: [ConfigService], | ||
imports: undefined, | ||
}), | ||
CaslModule, | ||
AsyncOperationsModule, | ||
], | ||
providers: [ | ||
AuthService, | ||
LocalStrategy, | ||
JwtStrategy, | ||
ApiKeyStrategy, | ||
Logger, | ||
PasswordReset, | ||
], | ||
exports: [AuthService], | ||
}) | ||
export class AuthModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { AuthService } from './auth.service'; | ||
|
||
describe('AuthService', () => { | ||
let service: AuthService; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
providers: [AuthService], | ||
}).compile(); | ||
|
||
service = module.get<AuthService>(AuthService); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(service).toBeDefined(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
import { HttpException, HttpStatus, Injectable } from "@nestjs/common"; | ||
import { JwtService } from "@nestjs/jwt"; | ||
import { instanceToPlain } from "class-transformer"; | ||
import { CaslAbilityFactory } from "../casl/casl-ability.factory"; | ||
import { UserService } from "../user/user.service"; | ||
import { ConfigService } from "@nestjs/config"; | ||
import { PasswordResetService } from "../util/passwordReset.service"; | ||
import { AsyncAction, AsyncOperationsInterface } from "../async-operations/async-operations.interface"; | ||
import { PasswordHashService } from "../util/passwordHash.service"; | ||
import { AsyncActionType } from "../enums/async.action.type.enum"; | ||
import { BasicResponseDto } from "../dtos/basic.response.dto"; | ||
import { HelperService } from "../util/helpers.service"; | ||
import { JWTPayload } from "../dtos/jwt.payload"; | ||
import { EmailTemplates } from "../email-helper/email.template"; | ||
import { API_KEY_SEPARATOR } from "../constants"; | ||
import { UserState } from "../enums/user.enum"; | ||
import { User } from "../entities/user.entity"; | ||
|
||
@Injectable() | ||
export class AuthService { | ||
constructor( | ||
private readonly userService: UserService, | ||
private readonly jwtService: JwtService, | ||
private configService: ConfigService, | ||
private helperService: HelperService, | ||
private passwordReset: PasswordResetService, | ||
public caslAbilityFactory: CaslAbilityFactory, | ||
private asyncOperationsInterface: AsyncOperationsInterface, | ||
private passwordHashService: PasswordHashService | ||
) {} | ||
|
||
async validateUser(username: string, pass: string): Promise<any> { | ||
let validationResponse : {user: Omit<User, 'password'> | null; message: string}; | ||
|
||
pass = this.passwordHashService.getPasswordHash(pass); | ||
const user = await this.userService.getUserCredentials(username?.toLowerCase()); | ||
|
||
if (user) { | ||
if (user.state == UserState.ACTIVE) { | ||
if (user.password === pass) { | ||
const { password, ...result } = user; | ||
validationResponse = { user: result, message: 'Validated'}; | ||
} else { | ||
validationResponse = {user : null, message: "incorrectPassword"} | ||
} | ||
} else { | ||
validationResponse = {user : null, message: "accountDeactivated"} | ||
} | ||
} else { | ||
validationResponse = {user : null, message: "invalidUsername"} | ||
} | ||
|
||
return validationResponse; | ||
} | ||
|
||
async validateApiKey(apiKey: string): Promise<any> { | ||
const parts = Buffer.from(apiKey, "base64") | ||
.toString("utf-8") | ||
.split(API_KEY_SEPARATOR); | ||
if (parts.length != 2) { | ||
return null; | ||
} | ||
const user = await this.userService.getUserCredentials(parts[0]?.toLowerCase()); | ||
if (user && user.apiKey === apiKey) { | ||
const { password, apiKey, ...result } = user; | ||
return result; | ||
} | ||
return null; | ||
} | ||
|
||
async login(user: any) { | ||
const payload = new JWTPayload( | ||
user.organisation, | ||
user.name, | ||
user.id, | ||
user.role, | ||
user.subRole, | ||
user.sector, | ||
user.email, | ||
user.validatePermission, | ||
user.subRolePermission, | ||
user.ghgInventoryPermission, | ||
); | ||
const ability = this.caslAbilityFactory.createForUser(user); | ||
return { | ||
access_token: this.jwtService.sign(instanceToPlain(payload)), | ||
role: user.role, | ||
subRole: user.subRole, | ||
id: user.id, | ||
name: user.name, | ||
company: user.organisation, | ||
ability: JSON.stringify(ability), | ||
sector: user.sector, | ||
userState: user.state, | ||
validatePermission: user.validatePermission, | ||
subRolePermission: user.subRolePermission, | ||
ghgInventoryPermission: user.ghgInventoryPermission, | ||
}; | ||
} | ||
|
||
async forgotPassword(email: any) { | ||
const hostAddress = this.configService.get("host"); | ||
const userDetails = await this.userService.findOne(email); | ||
if (userDetails && userDetails.state == UserState.ACTIVE) { | ||
console.table(userDetails); | ||
const requestId = this.helperService.generateRandomPassword(); | ||
const date = Date.now(); | ||
const expireDate = date + 3600 * 1000; // 1 hout expire time | ||
const passwordResetD = { | ||
email: email, | ||
token: requestId, | ||
expireTime: expireDate, | ||
}; | ||
await this.passwordReset.deletePasswordResetD(email); | ||
await this.passwordReset.insertPasswordResetD(passwordResetD); | ||
|
||
const templateData = { | ||
name: userDetails.name, | ||
requestId: requestId, | ||
home: hostAddress, | ||
countryName: this.configService.get("systemCountryName"), | ||
}; | ||
|
||
const action: AsyncAction = { | ||
actionType: AsyncActionType.Email, | ||
actionProps: { | ||
emailType: EmailTemplates.FORGOT_PASSOWRD.id, | ||
sender: email, | ||
subject: this.helperService.getEmailTemplateMessage( | ||
EmailTemplates.FORGOT_PASSOWRD["subject"], | ||
templateData, | ||
true | ||
), | ||
emailBody: this.helperService.getEmailTemplateMessage( | ||
EmailTemplates.FORGOT_PASSOWRD["html"], | ||
templateData, | ||
false | ||
), | ||
}, | ||
}; | ||
|
||
await this.asyncOperationsInterface.AddAction(action); | ||
|
||
return new BasicResponseDto( | ||
HttpStatus.OK, | ||
this.helperService.formatReqMessagesString("user.resetEmailSent", []) | ||
); | ||
} else { | ||
throw new HttpException( | ||
this.helperService.formatReqMessagesString( | ||
"user.forgotPwdUserNotFound", | ||
[] | ||
), | ||
HttpStatus.NOT_FOUND | ||
); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import { AuthGuard } from '@nestjs/passport'; | ||
|
||
@Injectable() | ||
export class ApiKeyJwtAuthGuard extends AuthGuard(["jwt", "api-key"]) { | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import { AuthGuard } from '@nestjs/passport'; | ||
|
||
@Injectable() | ||
export class JwtAuthGuard extends AuthGuard('jwt') {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import { AuthGuard } from '@nestjs/passport'; | ||
|
||
@Injectable() | ||
export class LocalAuthGuard extends AuthGuard('local') {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { Injectable, Logger, UnauthorizedException } from '@nestjs/common'; | ||
import { ConfigService } from '@nestjs/config'; | ||
import { PassportStrategy } from '@nestjs/passport'; | ||
import { HeaderAPIKeyStrategy } from 'passport-headerapikey'; | ||
import { AuthService } from '../auth.service'; | ||
|
||
@Injectable() | ||
export class ApiKeyStrategy extends PassportStrategy(HeaderAPIKeyStrategy, 'api-key') { | ||
constructor(private authService: AuthService) { | ||
const headerKeyApiKey = 'api_key'; | ||
|
||
super({ header: headerKeyApiKey, prefix: '' }, true, async (apiKey, done) => { | ||
const user = this.authService.validateApiKey(apiKey) | ||
if (user) { | ||
done(null, user); | ||
} | ||
done(new UnauthorizedException(), null); | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { Injectable, Logger, UnauthorizedException } from '@nestjs/common'; | ||
import { ConfigService } from '@nestjs/config'; | ||
import { PassportStrategy } from '@nestjs/passport'; | ||
import { plainToClass } from 'class-transformer'; | ||
import { ExtractJwt, Strategy } from 'passport-jwt'; | ||
import { JWTPayload } from '../../dtos/jwt.payload'; | ||
import { UserState } from 'src/enums/user.enum'; | ||
import { UserService } from 'src/user/user.service'; | ||
|
||
|
||
@Injectable() | ||
export class JwtStrategy extends PassportStrategy(Strategy) { | ||
constructor(private configService: ConfigService, private logger: Logger, private readonly userService: UserService,) { | ||
const secret = configService.get<string>('jwt.userSecret'); | ||
super({ | ||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), | ||
ignoreExpiration: false, | ||
secretOrKey: secret, | ||
}); | ||
} | ||
|
||
async validate(payload: any) { | ||
const jwtPayload: JWTPayload = plainToClass(JWTPayload, payload) | ||
const user = await this.userService.getUserCredentials(jwtPayload.un); | ||
if (user.state !== UserState.ACTIVE) { | ||
throw new UnauthorizedException('user deactivated'); | ||
} | ||
return { id: user.id, companyName: user.organisation, role: user.role, subRole: user.subRole, name: user.name, sector: user.sector, validatePermission: user.validatePermission, subRolePermission:user.subRolePermission, ghgInventoryPermission:user.ghgInventoryPermission }; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { Injectable, UnauthorizedException } from '@nestjs/common'; | ||
import { PassportStrategy } from '@nestjs/passport'; | ||
import { Strategy } from 'passport-local'; | ||
import { AuthService } from '../auth.service'; | ||
import { User } from 'src/entities/user.entity'; | ||
|
||
@Injectable() | ||
export class LocalStrategy extends PassportStrategy(Strategy) { | ||
constructor(private readonly authService: AuthService) { | ||
super(); | ||
} | ||
|
||
async validate(username: string, password: string): Promise<any> { | ||
const validationResponse: {user: Omit<User, 'password'> | null; message: string} = await this.authService.validateUser(username, password); | ||
if (!validationResponse.user) { | ||
throw new UnauthorizedException(); | ||
} | ||
return validationResponse.user; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
export enum Action { | ||
Manage = 'manage', | ||
Create = 'create', | ||
Read = 'read', | ||
Update = 'update', | ||
Delete = 'delete', | ||
Approve = 'approve', | ||
Reject = 'reject', | ||
Validate = 'validate', | ||
ForceResetPassword = 'forceResetPassword', | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { CaslAbilityFactory } from './casl-ability.factory'; | ||
|
||
describe('CaslAbilityFactory', () => { | ||
it('should be defined', () => { | ||
expect(new CaslAbilityFactory()).toBeDefined(); | ||
}); | ||
}); |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { Module } from "@nestjs/common"; | ||
import { CaslAbilityFactory } from "./casl-ability.factory"; | ||
import { UtilModule } from "../util/util.module"; | ||
|
||
@Module({ | ||
imports: [UtilModule], | ||
providers: [CaslAbilityFactory], | ||
exports: [CaslAbilityFactory], | ||
}) | ||
export class CaslModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { SetMetadata } from "@nestjs/common"; | ||
import { PolicyHandler } from "./policy.handler"; | ||
|
||
export const CHECK_POLICIES_KEY = 'check_policy'; | ||
export const CheckPolicies = (...handlers: PolicyHandler[]) => | ||
SetMetadata(CHECK_POLICIES_KEY, handlers); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
import { | ||
CanActivate, | ||
ExecutionContext, | ||
ForbiddenException, | ||
Injectable, | ||
mixin, | ||
} from "@nestjs/common"; | ||
import { Reflector } from "@nestjs/core"; | ||
import { plainToClass } from "class-transformer"; | ||
import { Stat } from "../dtos/stat.dto"; | ||
import { EntitySubject } from "../entities/entity.subject"; | ||
import { User } from "../entities/user.entity"; | ||
import { Action } from "./action.enum"; | ||
import { CaslAbilityFactory, AppAbility } from "./casl-ability.factory"; | ||
import { CHECK_POLICIES_KEY } from "./policy.decorator"; | ||
import { PolicyHandler } from "./policy.handler"; | ||
import { HelperService } from "../util/helpers.service"; | ||
const { rulesToQuery } = require("@casl/ability/extra"); | ||
|
||
@Injectable() | ||
export class PoliciesGuard implements CanActivate { | ||
constructor( | ||
public reflector: Reflector, | ||
public caslAbilityFactory: CaslAbilityFactory, | ||
public helperService: HelperService | ||
) {} | ||
|
||
async canActivate(context: ExecutionContext): Promise<boolean> { | ||
const policyHandlers = | ||
this.reflector.get<PolicyHandler[]>( | ||
CHECK_POLICIES_KEY, | ||
context.getHandler() | ||
) || []; | ||
|
||
const { user, body } = context.switchToHttp().getRequest(); | ||
const ability = this.caslAbilityFactory.createForUser(user); | ||
const pHandlers = policyHandlers.every((handler) => | ||
this.execPolicyHandler(handler, ability, body) | ||
); | ||
if (pHandlers) { | ||
return pHandlers; | ||
} else { | ||
throw new ForbiddenException( | ||
this.helperService.formatReqMessagesString("user.userUnAUth", []) | ||
); | ||
} | ||
} | ||
|
||
public execPolicyHandler( | ||
handler: PolicyHandler, | ||
ability: AppAbility, | ||
body: any | ||
) { | ||
if (typeof handler === "function") { | ||
return handler(ability, body); | ||
} | ||
return handler.handle(ability, body); | ||
} | ||
} | ||
|
||
export const PoliciesGuardEx = ( | ||
injectQuery: boolean, | ||
action?: Action, | ||
subject?: typeof EntitySubject, | ||
onlyInject?: boolean, | ||
dropArrayFields?: boolean | ||
) => { | ||
@Injectable() | ||
class PoliciesGuardMixin implements CanActivate { | ||
constructor( | ||
public reflector: Reflector, | ||
public caslAbilityFactory: CaslAbilityFactory, | ||
public helperService: HelperService | ||
) {} | ||
|
||
parseMongoQueryToSQL(mongoQuery, isNot = false, key = undefined) { | ||
let final = undefined; | ||
for (let operator in mongoQuery) { | ||
if (operator.startsWith("$")) { | ||
if (operator == "$and" || operator == "$or") { | ||
const val = mongoQuery[operator].map(st => this.parseMongoQueryToSQL(st)).join(` ${operator.replace("$", '')} `) | ||
final = final == undefined ? val : `${final} and ${val}` | ||
} else if (operator == "$not") { | ||
return this.parseMongoQueryToSQL(mongoQuery["$not"], !isNot) | ||
} else if (operator == "$eq") { | ||
const value = (typeof mongoQuery["$eq"] === "number") ? String(mongoQuery["$eq"]) : `'${mongoQuery["$eq"]}'` | ||
return `"${key}" ${isNot ? "!=" : "="} ${value}` | ||
} else if (operator == "$ne") { | ||
const value = (typeof mongoQuery["$ne"] === "number") ? String(mongoQuery["$ne"]) : `'${mongoQuery["$ne"]}'` | ||
return `"${key}" ${isNot ? "=" : "!="} ${value}` | ||
} | ||
} else { | ||
return this.parseMongoQueryToSQL(mongoQuery[operator], isNot, operator) | ||
} | ||
} | ||
return final; | ||
} | ||
|
||
async canActivate(context: ExecutionContext): Promise<boolean> { | ||
const policyHandlers = | ||
this.reflector.get<PolicyHandler[]>( | ||
CHECK_POLICIES_KEY, | ||
context.getHandler() | ||
) || []; | ||
|
||
const { user, body } = context.switchToHttp().getRequest(); | ||
const ability = this.caslAbilityFactory.createForUser(user); | ||
|
||
if (injectQuery) { | ||
context.switchToHttp().getRequest()['ability'] = ability; | ||
|
||
const mongoQuery = JSON.stringify(rulesToQuery(ability, action, subject, rule => { | ||
return rule.inverted ? { $not: rule.conditions } : rule.conditions | ||
})) | ||
console.log(JSON.stringify(mongoQuery)) | ||
|
||
if (mongoQuery && mongoQuery != "" && mongoQuery != "{}" && mongoQuery != '{"$or":[{}]}') { | ||
// const whereQuery = this.parseMongoQueryToSQL(JSON.parse(mongoQuery)); | ||
// console.log("Where", whereQuery) | ||
context.switchToHttp().getRequest()['abilityCondition'] = JSON.parse(mongoQuery); | ||
} | ||
} | ||
|
||
if (dropArrayFields) { | ||
const obj = Object.assign(new subject(), body); | ||
let abilityCan: boolean = true; | ||
for (const key in obj) { | ||
const possible = []; | ||
if (obj[key] instanceof Array) { | ||
// console.log(obj[key]); | ||
for (const en of obj[key]) { | ||
for (const key2 in en) { | ||
// console.log(action, en, key2); | ||
if (ability.can(action, plainToClass(Stat, en), key2)) { | ||
possible.push(en); | ||
} | ||
} | ||
} | ||
obj[key] = possible; | ||
context.switchToHttp().getRequest()["body"] = obj; | ||
abilityCan = possible.length > 0; | ||
if (abilityCan) { | ||
return abilityCan; | ||
} else { | ||
throw new ForbiddenException( | ||
this.helperService.formatReqMessagesString( | ||
"user.userUnAUth", | ||
[] | ||
) | ||
); | ||
} | ||
} | ||
} | ||
} | ||
|
||
if (policyHandlers.length == 0 && action && subject && !onlyInject) { | ||
const obj = Object.assign(new subject(), body); | ||
let abilityCan: boolean = true; | ||
// console.log(obj); | ||
if (action == Action.Update) { | ||
// if (obj instanceof User && obj.organisationId == undefined) { | ||
// obj.organisationId = user.companyId; | ||
// } | ||
for (const key in obj) { | ||
if (!ability.can(action, obj, key)) { | ||
console.log( | ||
"Failed due to", | ||
JSON.stringify(ability), | ||
action, | ||
obj, | ||
key | ||
); | ||
abilityCan = false; | ||
} | ||
} | ||
} else if (action == Action.Delete) { | ||
abilityCan = ability.can(action, subject); | ||
} else { | ||
abilityCan = ability.can(action, obj); | ||
} | ||
if (abilityCan) { | ||
return abilityCan; | ||
} else { | ||
throw new ForbiddenException( | ||
this.helperService.formatReqMessagesString("user.userUnAUth", []) | ||
); | ||
} | ||
} | ||
|
||
const pHandler = policyHandlers.every((handler) => | ||
this.execPolicyHandler(handler, ability, body) | ||
); | ||
if (pHandler) { | ||
return pHandler; | ||
} else { | ||
throw new ForbiddenException( | ||
this.helperService.formatReqMessagesString("user.userUnAUth", []) | ||
); | ||
} | ||
} | ||
|
||
public execPolicyHandler( | ||
handler: PolicyHandler, | ||
ability: AppAbility, | ||
body: any | ||
) { | ||
if (typeof handler === "function") { | ||
return handler(ability, body); | ||
} | ||
return handler.handle(ability, body); | ||
} | ||
} | ||
|
||
const guard = mixin(PoliciesGuardMixin); | ||
return guard; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { AppAbility } from './casl-ability.factory'; | ||
|
||
interface IPolicyHandler { | ||
handle(ability: AppAbility, req: any): boolean; | ||
} | ||
|
||
type PolicyHandlerCallback = (ability: AppAbility, req: any) => boolean; | ||
|
||
export type PolicyHandler = IPolicyHandler | PolicyHandlerCallback; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
export enum Role { | ||
Observer = 'Observer', | ||
GovernmentUser = 'GovernmentUser', | ||
Admin = 'Admin', | ||
Root = 'Root', | ||
} | ||
|
||
export enum SubRole { | ||
GovernmentDepartment = 'GovernmentDepartment', | ||
Consultant = 'Consultant', | ||
SEO = 'SEO', | ||
TechnicalReviewer = 'TechnicalReviewer', | ||
DevelopmentPartner = 'DevelopmentPartner' | ||
} | ||
|
||
export const roleSubRoleMap = { | ||
'GovernmentUser': ['GovernmentDepartment', 'Consultant', 'SEO'], | ||
'Observer': ['TechnicalReviewer', 'DevelopmentPartner'] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
export const sectoralScopesMapped: any = { | ||
Energy: [ | ||
'Energy Industries (Renewable – / Non-Renewable Sources)', | ||
'Energy Distribution', | ||
'Energy Demand', | ||
], | ||
Transport: ['Transport'], | ||
Manufacturing: ['Manufacturing Industries', 'Chemical Industries', 'Metal Production'], | ||
Forestry: ['Afforestation and Reforestation'], | ||
Waste: ['Waste Handling and Disposal', 'Fugitive Emissions From Fuels (Solid, Oil and Gas)'], | ||
Agriculture: ['Agriculture'], | ||
Other: [ | ||
'Mining/Mineral Production', | ||
'Construction', | ||
'Fugitive Emissions From Production and Consumption of Halocarbons and Sulphur Hexafluoride', | ||
'Solvent Use', | ||
], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
export default () => ({ | ||
stage: process.env.STAGE || "local", | ||
systemCountry: process.env.systemCountryCode || "NG", | ||
systemCountryName: process.env.systemCountryName || "CountryX", | ||
systemCountryGovernmentName: process.env.systemCountryGovernmentName || "Government of CountryX", | ||
systemContinentName: process.env.systemContinentName || "CountryX", | ||
defaultCreditUnit: process.env.defaultCreditUnit || "ITMO", | ||
year: parseInt(process.env.REPORT_YEAR), | ||
dateTimeFormat: "DD LLLL yyyy @ HH:mm", | ||
dateFormat: "DD LLLL yyyy", | ||
database: { | ||
type: "postgres", | ||
host: process.env.DB_HOST || "localhost", | ||
port: parseInt(process.env.DB_PORT) || 5432, | ||
username: process.env.DB_USER || "hquser", | ||
password: process.env.DB_PASSWORD || "", | ||
database: process.env.DB_NAME || "carbondev", | ||
synchronize: process.env.NODE_ENV == "prod" ? true : true, | ||
autoLoadEntities: true, | ||
logging: ["error"], | ||
}, | ||
jwt: { | ||
expiresIn: process.env.EXPIRES_IN || "7200", | ||
userSecret: process.env.USER_JWT_SECRET || "1324", | ||
adminSecret: process.env.ADMIN_JWT_SECRET || "8654", | ||
encodePassword: process.env.ENCODE_PASSWORD || false | ||
}, | ||
ledger: { | ||
name: "carbon-registry-" + (process.env.NODE_ENV || "dev"), | ||
table: "programmes", | ||
overallTable: "overall", | ||
companyTable: "company", | ||
}, | ||
email: { | ||
source: process.env.SOURCE_EMAIL || "info@xeptagon.com", | ||
endpoint: | ||
process.env.SMTP_ENDPOINT || | ||
"vpce-02cef9e74f152b675-b00ybiai.email-smtp.us-east-1.vpce.amazonaws.com", | ||
username: process.env.SMTP_USERNAME || "AKIAUMXKTXDJIOFY2QXL", | ||
password: process.env.SMTP_PASSWORD, | ||
disabled: process.env.IS_EMAIL_DISABLED === "true" ? true : false, | ||
disableLowPriorityEmails: | ||
process.env.DISABLE_LOW_PRIORITY_EMAIL === "true" ? true : false, | ||
// getemailprefix: process.env.EMAILPREFIX || "🏬📐 🇦🇶", | ||
getemailprefix: process.env.EMAILPREFIX || "", | ||
adresss: process.env.HOST_ADDRESS || "Address <br>Region, Country Zipcode" | ||
}, | ||
s3CommonBucket: { | ||
name: process.env.S3_COMMON_BUCKET || "carbon-common-dev", | ||
}, | ||
host: process.env.HOST || "https://test.carbreg.org", | ||
backendHost: process.env.BACKEND_HOST || "http://localhost:3000", | ||
liveChat: "https://undp2020cdo.typeform.com/to/emSWOmDo", | ||
mapbox: { | ||
key: process.env.MAPBOX_PK, | ||
}, | ||
openstreet: { | ||
retrieve: process.env.OPENSTREET_QUERY === "true" || false, | ||
}, | ||
asyncQueueName: | ||
process.env.ASYNC_QUEUE_NAME || | ||
"https://sqs.us-east-1.amazonaws.com/302213478610/AsyncQueuedev.fifo", | ||
ITMOSystem: { | ||
endpoint: | ||
process.env.ITMO_ENDPOINT || | ||
"https://dev-digital-carbon-finance-webapp-api-rxloyxnj3dbso.azurewebsites.net/api/v1/", | ||
apiKey: process.env.ITMO_API_KEY, | ||
email: process.env.ITMO_EMAIL, | ||
password: process.env.ITMO_PASSWORD, | ||
enable: process.env.ITMO_ENABLE === "true" ? true : false, | ||
}, | ||
CERTIFIER:{ | ||
image:process.env.CERTIFIER_IMAGE | ||
}, | ||
registry: { | ||
syncEnable: process.env.SYNC_ENABLE === "true" ? true : false, | ||
endpoint: process.env.SYNC_ENDPOINT || 'https://u4h9swxm8b.execute-api.us-east-1.amazonaws.com/dev', | ||
apiToken: process.env.SYNC_API_TOKEN | ||
}, | ||
docGenerate: { | ||
ministerName: process.env.MINISTER_NAME || 'Minister X', | ||
ministerNameAndDesignation: process.env.MINISTER_NAME_AND_DESIGNATION || '\nHonorable Minister X\nMinister\nMinistry of Environment, Forestry & Tourism', | ||
ministryName: "Ministry of Environment, Forestry & Tourism", | ||
countryCapital: process.env.COUNTRY_CAPITAL || "Capital X", | ||
contactEmailForQuestions: process.env.CONTACT_EMAIL || "contactus@email.com" | ||
}, | ||
cadTrust: { | ||
enable: process.env.CADTRUST_ENABLE === "true" ? true : false, | ||
endpoint: process.env.CADTRUST_ENDPOINT || "http://44.212.139.61:31310/" | ||
}, | ||
systemType: process.env.SYSTEM_TYPE || "CARBON_UNIFIED_SYSTEM", | ||
systemName: process.env.SYSTEM_NAME || "SystemX", | ||
environmentalManagementActHyperlink: process.env.ENVIRONMENTAL_MANAGEMENT_ACT_HYPERLINK || "", | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export const API_KEY_SEPARATOR = '___' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,13 @@ | ||
// lambda.ts | ||
import { Handler, Context } from 'aws-lambda'; | ||
import { NestFactory } from '@nestjs/core'; | ||
import { getLogger } from '@undp/carbon-services-lib'; | ||
import { DataImporterModule } from '@undp/carbon-services-lib'; | ||
import { DataImporterService } from '@undp/carbon-services-lib'; | ||
// // lambda.ts | ||
// import { Handler, Context } from 'aws-lambda'; | ||
// import { NestFactory } from '@nestjs/core'; | ||
// import { getLogger } from '@undp/carbon-services-lib'; | ||
// import { DataImporterModule } from '@undp/carbon-services-lib'; | ||
// import { DataImporterService } from '@undp/carbon-services-lib'; | ||
|
||
export const handler: Handler = async (event: any, context: Context) => { | ||
const app = await NestFactory.createApplicationContext(DataImporterModule, { | ||
logger: getLogger(DataImporterModule), | ||
}); | ||
await app.get(DataImporterService).importData(event); | ||
} | ||
// export const handler: Handler = async (event: any, context: Context) => { | ||
// const app = await NestFactory.createApplicationContext(DataImporterModule, { | ||
// logger: getLogger(DataImporterModule), | ||
// }); | ||
// await app.get(DataImporterService).importData(event); | ||
// } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { ApiProperty, getSchemaPath } from "@nestjs/swagger"; | ||
import { ArrayMinSize, IsNotEmpty, IsNumber, IsString } from "class-validator"; | ||
import { IsTwoDecimalPoints } from "../util/twoDecimalPointNumber.decorator"; | ||
|
||
export class AchievementDto { | ||
|
||
@IsNotEmpty() | ||
@IsNumber() | ||
@ApiProperty() | ||
kpiId: number; | ||
|
||
@IsNotEmpty() | ||
@IsString() | ||
@ApiProperty() | ||
activityId: string; | ||
|
||
@IsTwoDecimalPoints() | ||
@IsNotEmpty() | ||
@IsNumber() | ||
@ApiProperty() | ||
achieved: number; | ||
} | ||
|
||
export class AchievementDtoList { | ||
|
||
@ArrayMinSize(1) | ||
@IsNotEmpty({ each: true }) | ||
@ApiProperty({ | ||
type: "array", | ||
example: [{ | ||
kpiId: "1", | ||
activityId: "T00001", | ||
achieved: 100 | ||
}], | ||
items: { | ||
$ref: getSchemaPath(AchievementDto), | ||
}, | ||
}) | ||
achievements: AchievementDto[] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
import { ApiProperty, ApiPropertyOptional, getSchemaPath } from "@nestjs/swagger"; | ||
import { IsEnum, IsNotEmpty, IsString, IsOptional, ValidateNested, IsNumber, Min, Max, MaxLength, ArrayMinSize, IsArray } from "class-validator"; | ||
import { ActionStatus, InstrumentType, NatAnchor } from "../enums/action.enum"; | ||
import { KpiDto } from "./kpi.dto"; | ||
import { DocumentDto } from "./document.dto"; | ||
import { KpiUnits } from "../enums/kpi.enum"; | ||
import { Sector } from "../enums/sector.enum"; | ||
import { ActionType } from "../enums/action.enum"; | ||
|
||
export class ActionDto { | ||
|
||
actionId: string; | ||
|
||
@IsNotEmpty() | ||
@IsString() | ||
@ApiProperty() | ||
title: string; | ||
|
||
@IsNotEmpty() | ||
@IsString() | ||
@ApiProperty() | ||
description: string; | ||
|
||
@IsNotEmpty() | ||
@IsString() | ||
@ApiProperty() | ||
objective: string; | ||
|
||
@IsNotEmpty() | ||
@IsEnum(Sector, { | ||
each: true, | ||
message: 'Invalid Affected Sector. Supported following types:' + Object.values(Sector) | ||
}) | ||
@ApiProperty({ | ||
type: [String], | ||
enum: Object.values(Sector), | ||
}) | ||
sector: Sector; | ||
|
||
@IsNotEmpty() | ||
@IsEnum(ActionType, { | ||
each: true, | ||
message: 'Invalid Action Type:' + Object.values(ActionType) | ||
}) | ||
@ApiProperty({ | ||
type: [String], | ||
enum: Object.values(ActionType), | ||
}) | ||
type: ActionType; | ||
|
||
@IsArray() | ||
@ArrayMinSize(1) | ||
@MaxLength(100, { each: true }) | ||
@IsNotEmpty({ each: true }) | ||
@IsEnum(InstrumentType, { | ||
each: true, | ||
message: "Invalid instrument type. Supported following instrument types:" + Object.values(InstrumentType), | ||
}) | ||
@ApiProperty({ | ||
type: [String], | ||
enum: Object.values(InstrumentType), | ||
}) | ||
instrumentType: InstrumentType[]; | ||
|
||
@IsNotEmpty() | ||
@ApiProperty({ enum: ActionStatus }) | ||
@IsEnum(ActionStatus, { | ||
message: "Invalid status. Supported following statuses:" + Object.values(ActionStatus), | ||
}) | ||
status: ActionStatus; | ||
|
||
@IsNotEmpty() | ||
@IsNumber() | ||
@Min(2013) | ||
@Max(2049) | ||
@ApiProperty() | ||
startYear: number; | ||
|
||
@IsArray() | ||
@ArrayMinSize(1) | ||
@MaxLength(100, { each: true }) | ||
@IsNotEmpty({ each: true }) | ||
@IsEnum(NatAnchor, { | ||
each: true, | ||
message: "Invalid Anchored National Strategy. Supported following strategies:" + Object.values(NatAnchor), | ||
}) | ||
@ApiProperty({ | ||
type: [String], | ||
enum: Object.values(NatAnchor), | ||
}) | ||
natAnchor: NatAnchor[]; | ||
|
||
@IsOptional() | ||
@ApiPropertyOptional( | ||
{ | ||
type: "array", | ||
example: [{ | ||
title: "document 1", | ||
data: "base64 document string" | ||
}], | ||
items: { | ||
$ref: getSchemaPath(DocumentDto), | ||
}, | ||
} | ||
) | ||
documents: DocumentDto[]; | ||
|
||
@IsOptional() | ||
@ApiPropertyOptional() | ||
linkedProgrammes: string[]; | ||
|
||
@IsOptional() | ||
@ValidateNested() | ||
@ApiPropertyOptional( | ||
{ | ||
type: "array", | ||
example: [{ | ||
name: "KPI 1", | ||
kpiUnit: KpiUnits.GWp_INSTALLED, | ||
creatorType: "action", | ||
expected: 100 | ||
}], | ||
items: { | ||
$ref: getSchemaPath(KpiDto), | ||
}, | ||
} | ||
) | ||
kpis: KpiDto[]; | ||
|
||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
import { ApiProperty, ApiPropertyOptional, getSchemaPath } from "@nestjs/swagger"; | ||
import { IsEnum, IsNotEmpty, IsString, IsOptional, ValidateNested, IsNumber, Min, Max, isNotEmpty, ArrayMinSize, MaxLength, IsArray } from "class-validator"; | ||
import { ActionStatus, ActionType, InstrumentType, NatAnchor } from "../enums/action.enum"; | ||
import { KpiDto } from "./kpi.dto"; | ||
import { DocumentDto } from "./document.dto"; | ||
import { KpiUpdateDto } from "./kpi.update.dto"; | ||
import { KpiUnits } from "../enums/kpi.enum"; | ||
import { Sector } from "../enums/sector.enum"; | ||
import { KPIAction } from "../enums/shared.enum"; | ||
|
||
export class ActionUpdateDto { | ||
|
||
@IsString() | ||
@IsNotEmpty() | ||
@ApiProperty() | ||
actionId: string; | ||
|
||
@IsNotEmpty() | ||
@IsString() | ||
@ApiProperty() | ||
title: string; | ||
|
||
@IsNotEmpty() | ||
@IsString() | ||
@ApiProperty() | ||
description: string; | ||
|
||
@IsNotEmpty() | ||
@IsString() | ||
@ApiProperty() | ||
objective: string; | ||
|
||
@IsNotEmpty() | ||
@IsEnum(Sector, { | ||
each: true, | ||
message: 'Invalid Affected Sector. Supported following types:' + Object.values(Sector) | ||
}) | ||
@ApiProperty({ | ||
type: [String], | ||
enum: Object.values(Sector), | ||
}) | ||
sector: Sector; | ||
|
||
@IsNotEmpty() | ||
@IsEnum(ActionType, { | ||
each: true, | ||
message: 'Invalid Action Type:' + Object.values(ActionType) | ||
}) | ||
@ApiProperty({ | ||
type: [String], | ||
enum: Object.values(ActionType), | ||
}) | ||
type: ActionType; | ||
|
||
@ArrayMinSize(1) | ||
@MaxLength(100, { each: true }) | ||
@IsNotEmpty({ each: true }) | ||
@IsEnum(InstrumentType, { | ||
each: true, | ||
message: "Invalid instrument type. Supported following instrument types:" + Object.values(InstrumentType), | ||
}) | ||
@ApiProperty({ | ||
type: [String], | ||
enum: Object.values(InstrumentType), | ||
}) | ||
instrumentType: InstrumentType[]; | ||
|
||
@IsNotEmpty() | ||
@ApiProperty({ enum: ActionStatus }) | ||
@IsEnum(ActionStatus, { | ||
message: "Invalid status. Supported following statuses:" + Object.values(ActionStatus), | ||
}) | ||
status: ActionStatus; | ||
|
||
@IsNotEmpty() | ||
@IsNumber() | ||
@Min(2013) | ||
@Max(2049) | ||
@ApiProperty() | ||
startYear: number; | ||
|
||
@IsArray() | ||
@ArrayMinSize(1) | ||
@MaxLength(100, { each: true }) | ||
@IsNotEmpty({ each: true }) | ||
@IsEnum(NatAnchor, { | ||
each: true, | ||
message: "Invalid Anchored National Strategy. Supported following strategies:" + Object.values(NatAnchor), | ||
}) | ||
@ApiProperty({ | ||
type: [String], | ||
enum: Object.values(NatAnchor), | ||
}) | ||
natAnchor: NatAnchor[]; | ||
|
||
@IsOptional() | ||
@ApiPropertyOptional( | ||
{ | ||
type: "array", | ||
example: [{ | ||
title: "document 1", | ||
data: "base64 document string" | ||
}], | ||
items: { | ||
$ref: getSchemaPath(DocumentDto), | ||
}, | ||
} | ||
) | ||
newDocuments: DocumentDto[]; | ||
|
||
@IsOptional() | ||
@ApiPropertyOptional( | ||
{ | ||
type: "array", | ||
example: ["http://test.com/documents/action_documents/testDoc1_1713334127897.csv"], | ||
} | ||
) | ||
removedDocuments: string[]; | ||
|
||
@IsOptional() | ||
@ValidateNested() | ||
@ApiPropertyOptional( | ||
{ | ||
type: "array", | ||
example: [{ | ||
kpiId: "1", | ||
kpiUnit: KpiUnits.GWp_INSTALLED, | ||
name: "KPI 1", | ||
creatorType: "action", | ||
expected: 100, | ||
KPIAction: KPIAction.CREATED, // To check KPI is Updated or not for Update Timeline | ||
}], | ||
items: { | ||
$ref: getSchemaPath(KpiUpdateDto), | ||
}, | ||
} | ||
) | ||
kpis: KpiUpdateDto[]; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
import { ApiProperty, ApiPropertyOptional, getSchemaPath } from "@nestjs/swagger"; | ||
import { ArrayMinSize, IsArray, IsBoolean, IsEnum, IsIn, IsNotEmpty, IsNumber, IsOptional, IsString, MaxLength, ValidateIf } from "class-validator"; | ||
import { ActivityStatus, ImpleMeans, Measure, SupportType, TechnologyType } from "../enums/activity.enum"; | ||
import { EntityType, GHGS, IntImplementor, NatImplementor } from "../enums/shared.enum"; | ||
import { DocumentDto } from "./document.dto"; | ||
import { Type } from "class-transformer"; | ||
|
||
export class ActivityDto { | ||
|
||
activityId: string; | ||
|
||
@IsNotEmpty() | ||
@IsString() | ||
@ApiProperty() | ||
title: string; | ||
|
||
@IsNotEmpty() | ||
@IsString() | ||
@ApiProperty() | ||
description: string; | ||
|
||
@IsNotEmpty() | ||
@ApiProperty({ enum: [EntityType.ACTION, EntityType.PROGRAMME, EntityType.PROJECT] }) | ||
@IsIn([EntityType.ACTION, EntityType.PROGRAMME, EntityType.PROJECT], { | ||
message: 'Invalid Entity Type. Supported types are:' + Object.values([EntityType.ACTION, EntityType.PROGRAMME, EntityType.PROJECT]), | ||
}) | ||
parentType: EntityType; | ||
|
||
@IsNotEmpty() | ||
@IsString() | ||
@ApiProperty() | ||
parentId: string; | ||
|
||
@ValidateIf((c) => c.measure) | ||
@IsNotEmpty() | ||
@ApiPropertyOptional({ enum: Measure }) | ||
@IsEnum(Measure, { | ||
message: "Invalid Measure type. Supported following types:" + Object.values(Measure), | ||
}) | ||
measure: Measure; | ||
|
||
@IsNotEmpty() | ||
@ApiProperty({ enum: ActivityStatus }) | ||
@IsEnum(ActivityStatus, { | ||
message: "Invalid activity status. Supported following status:" + Object.values(ActivityStatus), | ||
}) | ||
status: ActivityStatus; | ||
|
||
@ValidateIf((c) => c.nationalImplementingEntity) | ||
@IsOptional() | ||
@IsArray() | ||
@ArrayMinSize(1) | ||
@MaxLength(100, { each: true }) | ||
@IsNotEmpty({ each: true }) | ||
@IsEnum(NatImplementor, { | ||
each: true, | ||
message: 'Invalid National Implementing Entity. Supported following entities:' + Object.values(NatImplementor) | ||
}) | ||
@ApiPropertyOptional({ | ||
type: [String], | ||
enum: Object.values(NatImplementor), | ||
}) | ||
nationalImplementingEntity: NatImplementor[] | ||
|
||
@ValidateIf((c) => c.internationalImplementingEntity) | ||
@IsOptional() | ||
@IsArray() | ||
@ArrayMinSize(1) | ||
@MaxLength(100, { each: true }) | ||
@IsNotEmpty({ each: true }) | ||
@IsEnum(IntImplementor, { | ||
each: true, | ||
message: 'Invalid International Implementing Entity. Supported following entities:' + Object.values(IntImplementor) | ||
}) | ||
@ApiPropertyOptional({ | ||
type: [String], | ||
enum: Object.values(IntImplementor), | ||
}) | ||
internationalImplementingEntity: IntImplementor[] | ||
|
||
@IsOptional() | ||
@IsBoolean() | ||
@ApiPropertyOptional() | ||
anchoredInNationalStrategy: boolean; | ||
|
||
@ValidateIf((c) => c.meansOfImplementation) | ||
@IsNotEmpty() | ||
@ApiPropertyOptional({ enum: ImpleMeans }) | ||
@IsEnum(ImpleMeans, { | ||
message: "Invalid Means of Implementation. Supported following types:" + Object.values(ImpleMeans), | ||
}) | ||
meansOfImplementation: ImpleMeans; | ||
|
||
@ValidateIf((c) => c.technologyType) | ||
@IsNotEmpty() | ||
@ApiPropertyOptional({ enum: TechnologyType }) | ||
@IsEnum(TechnologyType, { | ||
message: "Invalid Technology Type. Supported following types:" + Object.values(TechnologyType), | ||
}) | ||
technologyType: TechnologyType; | ||
|
||
@IsOptional() | ||
@IsString() | ||
@ApiPropertyOptional() | ||
etfDescription: string; | ||
|
||
@IsOptional() | ||
@ApiPropertyOptional( | ||
{ | ||
type: "array", | ||
example: [{ | ||
title: "document 1", | ||
data: "base64 document string" | ||
}], | ||
items: { | ||
$ref: getSchemaPath(DocumentDto), | ||
}, | ||
} | ||
) | ||
documents: DocumentDto[]; | ||
|
||
@ValidateIf((c) => c.ghgsAffected) | ||
@IsArray() | ||
@ArrayMinSize(1) | ||
@MaxLength(100, { each: true }) | ||
@IsNotEmpty({ each: true }) | ||
@IsEnum(GHGS, { | ||
each: true, | ||
message: "Invalid GHG. Supported following types:" + Object.values(GHGS), | ||
}) | ||
@ApiPropertyOptional({ | ||
type: [String], | ||
enum: Object.values(GHGS), | ||
}) | ||
ghgsAffected: GHGS[]; | ||
|
||
@IsNumber() | ||
@ApiProperty() | ||
achievedGHGReduction: number; | ||
|
||
@IsNumber() | ||
@ApiProperty() | ||
expectedGHGReduction: number; | ||
|
||
@IsOptional() | ||
@ApiPropertyOptional() | ||
@IsString() | ||
comments: string; | ||
|
||
@IsOptional() | ||
@ApiPropertyOptional({ | ||
type: "object", | ||
example: { | ||
mitigationMethodology: "CO2", | ||
mitigationMethodologyDescription: "test", | ||
mitigationCalcEntity: "ABB", | ||
comments: "test mitigation comments", | ||
methodologyDocuments: [{ | ||
title: "mitigation document 1", | ||
data: "base64 document string" | ||
}], | ||
resultDocuments: [{ | ||
title: "result document 1", | ||
data: "base64 document string" | ||
}] | ||
}, | ||
}) | ||
mitigationInfo: any; | ||
|
||
@IsOptional() | ||
@ApiPropertyOptional({ | ||
type: "object", | ||
example: { | ||
expected: { | ||
baselineEmissions: [7,8,9,7], | ||
activityEmissionsWithM: [7,8,9,7], | ||
activityEmissionsWithAM: [7,8,9,7], | ||
expectedEmissionReductWithM: [7,8,9,7], | ||
expectedEmissionReductWithAM: [7,8,9,7], | ||
total: { | ||
baselineEmissions:31, | ||
activityEmissionsWithM:31, | ||
activityEmissionsWithAM:31, | ||
expectedEmissionReductWithM:31, | ||
expectedEmissionReductWithAM:31, | ||
} | ||
}, | ||
actual: { | ||
baselineActualEmissions: [7,8,9,7], | ||
activityActualEmissions: [7,8,9,7], | ||
actualEmissionReduct: [7,8,9,7], | ||
total: { | ||
baselineActualEmissions:31, | ||
activityActualEmissions:31, | ||
actualEmissionReduct:31, | ||
} | ||
}, | ||
startYear: 2015, | ||
}, | ||
}) | ||
mitigationTimeline: any; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { ActivityStatus, ImpleMeans, Measure, TechnologyType } from "../enums/activity.enum"; | ||
import { EntityType, IntImplementor, NatImplementor } from "../enums/shared.enum"; | ||
import { DocumentDto } from "./document.dto"; | ||
|
||
export class ActivityResponseDto { | ||
|
||
activityId: string; | ||
|
||
title: string; | ||
|
||
description: string; | ||
|
||
parentType: EntityType; | ||
|
||
parentId: string; | ||
|
||
measure: Measure; | ||
|
||
status: ActivityStatus; | ||
|
||
nationalImplementingEntity: NatImplementor[] | ||
|
||
internationalImplementingEntity: IntImplementor[] | ||
|
||
anchoredInNationalStrategy: boolean; | ||
|
||
meansOfImplementation: ImpleMeans; | ||
|
||
technologyType: TechnologyType; | ||
|
||
etfDescription: string; | ||
|
||
documents: DocumentDto[]; | ||
|
||
achievedGHGReduction: number; | ||
|
||
expectedGHGReduction: number; | ||
|
||
comments: string; | ||
|
||
mitigationInfo: any; | ||
|
||
mitigationTimeline: any; | ||
|
||
migratedData: any; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
import { ApiProperty, ApiPropertyOptional, getSchemaPath } from "@nestjs/swagger"; | ||
import { ArrayMinSize, IsArray, IsBoolean, IsEnum, IsIn, IsNotEmpty, IsNumber, IsOptional, IsString, MaxLength, ValidateIf } from "class-validator"; | ||
import { ActivityStatus, ImpleMeans, Measure, TechnologyType } from "../enums/activity.enum"; | ||
import { EntityType, GHGS, IntImplementor, NatImplementor } from "../enums/shared.enum"; | ||
import { DocumentDto } from "./document.dto"; | ||
|
||
export class ActivityUpdateDto { | ||
|
||
@IsNotEmpty() | ||
@IsString() | ||
@ApiProperty() | ||
activityId: string; | ||
|
||
@IsNotEmpty() | ||
@IsString() | ||
@ApiProperty() | ||
title: string; | ||
|
||
@IsNotEmpty() | ||
@IsString() | ||
@ApiProperty() | ||
description: string; | ||
|
||
@ValidateIf((c) => c.parentType) | ||
@IsNotEmpty() | ||
@ApiProperty({ enum: [EntityType.ACTION, EntityType.PROGRAMME, EntityType.PROJECT] }) | ||
@IsIn([EntityType.ACTION, EntityType.PROGRAMME, EntityType.PROJECT], { | ||
message: 'Invalid Entity Type. Supported types are:' + Object.values([EntityType.ACTION, EntityType.PROGRAMME, EntityType.PROJECT]), | ||
}) | ||
parentType: EntityType; | ||
|
||
@IsOptional() | ||
@IsString() | ||
@ApiPropertyOptional() | ||
parentId: string; | ||
|
||
@ValidateIf((c) => c.measure) | ||
@IsNotEmpty() | ||
@ApiPropertyOptional({ enum: Measure }) | ||
@IsEnum(Measure, { | ||
message: "Invalid Measure type. Supported following types:" + Object.values(Measure), | ||
}) | ||
measure: Measure; | ||
|
||
@IsNotEmpty() | ||
@ApiProperty({ enum: ActivityStatus }) | ||
@IsEnum(ActivityStatus, { | ||
message: "Invalid activity status. Supported following status:" + Object.values(ActivityStatus), | ||
}) | ||
status: ActivityStatus; | ||
|
||
@ValidateIf((c) => c.nationalImplementingEntity) | ||
@IsOptional() | ||
@IsArray() | ||
@MaxLength(100, { each: true }) | ||
@IsNotEmpty({ each: true }) | ||
@IsEnum(NatImplementor, { | ||
each: true, | ||
message: 'Invalid National Implementing Entity. Supported following entities:' + Object.values(NatImplementor) | ||
}) | ||
@ApiPropertyOptional({ | ||
type: [String], | ||
enum: Object.values(NatImplementor), | ||
}) | ||
nationalImplementingEntity: NatImplementor[] | ||
|
||
@ValidateIf((c) => c.internationalImplementingEntity) | ||
@IsOptional() | ||
@IsArray() | ||
@MaxLength(100, { each: true }) | ||
@IsNotEmpty({ each: true }) | ||
@IsEnum(IntImplementor, { | ||
each: true, | ||
message: 'Invalid International Implementing Entity. Supported following entities:' + Object.values(IntImplementor) | ||
}) | ||
@ApiPropertyOptional({ | ||
type: [String], | ||
enum: Object.values(IntImplementor), | ||
}) | ||
internationalImplementingEntity: IntImplementor[] | ||
|
||
@IsOptional() | ||
@IsBoolean() | ||
@ApiPropertyOptional() | ||
anchoredInNationalStrategy: boolean; | ||
|
||
@ValidateIf((c) => c.meansOfImplementation) | ||
@IsNotEmpty() | ||
@ApiPropertyOptional({ enum: ImpleMeans }) | ||
@IsEnum(ImpleMeans, { | ||
message: "Invalid Means of Implementation. Supported following types:" + Object.values(ImpleMeans), | ||
}) | ||
meansOfImplementation: ImpleMeans; | ||
|
||
@ValidateIf((c) => c.technologyType) | ||
@IsNotEmpty() | ||
@ApiPropertyOptional({ enum: TechnologyType }) | ||
@IsEnum(TechnologyType, { | ||
message: "Invalid Technology Type. Supported following types:" + Object.values(TechnologyType), | ||
}) | ||
technologyType: TechnologyType; | ||
|
||
@IsOptional() | ||
@IsString() | ||
@ApiPropertyOptional() | ||
etfDescription: string; | ||
|
||
@IsOptional() | ||
@ApiPropertyOptional( | ||
{ | ||
type: "array", | ||
example: [{ | ||
title: "document 1", | ||
data: "base64 document string" | ||
}], | ||
items: { | ||
$ref: getSchemaPath(DocumentDto), | ||
}, | ||
} | ||
) | ||
newDocuments: DocumentDto[]; | ||
|
||
@IsOptional() | ||
@ApiPropertyOptional( | ||
{ | ||
type: "array", | ||
example: ["http://test.com/documents/activity_documents/testDoc1_1713334127897.csv"], | ||
} | ||
) | ||
removedDocuments: string[]; | ||
|
||
@ValidateIf((c) => c.ghgsAffected) | ||
@IsArray() | ||
@ArrayMinSize(1) | ||
@MaxLength(100, { each: true }) | ||
@IsNotEmpty({ each: true }) | ||
@IsEnum(GHGS, { | ||
each: true, | ||
message: "Invalid GHG. Supported following types:" + Object.values(GHGS), | ||
}) | ||
@ApiPropertyOptional({ | ||
type: [String], | ||
enum: Object.values(GHGS), | ||
}) | ||
ghgsAffected: GHGS[]; | ||
|
||
@IsNumber() | ||
@ApiProperty() | ||
achievedGHGReduction: number; | ||
|
||
@IsNumber() | ||
@ApiProperty() | ||
expectedGHGReduction: number; | ||
|
||
@IsOptional() | ||
@ApiPropertyOptional() | ||
@IsString() | ||
comments: string; | ||
|
||
@IsOptional() | ||
@ApiPropertyOptional({ | ||
type: "object", | ||
example: { | ||
mitigationMethodology: "CO2", | ||
mitigationMethodologyDescription: "test", | ||
mitigationCalcEntity: "ABB", | ||
comments: "test mitigation comments", | ||
methodologyDocuments: [ | ||
{ | ||
title: "added mitigation document ", | ||
data: "base64 document string" | ||
}, | ||
{ | ||
createdTime: 12556988775, | ||
title: "existing document", | ||
updatedTime: undefined, | ||
url: "www.test.com/documents/activity_documents/crr_mit_01.pdf", | ||
} | ||
], | ||
resultDocuments: [ | ||
{ | ||
title: "added result document ", | ||
data: "base64 document string" | ||
}, | ||
{ | ||
createdTime: 12556988775, | ||
title: "existing document", | ||
updatedTime: undefined, | ||
url: "www.test.com/documents/activity_documents/crr_rep_01.pdf", | ||
} | ||
] | ||
}, | ||
}) | ||
mitigationInfo: any; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export class BasicResponseDto { | ||
constructor(public statusCode: number, public message: string) { | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
|
||
export class chartStatsRequestDto { | ||
type: string; | ||
value?: string; | ||
companyId?: any; | ||
startDate?: number; | ||
endDate?: number; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
export class DataCountResponseDto { | ||
stats: any; | ||
lastUpdate: number; | ||
|
||
constructor(stats: any, lastUpdate: number) { | ||
this.stats = stats; | ||
this.lastUpdate = lastUpdate; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export class DataExportDto {} |
Oops, something went wrong.