Skip to content

Commit

Permalink
feat: muistutuksen kasittely (HASSU-775) (#324)
Browse files Browse the repository at this point in the history
* initial commit

* wip

* kuittauksen lahetys, email validointi

* testit
  • Loading branch information
kettunju authored Aug 19, 2022
1 parent 8326f14 commit c105cdb
Show file tree
Hide file tree
Showing 10 changed files with 539 additions and 4 deletions.
4 changes: 3 additions & 1 deletion backend/src/apiHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
HaeVelhoProjektiAineistoLinkkiQueryVariables,
LaskePaattymisPaivaQueryVariables,
LataaProjektiQueryVariables,
LisaaMuistutusMutationVariables,
LisaaPalauteMutationVariables,
ListaaKayttajatQueryVariables,
ListaaLisaAineistoQueryVariables,
Expand Down Expand Up @@ -46,6 +47,7 @@ import { ClientError } from "./error/ClientError";
import { SystemError } from "./error/SystemError";
import { tilaHandler } from "./handler/tila/tilaHandler";
import { lisaAineistoHandler } from "./handler/lisaAineistoHandler";
import { muistutusHandler } from "./muistutus/muistutusHandler";

export type AppSyncEventArguments =
| unknown
Expand Down Expand Up @@ -104,7 +106,7 @@ async function executeOperation(event: AppSyncResolverEvent<AppSyncEventArgument
case apiConfig.listKirjaamoOsoitteet.name:
return listKirjaamoOsoitteet();
case apiConfig.lisaaMuistutus.name:
return "Kiitos muistutuksesta!"; //TODO: muistutuksen tallennus
return muistutusHandler.kasitteleMuistutus(event.arguments as LisaaMuistutusMutationVariables);
case apiConfig.listaaLisaAineisto.name:
return lisaAineistoHandler.listaaLisaAineisto(event.arguments as ListaaLisaAineistoQueryVariables);
default:
Expand Down
13 changes: 13 additions & 0 deletions backend/src/database/model/nahtavillaoloVaihe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,16 @@ export type NahtavillaoloPDF = {
nahtavillaoloIlmoitusPDFPath: string;
nahtavillaoloIlmoitusKiinteistonOmistajallePDFPath: string;
};

export type Muistutus = {
id: string;
vastaanotettu: string;
etunimi?: string | null;
sukunimi?: string | null;
katuosoite?: string | null;
postinumeroJaPostitoimipaikka?: string | null;
sahkoposti?: string | null;
puhelinnumero?: string | null;
muistutus?: string | null;
liite?: string | null;
};
51 changes: 50 additions & 1 deletion backend/src/email/emailTemplates.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import get from "lodash/get";
import { Kayttaja } from "../../../common/graphql/apiModel";
import { Kayttaja, Viranomainen } from "../../../common/graphql/apiModel";
import { config } from "../config";
import { DBProjekti } from "../database/model/projekti";
import { EmailOptions } from "./email";
import { linkSuunnitteluVaihe } from "../../../common/links";
import { Muistutus } from "../database/model";

function template(strs: TemplateStringsArray, ...exprs: string[]) {
return function (obj: unknown) {
Expand Down Expand Up @@ -50,6 +51,30 @@ Voit tarkastella aloituskuulutusta osoitteessa https://${"domain"}/yllapito/proj
Saat tämän viestin, koska sinut on merkitty aloituskuulutuksen projektipäälliköksi. Tämä on automaattinen sähköposti, johon ei voi vastata.`;
const hyvaksyttyPDFVastaanottajat = template`${"velho.vastuuhenkilonEmail"}`;
const muistutusTeksti = template`
Muistutus vastaanotettu
${"vastaanotettu"}
Nimi
${"etunimi"} ${"sukunimi"}
Postiosoite
${"katuosoite"} ${"postinumeroJaPostitoimipaikka"}
Sähköposti
${"sahkoposti"}
Puhelinnumero
${"puhelinnumero"}
Suunnitelman asiatunnus
${"asiatunnus"}
Muistutus
${"muistutus"}
`;
const muistutusOtsikko = template`Muistutus - ${"id"}`;
const muistuttajanOtsikko = template`Vahvistus muistutuksen jättämisestä Valtion liikenneväylien suunnittelu -järjestelmän kautta`;

export function createPerustamisEmail(projekti: DBProjekti): EmailOptions {
return {
Expand Down Expand Up @@ -90,3 +115,27 @@ export function createNewFeedbackAvailableEmail(oid: string, recipient: string):
to: recipient,
};
}

export function createMuistutusKirjaamolleEmail(projekti: DBProjekti, muistutus: Muistutus, sahkoposti: string): EmailOptions {
const asiatunnus =
projekti.velho.suunnittelustaVastaavaViranomainen === Viranomainen.VAYLAVIRASTO
? projekti.velho.asiatunnusELY
: projekti.velho.asiatunnusVayla;
return {
subject: muistutusOtsikko(muistutus),
text: muistutusTeksti({asiatunnus, ...muistutus }),
to: sahkoposti,
};
}

export function createKuittausMuistuttajalleEmail(projekti: DBProjekti, muistutus: Muistutus): EmailOptions {
const asiatunnus =
projekti.velho.suunnittelustaVastaavaViranomainen === Viranomainen.VAYLAVIRASTO
? projekti.velho.asiatunnusELY
: projekti.velho.asiatunnusVayla;
return {
subject: muistuttajanOtsikko(muistutus),
text: muistutusTeksti({asiatunnus, ...muistutus }),
to: muistutus.sahkoposti,
};
}
4 changes: 2 additions & 2 deletions backend/src/handler/emailHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { localDateTimeString } from "../util/dateUtil";
import { GetObjectOutput } from "aws-sdk/clients/s3";
import { getS3 } from "../aws/client";

async function getFileAttachment(oid: string, key: string): Promise<Mail.Attachment> {
export async function getFileAttachment(oid: string, key: string): Promise<Mail.Attachment> {
log.info("haetaan s3:sta liitetiedosto", key);

try {
Expand All @@ -33,7 +33,7 @@ async function getFileAttachment(oid: string, key: string): Promise<Mail.Attachm
return {
filename: getFilename(key),
contentDisposition: "attachment",
contentType: "application/pdf",
contentType: output.ContentType || "application/pdf",
content: output.Body,
};
} else {
Expand Down
9 changes: 9 additions & 0 deletions backend/src/muistutus/muistutusAdapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { MuistutusInput } from "../../../common/graphql/apiModel";
import { Muistutus } from "../database/model";
import { localDateTimeString } from "../util/dateUtil";
import { uuid } from "../util/uuid";

export function adaptMuistutusInput(muistutus: MuistutusInput): Muistutus {
const aikaleima = localDateTimeString();
return { ...muistutus, vastaanotettu: aikaleima, id: uuid.v4() };
}
48 changes: 48 additions & 0 deletions backend/src/muistutus/muistutusEmailService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { DBProjekti, Muistutus } from "../database/model";
import { emailClient } from "../email/email";
import { createMuistutusKirjaamolleEmail, createKuittausMuistuttajalleEmail } from "../email/emailTemplates";
import { getFileAttachment } from "../handler/emailHandler";
import { kirjaamoOsoitteetService } from "../kirjaamoOsoitteet/kirjaamoOsoitteetService";
import { log } from "../logger";

class MuistutusEmailService {
async sendEmailToMuistuttaja(projekti: DBProjekti, muistutus: Muistutus) {
log.info("Lähetetään kuittaus muistutuksen tekijälle");
const emailOptions = createKuittausMuistuttajalleEmail(projekti, muistutus);
await emailClient.sendEmail(emailOptions);
log.info("Kuittaus muistuttajalle lähetetty: " + emailOptions.to);
}

async sendEmailToKirjaamo(projekti: DBProjekti, muistutus: Muistutus) {
const vastaavaViranomainen = projekti.velho.suunnittelustaVastaavaViranomainen;
const kirjaamot = await kirjaamoOsoitteetService.listKirjaamoOsoitteet();
// Hassussa on kahden tyyppista Viranomais -enumia, jotka eivat ole kuitenkaan taysin yhtenaisia kattavuudeltaan
// mutta string arvoiltaan ovat samoja, silloin kun viranomainen molemmista loytyy
const sahkoposti = kirjaamot.find(({ nimi }) => nimi.toString() === vastaavaViranomainen.toString())?.sahkoposti;
log.info("Muistutuksen vastaanottaja: ", sahkoposti);
if (!sahkoposti) {
log.error("Vastaavan viranomaisen kirjaamon sähköpostiosoitetta ei löytynyt", vastaavaViranomainen);
throw new Error("Muistutusta ei voitu lähettää kirjaamoon, syy: kirjaamon osoitetta ei löytynyt");
}

const emailOptions = createMuistutusKirjaamolleEmail(projekti, muistutus, sahkoposti);

if (muistutus.liite) {
log.info("haetaan muistutuksen liite: ", muistutus.liite);
const liite = await getFileAttachment(projekti.oid, muistutus.liite);
emailOptions.attachments = [liite];
emailOptions.text.toString().concat(`
Muistutukseen on lisätty liite`);
} else {
emailOptions.text.toString().concat(`
Muistutukseen ei ole lisätty liitettä`);
}

await emailClient.sendEmail(emailOptions);
log.info("Muistutuksen sisältävä sähköposti lähetetty: " + emailOptions.to);
}
}

export const muistutusEmailService = new MuistutusEmailService();
53 changes: 53 additions & 0 deletions backend/src/muistutus/muistutusHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { LisaaMuistutusMutationVariables, Status } from "../../../common/graphql/apiModel";
import { NotFoundError } from "../error/NotFoundError";
import { projektiDatabase } from "../database/projektiDatabase";
import { fileService } from "../files/fileService";
import { projektiAdapterJulkinen } from "../handler/projektiAdapterJulkinen";
import { muistutusEmailService } from "./muistutusEmailService";
import { adaptMuistutusInput } from "./muistutusAdapter";
import { isValidEmail } from "../util/emailUtil";
import { log } from "../logger";

class MuistutusHandler {
async kasitteleMuistutus({ oid, muistutus: muistutusInput }: LisaaMuistutusMutationVariables) {
const projektiFromDB = await projektiDatabase.loadProjektiByOid(oid);
if (!projektiFromDB) {
log.error("Projektia ei löydy");
throw new NotFoundError("Projektia ei löydy");
}
const julkinenProjekti = projektiAdapterJulkinen.adaptProjekti(projektiFromDB);
if (!julkinenProjekti) {
log.error("Projektia ei löydy tai se ei ole vielä julkinen");
throw new NotFoundError("Projektia ei löydy tai se ei ole vielä julkinen");
}

if (julkinenProjekti.status !== Status.NAHTAVILLAOLO) {
log.error("Projekti ei ole nähtävilläolovaiheessa, joten muistutuksia ei voi antaa", julkinenProjekti.status);
throw new NotFoundError("Projekti ei ole nähtävilläolovaiheessa, joten muistutuksia ei voi antaa");
}

const muistutus = adaptMuistutusInput(muistutusInput);
if (muistutus.liite) {
muistutus.liite = await fileService.persistFileToProjekti({
uploadedFileSource: muistutus.liite,
oid,
targetFilePathInProjekti: "muistutukset/" + muistutus.id,
});
}

await muistutusEmailService.sendEmailToKirjaamo(projektiFromDB, muistutus);

if (muistutus.sahkoposti && isValidEmail(muistutus.sahkoposti)) {
await muistutusEmailService.sendEmailToMuistuttaja(projektiFromDB, muistutus);
} else {
log.error("Muistuttajalle ei voitu lähettää kuittausviestiä: ", muistutus.sahkoposti);
}

//TODO: kasvata ja tallenna lähetettyjen muistutusten määrää projektilla ->
// ehkä aikaleimoina, niin lisää jäljitettävyyttä?

return "OK";
}
}

export const muistutusHandler = new MuistutusHandler();
8 changes: 8 additions & 0 deletions backend/src/util/emailUtil.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// eslint-disable-next-line no-useless-escape
const emailRegex = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/g;
export function isValidEmail(email: string): boolean {
if (emailRegex.test(email)) {
return true;
}
return false;
}
Loading

0 comments on commit c105cdb

Please sign in to comment.