Skip to content

Commit

Permalink
fix: HASSUYP-376 Aseta Velhon sijaintigeometriat tallentumaan tiedost…
Browse files Browse the repository at this point in the history
…oon (#1247)

* fix: HASSUYP-376 Aseta Velhon sijaintigeometriat tallentumaan tiedostoon

* korjaa karttatiedoston poistaminen ja laita useEffectit catchissä asettaamaan geoJSON nulliksi

---------

Co-authored-by: tomi korkalainen <tomi.korkalainen@cgi.com>
  • Loading branch information
tkork and tomi korkalainen authored Jul 30, 2024
1 parent cf71bcb commit ec5146a
Show file tree
Hide file tree
Showing 11 changed files with 136 additions and 44 deletions.
7 changes: 3 additions & 4 deletions backend/integrationtest/api/records/JATKOPAATOS_1_ALKU.json
Original file line number Diff line number Diff line change
Expand Up @@ -1480,7 +1480,6 @@
"vahainenMenettely": false,
"velho": {
"asiatunnusVayla": "HASSU/123/2023",
"geoJSON": null,
"kunnat": [
91,
92
Expand All @@ -1490,11 +1489,11 @@
"maakunnat": [
1
],
"nimi": "HASSU AUTOMAATTITESTIPROJEKTI1",
"nimi": "VLS-TESTI HASSU AUTOMAATTITESTIPROJEKTI1",
"suunnittelustaVastaavaViranomainen": "VAYLAVIRASTO",
"tyyppi": "TIE",
"varahenkilonEmail": null,
"vastuuhenkilonEmail": "valhe.kouneli@cgi.com",
"varahenkilonEmail": "mikko.haapamaki102@cgi.com",
"vastuuhenkilonEmail": "mikko.haapamki101@cgi.com",
"vaylamuoto": [
"tie"
]
Expand Down
14 changes: 14 additions & 0 deletions backend/src/files/ProjektiPath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ export class ProjektiPaths extends PathTuple {
return new EULogotPaths(this);
}

sijaintitieto(): PathTuple {
return new SijaintitietoPaths(this);
}

karttarajaus(): PathTuple {
return new KarttarajausPaths(this);
}
Expand Down Expand Up @@ -174,6 +178,16 @@ class EULogotPaths extends SimpleRootPath {
}
}

class SijaintitietoPaths extends SimpleRootPath {
constructor(parent: PathTuple) {
super(parent);
}

get rootPath(): string {
return "sijaintitieto";
}
}

class KarttarajausPaths extends SimpleRootPath {
constructor(parent: PathTuple) {
super(parent);
Expand Down
5 changes: 4 additions & 1 deletion backend/src/projekti/projektiHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,10 @@ async function handleJatkopaatos1SaamePDF(dbProjekti: DBProjekti) {
saamePDFt,
new ProjektiPaths(dbProjekti.oid).jatkoPaatos1Vaihe(jatkoPaatos1Vaihe),
["kuulutusPDF", "kuulutusIlmoitusPDF"],
[API.AsiakirjaTyyppi.JATKOPAATOSKUULUTUS, API.AsiakirjaTyyppi.ILMOITUS_JATKOPAATOSKUULUTUKSESTA_KUNNALLE_JA_TOISELLE_VIRANOMAISELLE],
[
API.AsiakirjaTyyppi.JATKOPAATOSKUULUTUS,
API.AsiakirjaTyyppi.ILMOITUS_JATKOPAATOSKUULUTUKSESTA_KUNNALLE_JA_TOISELLE_VIRANOMAISELLE,
],
[API.Kieli.POHJOISSAAME, API.Kieli.POHJOISSAAME]
);
}
Expand Down
9 changes: 9 additions & 0 deletions backend/src/sqsEvents/sqsEventHandlerLambda.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,9 +329,18 @@ async function synchronizeKuntaLogo(ctx: ImportContext) {
}
}

async function synchronizeSijaintitieto(ctx: ImportContext) {
const status = ctx.projektiStatus;
const projekti = ctx.projekti;
if (status && status !== API.Status.EI_JULKAISTU && status !== API.Status.EI_JULKAISTU_PROJEKTIN_HENKILOT) {
await synchronizeFilesToPublic(projekti.oid, new ProjektiPaths(projekti.oid).sijaintitieto(), dayjs("2000-01-01"));
}
}

async function synchronizeAll(ctx: ImportContext): Promise<boolean> {
await synchronizeEULogot(ctx);
await synchronizeKuntaLogo(ctx);
await synchronizeSijaintitieto(ctx);

const manager: ProjektiTiedostoManager = ctx.manager;
return (
Expand Down
50 changes: 39 additions & 11 deletions backend/src/velho/velhoAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@ import difference from "lodash/difference";
import { kuntametadata } from "hassu-common/kuntametadata";
import { log } from "../logger";
import { assertIsDefined } from "../util/assertions";
import { IllegalArgumentError, VelhoGeoJsonSizeExceededError } from "hassu-common/error";
import { IllegalArgumentError } from "hassu-common/error";
import { parseDate } from "../util/dateUtil";
import isEqual from "lodash/isEqual";
import { asianhallintaService } from "../asianhallinta/asianhallintaService";
import { isProjektiAsianhallintaIntegrationEnabled } from "../util/isProjektiAsianhallintaIntegrationEnabled";
import { fileService } from "../files/fileService";
import { ProjektiPaths } from "../files/ProjektiPath";
import { eventSqsClient } from "../sqsEvents/eventSqsClient";

let metaDataJSON: any;

Expand Down Expand Up @@ -177,7 +180,11 @@ function getKunnat(data: ProjektiProjekti): number[] | undefined {
return kunnat.sort(numberSorter);
}
try {
return data.ominaisuudet["muu-kunta"]?.split(",").map(s => s.trim()).map(kuntametadata.idForKuntaName).sort();
return data.ominaisuudet["muu-kunta"]
?.split(",")
.map((s) => s.trim())
.map(kuntametadata.idForKuntaName)
.sort();
} catch (e) {
log.error("Virheellinen muu-kunta: '" + data.ominaisuudet["muu-kunta"] + "'");
return undefined;
Expand All @@ -198,7 +205,11 @@ function getMaakunnat(data: ProjektiProjekti) {
return maakunnat.sort(numberSorter);
}
try {
return data.ominaisuudet["muu-maakunta"]?.split(",").map(s => s.trim()).map(kuntametadata.idForMaakuntaName).sort();
return data.ominaisuudet["muu-maakunta"]
?.split(",")
.map((s) => s.trim())
.map(kuntametadata.idForMaakuntaName)
.sort();
} catch (e) {
log.error("Virheellinen muu-maakunta: '" + data.ominaisuudet["muu-maakunta"] + "'");
return undefined;
Expand Down Expand Up @@ -240,14 +251,15 @@ export async function adaptProjekti(data: ProjektiProjekti, linkitetytProjektit?
asiatunnusVayla: data.ominaisuudet["asiatunnus-vaylavirasto"],
asiatunnusELY: data.ominaisuudet["asiatunnus-ely"],
linkitetytProjektit: linkitetytProjektit ? getLinkitetytProjektit(linkitetytProjektit) : null,
geoJSON: getGeoJSON(data),
},
kasittelynTila: adaptKasittelynTilaFromVelho(data.ominaisuudet),
kayttoOikeudet: [],
};

const asiaId = (await isProjektiAsianhallintaIntegrationEnabled(projekti)) ? await haeAsiaId(projekti) : undefined;

await persistGeoJsonFile(data);

projekti.asianhallinta = { inaktiivinen: false, asiaId };

return projekti;
Expand All @@ -262,10 +274,30 @@ async function haeAsiaId(projekti: DBProjekti) {
}
}

// 100KB
const MAX_GEOJSON_SIZE = 100 * 1000;
async function persistGeoJsonFile(data: ProjektiProjekti) {
const geoJSON = getGeoJSON(data);
const fileName = "sijaintitieto/sijaintitieto.geojson";
if (geoJSON) {
log.info("Tallennetaan sijaintitieto geoJSON-tiedostoon");
await fileService.createFileToProjekti({
oid: data.oid,
fileName,
path: new ProjektiPaths(data.oid),
contents: Buffer.from(geoJSON, "utf-8"),
contentType: "application/geo+json",
});
} else {
log.info("Poistetaan sijaintitieto geoJSON-tiedosto");
await fileService.deleteYllapitoFileFromProjekti({
filePathInProjekti: "/" + fileName,
oid: data.oid,
reason: "Velhossa ei ole sijaintitietoja",
});
}
await eventSqsClient.synchronizeAineisto(data.oid);
}

function getGeoJSON(data: ProjektiProjekti) {
function getGeoJSON(data: ProjektiProjekti): string | null {
const geometrycollection = data.geometrycollection?.geometries;
const geometriaEiOleKeskipiste = (geometry: ProjektiProjektiLuontiMitattugeometriaGeometria): boolean =>
!isEqual(geometry, data.keskipiste?.["geometria-wgs84"]);
Expand All @@ -291,10 +323,6 @@ function getGeoJSON(data: ProjektiProjekti) {

const geoJsonString = JSON.stringify(geoJSON);

if (new Blob([geoJsonString]).size > MAX_GEOJSON_SIZE) {
throw new VelhoGeoJsonSizeExceededError("Velhoon asetetut projektin geometriat ylittävät 100kB maksimikoon");
}

return geoJsonString;
}

Expand Down
2 changes: 0 additions & 2 deletions backend/test/velho/__snapshots__/velhoAdapter.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ Object {
"velho": Object {
"asiatunnusELY": undefined,
"asiatunnusVayla": undefined,
"geoJSON": null,
"kunnat": Array [
49,
91,
Expand Down Expand Up @@ -50,7 +49,6 @@ Object {
"velho": Object {
"asiatunnusELY": undefined,
"asiatunnusVayla": undefined,
"geoJSON": null,
"kunnat": Array [
49,
91,
Expand Down
10 changes: 0 additions & 10 deletions common/error/VelhoGeoJsonSizeExceededError.ts

This file was deleted.

1 change: 0 additions & 1 deletion common/error/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,3 @@ export * from "./SystemError";
export * from "./velhoError";
export * from "./velhoUnavailableError";
export * from "./AineistoMuokkausError";
export * from "./VelhoGeoJsonSizeExceededError";
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { ComponentProps } from "react";
import React, { ComponentProps, useEffect, useState } from "react";
import { useProjektiJulkinen } from "src/hooks/useProjektiJulkinen";
import HassuStack from "@components/layout/HassuStack";
import Section from "@components/layout/Section";
Expand All @@ -10,10 +10,37 @@ import { formatNimi } from "../../../util/userUtil";
import { muodostaOrganisaatioTeksti } from "src/util/kayttajaTransformationUtil";
import { KarttaKansalaiselle } from "../common/KarttaKansalaiselle";
import { SideCard, SideCardHeading, SideCardContent } from "./SideCard";
import axios from "axios";

const ProjektiSideNavigation = styled((props) => {
const { t, lang } = useTranslation("projekti-side-bar");
const { data: projekti } = useProjektiJulkinen();

const [geoJSON, setGeoJSON] = useState<string | null>(projekti?.velho.geoJSON ?? null);

useEffect(() => {
const updateGeoJson = async (oid: string) => {
try {
const response = await axios.get(`/tiedostot/suunnitelma/${oid}/sijaintitieto/sijaintitieto.geojson`, {
responseType: "blob",
headers: { "Cache-Control": "no-cache", Pragma: "no-cache", Expires: "0" },
});

if (!(response.data instanceof Blob)) {
return;
}
const text = await response.data.text();
setGeoJSON(text);
} catch (e) {
setGeoJSON(null);
// Ei tehdä mitään. Karttarajaustiedostoa ei toistaiseksi ole
}
};
if (projekti?.oid && !projekti?.velho.geoJSON) {
updateGeoJson(projekti.oid);
}
}, [projekti]);

if (!projekti) {
return <></>;
}
Expand Down Expand Up @@ -73,10 +100,10 @@ const ProjektiSideNavigation = styled((props) => {
</SideCardContent>
</SideCard>
<SideCard>
{projekti.velho.geoJSON && (
{geoJSON && (
<>
<SideCardHeading>{t("suunnitelma_kartalla")}</SideCardHeading>
<KarttaKansalaiselle geoJSON={projekti.velho.geoJSON} />
<KarttaKansalaiselle geoJSON={geoJSON} />
</>
)}
</SideCard>
Expand Down
37 changes: 35 additions & 2 deletions src/components/projekti/perusosio/Perusosio.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import React, { useEffect, useState } from "react";
import ProjektiPerustiedot from "./ProjektiPerustiedot";
import { ProjektiLisatiedolla } from "hassu-common/ProjektiValidationContext";
import ProjektiKuntatiedot from "./ProjektiKuntatiedot";
Expand All @@ -11,6 +11,8 @@ import { FormState, UseFormRegister } from "react-hook-form";
import { FormValues } from "@pages/yllapito/projekti/[oid]";
import SectionContent from "@components/layout/SectionContent";
import { H3, H4 } from "../../Headings";
import axios from "axios";
import useSnackbars from "src/hooks/useSnackbars";

export interface PerusosioProps {
projekti: ProjektiLisatiedolla;
Expand All @@ -23,6 +25,37 @@ interface ProjektinPerusosioProps extends PerusosioProps {
}

export default function ProjektinPerusosio({ projekti, register, formState, lukutila }: Readonly<ProjektinPerusosioProps>) {
const [geoJSON, setGeoJSON] = useState<string | null>(projekti.velho.geoJSON ?? null);
const { showErrorMessage } = useSnackbars();

useEffect(() => {
const updateGeoJson = async () => {
try {
const response = await axios.get(`/yllapito/tiedostot/projekti/${projekti.oid}/sijaintitieto/sijaintitieto.geojson`, {
responseType: "blob",
headers: { "Cache-Control": "no-cache", Pragma: "no-cache", Expires: "0" },
});

if (!(response.data instanceof Blob)) {
return;
}
const text = await response.data.text();
setGeoJSON(text);
} catch (e) {
if (axios.isAxiosError(e) && e.response?.status === 404) {
// Ei tehdä mitään. Karttarajaustiedostoa ei toistaiseksi ole
setGeoJSON(null);
} else {
console.log(e);
showErrorMessage("Projektin sijaintitiedon lataaminen epäonnistui");
}
}
};
if (!projekti.velho.geoJSON) {
updateGeoJson();
}
}, [projekti, showErrorMessage]);

return (
<>
<Section>
Expand All @@ -33,7 +66,7 @@ export default function ProjektinPerusosio({ projekti, register, formState, luku
<ProjektiKuntatiedot projekti={projekti} />
<ContentSpacer>
<H4 className="vayla-smallest-title">Projekti kartalla</H4>
<KarttaKansalaiselle geoJSON={projekti.velho.geoJSON} />
<KarttaKansalaiselle geoJSON={geoJSON} />
</ContentSpacer>
</Section>
{register && formState && (
Expand Down
12 changes: 2 additions & 10 deletions src/util/errorMessageUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ type ErrorInfo = {
};

// Ei nayteta korrelaatio IDeita eikä virheyksityiskohtia kansalaisille tuotanto- ja koulutusympäristöissä
const showErrorDetails = (props: GenerateErrorMessageProps): boolean => (process.env.ENVIRONMENT !== "prod" && process.env.ENVIRONMENT !== "training") || props.isYllapito;
const showErrorDetails = (props: GenerateErrorMessageProps): boolean =>
(process.env.ENVIRONMENT !== "prod" && process.env.ENVIRONMENT !== "training") || props.isYllapito;

// Jos halutaan näyttää ei-geneerinen virheviesti api-virheestä,
// lisätään tähän arrayhin validator ja errorMessage -pari.
Expand Down Expand Up @@ -45,15 +46,6 @@ const nonGenericErrorMessages: { validator: NonGenericErrorMessageValidator; err
},
errorMessage: () => "Kiinteistönomistajatietojen tallennus epäonnistui.",
},
{
validator: ({ errorResponse }) => matchErrorClass(errorResponse, "VelhoGeoJsonSizeExceededError"),
errorMessage: (props) =>
constructErrorClassSpecificErrorMessage(
props,
"VelhoGeoJsonSizeExceededError",
"Projektivelhoon asetetut Projektin geometriat ylittävät VLS-järjestelmän 100kB maksimikoon. Yksinkertaista geometriaa ja yritä uudelleen."
),
},
{
validator: ({ errorResponse }) => matchErrorClass(errorResponse, "VelhoUnavailableError"),
errorMessage: (props) => constructErrorClassSpecificErrorMessage(props, "VelhoUnavailableError", "Projektivelhoon ei saatu yhteyttä."),
Expand Down

0 comments on commit ec5146a

Please sign in to comment.