diff --git a/.gitignore b/.gitignore index cd48c722f5..1c3f70a8d3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ node_modules -.husky +.husky/_ .idea .vscode .zed diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100755 index 0000000000..b147a8bfb8 --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +pnpm exec commitlint --edit "$1" diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000000..26fc3643d4 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +pnpm exec lint-staged diff --git a/.prettierignore b/.prettierignore index 23a834c442..470bcb1473 100644 --- a/.prettierignore +++ b/.prettierignore @@ -21,9 +21,5 @@ logs .idea CHANGELOG.md +CHANGELOG .husky - - -controlplane/migrations - -composition/tests/unstaged-tests \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 822a175af5..78e4bd0e2c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -65,8 +65,8 @@ use ( We use [conventionalcommits](https://www.conventionalcommits.org/en/v1.0.0-beta.2/#why-use-conventional-commits) for changelog generation and more structured commit messages. -In order to enforce this standard we use a linter on pre-commit hook. This functionality is provided by [husky](https://typicode.github.io/husky/#/). -In some setup, you have to tell husky where to find your package manager or binaries. Here is the file `.huskyrc` you have to put in your user home directory. +If you want to enforce this standard, you can set up a pre-commit hook. This functionality is provided by [husky](https://typicode.github.io/husky/#/). Run `pnpm husky` to install pre-commit hooks which format code and check commit message format. +In some setup, you have to tell husky where to find your package manager or binaries. Here is the file `husky.sh` which you should put in your home's configuration directory (`~/.config/husky/husky.sh`). ```bash export NVM_DIR=/home/starptech/.nvm @@ -170,18 +170,22 @@ We manage multiple compose files: We use Codecov for code coverage. Code coverage is used for both PRs and also our default branch main. #### Workaround for uploading to main + Codecov relies on commit hashes to map code to Codecov reports, however unfortunately we use "Squash Merge", which means that the HEAD of any merged PR is never present on main post-merge, leading to no code coverage uploaded for main (which is used as a base for comparisons). In order to circumvent this we do the following steps: -* Upload Codecov reports from PR runs to GitHub as artifacts for the PR's HEAD commit -* Upon merge we find the PR and it's HEAD commit using the GitHub rest API -* We find the github artifact from this commit hash -* We then upload it again, but this time it acts as if it was uploaded from main + +- Upload Codecov reports from PR runs to GitHub as artifacts for the PR's HEAD commit +- Upon merge we find the PR and it's HEAD commit using the GitHub rest API +- We find the github artifact from this commit hash +- We then upload it again, but this time it acts as if it was uploaded from main #### Adding new projects + When you add a new project (i.e. a subfolder in the cosmo repository root), if you wish to add code coverage for it you can follow these steps: -* Add a flag in the codecov.yaml for your project with the folder name, this is important so coverage does not get overwritten (for example if a router pr gets merged, cli coverage won't be set to empty for that commit) +- Add a flag in the codecov.yaml for your project with the folder name, this is important so coverage does not get overwritten (for example if a router pr gets merged, cli coverage won't be set to empty for that commit) + ```yaml flags: router: @@ -189,10 +193,13 @@ flags: - router carryforward: true ``` -* You should be running tests in ci for your project via a GH Workflow, You need to add the GH Workflow name to `codecov-post-merge.yaml`, the line you are looking for will look something like this + +- You should be running tests in ci for your project via a GH Workflow, You need to add the GH Workflow name to `codecov-post-merge.yaml`, the line you are looking for will look something like this + ```yaml workflow-paths: .github/workflows/cli-ci.yaml,.github/workflows/router-ci.yaml, ``` + Ensure you add the full relative path from the cosmo repository root and it is comma-separated, e.g.: for graphqlmetrics `,.github/workflows/graphqlmetrics-ci.yaml` **Clean up a compose stack before starting another one!** diff --git a/Makefile b/Makefile index 03815cdc5f..49942744fb 100644 --- a/Makefile +++ b/Makefile @@ -52,6 +52,24 @@ infra-debug-down-v: infra-debug-up: docker compose -f docker-compose.yml --profile debug up --remove-orphans --detach +format-all: + pnpm -r --parallel format + +format: + @package="$(word 2,$(MAKECMDGOALS))"; \ + if [ -z "$$package" ]; then \ + echo "Usage: make format "; \ + exit 1; \ + fi; \ + pnpm --filter "./$$package" --fail-if-no-match run format + +ifneq ($(filter format,$(MAKECMDGOALS)),) +FORMAT_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)) +.PHONY: $(FORMAT_ARGS) +$(FORMAT_ARGS): + @: +endif + seed: pnpm -r run --filter './controlplane' seed diff --git a/admission-server/package.json b/admission-server/package.json index 42abeee42e..da8bcc972f 100644 --- a/admission-server/package.json +++ b/admission-server/package.json @@ -6,7 +6,8 @@ ], "scripts": { "dev": "tsx watch src/index.ts", - "build": "rm -rf dist && tsc" + "build": "rm -rf dist && tsc", + "format": "prettier -w -c ." }, "dependencies": { "@hono/node-server": "1.13.7", @@ -19,4 +20,4 @@ "tsx": "^4.16.0", "typescript": "^5.3.2" } -} \ No newline at end of file +} diff --git a/aws-lambda-router/package.json b/aws-lambda-router/package.json index d4232c84b7..1f67fa56bb 100644 --- a/aws-lambda-router/package.json +++ b/aws-lambda-router/package.json @@ -11,6 +11,9 @@ "aws-lambda", "serverless" ], + "scripts": { + "format": "go fmt ./..." + }, "author": { "name": "WunderGraph Maintainers", "email": "info@wundergraph.com" diff --git a/cdn-server/.prettierignore b/cdn-server/.prettierignore new file mode 100644 index 0000000000..1b763b1bae --- /dev/null +++ b/cdn-server/.prettierignore @@ -0,0 +1 @@ +CHANGELOG.md diff --git a/cdn-server/package.json b/cdn-server/package.json index 407c201b25..060bbdc500 100644 --- a/cdn-server/package.json +++ b/cdn-server/package.json @@ -19,7 +19,7 @@ "build": "rm -rf dist && tsc", "start": "tsx src/index.ts", "lint": "eslint --cache && prettier -c src", - "format:fix": "prettier --write -c src", + "format": "prettier -w -c .", "postversion": "name=$(jq -r .name package.json); version=$(jq -r .version package.json); gh workflow run image-release.yml -F name=$name -F workingDirectory=cdn-server -F tag=$version -F dockerContext=." }, "dependencies": { @@ -36,4 +36,4 @@ "tsx": "^4.16.0", "typescript": "5.5.2" } -} \ No newline at end of file +} diff --git a/cli/.prettierignore b/cli/.prettierignore new file mode 100644 index 0000000000..1b763b1bae --- /dev/null +++ b/cli/.prettierignore @@ -0,0 +1 @@ +CHANGELOG.md diff --git a/cli/package.json b/cli/package.json index e2177e4d39..dd055c059e 100644 --- a/cli/package.json +++ b/cli/package.json @@ -28,7 +28,8 @@ "test": "pnpm lint && vitest run test/", "coverage": "vitest run test/ --coverage", "lint": "eslint --cache --ext .ts,.mjs,.cjs . && prettier -c src", - "lint:fix": "eslint --cache --fix --ext .ts,.mjs,.cjs . && prettier --write -c src", + "lint:fix": "eslint --cache --fix --ext .ts,.mjs,.cjs . && pnpm format", + "format": "prettier -w -c .", "e2e": "bun test e2e/" }, "keywords": [ @@ -109,4 +110,4 @@ "vitest": "3.2.4" }, "gitHead": "c37aed755e1b19ed91d30f9b5f7041e15c56901a" -} \ No newline at end of file +} diff --git a/client-tests/apollo-js/package.json b/client-tests/apollo-js/package.json index 5fd7b91c70..b2ddde9c60 100644 --- a/client-tests/apollo-js/package.json +++ b/client-tests/apollo-js/package.json @@ -4,6 +4,7 @@ "description": "Apollo Client tests for GraphQL", "main": "index.js", "scripts": { + "format": "prettier -w .", "test": "vitest run", "test:watch": "vitest watch", "test:coverage": "vitest run --coverage" diff --git a/composition/.prettierignore b/composition/.prettierignore new file mode 100644 index 0000000000..802749858a --- /dev/null +++ b/composition/.prettierignore @@ -0,0 +1,3 @@ +tests/unstaged-tests +coverage/ +CHANGELOG.md diff --git a/composition/README.md b/composition/README.md index 9cee076a18..98d21a4bcf 100644 --- a/composition/README.md +++ b/composition/README.md @@ -2,7 +2,7 @@ [![npm version](https://badge.fury.io/js/%40wundergraph%2Fcomposition.svg)](https://badge.fury.io/js/%40wundergraph%2Fcomposition) -The WunderGraph composition library facilitates the federation of multiple subgraph schemas into a +The WunderGraph composition library facilitates the federation of multiple subgraph schemas into a single federated GraphQL schema. ### Prerequisites @@ -53,29 +53,31 @@ const subgraphB: Subgraph = { ### FederationResult -The `federateSubgraphs` function returns `FederationResult`, which is a union of `FederationResultSuccess` and +The `federateSubgraphs` function returns `FederationResult`, which is a union of `FederationResultSuccess` and `FederationResultFailure`. Both types in the union always define the following mutual properties: | property | Description | type | -|----------|----------------------------------------|----------------| +| -------- | -------------------------------------- | -------------- | | success | assertion of composition success | boolean | | warnings | array of composition warnings (if any) | Array | #### FederationResultSuccess + If federation was successful, the return type is `FederationResultSuccess`. -| property | Description | type | -|----------------------|-------------------------------------------------------------|-----------------------| -| federatedGraphAST | an AST object representation of the federated graph sdl | graphql.DocumentNode | -| federatedGraphSchema | a schema object representation of the federated graph sdl | graphql.GraphQLSchema | -| success | assertion that composition was successful | true | -| warnings | array of composition warnings (if any) | Array | +| property | Description | type | +| -------------------- | --------------------------------------------------------- | --------------------- | +| federatedGraphAST | an AST object representation of the federated graph sdl | graphql.DocumentNode | +| federatedGraphSchema | a schema object representation of the federated graph sdl | graphql.GraphQLSchema | +| success | assertion that composition was successful | true | +| warnings | array of composition warnings (if any) | Array | #### FederationResultFailure + If federation was unsuccessful, the return type is `FederationResultFailure`. | property | Description | type | -|----------|---------------------------------------------|----------------| +| -------- | ------------------------------------------- | -------------- | | errors | array of composition errors | Array | | success | assertion that composition was unsuccessful | false | | warnings | array of composition warnings (if any) | Array | @@ -112,9 +114,10 @@ for (const warning of result.warnings) { ### Errors Errors can happen in three main stages: + 1. While validating the subgraph metadata, e.g., validating that each `Subgraph` object has a unique name. 2. During the normalization process, which prepares the subgraph for federation. -(if this stage fails, federation will not be attempted) + (if this stage fails, federation will not be attempted) 3. During the federation process itself. All errors will be appended to the `FederationResultFailure.errors` array. @@ -127,7 +130,7 @@ This is easily achieved by passing string representation of the subgraph SDL to An example is shown below: ```typescript -import { Subgraph } from '@wundergraph/composition' +import { Subgraph } from '@wundergraph/composition'; import { parse } from 'graphql'; const subgraphA: Subgraph = { @@ -148,13 +151,15 @@ const subgraphA: Subgraph = { ### Subgraph Properties | property | Description | type | -|-------------|-------------------------------------------|----------------------| +| ----------- | ----------------------------------------- | -------------------- | | name | unique name of the subgraph | string | | url | unique endpoint for the subgraph | string | | definitions | an AST representation of the subgraph SDL | graphql.DocumentNode | ### Contributing + When adding or changing error, please ensure GraphQL types begin with a capital letter for clarity: + - Enum - Input Object - Interface diff --git a/composition/package.json b/composition/package.json index 16cb5e6dfd..7b8ef53cb3 100644 --- a/composition/package.json +++ b/composition/package.json @@ -19,8 +19,9 @@ "test": "vitest run", "test:core": "vitest run --exclude ./tests/unstaged-tests", "test:coverage": "vitest run --coverage", - "lint": "prettier --check src tests", - "lint:fix": "prettier --write src tests", + "lint": "prettier --check .", + "lint:fix": "pnpm format", + "format": "prettier -w -c .", "postversion": "node ./scripts/get-composition-version.mjs" }, "main": "./dist/index.js", diff --git a/composition/tsconfig.json b/composition/tsconfig.json index a291aedd7c..24a34bb350 100644 --- a/composition/tsconfig.json +++ b/composition/tsconfig.json @@ -4,7 +4,7 @@ "declaration": true, "outDir": "./dist", "module": "NodeNext", - "moduleResolution": "NodeNext", + "moduleResolution": "NodeNext" }, "include": ["src/**/*"], "exclude": ["node_modules"] diff --git a/composition/vite.config.ts b/composition/vite.config.ts index b2c35efdc2..a562146505 100644 --- a/composition/vite.config.ts +++ b/composition/vite.config.ts @@ -5,4 +5,4 @@ export default defineConfig({ // Ensure always the CJS version is used otherwise we might conflict with multiple versions of graphql alias: [{ find: /^graphql$/, replacement: 'graphql/index.js' }], }, -}); \ No newline at end of file +}); diff --git a/controlplane/.prettierignore b/controlplane/.prettierignore new file mode 100644 index 0000000000..3a91ac5531 --- /dev/null +++ b/controlplane/.prettierignore @@ -0,0 +1,3 @@ +migrations/ +src/templates/emails/ +CHANGELOG.md diff --git a/controlplane/README.md b/controlplane/README.md index 00932a69f5..a0759005c7 100644 --- a/controlplane/README.md +++ b/controlplane/README.md @@ -75,4 +75,4 @@ A user session "cookie" is valid for 1 day. The refresh token has the same lifes That implies that the user can interact with the app for 1 day until the refresh token is expired. The user can renew the session by calling the session endpoint `/v1/auth/session`. The session endpoint will refresh the access and refresh token and update the session cookie. The user can interact with the app for another day. -__Summary: If the frontend ensure that the session endpoint `/v1/auth/session` is called on focus and load. The user might never be logged out again.__ +**Summary: If the frontend ensure that the session endpoint `/v1/auth/session` is called on focus and load. The user might never be logged out again.** diff --git a/controlplane/emails/package.json b/controlplane/emails/package.json index 05ef7fc150..25082dc5b4 100644 --- a/controlplane/emails/package.json +++ b/controlplane/emails/package.json @@ -6,7 +6,9 @@ "type": "module", "scripts": { "dev": "email dev --dir src --port 3400", - "build": "email export --dir src --outDir ../src/templates/emails --pretty && pnpm run --filter ../ lint:fix" + "build": "email export --dir src --outDir ../src/templates/emails --pretty && pnpm run --filter ../ lint:fix", + "format": "prettier -w .", + "lint:fix": "eslint --cache --fix --ext .ts,.mjs,.cjs . && pnpm format" }, "author": { "name": "WunderGraph Maintainers", diff --git a/controlplane/emails/src/components/cosmo-logo.tsx b/controlplane/emails/src/components/cosmo-logo.tsx index 44987eb3b8..480c2adf48 100644 --- a/controlplane/emails/src/components/cosmo-logo.tsx +++ b/controlplane/emails/src/components/cosmo-logo.tsx @@ -7,13 +7,8 @@ export function CosmoLogo() { href="https://wundergraph.com/" className="flex items-center space-x-2 font-bold text-slate-800 no-underline w-full" > - WunderGraph + WunderGraph WunderGraph ); -} \ No newline at end of file +} diff --git a/controlplane/emails/src/components/main-container.tsx b/controlplane/emails/src/components/main-container.tsx index 7bbc34ea64..1d761cdf5a 100644 --- a/controlplane/emails/src/components/main-container.tsx +++ b/controlplane/emails/src/components/main-container.tsx @@ -12,4 +12,4 @@ export function MainContainer({ children }: React.PropsWithChildren) { {children} ); -} \ No newline at end of file +} diff --git a/controlplane/emails/src/components/tailwind-config.tsx b/controlplane/emails/src/components/tailwind-config.tsx index 440320ee44..ea038e9746 100644 --- a/controlplane/emails/src/components/tailwind-config.tsx +++ b/controlplane/emails/src/components/tailwind-config.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Tailwind, } from '@react-email/components'; +import { Tailwind } from '@react-email/components'; export function TailwindConfig({ children }: React.PropsWithChildren) { return ( @@ -9,12 +9,12 @@ export function TailwindConfig({ children }: React.PropsWithChildren) { extend: { colors: { brand: '#ea4899', - } - } - } + }, + }, + }, }} > {children} ); -} \ No newline at end of file +} diff --git a/controlplane/emails/src/organizationDeletionQueued.tsx b/controlplane/emails/src/organizationDeletionQueued.tsx index 0bfa0fe817..8b0ede1ccd 100644 --- a/controlplane/emails/src/organizationDeletionQueued.tsx +++ b/controlplane/emails/src/organizationDeletionQueued.tsx @@ -1,18 +1,7 @@ -import { - Body, - Button, - Head, - Heading, - Html, - Link, - Preview, - Section, - Text, - Row, -} from '@react-email/components'; +import { Body, Button, Head, Heading, Html, Link, Preview, Section, Text, Row } from '@react-email/components'; import * as React from 'react'; import { TailwindConfig } from '@/components/tailwind-config.js'; -import { MainContainer } from '@/components/main-container.js' +import { MainContainer } from '@/components/main-container.js'; export const OrganizationQueuedForDeletionEmail = () => { return ( @@ -26,20 +15,18 @@ export const OrganizationQueuedForDeletionEmail = () => { Organization Queued for Deletion - The organization [%= organizationName %] has been scheduled for - {' '}deletion by: + The organization [%= organizationName %] has been scheduled for{' '} + deletion by:
User: [%= userDisplayName %] Date: [%= queuedOnDate %]
- This irreversible deletion will take place on [%= deletionDate %]{' '} - and will permanently remove the organization and all data associated with it. - - - If this was unintentional and you would like to cancel the deletion: + This irreversible deletion will take place on [%= deletionDate %] and + will permanently remove the organization and all data associated with it. + If this was unintentional and you would like to cancel the deletion: