# Project plan — ohlief-app-api

Evidence date: 2026-10-08.

Source repository: `git@bitbucket.org:overdrive/ohlief-app-api.git`.

Inspected revision: `master` at `08d6e03` (2020-07-31, Jaco Brits, "Added Christine email address ,christine@ohlief.com"). The scan-host path `~/projects/sonar_scan/repos/ohlief-app-api` was not on this machine. The tree below was read from that git revision (13 commits, full history fetched).

This plan describes what the repository contains. It does not decide what the product should become.

There is no product requirements document in the source repository and no prior Silicon Ark set for `ohlief-app-api/`. `README.md` is two lines: the title **Oh Lief Nodejs Lambda API** and an unclosed `**Functions` marker. `package.json` still describes the upstream [Serverless Node.js Starter](https://github.com/AnomalyInnovations/serverless-nodejs-starter) and still points `repository.url` at that GitHub project. `LICENSE` is the MIT licence, copyright 2017 Anomaly Innovations.

Silicon Ark already documents the browser client in `ohlief-app-client/` (source `master` `98891e4`, 2020-07-05). That client calls HTTP paths this repository declares. This repository does not name the client. That they are one deployed system is **Inferred** from the shared names (`ohlief`, region `eu-west-1`, stage `prod`, bucket `ohlief-app-uploads`) and **Requires Confirmation** against the live AWS account. The API id in the client (`0tsz4u0k70`) does not appear in this tree.

## Product

**Documented** name: service `ohLief-app-api` in `serverless.yml`, npm name `ohLief-app-api` version `1.1.1`.

What the code does is a set of AWS Lambda functions. The EDI path is:

- Pull pipe-delimited order files from an FTP server, match each line to a product by package barcode, and store a row in DynamoDB.
- Let a caller change `invoiceQty` on an order row, then mark every row with that order number as `type` `invoice`.
- Build one pipe-delimited invoice file from rows whose `type` is `invoice` and `process` is `"0"`, upload it to a second FTP server, and send an email.

A product catalogue (create, read, update, list) shares the same DynamoDB table. A notes-style create, a delete-by-caller, and a Stripe "Scratch charge" route are still declared. They come from the starter and from the early commits. The Oh-lief client still has screens that call the note routes. See features.

No written statement of product owner, trading-partner contract, or go-live date was found.

## Branch reality

| Line | Tip | Date | What it is |
|---|---|---|---|
| `master` (default; `origin/HEAD`) | `08d6e03` | 2020-07-31 | Only branch. 13 commits, all by Jaco Brits, from 2020-02-15 to 2020-07-31 |

Nothing in git is dated after 2020-07-31. Whether the stack is still deployed is **Unknown / Requires Confirmation**.

| Commit | Date | Subject |
|---|---|---|
| `74115e6` | 2020-02-15 | Api commit (starter: `handler.js`, Jest test, short `serverless.yml`) |
| `6b25097` | 2020-02-15 | Update Readme (starter readme replaced by the two-line title) |
| `7e071a9` | 2020-02-16 | Update all changes to API (notes CRUD, billing, DynamoDB helper) |
| `422f3c0` | 2020-02-17 | Setting up enviroment (Cognito, DynamoDB, S3 resources) |
| `2b687ed` | 2020-02-17 | 1.1.1 (package version only) |
| `f1990b0` | 2020-03-25 | latest and extra apis (FTP import, invoice update, invoice FTP, list routes) |
| `f23216d` | 2020-06-17 | notification build in (product routes, SES email, route reshuffle) |
| `b18f33c` | 2020-06-19 | notification update naming and trailspaces |
| `64c6bfb` | 2020-07-05 | Updated email adresses |
| `d1a5923` | 2020-07-05 | Update content checker (`content.length > 10` before invoice FTP and email) |
| `4e2c14e` | 2020-07-05 | Remove accounts email |
| `a031ae1` | 2020-07-07 | add accounts@ohlief.com (product partition key becomes the literal `ProductRow`) |
| `08d6e03` | 2020-07-31 | Added Christine email address |

## Objectives

No objectives file exists. The behaviour the commits introduced, in order:

1. Starter Lambda plus a notes-style CRUD API and a Stripe charge, with Cognito, an S3 bucket, and a DynamoDB table in the template.
2. Order import from FTP, invoice listing, and an order-number update that sets `type` to `invoice`.
3. Product create, get, list, and update. Email on order import and on invoice export.
4. A length check so a short invoice buffer does not upload or email. Notification addresses changed across the July 2020 commits. At HEAD the recipients are `jaco@overdrive.co.za`, `accounts@ohlief.com`, and `christine@ohlief.com`.

## Scope

### In scope of `master` at `08d6e03`

- `serverless.yml` service `ohLief-app-api`: Node.js 10.x, stage `prod`, region `eu-west-1`, AWS CLI profile `ohlief`.
- Fifteen functions declared in that file (route table in `technical-documentation.md`).
- DynamoDB access through `libs/dynamodb-lib.js`. The table name the functions read is the environment value `tableName`, set in the template to the literal string `order`.
- FTP import (`ftptos3.js`) and invoice export (`invoicetoftp.js`), including SES mail.
- CloudFormation pieces under `resources/` for API Gateway CORS errors, a DynamoDB table, an S3 bucket, a Cognito user pool, and a Cognito identity pool.
- One Jest test of the starter `hello` handler. `hello` is not declared in `serverless.yml`.
- Handler files that are not declared as functions: `handler.js`, `filetoDDb.js`, `invoiceToFtp_old.js`.

### Out of scope / not in this repository

- The live AWS account, the deployed API id, and whether stage `prod` was ever deployed from this file.
- The FTP servers. Host, user, and password are environment names only.
- A written EDI specification. Field positions are whatever `ftptos3.js` and `invoicetoftp.js` read and write.
- The React client. Its calls are evidence in `silicon_ark/ohlief-app-client/`, not in this tree.
- DNS for `http://orders.ohlief.com` (linked from the notification email). Whether that host is the client is **Unknown / Requires Confirmation**.
- A pipeline, Sonar project, Dockerfile, or custom domain. The onboarding scan listed `sonar-project.properties`. It is not in the git tree at HEAD.

## System shape

```text
FTP order host                         FTP invoice host
(FTP_HOST_ORDER)                       (FTP_HOST_INVOICE)
        |                                      ^
        | GET /ftptos3                         | GET /invoicetoftp/{id}
        | authorizer commented out             | authorizer commented out
        v                                      | id is ignored
API Gateway   service ohLief-app-api   region eu-west-1   stage prod
        |
        +-- aws_iam on the other HTTP routes
        |
        v
Lambda (Node.js 10.x)
        |
        +-- DynamoDB table name from env tableName, which the template sets to "order"
        |     partition userId + sort transactionId
        |     EDI rows use userId "OrderRow" or "ProductRow"
        +-- S3 bucket name hardcoded "ohlief-app-uploads" (order-file copy)
        +-- SES source accounts@ohlief.com, configuration set "info"
        +-- Stripe (POST /billing only), currency usd
```

The CloudFormation table is named `${stage}-ohlief` (so `prod-ohlief` at the default stage). The functions do not use that name. They use `order`. See technical documentation.

## Major components

| Area | Where | What it is |
|---|---|---|
| HTTP surface | `serverless.yml` | Fifteen functions, CORS, IAM authorizer on all but two |
| Order intake | `ftptos3.js` | FTP list, product match, DynamoDB put, copy to `processed/`, S3, email |
| Invoice flip | `invoiceupdate.js` | Scan by `orderNumber`, set `type` to `invoice` |
| Invoice drop | `invoicetoftp.js` | Scan unprocessed invoices, pipe file, FTP put, email |
| Order and product IO | `get.js`, `list.js`, `update.js`, `createProduct.js`, `getproduct.js`, `listProducts.js`, `updateProduct.js`, `listinvoices.js`, `listOrders.js` | DynamoDB get, scan, query, put, update |
| Notes leftover | `create.js`, `delete.js`, `handler.js` | Starter note create, delete by caller identity, unused `hello` |
| Billing leftover | `billing.js`, `libs/billing-lib.js` | Stripe charge from a storage amount |
| Shared helpers | `libs/dynamodb-lib.js`, `libs/response-lib.js` | DocumentClient promise wrapper; HTTP 200 / 500 with CORS headers |
| Not deployed | `filetoDDb.js`, `invoiceToFtp_old.js` | A device-CSV script aimed at `ap-southeast-2`, and an unfinished earlier invoice export |

## Current status

At HEAD the template can deploy an API that imports order files, stores products and order lines, updates an invoice quantity, marks an order number as invoiced, and pushes an invoice file plus an email. Several starter paths are still routed. Two FTP routes have no IAM authorizer and no schedule (the `rate(1 minute)` blocks are commented out). Nothing in this repo calls those routes. Who invokes them in production is **Unknown / Requires Confirmation**.

The last commit only adds `christine@ohlief.com` to the two recipient lists. There is no open branch and no commit after 2020-07-31.

## Known dependencies

| Dependency | Role |
|---|---|
| `serverless-bundle` 1.2.5, `serverless-offline` 5.3.3, `serverless-dotenv-plugin` 2.1.1 | Package, local HTTP, `.env` loading. Dev dependencies |
| `aws-sdk` 2.639.0 | DynamoDB, S3, SES. Dev dependency, bundled by the Framework packaging |
| `ftp` 0.3.10 | Order and invoice FTP |
| `stripe` 8.18.0 | `POST /billing` only |
| `uuid` 3.4.0 | `noteId` and `transactionId` |
| `unzipper` 0.10.10, `xlsx` 0.15.5 | Declared. No import of either name exists in the `.js` tree |
| Amazon API Gateway, Lambda, DynamoDB, S3, Cognito, SES, Stripe | Named by the template and the handlers |

`filetoDDb.js` requires `csv-parse` and `async`. Those packages are not in `package.json`.

## Requires confirmation

- Whether stack `ohLief-app-api` stage `prod` is still deployed, and whether it is the API at `https://0tsz4u0k70.execute-api.eu-west-1.amazonaws.com/prod` named in the client.
- Which DynamoDB table production uses: the literal `order` in `provider.environment`, or the CloudFormation table `${stage}-ohlief`.
- Whether bucket `ohlief-app-uploads` was created by this template (the template bucket has no `BucketName`) or outside it.
- Whether the Cognito pool in this template is the pool the client pins (`eu-west-1_rXTL9gyYP`), or a different pool.
- Who calls `GET /ftptos3` and `GET /invoicetoftp/{id}`, and whether those routes are meant to be unauthenticated.
- The trading-partner meaning of the pipe file (the client copy says "FTP EDI" and "xProcure"). This repository does not name a standard.
- Whether `http://orders.ohlief.com` is the client host.
- Whether the Stripe route and the notes create/delete routes are still required.
- The static AWS credentials assigned in `invoicetoftp.js`. Values are omitted from these documents. They should be treated as exposed and rotated. See technical documentation.
