diff --git a/README.md b/README.md index 7440242..0d8c678 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,47 @@ Make any microservice have a GraphQL implementation. +[![CI](https://github.com/charles2ke/GraphQL/actions/workflows/ci.yml/badge.svg)](https://github.com/charles2ke/GraphQL/actions/workflows/ci.yml) +[![Deploy website to GitHub Pages](https://github.com/charles2ke/GraphQL/actions/workflows/deploy-pages.yml/badge.svg)](https://github.com/charles2ke/GraphQL/actions/workflows/deploy-pages.yml) +[![Node.js 20+](https://img.shields.io/badge/node-%3E%3D20-brightgreen)](https://nodejs.org/) +[![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue)](LICENSE) + **Live website: ** This repository contains a minimal, working backend service that exposes a GraphQL -API for a small `User` / `Post` domain. It uses in-memory storage, so it runs from a -clean checkout without any database or other external dependency. +API for a small `User` / `Post` domain, plus an optional finance surface that +aggregates three upstream domains. It uses in-memory storage and mock connectors, +so it runs from a clean checkout without any database or other external +dependency. + +## Quick start + +```bash +git clone https://github.com/charles2ke/GraphQL.git +cd GraphQL +npm install +npm start # http://localhost:4000/graphql +npm test # node:test suite +``` + +Open in a browser to explore the schema in the +Apollo Sandbox, or jump to [Example queries](#example-queries). + +## Contents + +- [Stack](#stack) +- [Project structure](#project-structure) +- [Installation and running locally](#installation-and-running-locally) +- [Learning website](#learning-website) +- [Tests](#tests) +- [Continuous integration](#continuous-integration) +- [API](#api) +- [Streaming](#streaming) +- [CORS](#cors) +- [Excel export](#excel-export) +- [Finance cluster integration](#finance-cluster-integration) +- [Notes](#notes) +- [Security and license](#security-and-license) ## Stack @@ -19,21 +55,29 @@ clean checkout without any database or other external dependency. ``` src/ - index.js # HTTP bootstrap: Express app + /graphql endpoint + index.js # HTTP bootstrap: Express app + /graphql, /export, /graphql/stream server.js # Apollo Server factory (reused by the tests) schema.js # GraphQL type definitions and executable schema factory resolvers.js # Query / Mutation / Subscription / field resolvers - config/finance.js # Environment-driven finance connector config - connectors/ # Replaceable OpenTrading, Portfolio-Watcher, tax-break adapters + cache/index.js # Pluggable TTL cache (memory, file, shared provider) + config/ # Environment-driven finance and CORS configuration + connectors/ # OpenTrading, Portfolio-Watcher, tax-break adapters + HTTP client data/store.js # In-memory data store with seed data domain/finance.js # Canonical finance models and normalization helpers export/ # Excel (.xlsx) writer, dataset registry, and /export routes + observability/ # Structured logging, metrics, error classification, Apollo plugin services/financeService.js # Finance aggregation, caching, and error handling streaming/ # In-process pub/sub and the SSE streaming endpoint + validation/financeArgs.js # Shared finance argument validation test/ - graphql.test.js # API tests executed against the schema - export.test.js # Excel export writer, dataset, and route tests - streaming.test.js # Pub/sub and Server-Sent Events streaming tests + graphql.test.js # API tests executed against the schema + finance.test.js # Finance service, filtering, and pagination tests + connectors.test.js # Connector selection and HTTP client tests + cors.test.js # CORS allow-list tests + export.test.js # Excel export writer, dataset, and route tests + observability.test.js # Logging, metrics, and error classification tests + resilience.test.js # Timeout, retry, and partial-failure tests + streaming.test.js # Pub/sub and Server-Sent Events streaming tests website/ src/App.jsx # Learning site: primer, tips, API Explorer src/backendSamples.js # GraphQL server samples in 10 backend languages @@ -43,17 +87,12 @@ website/ deploy-pages.yml # Builds website/ and publishes it to GitHub Pages ``` -## Installation +## Installation and running locally ```bash git clone https://github.com/charles2ke/GraphQL.git cd GraphQL npm install -``` - -## Running locally - -```bash npm start # or: npm run dev (restarts on file changes) ``` @@ -124,9 +163,210 @@ every pull request, and on demand from the Actions tab. It has two jobs: Runs are grouped per branch and superseded runs are cancelled automatically. -## Finance Cluster Integration (Priority 1) +## API + +| Type | Operation | Description | +| --- | --- | --- | +| Query | `users` | List all users | +| Query | `user(id: ID!)` | Fetch a single user, `null` when unknown | +| Query | `posts` | List all posts | +| Query | `post(id: ID!)` | Fetch a single post, `null` when unknown | +| Query | `portfolioOverview(accountId, from, to, limit, offset)` | Fetch finance accounts, positions, snapshots, and P/L | +| Query | `tradeHistory(accountId, symbol, side, status, from, to, limit, offset)` | Fetch trades/orders enriched with tax events | +| Query | `taxEstimate(taxYear, accountId, symbol, from, to, limit, offset)` | Estimate tax from tax-relevant trading activity | +| Mutation | `createUser(name, email)` | Create a user | +| Mutation | `createPost(title, content, authorId)` | Create a post for an existing user | +| Subscription | `userCreated` | Streams every newly created user | +| Subscription | `postCreated(authorId)` | Streams new posts, optionally for one author | + +The finance operations are documented in detail under +[Finance cluster integration](#finance-cluster-integration); subscriptions are +delivered over SSE, see [Streaming](#streaming). + +### Example queries + +List users with their posts: + +```graphql +query Users { + users { + id + name + email + posts { + id + title + } + } +} +``` + +Fetch one user: + +```graphql +query User { + user(id: "1") { + name + email + } +} +``` + +List posts with their author: + +```graphql +query Posts { + posts { + id + title + content + author { + id + name + } + } +} +``` + +### Example mutations + +```graphql +mutation CreateUser { + createUser(name: "Grace Hopper", email: "grace@example.com") { + id + name + } +} +``` + +```graphql +mutation CreatePost { + createPost(title: "Nanoseconds", content: "A talk about wire lengths.", authorId: "1") { + id + title + author { + name + } + } +} +``` + +Creating a post for an unknown `authorId` returns a `BAD_USER_INPUT` error. + +### With curl + +```bash +curl http://localhost:4000/graphql \ + -H 'Content-Type: application/json' \ + -d '{"query":"{ users { id name posts { title } } }"}' +``` + +## Streaming + +Subscriptions can be streamed over Server-Sent Events at `POST /graphql/stream` +or `GET /graphql/stream`. **`GET` is read-only streaming**: it only accepts `query` +and `subscription` operations, taken from plain `query`/`variables`/ +`operationName` query-string parameters, and is therefore safe to treat as a +"simple" cross-origin request. This is *not* the same payload contract as +`/graphql` — **mutations sent with `GET` are rejected with `405 Method Not +Allowed`** so a mutating operation can never be triggered from a plain +cross-site navigation or ``/`