// Amazon SNS from both sides: a shipping event published with attributes, a batch of 13 with one
// message SNS refused, subscribers listed past a page, and an endpoint taking what SNS posts,
// checked against SNS's certificate and confirming its own subscription.
//
//   AWS_REGION=eu-west-1 AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... quillo run aws_sns.qwe
//   then open http://localhost:8080/publish, /batch, /subscribers, /incoming-check
//
// Point an https subscription at POST /sns. SNS_API_URL reaches LocalStack or a test server, and
// SNS_CERT_HOSTS names a test server allowed to hold the signing certificate.

use "aws_sns.qwer"

let TOPIC = "arn:aws:sns:eu-west-1:123456789012:shipping"

route GET /publish = {
    sent: sns_publish(TOPIC, { order: 1001, carrier: "DHL" }, { subject: "Order shipped", attributes: { kind: "shipped", weight: 2.5, tags: ["eu", "fragile"] } })?.message_id,
    made: sns_create_topic("shipping", {})?,
    too_long: sns_publish(TOPIC, "x", { subject: "line one\nline two" }).error,
    no_topic: sns_publish("arn:aws:sns:eu-west-1:123456789012:none", "x", {}).error,
}

route GET /batch {
    let entries = map(range(0, 13), (i) => { id: `m${i}`, message: if i == 11 { "REFUSE" } else { `event ${i}` }, attributes: { n: i } })
    let done = sns_publish_batch(TOPIC, entries, {})?
    { sent: len(done.sent), requests: done.requests, failed: done.failed }
}

route GET /subscribers {
    let arn = sns_subscribe(TOPIC, "https", "https://shop.example.com/sns", { attributes: { RawMessageDelivery: "true" } })?
    let list = sns_subscriptions(TOPIC, {})?
    { arn: arn, count: len(list), first: list[0].protocol, topics: len(sns_topics({})?), gone: sns_unsubscribe("arn:aws:sns:eu-west-1:123456789012:shipping:0001", {})? }
}

// What SNS posts here is believed only after its signature checks out.
route POST /sns {
    let got = sns_handle(request.body, { topics: [TOPIC] })
    if is_err(got) { reply 403 got.error }
    { type: got.type }
}

// The stand-in signs messages as SNS does; each is taken or refused by the check above.
route GET /incoming-check {
    let base = env("SNS_API_URL", "")
    let v1 = http.get(base + "/_sample?kind=notification&version=1").body
    let v2 = http.get(base + "/_sample?kind=notification&version=2").body
    let confirm = http.get(base + "/_sample?kind=confirm&version=1").body
    let tampered = replace(v2, "shipped", "stolen")
    let evil = http.get(base + "/_sample?kind=evil-cert&version=1").body
    let old = http.get(base + "/_sample?kind=old&version=1").body
    let other = http.get(base + "/_sample?kind=other-topic&version=1").body
    let good = sns_verify(v1, {})?
    {
        v1: good.subject,
        attr: good.attributes.kind,
        body: good.json.status,
        v2: sns_verify(v2, { topics: [TOPIC] })?.message_id,
        confirmed: sns_handle(confirm, {})?.subscription,
        tampered: sns_verify(tampered, {}).error,
        evil_cert: sns_verify(evil, {}).error,
        replay: sns_verify(old, {}).error,
        wrong_topic: sns_verify(other, { topics: [TOPIC] }).error,
        not_sns: sns_verify("{\"Type\":\"Other\"}", {}).error,
    }
}
