1import * as dataexchange from "@distilled.cloud/aws/dataexchange";
2import * as Effect from "effect/Effect";
3import * as Layer from "effect/Layer";
4import * as Binding from "../../Binding.ts";
5import { isBindingHost } from "../Lambda/Function.ts";
6import { StartJob } from "./StartJob.ts";
7
8/**
9 * Bespoke implementation (not the shared account builder): a started job
10 * runs with the caller's forwarded permissions, so on top of the
11 * `dataexchange:*` actions it needs S3 access to AWS Data Exchange's own
12 * service buckets (`arn:aws:s3:::*aws-data-exchange*`) — import jobs write
13 * staged assets there (`s3:PutObject`/`s3:PutObjectAcl`), export jobs read
14 * them (`s3:GetObject`). Mirrors the AWS-managed
15 * `AWSDataExchangeProviderFullAccess` policy, including the `aws:CalledVia`
16 * condition that limits the S3 grant to calls made through Data Exchange.
17 */
18export const StartJobHttp = Layer.effect(
19 StartJob,
20 Effect.gen(function* () {
21 const op = yield* dataexchange.startJob;
22
23 return Effect.fn(function* () {
24 if (!globalThis.__ALCHEMY_RUNTIME__) {
25 const host = yield* Binding.Host;
26 if (isBindingHost(host)) {
27 yield* host.bind`Allow(${host}, AWS.DataExchange.StartJob())`({
28 policyStatements: [
29 {
30 Effect: "Allow",
31 Action: [
32 "dataexchange:StartJob",
33 "dataexchange:CreateAsset",
34 "dataexchange:GetAsset",
35 ],
36 Resource: ["*"],
37 },
38 {
39 Effect: "Allow",
40 Action: ["s3:GetObject", "s3:PutObject", "s3:PutObjectAcl"],
41 Resource: ["arn:aws:s3:::*aws-data-exchange*"],
42 Condition: {
43 "ForAnyValue:StringEquals": {
44 "aws:CalledVia": ["dataexchange.amazonaws.com"],
45 },
46 },
47 },
48 ],
49 });
50 }
51 }
52 return Effect.fn("AWS.DataExchange.StartJob")(function* (
53 request: dataexchange.StartJobRequest,
54 ) {
55 return yield* op(request);
56 });
57 });
58 }),
59);