# orchard-suite

This repository contains common Typescript modules which can be reused among Orchard Javascript or Typescript based implementations. It is built with [pnpm](https://pnpm.io) and [changesets](https://github.com/atlassian/changesets).

## Introduction

Please watch the [Introduction to The Monorepo](https://drive.google.com/file/d/1Wn_4AmuksrBVy6f9k-CyuS_E7MR6QZ8y/view) video before you get started!

## Setup

Follow this guide to get your environment set up to work with private packages.
https://www.notion.so/Javascript-Package-Management-Setup-9553d5d491c94835aa787fdf0fc4838d

### Install pnpm using corepack

We are currently on **version 8** of pnpm.

```bash
corepack enable
```

### Install dependencies using pnpm

```bash
pnpm install
```

### Build dependent packages

If you work on a package that depends on other packages in the repo, then those packages needs to be built first. Running `pnpm build` inside a package folder will also build the referenced packages. This has to be done before running linting or unit tests. It will also improve your dev experience since the IDE can pick up the type definitions.

#### Example:

Say you are going to make changes to the datasource-neo4j package.
Before you start developing you should build it.

```bash
cd packages/datasource-neo4j

pnpm build
```

This will **recursively** build the dependencies found in the package.json:

```json
    dependencies: {
        "@theorchard/connector-neo4j": "workspace:*",
        "@theorchard/graphql-server": "workspace:*",
```

## Packages

https://github.com/orgs/theorchard/packages?repo_name=orchard-suite

### Graphql packages

-   [`@theorchard/connector-ows-playlist`](packages/connector-ows-playlist) A connector for communicating with the ows-playlist microservice.
-   [`@theorchard/datasource-kafka`](packages/datasource-kafka) a `DataSource` for interacting with Kafka.
-   [`@theorchard/datasource-zendesk`](packages/datasource-zendesk) a `RESTDataSource` for interacting with Zendesk.
-   [`@theorchard/connector-universal-links`](packages/connector-universal-links) a `RESTDataSource` for interacting with [Firebase](https://firebase.google.com/) in order to generate universal links.
-   [`@theorchard/connector-splitio`](packages/connector-splitio) common logic for checking feature flags in GraphQL services using the Split.io SDK.
-   [`@theorchard/connector-neo4j`](packages/connector-neo4j) common logic for creating neo4j drivers.
-   [`@theorchard/connector-s3-upload-token`](packages/connector-s3-upload-token) common logic for getting a temporary access token to upload a file to an s3 bucket.
-   [`@theorchard/graphql-server`](packages/graphql-server) common bootstrap logic for running graphql servers.

### Orchard Suite packages

https://www.notion.so/The-Orchard-Suite-Framework-2277a0e1ac32464a863757c1e5added8

-   [`@theorchard/suite-frontend`](./packages/suite-frontend) includes all you need to start building orchard suite apps.
-   [`@theorchard/suite-components`](./packages/suite-components) common React components used in Orchard suite apps.
-   [`@theorchard/suite-apollo`](./packages/suite-apollo) common apollo functions to reduce the bootstrap code.
-   [`@theorchard/suite-icons`](./packages/suite-icons) common icons and graphical assets used in Orchard suite apps.
-   [`@theorchard/suite-identity`](./packages/suite-identity) utils and react hooks to manage Orchard user identities.
-   [`@theorchard/suite-i18n`](./packages/suite-i18n) utils for internationalizing frontend apps.
-   [`@theorchard/suite-theming`](./packages/suite-theming) utils to theme frontend apps.
-   [`@theorchard/suite-config`](./packages/suite-config) Shared suite application configuration.
-   [`@theorchard/suite-utils`](./packages/suite-utils) Common utils for suite apps.

### Tooling and linting packages

-   [`@theorchard/stylelint-config`](./packages/stylelint-config) shared css/scss linting rules for front end projects.
-   [`@theorchard/eslint-config-react-ts`](./packages/eslint-config-react-ts) linting rules for react projects using typescript.
-   [`@theorchard/eslint-config-ts`](./packages/eslint-config-ts) linting rules for node projects using typescript.
-   [`@theorchard/frontend-cli`](./packages/frontend-cli) development and build tool for creating Orchard front-ends.
-   [`@theorchard/frontend-vite`](./packages/frontend-vite) Vite plugins and config helpers for suite apps. A standalone alternative to `frontend-cli-vite` that lets you bring your own Vite setup.
-   [`@theorchard/openapi-typegen-cli`](./packages/openapi-typegen-cli) CLI to generate TypeScript types from OpenAPI schemas, with a config-file workflow and interactive TUI.

### Utility packages

-   [`@theorchard/ows-logger`](packages/ows-logger) A logger for JS/TS projects to format the log in ows format similar to python-owslogger.
-   [`@theorchard/identity`](packages/identity) common identity headers and types.
-   [`@theorchard/field-validator`](packages/field-validator) A class utility that lets you do simple and fast type checks on object properties.
-   [`@theorchard/social-media-link-parser`](./packages/social-media-link-parser) parses various kinds of user input related to social media links.

## Creating a new package

-   Ensure that each package has its own README file which clearly explains how it can be used. This should likely include example code that illustrates how any exported classes, functions, etc. can be utilized.
-   Add the package to the list of existing packages above, with a tl;dr about its purpose (and link it properly, please).
-   In order to add a dependency to a package use `pnpm add <dependency>` from the package's folder.
-   Make sure to references to other packages in tsconfig.esm.json and tsconfig.cjs.json.

## Publishing a new package version

In short:

-   Add your changes.
-   Make sure your tests are passing running `pnpm test:changed`.
-   Run at the root: `pnpm new-version`.
-   Follow the changeset instructions.
-   Commit your changes and create a PR.
-   The new package(s) will be published once your PR is merged.

**DO NOT run the `pnpm publish` command locally.**
This will publish the packages without your changes being merged to the master branch.

### Reverting changes

If you published a new package that you want to revert; you have two options.

1. Create a new PR with the reverted changes, but keep the changelog and bump the version. The "bad" version will still be around, so a simple github revert PR wont do.
2. In case you published a very bad version, a version that nobody should ever use. Then you can create a normal github revert PR and a devops ticket to delete the package from the registry.

### Patching previous major versions ( suite packages only )

Major versions often include breaking changes and can for various reasons be hard for teams to quickly integrate. Important patches to newer major versions that also can be applied to previous, should therefore be deployed regularly.
Follow the given steps to patch previous majors:

#### 1. Run `suite:patch`

At the root of this project, run `pnpm suite:patch`. This will fire up the monorepo-cli "patch" task which will guide you trough the patching process. Be aware that patching previous versions can be sometimes difficult if you have many related changes across dependencies. If you are uncertain, please contact the OSP team.

#### 2. Choose which package to patch

The first thing you will be prompted to do, is to choose which library you want to patch.
Currently we only support patching the suite main packages. suite-components, suite-frontend and suite-icons.

#### 3. Choose which release to patch

The next prompt will ask you which version of the chosen package you want to patch. This is usually the latest version from the previous major. When choosing a version, the tooling will then continue to check out the corresponding tag for that version and create a new `PATCH-` branch locally and remotely on the upstream repo.

#### 4. Cherry pick patches

The next prompt will let you choose one or more patches to cherry pick into your new branch.
NB! The tooling will do one at the time. Please make sure you have fixed all conflicts and committed them before continuing with the next.

Resolving conflicts is the hardest part of patching previous versions. You should take the time to carefully review and manually change the changelogs, code and dependencies to your need. Please note the following:

-   Manually bump the version of your package and add a new entry on the top in the changelog.
-   The changelogs should not include newer entries than the version you are patching.
-   **Do not run the `pnpn new-version` script!**, since it will create new versions for the existing dependencies.
-   Review dependencies on workspace packages. Please make sure they are targeting the latest compatible version.

#### 5. Create your PR

Once you are done, the tooling will automatically create a PR against the remote upstream patch branch. Have your team review it as normal and merge it. Once merged, the jenkins pipeline will deploy your new package with a `patch` tag.

## Managing the repo

Working with this repo is slightly different than other js/ts based Orchard projects. Since there are many sub projects and they are interconnected we need a better tooling than yarn. yarn has the notion of workspaces, but it has some big drawbacks. One of them is that you have to repeat dev-dependencies and dependencies for every package. And it makes it hard to ensure all packages are using the same library, e.g jest.

**pnpm** is a new package manager that is specifically tailored to handle monorepos.

-   Define your dev-dependencies at the root. All packages will use the same.
-   Run commands across all packages with inter-dependencies in mind.
-   Works perfect with changeset.
-   Very fast
-   Used by big companies, like Microsoft. ( It's the basis for their rushjs.io tooling )

It is highly recommended that you read more about pnpm and what it can do.
https://pnpm.js.org/

### Root scripts for building, testing and deployment

All the scripts in the root package.json runs the command against every package in the repo.

#### `pnpm [cmd]`

Runs a script defined in every `/packages/*/package.json` files.
If there is no matching script, it will be skipped.

e.g run linter for all packages:
`pnpm lint`

### Examples

```bash

# build and run all unit tests and linters for changed packages ( it's what the PR job does )
pnpm test:changed

# run all unit tests
pnpm test:unit

# run all linters
pnpm lint

# run unit tests just for one package
pnpm --filter @theorchard/suite-i18n test:unit

# run unit tests just for packages matching pattern
pnpm --filter "*graphql" test:unit

# build all suite packages
pnpm --filter "*suite*" build

# run a script only defined for your package(s)
# the -r argument tells pnpm to look for this script in all packages.
pnpm -r my-script

```

There are more ways to filter your commands to target specific packages. https://pnpm.io/filtering

## Troubleshooting

### I get errors when running linter or unit tests that seems wrong

This is most likely due to outdated builds. Try to clean and rebuild all packages with: `pnpm clean` and then `pnpm build`.

## FAQ

### Why do we need this?

There are non-trivial pieces of code that we use throughout our Javascript and Typescript apps/services at The Orchard which benefit from standardization. If you have non-trivial code that needs to be re-used across frontend or backend javascript implementations, this may be a good place for you to add such code.

### Isn't this a monorepo? Isn't that bad? I've heard that's bad.

There is a school of thought that says, yes, this is in fact a bad idea. However, many organizations such as [Facebook](https://www.youtube.com/watch?v=X0VH78ye4yY), [Google](https://www.youtube.com/watch?v=W71BTkUbdqE), and [Twitter](https://www.youtube.com/watch?v=bjh4DHuOf4E) have found value in this approach.

Now, it's pretty easy to put together a list of organizations and projects that do a thing a certain way and argue that the thing is great because of that. On its own, that's not a super compelling argument, so here are some Orchard-specific reasons why this is desirable:

-   _If you need a new package, you don't need a new repository._
-   _If you need a new package, you don't need a new pipeline._
-   _If you need a new package, you don't need to repeat patterns for typescript, linting rules, unit test frameworks._

### When is a good time to create a new package?

Keep in mind that a new package added to this repository can require ongoing maintenance: when you make changes you will have to increment build numbers, deploy new versions, and inform clients that there are new versions of the package available. Therefore, it's useful to consider how frequently your proposed package will be changed, before committing to adding it to this repository.
For example, if you were to add an `@orchard/features` package to this repository containing all active feature flag enum values, this would need a version bump, a new deployment, and client updates every time feature flags were added/removed. That sounds like a lot of work, right? As a general rule, it's useful to add something to this repository _once you are sure that the initial version will be fairly static_. For example, the patterns used by [`@orchard/ows-data-source`](/packages/ows-data-source) were very well understood and iterated upon for quite some time before that package was upstreamed as a dependency. If we had added this package at the start, we would have had quite the headache each time the interface changed! So it's good to keep in mind that adding code here creates a bit of an internal API SLA between the author and the clients who will utilize the dependency.

### Making changes to a package and testing those changes in a service that uses the package

If you are making changes to an orchard suite package and you want to test changes to the package, you can build the package and reference it in a frontend service that uses the built package.

Example: I have made changes to graphql-server but now I need to test those changes. How do I do that?

1. Inside the `orchard-suite` repo I go in to `graphql-server` package and I go to my terminal and type `pnpm build && pnpm pack`. This should build the package and zip it up as `theorchard-graphql-server-[version].tgz`
2. Now choose a frontend service would use graphql-server. In the example's case, graphql-product uses graphql-server so I choose that service.
3. Inside graphql-product I edit the `package.json` and look for `graphql-server` under `dependencies` and change the version number to be `file:[relative or absolute path]/orchard-suite/packages/graphql-server/theorchard-graphql-server-[version].tgz`
4. After you've pointed your dependency to your local file, run a `yarn install --force` to reinstall your packages.

Credit to this guide goes to @ivnnv. Any questions can be directed to him or @msolvaag.
