diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 000000000..0bef4f175
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,22 @@
+// For format details, see https://aka.ms/devcontainer.json. For config options, see the
+// README at: https://github.com/devcontainers/templates/tree/main/src/debian
+{
+ "name": "Debian",
+ // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
+ "image": "mcr.microsoft.com/devcontainers/base:trixie",
+ "features": {
+ "ghcr.io/devcontainers/features/node:1": {}
+ }
+
+ // Features to add to the dev container. More info: https://containers.dev/features.
+ // "features": {},
+
+ // Use 'forwardPorts' to make a list of ports inside the container available locally.
+ // "forwardPorts": [],
+
+ // Configure tool-specific properties.
+ // "customizations": {},
+
+ // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
+ // "remoteUser": "root"
+}
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 000000000..9cd2d2b67
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,41 @@
+stages:
+ - build
+ - publish
+
+build_image:
+ stage: build
+ image:
+ name: gcr.io/kaniko-project/executor:debug
+ entrypoint: [""]
+ script:
+ - |-
+ tag=$CI_COMMIT_SHORT_SHA
+ if [[ -n "$CI_COMMIT_TAG" ]]; then
+ tag=$CI_COMMIT_TAG
+ fi
+ - mkdir -p /kaniko/.docker
+ - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
+ - >
+ /kaniko/executor
+ --context $CI_PROJECT_DIR
+ --dockerfile $CI_PROJECT_DIR/Dockerfile
+ --destination $CI_REGISTRY_IMAGE:${tag}
+ --cache=true
+
+push_image_harbor:
+ stage: publish
+ image:
+ name: gcr.io/kaniko-project/executor:debug
+ entrypoint: ['']
+ script:
+ - mkdir -p /kaniko/.docker
+ - echo "{\"auths\":{\"${HARBOR_HOST}\":{\"auth\":\"$(echo -n ${HARBOR_USERNAME}:${HARBOR_PASSWORD} | base64 -w 0)\"}}}" > /kaniko/.docker/config.json
+ - >-
+ /kaniko/executor
+ --context "${CI_PROJECT_DIR}"
+ --dockerfile ${CI_PROJECT_DIR}/Dockerfile
+ --destination "${HARBOR_HOST}/${HARBOR_PROJECT}/${CI_PROJECT_NAME}:${CI_COMMIT_TAG}"
+ --cache=true
+ rules:
+ - if: $CI_COMMIT_TAG
+
diff --git a/nest-cli.json b/nest-cli.json
index 473da2a0a..b273600d6 100644
--- a/nest-cli.json
+++ b/nest-cli.json
@@ -2,6 +2,9 @@
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
+ "assets": [
+ { "include": "common/email-templates/*.hbs", "watchAssets": true }
+ ],
"plugins": [{
"name": "@nestjs/swagger",
"options": {
diff --git a/src/common/common.module.ts b/src/common/common.module.ts
index b43ed68bb..35bbb9ab7 100644
--- a/src/common/common.module.ts
+++ b/src/common/common.module.ts
@@ -1,5 +1,5 @@
import { Module } from "@nestjs/common";
-import { MailService } from "./mail.service";
+import { MailService } from "./services/mail.service";
@Module({
providers: [MailService],
diff --git a/src/common/email-templates/doi-registered.hbs b/src/common/email-templates/doi-registered.hbs
new file mode 100644
index 000000000..8ece3d055
--- /dev/null
+++ b/src/common/email-templates/doi-registered.hbs
@@ -0,0 +1,8 @@
+
Hello,
+
+Your DOI has been successfully registered: {{doi}}
+
+Please proceed to DESY's publication database PubDB.
+
+Best regards,
+Your Service Team
\ No newline at end of file
diff --git a/src/common/handlebars-helpers.ts b/src/common/handlebars-helpers.ts
index d52dc4a2c..58fea4e9e 100644
--- a/src/common/handlebars-helpers.ts
+++ b/src/common/handlebars-helpers.ts
@@ -132,6 +132,9 @@ export const urlencode = (context: string): string => {
return encodeURIComponent(context);
};
+export const exchangeString = (registerDoiUri: string, search: string, replacement: string): string => {
+ return registerDoiUri.replace(search, replacement);
+};
/**
* Format a unit using mathjs.
*
@@ -191,6 +194,7 @@ export const handlebarsHelpers: hb.HelperDeclareSpec = {
jsonify: jsonify,
job_v3: job_v3,
urlencode: urlencode,
+ exchangeString: exchangeString,
base64enc: base64enc,
base64dec: atob,
formatUnit: formatUnit,
diff --git a/src/common/mail.service.ts b/src/common/services/mail.service.ts
similarity index 55%
rename from src/common/mail.service.ts
rename to src/common/services/mail.service.ts
index 705276505..b3f0cc7d8 100644
--- a/src/common/mail.service.ts
+++ b/src/common/services/mail.service.ts
@@ -1,5 +1,6 @@
import { ISendMailOptions, MailerService } from "@nestjs-modules/mailer";
import { Injectable, Logger } from "@nestjs/common";
+import { ConfigService } from "@nestjs/config";
import { isAxiosError } from "@nestjs/terminus/dist/utils";
import { SentMessageInfo } from "nodemailer";
@@ -10,7 +11,10 @@ import { SentMessageInfo } from "nodemailer";
*/
@Injectable()
export class MailService {
- constructor(private readonly mailerService: MailerService) {}
+ constructor(
+ private readonly mailerService: MailerService,
+ private readonly configService: ConfigService,
+ ) {}
async sendMail(options: ISendMailOptions): Promise {
try {
@@ -39,4 +43,32 @@ export class MailService {
}
}
}
+
+ async sendTemplateEmail(doi: string, userEmail: string): Promise {
+ const templateName = "doi-registered"; // hdb
+ const registerDoiUri = this.configService.get("registerDoiUri");
+ Logger.debug(`registerDoiUri is ${registerDoiUri}`);
+
+ try {
+ const mailOptions: ISendMailOptions = {
+ to: userEmail,
+ subject: `DOI registration confirmed: ${doi}.`,
+ template: templateName,
+ context: {
+ registerDoiUri: registerDoiUri,
+ doi: doi,
+ },
+ };
+
+ if (!mailOptions.template) {
+ throw new Error("INTERNAL ERROR: template property is missing!");
+ }
+
+ Logger.debug(`mailOptions: ${JSON.stringify(mailOptions)}`);
+ await this.sendMail(mailOptions);
+ Logger.log(`DOI registration email sent to ${userEmail}.`);
+ } catch (error) {
+ Logger.error(`Failed to send email: ${JSON.stringify(error)}`);
+ }
+ }
}
diff --git a/src/config/configuration.ts b/src/config/configuration.ts
index b613033ef..0e318fa3e 100644
--- a/src/config/configuration.ts
+++ b/src/config/configuration.ts
@@ -4,6 +4,7 @@ import localconfiguration from "./localconfiguration";
import { boolean } from "mathjs";
import { DEFAULT_PROPOSAL_TYPE } from "src/proposals/schemas/proposal.schema";
import { DatasetType } from "src/datasets/types/dataset-type.enum";
+import { Logger } from "@nestjs/common";
const configuration = () => {
const accessGroupsStaticValues =
@@ -85,12 +86,14 @@ const configuration = () => {
publishedDataConfig:
process.env.PUBLISHED_DATA_CONFIG_FILE || "publishedDataConfig.json",
};
+
Object.keys(jsonConfigFileList).forEach((key) => {
const filePath = jsonConfigFileList[key];
if (fs.existsSync(filePath)) {
const data = fs.readFileSync(filePath, "utf8");
try {
jsonConfigMap[key] = JSON.parse(data);
+ Logger.debug(`From ${filePath} parsing data ${data}.`);
} catch (error) {
console.error(
"Error json config file parsing " + filePath + " : " + error,
@@ -123,6 +126,7 @@ const configuration = () => {
`Example configuration file ${exampleFilePath} does not exist. Using empty configuration for ${key}`,
);
jsonConfigMap[key] = {};
+ Logger.debug(`jsonConfigMap[key] is ${jsonConfigMap[key]}`);
}
}
}
diff --git a/src/config/job-config/actions/emailaction/emailaction.service.ts b/src/config/job-config/actions/emailaction/emailaction.service.ts
index cdc217aa3..669a8d90a 100644
--- a/src/config/job-config/actions/emailaction/emailaction.service.ts
+++ b/src/config/job-config/actions/emailaction/emailaction.service.ts
@@ -6,7 +6,7 @@ import {
} from "../../jobconfig.interface";
import { isEmailJobActionOptions } from "./emailaction.interface";
import { EmailJobAction } from "./emailaction";
-import { MailService } from "src/common/mail.service";
+import { MailService } from "src/common/services/mail.service";
@Injectable()
export class EmailJobActionCreator implements JobActionCreator {
diff --git a/src/config/job-config/actions/emailaction/emailaction.spec.ts b/src/config/job-config/actions/emailaction/emailaction.spec.ts
index a9c3c3b90..e1fafafc8 100644
--- a/src/config/job-config/actions/emailaction/emailaction.spec.ts
+++ b/src/config/job-config/actions/emailaction/emailaction.spec.ts
@@ -11,7 +11,7 @@
import { EmailJobAction } from "./emailaction";
import { EmailJobActionOptions } from "./emailaction.interface";
import { JobClass } from "../../../../jobs/schemas/job.schema";
-import { MailService } from "src/common/mail.service";
+import { MailService } from "src/common/services/mail.service";
import { MailerService } from "@nestjs-modules/mailer";
import { DatasetClass } from "src/datasets/schemas/dataset.schema";
import { CreateJobDto } from "src/jobs/dto/create-job.dto";
diff --git a/src/config/job-config/actions/emailaction/emailaction.ts b/src/config/job-config/actions/emailaction/emailaction.ts
index 11a77db7b..b5efe01ce 100644
--- a/src/config/job-config/actions/emailaction/emailaction.ts
+++ b/src/config/job-config/actions/emailaction/emailaction.ts
@@ -13,7 +13,7 @@ import {
} from "../../jobconfig.interface";
import { ISendMailOptions } from "@nestjs-modules/mailer";
import { actionType, EmailJobActionOptions } from "./emailaction.interface";
-import { MailService } from "src/common/mail.service";
+import { MailService } from "src/common/services/mail.service";
/**
* Send an email following a job
diff --git a/src/jobs/jobs.service.spec.ts b/src/jobs/jobs.service.spec.ts
index 2016267c5..1662fd1f7 100644
--- a/src/jobs/jobs.service.spec.ts
+++ b/src/jobs/jobs.service.spec.ts
@@ -2,7 +2,7 @@ import { ConfigService } from "@nestjs/config";
import { getModelToken } from "@nestjs/mongoose";
import { Test, TestingModule } from "@nestjs/testing";
import { Model } from "mongoose";
-import { MailService } from "src/common/mail.service";
+import { MailService } from "src/common/services/mail.service";
import { DatasetsService } from "src/datasets/datasets.service";
import { DatasetsAccessService } from "src/datasets/datasets-access.service";
import { PoliciesService } from "src/policies/policies.service";
diff --git a/src/published-data/published-data.module.ts b/src/published-data/published-data.module.ts
index 3878ac7bd..682f03c01 100644
--- a/src/published-data/published-data.module.ts
+++ b/src/published-data/published-data.module.ts
@@ -18,6 +18,7 @@ import {
} from "./schemas/published-data.schema";
import { applyHistoryPluginOnce } from "src/common/mongoose/plugins/history.plugin.util";
import { PublishedDataV4Controller } from "./published-data.v4.controller";
+import { MailService } from "src/common/services/mail.service";
@Module({
imports: [
@@ -65,6 +66,6 @@ import { PublishedDataV4Controller } from "./published-data.v4.controller";
ProposalsModule,
],
controllers: [PublishedDataController, PublishedDataV4Controller],
- providers: [PublishedDataService],
+ providers: [PublishedDataService, MailService],
})
export class PublishedDataModule {}
diff --git a/src/published-data/published-data.v4.controller.ts b/src/published-data/published-data.v4.controller.ts
index 6f25c91dc..dc44cb23d 100644
--- a/src/published-data/published-data.v4.controller.ts
+++ b/src/published-data/published-data.v4.controller.ts
@@ -57,6 +57,8 @@ import {
PublishedData,
PublishedDataDocument,
} from "./schemas/published-data.schema";
+import { MailService } from "src/common/services/mail.service";
+import { logger } from "handlebars";
@ApiBearerAuth()
@ApiTags("published data v4")
@@ -76,6 +78,7 @@ export class PublishedDataV4Controller {
private readonly proposalsService: ProposalsService,
private readonly publishedDataService: PublishedDataService,
private caslAbilityFactory: CaslAbilityFactory,
+ private readonly mailService: MailService,
) {}
@AllowAny()
@@ -617,6 +620,9 @@ export class PublishedDataV4Controller {
};
try {
+
+ console.log("registerDataciteDoiOptions is", JSON.stringify(registerDataciteDoiOptions));
+
await firstValueFrom(
this.httpService.request(registerDataciteDoiOptions),
);
@@ -636,6 +642,12 @@ export class PublishedDataV4Controller {
{ status: PublishedDataStatus.REGISTERED, registeredTime: new Date() },
);
+ const user = request.user as JWTUser;
+ await this.mailService.sendTemplateEmail(
+ publishedData.doi, // Replacements for the template
+ user.email, // Recipient email
+ );
+
return res;
}