I wonder what SST constructs I should use for our ...
# help
d
I wonder what SST constructs I should use for our ReactSite to be notified of an update in a dynamodb entry? We are not using GraphQL so subscriptions aren’t a possibility here. Our workflow is quite simple: • POST api/notes (saves a note to the Table) • We then upload an image cover to the note • The upload triggers a lambda on S3 • The lambda updates the “status” of the note we created above • This status change should update on the front end the status field Multiple “partial” solutions that come to mind: • Websocket API - but it feels overkill • Using the lambda to trigger the event - feels redundant because we will anyway update the DynamoDB • Maybe Topic but I’m not sure how easy it is to add this to ReactSite (I see there is an example in the Docs for that - but there isn’t the frontend part of it) • Dynamo Stream seem to to be able to “trigger the event” Any ideas?
o
I’d probably use a dynamo stream to trigger a lambda, which calls a service like pusher. Depends on if this is a one off use case, or you need to do this a lot.
d
We do upload a cover to note only once at the begining and the status will change about 4 times in the first 3 minutes. There is no point to subscribe after that. So really what we need is to subscribe to those conversion statuses one the first create. There will be only 1 person connected to the pipe to receive those status as well. So I feel a pub/Sub type thing might be very over killed
a
You identified two choices: 3rd party services and API Gateway Websockets. With the limited time frame, is it acceptable to just poll? Final option I know of is to put your web client onto an event bus; AWS IoT Core pub/sub can actually do that. (No other IoT needed.) This little library helps and is easier than managing websockets. If you ultimately find something else, please share here. I'd love to know about other possibilities.
d
At this point, considering that I want to explore SST I would like ideally to stay within the AWS built in products. Furthermore, maybe even stay with SST possibilities. I think I'm going to try “Topic” construct with DynamoDB stream first.
I did actually solve my problem. The solution I found: • DynamoDB stream triggers a Lambda • Lambda publishes to IoT • Amplify.PubSub subscribe to IoT • My create API endpoint will Invoke a lambda that will allow the current user to sub, read, connect to IoT All of this done with SST and CDK. IoT Stack
Copy code
import * as sst from '@serverless-stack/resources';
import * as customResource from '@aws-cdk/custom-resources';
import * as iot from '@aws-cdk/aws-iot';

export default class IotStack extends sst.Stack {
  iotEndpointAddress;

  attachPolicyFunction;

  constructor(scope, id, props) {
    super(scope, id, props);

    const getIoTEndpoint = new customResource.AwsCustomResource(this, 'IoTEndpoint', {
      onCreate: {
        service: 'Iot',
        action: 'describeEndpoint',
        physicalResourceId: customResource.PhysicalResourceId.fromResponse('endpointAddress'),
        parameters: {
          "endpointType": "iot:Data-ATS"
        }
      },
      policy: customResource.AwsCustomResourcePolicy.fromSdkCalls({
        resources: customResource.AwsCustomResourcePolicy.ANY_RESOURCE
      })
    });

    this.iotEndpointAddress = getIoTEndpoint.getResponseField('endpointAddress');

    const iotAuthPolicy = new iot.CfnPolicy(this, 'IotAuthPolicy', {
      policyName: 'iot-auth-policy',
      policyDocument: {
        "Version": "2012-10-17",
        "Statement": [
          {
            "Effect": "Allow",
            "Action": "iot:*",
            "Resource": "*"
          }
        ]
      },
    });

    this.attachPolicyFunction = new sst.Function(this, 'AttachPolicy', {
      handler: 'src/functions/attachPolicy.main',
      permissions: ["iot:AttachPrincipalPolicy"],
      environment: {
        IOT_AUTH_POLICY: iotAuthPolicy.ref
      },
    });
  }
}
attach policy to user:
Copy code
import { Iot } from 'aws-sdk';
import handler from '../util/handler';

export const main = handler(async (event) => {
  await new Iot().attachPrincipalPolicy({
    principal: event.requestContext.authorizer.iam.cognitoIdentity.identityId,
    policyName: process.env.IOT_AUTH_POLICY,
  }).promise();
});
On my ReactSite, I the calls to PubSub have to be in a certain order: index.js
Copy code
import Amplify, { PubSub } from 'aws-amplify';
import { AWSIoTProvider } from '@aws-amplify/pubsub/lib/Providers';

Amplify.addPluggable(new AWSIoTProvider({
  aws_pubsub_region: config.pubsub.REGION,
  aws_pubsub_endpoint: config.pubsub.ENDPOINT,
}));

Amplify.configure({...});

PubSub.configure();
In my note.js
Copy code
const sub = PubSub.subscribe(`updateStatus-${id}`).subscribe({
      next: data => alert(JSON.stringify(data.value)),
      error: error => console.error(error),
      close: () => alert('Done'),
    });

    // sub.unsubscribe();
Finally, send the message:
Copy code
import { IotData } from 'aws-sdk';
import handler from '../util/handler';

export const main = handler(async (event) => {
  const data = ...;
  await new IotData({ endpoint: process.env.IOT_ENDPOINT }).publish({
    topic: `updateStatus-${newImage.noteId}`,
    payload: JSON.stringify(data);
    qos: 0
  }).promise();
});
a
Copy code
"Effect": "Allow",
"Action": "iot:*"
Looks like you are giving every authenticated user access to listen in to all traffic and send data spoofing anybody else.