Skip to content

Commit

Permalink
Merge pull request #90 from Opetushallitus/OY-5034_tyopoydan_emailien…
Browse files Browse the repository at this point in the history
…_palastelu

OY-5034 tyopoydan emailien palastelu
  • Loading branch information
jkorri authored Jan 14, 2025
2 parents 5c3e2d4 + 541921d commit 55d00c6
Show file tree
Hide file tree
Showing 13 changed files with 282 additions and 161 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
cache: 'maven'

- name: Deploy Client Library
if: ${{ success() }}
if: ${{ success() && format('refs/heads/{0}', github.event.repository.default_branch) == github.ref }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: mvn deploy --batch-mode -DskipTests -pl kirjasto -am
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import com.fasterxml.jackson.datatype.jdk8.Jdk8Module
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.nimbusds.jose.util.StandardCharset
import fi.oph.viestinvalitys.business.{Kieli, Kontakti, Lahetys, Liite, LiitteenTila, Prioriteetti, SisallonTyyppi, VastaanottajanTila, Viesti}
import fi.oph.viestinvalitys.business.{Kieli, Kontakti, Lahetys, Liite, LiitteenTila, Prioriteetti, SisallonTyyppi, VastaanottajanSiirtyma, VastaanottajanTila, Viesti}
import fi.oph.viestinvalitys.lahetys.LambdaHandler.SAHKOPOSTIOSOITE_EI_VALIDI_ERROR
import fi.oph.viestinvalitys.security.{AuditLog, AuditOperation}
import fi.oph.viestinvalitys.util.AwsUtil
import fi.oph.viestinvalitys.vastaanotto.model.Lahetys.Lahettaja
Expand Down Expand Up @@ -767,4 +768,29 @@ class IntegraatioTest extends BaseIntegraatioTesti {
}, 60.seconds)
catch
case e: Exception => Assertions.fail("Vastaanottaja ei muuttunut delivery-tilaan sallitussa ajassa")

/**
* Testataan virheellisen vastaanottajan (sähköposti ei validi) lähetys. Pitää mennä virhetilaan.
*/
@Test def testLuoViestiVirheellinenVastaanottaja(): Unit =
// luodaan viesti
val luoViestiResult = mvc.perform(jsonPost(LahetysAPIConstants.LUO_VIESTI_PATH, getViesti(vastaanottajat =
java.util.List.of(VastaanottajaImpl(Optional.empty(), Optional.of("täysin väärä osoite")))))
.`with`(user("kayttaja").roles(SecurityConstants.SECURITY_ROOLI_LAHETYS_FULL.replace("ROLE_", ""))))
.andExpect(status().isOk()).andReturn()
val response = objectMapper.readValue(luoViestiResult.getResponse.getContentAsString, classOf[LuoViestiSuccessResponseImpl])
val vastaanottaja = kantaOperaatiot.getLahetyksenVastaanottajat(response.lahetysTunniste, Option.empty, Option.empty).head

// haetaan viestin ainoan vastaanottajan tila sekunnin välein kunnes tilassa VIRHE
var siirtyma: VastaanottajanSiirtyma = null
try
Await.ready(Future {
while (siirtyma==null || !VastaanottajanTila.VIRHE.equals(siirtyma.tila))
Thread.sleep(1000)
siirtyma = kantaOperaatiot.getVastaanottajanSiirtymat(vastaanottaja.tunniste).head
}, 60.seconds)
catch
case e: Exception => Assertions.fail("Vastaanottaja ei muuttunut delivery-tilaan sallitussa ajassa")

Assertions.assertEquals(SAHKOPOSTIOSOITE_EI_VALIDI_ERROR, siirtyma.lisatiedot)
}
41 changes: 36 additions & 5 deletions kirjasto/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
## Viestinvälityspalvelu kirjasto

Kirjaston avulla asiakasjärjestelmät voivat käyttää viestinvälityspalvelua java-rajapinnan läpi.
Kirjaston avulla asiakasjärjestelmät voivat käyttää viestinvälityspalvelua java-rajapinnan läpi. Käyttöön tarvitaan
tämä riippuvuus, sekä palvelutunnus jolla on tarvittavat oikeudet viestien lähettämiseksi. Kirjaston transitiiviset
riippuvuudet on pyritty minimoimaan.

### Käyttö

Expand All @@ -9,16 +11,20 @@ Client instanssi luodaan builderilla, esim:
```
ViestinvalitysClient client = ClientBuilder.viestinvalitysClientBuilder()
.withEndpoint(<viestinvälityspalvelun osoite, esim: "https://viestinvalitys.hahtuvaopintopolku.fi">)
.withUsername(<käyttäjätunnus>)
.withUsername(<palvelutunnus>)
.withPassword(<salana>)
.withCasEndpoint(<cas-osoite, esim: https://virkalija.hahtuvaopintopolku.fi/cas>)
.withCallerId(<caller id>)
.build()
```

Tämän jälkeen client-instanssilla voi luoda pyyntöjä jotka luovat liitteitä, lähetyksiä, ja viestejä, sekä tarkastelevat näiden tilaa, Esim. seuraavasti:
Tämän jälkeen client-instanssilla voi luoda pyyntöjä jotka luovat liitteitä, lähetyksiä, ja viestejä, sekä tarkastella
näiden tilaa, Esim. seuraavasti:

Voidaan joko luoda ensin lähetys ja liittää samaan lähetykseen useita viestejä
Voidaan joko luoda ensin lähetys ja liittää samaan lähetykseen useita viestejä. Lähetysten käyttö on tarpeellista esim.
tilanteissa joissa a) haluataan tarkastella useita vastaanottajakohtaisesti kustomoituja viestejä kokonaisuutena, tai
b) viestin kokonaisvastaanottajamäärä ylittää yksittäisen viestin maksimivastaanottajamäärän (ks. lähetysrajapinnan
Viesti-luokka), jolloin viesti täytyy palastella useammaksi viestiksi.

```
LuolahetysResponse luoLahetysResponse = viestinvalitysClient.luoLahetys(
Expand All @@ -33,7 +39,10 @@ ViestinvalitysBuilder.lahetysBuilder()
ViestinvalitysBuilder.viestiBuilder()
.withOtsikko("viestin otsikko")
.withHtmlSisalto("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" /><title></title></head><body style=\"margin: 0; font-family: 'Open Sans', Arial, sans-serif;\"><H1>Otsikko</h1><p>Viestin sisältö</p><p>Ystävällisin terveisin<br/>Opintopolku</p></body></html>")
.withHtmlSisalto("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"
\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html><head><meta http-equiv=\"Content-Type\"
content=\"text/html; charset=UTF-8\" /><title></title></head><body style=\"margin: 0; font-family: 'Open Sans',
Arial, sans-serif;\"><H1>Otsikko</h1><p>Viestin sisältö</p><p>Ystävällisin terveisin<br/>Opintopolku</p></body></html>")
.withKielet("fi")
.withVastaanottajat(ViestinvalitysBuilder.vastaanottajatBuilder()
.withVastaanottaja(Optional.empty(), "[email protected]")
Expand Down Expand Up @@ -64,6 +73,28 @@ Tai luoda viestejä erillisinä lähetyksinä
.withLahettaja(Optional.empty(), "[email protected]")
.build())
```

On suositeltavaa käyttää builder-luokkia lähetysten, viestien jne. luomiseen. Tällöin kaikki pakolliset kentät tulevat
kaikissa tilanteissa täytettyä.

### Validointi

Rajapinta pyrkii validoimaan kaikkien pyyntöjen kaikki kentät, ja kaikille kentille on pyritty asettamaan esim.
maksimipituus. Erityisesti tulee huomata että yksittäisellä viestillä on maksimimäärä vastaanottajia, ja tätä
suuremmalle vastaanottajajoukolle suunnatut viestit tulee palastella useammaksi yksittäiseksi viestiksi. Yksittäisten
kenttien rajoitteet on pyritty kuvaamaan kattavasti Swagger-kuvauksessa, joka puolestaan pyrkii perustumaan suoraan
lähdekoodissa olevien vakioihin ylläpidettävyyden varmistamiseksi.

### Idempotency-avain -toiminnallisuus

Rajapinta sisältää toiminnallisuuden jonka avulla voidaan varmistua siitä ettei samaa viestiä lähetetä toistuvasti
viestinvälityspalvelun tai asiakasjärjestelmän virheen seurauksena. Viestin mukaan voidaan liittää uniikki
idempotencyKey-avain, joka tallennetaan viestinvälityspalveluun. Mikäli sama asiakasjärjestelmä (ts. cas-identiteetti)
yrittää lähettää uutta viestiä samalla avaimella, palautetaan aikaisemman viestin tiedot.

HUOMAA että jos lähetettävä viesti on palasteltu useammaksi viestiksi koska vastaanottajien kokonaismäärä ylittää
yksittäisen viestin maksimivastaanottajamäärä, pitää kaikille viesteille luonnollisesti olla oma idempotency-avain!

### Kirjaston päivitys

Jos kirjastoa on tarve muuttaa tai päivittää, nosta projektin parent-pomissa oleva revision
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ import scala.jdk.CollectionConverters.*
* @param sahkopostiOsoite
*/
case class LahettajaImpl(
@(Schema @field)(example = "Opintopolku", maxLength = ViestiImpl.VIESTI_NIMI_MAX_PITUUS)
@(Schema @field)(example = "Opintopolku", maxLength = Viesti.VIESTI_NIMI_MAX_PITUUS)
@BeanProperty nimi: Optional[String],

@(Schema @field)(description="Domainin pitää olla opintopolku.fi", example = "[email protected]", requiredMode=RequiredMode.REQUIRED)
@(Schema @field)(description="Domainin pitää olla opintopolku.fi", example = "[email protected]", requiredMode=RequiredMode.REQUIRED, maxLength = Viesti.VIESTI_OSOITE_MAX_PITUUS)
@BeanProperty sahkopostiOsoite: Optional[String],
) extends Lahettaja {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,67 @@
import java.util.Optional;
import java.util.UUID;

/**
* Lähetettävä viesti.
*
* Instansseja voi luoda {@link ViestinvalitysBuilder#viestiBuilder} -metodilla.
*/
public interface Viesti {

static final int OTSIKKO_MAX_PITUUS = 255;

/**
* Viestin sisällön maksimipituus, tätä rajoittaa SES-palvelun hyväksymä viestin maksimikoko, jonka alle halutaan
* jäädä reilusti jotta mahdolliset liitteetkin mahtuvat mukaan.
*/
static final int SISALTO_MAX_PITUUS = 6291456; // =6*1024*1024

/**
* Viestin maksimikoko liitteineen.
*/
static final String VIESTI_MAX_SIZE_MB_STR = "8";
static final int VIESTI_MAX_SIZE = Integer.parseInt(VIESTI_MAX_SIZE_MB_STR) * 1024 * 1024;

/**
* Viestin lähettäjän ja yksittäisten vastaanottajien nimien maksimipituus.
*/
static final int VIESTI_NIMI_MAX_PITUUS = 128;
static final int VIESTI_OSOITE_MAX_PITUUS = 512;

static final int VIESTI_SALAISUUS_MIN_PITUUS = 8;
static final int VIESTI_SALAISUUS_MAX_PITUUS = 1024;
static final int VIESTI_MASKI_MIN_PITUUS = 8;
static final int VIESTI_MASKI_MAX_PITUUS = 1024;
static final int VIESTI_MASKIT_MAX_MAARA = 32;

static final int VIESTI_METADATA_AVAIMET_MAX_MAARA = 1024;
static final String VIESTI_METADATA_SALLITUT_MERKIT = "a-z, A-Z, 0-9 ja -_.";
static final String VIESTI_METADATA_AVAIN_MAX_PITUUS_STR = "64";
static final String VIESTI_METADATA_ARVO_MAX_PITUUS_STR = "64";
static final String VIESTI_METADATA_ARVOT_MAX_MAARA_STR = "1024";
static final int VIESTI_METADATA_AVAIN_MAX_PITUUS = Integer.parseInt(VIESTI_METADATA_AVAIN_MAX_PITUUS_STR);
static final int VIESTI_METADATA_ARVO_MAX_PITUUS = Integer.parseInt(VIESTI_METADATA_ARVO_MAX_PITUUS_STR);
static final int VIESTI_METADATA_ARVOT_MAX_MAARA = Integer.parseInt(VIESTI_METADATA_ARVOT_MAX_MAARA_STR);

/**
* Yhden viestin maksimi vastaanottajamäärä. Jos haluat lähettää viestin tätä suuremmalle vastaanottajajoukolle, viesti
* pitää palastella useammaksi viestiksi. <strong>HUOMAA</strong> että jos käytät palastelua yhdessä idempotency-avain
* -toiminnalisuuden kanssa, idempotency-avain pitää generoida jokaiselle viestille erikseen.
*/
static final int VIESTI_VASTAANOTTAJAT_MAX_MAARA = 512;
static final int VIESTI_LIITTEET_MAX_MAARA = 128;

static final int VIESTI_ORGANISAATIO_MAX_PITUUS = 64;
static final int VIESTI_OIKEUS_MAX_PITUUS = 64;
static final int VIESTI_KAYTTOOIKEUS_MAX_MAARA = 128;

static final int VIESTI_IDEMPOTENCY_KEY_MAX_PITUUS = 64;
static final String VIESTI_IDEMPOTENCY_KEY_MAX_PITUUS_STR = "64";
static final String VIESTI_IDEMPOTENCY_KEY_SALLITUT_MERKIT= "a-z, A-Z, 0-9 ja -_.";

static final String VIESTI_SISALTOTYYPPI_TEXT = "text";
static final String VIESTI_SISALTOTYYPPI_HTML = "html";

Optional<String> getOtsikko();

Optional<String> getSisalto();
Expand Down
Loading

0 comments on commit 55d00c6

Please sign in to comment.