Setting up an SNS trigger and processing the request in AWS Lambda

Setting up an SNS topic is pretty well documented, but I struggled with how to take action when the event is triggered.

Turns out it’s pretty straightforward. The event['detail-type'] will be Scheduled Event.

myFunction:
  Type: AWS::Serverless::Function
  Properties:
    Handler: app.handler
    Events:
      SNSTopicTrigger:
        Type: SNS
        Properties:
          Topic: !Ref rSNSTopic

rSNSTopic:
  Type: AWS::SNS::Topic
  Properties:
    DisplayName: SNSTrigger

anotherFunction:
    Properties:
      Policies:
        - AWSLambdaExecute
        - Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
                - sns:Publish
              Resource: !Ref rSNSTopic

SNS calling function

const message = {
  default: JSON.stringify({
    name: 'John Doe',
    timestamp: new Date(),
  }),
};
var params = {
  TopicArn: YOUR_SNS_TOPIC,
  Subject: 'An Important Event',
  MessageStructure: 'json',
  Message: JSON.stringify(message),
};
await sns.publish(params).promise();

SNS processing function

if (event.Records && event.Records[0].EventSource === 'aws:sns') {
  const message = JSON.parse(event.Records[0].Sns.Message);
  console.log(['SNS event', message]);
  doSomething(message);
}

read more

Setting up a cron job and processing the request in AWS Lambda

Setting up a cron job in AWS lambda is pretty trivial, but I struggled with how to take action when the event is triggered. Turns out it’s pretty straightforward. The event['detail-type'] will be Scheduled Event.

myFunction:
  Type: AWS::Serverless::Function
  Properties:
    Handler: app.handler
    Events:
      myCron:
        Type: Schedule
        Properties:
          Schedule: "cron(? 10 * * Mon *)"
if (event['detail-type'] === 'Scheduled Event') {
  console.log('cron triggered');
  return await somethingThatWasScheduled(event);
}

read more

Generating a Cybersource Flex Key using ColdFusion

Cybersource is a payment provider with poor examples and poor documentation. I struggled through this for about 25 hours before I found the right combination of settings.

First you need to include the cybersource-rest-client-java and AuthenticationSDK jars on your classpath. You can do this by adding the following to your Application.cfc or by dropping them into the \cfusion\lib folder

this.javaSettings = {LoadPaths = [".\libs\cybersource-rest-client-java-0.0.35.jar", ".\libs\AuthenticationSdk-0.0.17.jar"], loadColdFusionClassPath = true, reloadOnChange = false};

I’ve created a struct to house the default Cybersource credentials as the sample environment. You’ll want to replace test and production with your credentials, ideally pulling them from secrets and not hardcoded and placed into source control. The second parameter is your target origin, which varies by environment and potentially by user workstation as well.

<cfscript>
writeDump(retrieveFlexKey('sample', 'http://localhost:8500'));

public String function retrieveFlexKey(String environment, String targetOrigin) throws Exception {

  var flexPublicKey = "NoKeyReturned";

  var environmentDetails = {
    sample: {
      merchantID: "testrest",
      runEnvironment: "apitest.cybersource.com",
      merchantKeyId: "08c94330-f618-42a3-b09d-e1e43be5efda",
      merchantsecretKey: "yBJxy6LjM2TmcPGu+GaJrHtkke25fPpUX+UY6/L/1tE="
    },
    test: {
      merchantID: "testrest",
      runEnvironment: "apitest.cybersource.com",
      merchantKeyId: "08c94330-f618-42a3-b09d-e1e43be5efda",
      merchantsecretKey: "yBJxy6LjM2TmcPGu+GaJrHtkke25fPpUX+UY6/L/1tE="
    },
    production: {
      merchantID: "testrest",
      runEnvironment: "apitest.cybersource.com",
      merchantKeyId: "08c94330-f618-42a3-b09d-e1e43be5efda",
      merchantsecretKey: "yBJxy6LjM2TmcPGu+GaJrHtkke25fPpUX+UY6/L/1tE="
    }
  };

  try {
    var details = structKeyExists(environmentDetails, environment) ? environmentDetails[environment] : environmentDetails['sample'];
    props = createObject('java', 'java.util.Properties');
    props.setProperty("authenticationType", "http_signature");
    props.setProperty("merchantID", details['merchantID']);
    props.setProperty("runEnvironment", details['runEnvironment']);
    props.setProperty("merchantKeyId", details['merchantKeyId']);
    props.setProperty("merchantsecretKey", details['merchantsecretKey']);

    requestInfo = createObject('java', 'Model.GeneratePublicKeyRequest');
    requestInfo.encryptionType("RsaOaep256");
    requestInfo.targetOrigin(arguments.targetOrigin);

    merchantConfig = createObject('java', 'com.cybersource.authsdk.core.MerchantConfig').init(props);
    apiClient = createObject('java', 'Invokers.ApiClient');
    apiClient.merchantConfig = merchantConfig;

    keyGenerationApi = createObject('java', 'Api.KeyGenerationApi').init(apiClient);
    response = keyGenerationApi.generatePublicKey("JWT", requestInfo);

    responseCode = apiClient.responseCode;
    status = apiClient.status;
    writeDump(responseCode);
    writedump(status);
    if (responseCode == '200' && status == 'OK') {
      return response.getKeyId();
    }
  } catch (Exception e) {
    writeDump(e);
    // you'll want to login any errors somewhere
  }

  return flexPublicKey;
}
</cfscript>

read more

Cypress - Failed to deserialize the snapshot blob

Trying to run npx cypress open and I received an error of

It looks like this is your first time using Cypress: 8.2.0

Cypress failed to start.

# Fatal error in , line 0
# Failed to deserialize the V8 snapshot blob. This can mean that the snapshot blob file is corrupted or missing.

Deleting node_modules and running npm install had no effect, but I was able to get it working again running npx cypress install --force.

If that doesn’t work you can also rename the Cypress cache folder. On windows it’s \AppData\Local\Cypress\Cache\<version>

read more

Converting a Roth IRA to a Traditional IRA at Vanguard

I mistakenly put money into my Traditional IRA at Vanguard this year instead of my Roth IRA. I couldn’t for the life me find directions on how to change this, so I called Vanguard. It’s actually very simple.

  1. Login to your account
  2. Click on FORMS in the header
  3. Click Add or remove money, trade within your account
  4. Click Remove excess distributions or contributions, convert from a traditional to a Roth IRA, or recharacterize contributions Click Remove excess contributions, convert assets, or recharacterize contributions
  5. Fill out the rest of form

Disclaimer: I’m not a tax professional, just a dude who messed up his contributions.

read more

Concourse build angular app on pull request with cache

resource_types:
  - name: pull-request
    type: registry-image
    source:
      repository: teliaoss/github-pr-resource

resources:
  - name: pull-request
    type: pull-request
    icon: source-pull
    check_every: 8760h
    webhook_token: ((webhook-token))
    public: true
    source:
      repository: ((your-repository))
      access_token: ((access-token.git-access-token))

jobs:
  - name: test-pull-request
    plan:
      - get: pull-request
        trigger: true
        version: every
      - put: pull-request
        params:
          path: pull-request
          status: pending
      - task: unit-test
        config:
          platform: linux
          image_resource:
            type: registry-image
            source:
              repository: node
              tag: alpine # you can use any node image, alpine is the smallest
          inputs:
            - name: pull-request
          run:
            path: /bin/sh
            args:
              - -exc
              - |
                npm config set cache $(pwd)/.npm --global
                cd pull-request
                export NG_CLI_ANALYTICS=false
                # execute whatever commands you need here
                npm install --quiet
                npm run build:test
          caches:
            - path: .npm
            - path: pull-request/node_modules
        on_failure:
          put: pull-request
          params:
            path: pull-request
            status: failure
      - put: pull-request
        params:
          path: pull-request
          status: success

Github webhook URL - https://${concourse-url}/api/v1/teams/${concourse-team-name}/pipelines/{pipeline-name}/resources/pull-request/check/webhook?webhook_token=((webhook-token)) ((webhook-token)) can be anything you want it to be as long as it’s used consistently in your webhook URL and in your pipeline

read more

Concourse rename job and retain history

To rename a Concourse job and retain history, you can use the old_name attribute.

jobs:
  - name: build-8-jdk-centos
    old_name: 8-jdk-centos

Once you’ve fly’d the pipeline with the new and old_name attributes you can remove old_name and fly it again, it’s no longer needed.

A good reason to rename a job would be because of recent concourse deprecations with valid identifiers. Our existing job started with a number, which will stop being allowed in a future Concourse version.

DEPRECATION WARNING:

jobs.8-jdk-centos: '8-jdk-centos' is not a valid identifier: must start with a lowercase letter

read more

Using Kubernetes Secrets with Spring Boot

First, create a secret, for this example we’ll be storing a username and password

kubectl create secret generic mssqldbcreds --from-literal=spring.db.username=mrbusche --from-literal=spring.db.password=hunter2

Second, add a reference to deployment.yml for each key

spec:
  template:
    spec:
      containers:
        - env:
            - name: USERNAME
              valueFrom:
                secretKeyRef:
                  name: mssqldbcreds
                  key: spring.db.username
            - name: PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mssqldbcreds
                  key: spring.db.password

Finally, reference the value in your application.yml

spring:
  db:
    username: ${USERNAME}
    password: ${PASSWORD}

read more

iOS in app browser basic authentication

If you’re having issues not getting a basic auth prompt when using the iOS in app browser you can pass the username and password in the url. This obviously isn’t a solution if this is a production issue, but works great for test environments.

https://username:password@mywebsite.com

read more

Testing Node AWS Lambda handler function locally

Add a script to your package.json file

"scripts": {
    "local": "node -e \"console.log(require('./index').handler({}));\""
}

where index is the name of your js file with the code you want to execute

You can execute this script by running

npm run local

read more