Important
This repo has been moved to Pocket's Monorepo
The API that manages the ability to create and share lists of related content.
Express is the Node framework, Apollo Server is used in Express to expose a GraphQL API, and Prisma is used as the ORM. MySQL is the relational database, though in AWS this is Aurora Serverless.
- the infrastructure code is present in
.aws - the application code is in
src .circlecicontains circleCI setup
Clone the repo:
git clone [email protected]:Pocket/shareable-lists-api.gitcd shareable-lists-api
Prepare Prisma:
npm installnpx prisma generate(this generates Prisma Typescript types)
Start Docker container:
docker compose up --build -V
Once all the Docker containers are up and running, you should be able to reach
- the public API at
http://localhost:4029/ - the admin API at
http://localhost:4029/admin
Out of the box, the local installation doesn't have any actual data for you to fetch or manipulate through the API. To seed some sample data for your local dev environment, run
docker compose exec app npx prisma migrate resetNote that the above command will not be adding to any data you may have added to the database through other means - it will do a complete reset AND apply the seed script located at src/prisma/seed.ts.
The admin API requires HTTP headers be set to authorize operations.
To run queries against the /admin API in the GraphQL playground, you'll need to specify some HTTP headers. To do so:
- Open up the GraphQL playground at
http://localhost:4029and make sure your playground tab's address ishttp://localhost:4029/admin. - Click the HTTP HEADERS link at the bottom of the left hand side of the playground to reveal a text box.
- Enter the necessary headers (see sample below) into the box and try an operation - it should work!
The sample headers below allow full access to all queries and mutations:
{
"groups": "mozilliansorg_pocket_moderation_full",
"name": "Matt McPockets",
"username": "mmcpockets"
}Note that the groups header can contain mulitple values separated by commas (but still in a single string).
If you'd like to experiment with different levels of authorization, you can find the full list of Mozillian groups on our Shared Data document.
To test Sentry within a local instance of the API:
- Add
SENTRY_DSNenv to.docker/local.env. The DSN value can be found in the Sentry project - In
docker-compose.yaml, change the node environment underapptodevelopment:
environment:
- NODE_ENV=development
- Add a
try/catchblock in a query / mutation, throw an error and capture it withSentry.captureException(err) - In
src/public/server.tsand orsrc/admin/server.tsreplacenonProdPluginswith:
const nonProdPlugins = [
ApolloServerPluginLandingPageGraphQLPlayground(),
ApolloServerPluginInlineTrace(),
];
If you need to change the Prisma schema (in prisma/schema.prisma), you'll need to create a migration to ensure the database is in sync. After you have made your changes to schema.prisma, run
docker compose exec app npx prisma migrate dev --name some_meaningful_migration_nameThis will create a migration script in prisma/migrations and will automatically run the new migration. This will also re-create your Prisma Typescript types.
If your local environment gets messed up (it happens - for example switching to a branch with a different prisma schema), you can re-create your Prisma Typescript types by running npx prisma generate. (Note that you don't have to do this within the Docker container, but you can if you want.)
A userId in the headers is needed in order to run mutations:
{
"userId": "12345"
}We have two test commands, one for unit/functional tests and one for integration tests. These are both run by Jest and are differentiated by file names. Any file ending with .spec.ts will be run in the unit/functional suite, while integration tests should have the .integration.ts suffix.
Test are run via npm commands:
- Unit/functional:
npm test- Integration:
If tests rely on MySQL and AWS (which is mocked by a localstack Docker container locally):
docker compose exec app npm run test-integrationsIf tests don't rely on other services:
npm run test-integrations
This API sends two kinds of events to the Pocket Event Bridge --> Snowplow: shareable-list and shareable-list-item events.
shareable_listJSON schema: Snowplow schemashareable_list_itemJSON schema: [Snowplow schema] (https://console.snowplowanalytics.com/cf0fba6b-23b3-49a0-9d79-7ce18b8f9618/data-structures/5c6a2540cd75d3baef34f659a7902732616502c996e513770d7e2c8bad926fc6)- event triggers: object_update Snowplow schema
The API maps the GraphQL API types to the Snowplow types and sends both events to the Pocket Event Bridge. The core logic happens in src/snowplow/events.ts where the sendEvent function takes in a payload and sends it to the Pocket Bridge.
In src/aws/config/index.ts:
- Add a new event to
eventBridgeand define the source.
In src/snowplow/types.ts do the following:
- Add the expected Snowplow type.
- Add the event bridge event type to
EventBridgeEventTypeenum. - Add an
EventBusPayloadfor your type. - To the
EventBridgeEventOptionsinterface, add your event type and a boolean to indicate what event you are passing.
In src/snowplow/types.ts do the following:
- Add your transformer function which maps the GraphQL API type to the expected Snowplow type.
- Add the function which generates the Snowplow type payload.
- In
sendEventHelperfunction, add a conditional for what event option you are passing and callsendEventfunction and pass the payload. - Finally, in the
sendEventfunction, based on the boolean flag, set the event bridge source.
The unit tests for Snowplow events are defined in src/snowplow/events.spec.ts. [https://sinonjs.org/](Sinon JS)is used for test spies and stubs.
The Redis container runs on localhost:6379. To connect to the local Redis server (works for dev and prod as well, just find the Redis cluster primary endpoints in AWS ElastiCache and make sure to use the appropriate $(maws) role), one of the options is to install the redis-gui and set up the connection to localhost:6379.
The shareableListPublic query caches the responses and stores them for 60 seconds in the Redis db. Here is a screenshot of how it gets stored:
There may come a time when you need to reset the Dev environment.
For example, if you were testing a schema change and then want to test a different branch without that schema change, the Dev database and Prisma schema will be out of sync. Another common scenario is the need to reset all test data to the initial seed data provided by the seed script.
To reset the Dev database, follow the instructions in Confluence.
