Skip to content

Commit

Permalink
feat: resource configuration (#571)
Browse files Browse the repository at this point in the history
  • Loading branch information
basmasking authored Dec 23, 2024
1 parent 95f87ba commit 95b14c1
Show file tree
Hide file tree
Showing 24 changed files with 262 additions and 79 deletions.
20 changes: 9 additions & 11 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,18 @@ on:
branches: [ "main" ]
paths:
- 'packages/**'
paths-ignore:
- 'packages/**/node_modules/**'
- 'packages/**/dist/**'
- 'packages/jitar'
- 'packages/create-jitar'
- '!packages/**/node_modules/**'
- '!packages/**/dist/**'
- '!packages/jitar'
- '!packages/create-jitar'
pull_request:
branches: [ "main" ]
paths:
- 'packages/**'
paths-ignore:
- 'packages/**/node_modules/**'
- 'packages/**/dist/**'
- 'packages/jitar'
- 'packages/create-jitar'
- '!packages/**/node_modules/**'
- '!packages/**/dist/**'
- '!packages/jitar'
- '!packages/create-jitar'
schedule:
- cron: '39 10 * * 1'

Expand All @@ -39,7 +37,7 @@ jobs:
- 'packages/analysis'
- 'packages/build'
- 'packages/cli'
- 'packages/configurations'
- 'packages/configuration'
- 'packages/errors'
- 'packages/execution'
- 'packages/health'
Expand Down
3 changes: 1 addition & 2 deletions .github/workflows/nodejsci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:

strategy:
matrix:
node-version: [20.x, 21.x]
node-version: [20.x, 22.x]

steps:
- uses: actions/checkout@v4
Expand All @@ -30,5 +30,4 @@ jobs:
cache-dependency-path: '**/package-lock.json'

- run: npm ci
- run: npm install @nrwl/nx-linux-x64-gnu
- run: npm run review
1 change: 1 addition & 0 deletions documentation/docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export default defineConfig({
text: 'DEPLOY',
items: [
{ text: 'Segmentation', link: '/deploy/segmentation' },
{ text: 'Resources', link: '/deploy/resources' },
{ text: 'Environments', link: '/deploy/environments' },
{ text: 'Load balancing', link: '/deploy/load-balancing' },
{ text: 'Health checks', link: '/deploy/health-checks' },
Expand Down
4 changes: 2 additions & 2 deletions documentation/docs/deploy/environments.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
layout: doc

prev:
text: Segmentation
link: /deploy/segmentation
text: Resources
link: /deploy/resources

next:
text: Load balancing
Expand Down
41 changes: 41 additions & 0 deletions documentation/docs/deploy/resources.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
layout: doc

prev:
text: Segmentation
link: /deploy/segmentation

next:
text: Environments
link: /deploy/environments

---

# Resources

Resources are objects that maintain state. For example, a database connection or a connection to a file store. These resources are usually machine-wide and could be shared between different segments when they are deployed on the same machine.

In Jitar's [segmentation model](/deploy/segmentation), each segment is isolated from the others. This means that each segment has its own copy of any shared code. This feature is useful for creating independent deployable packages. However, it also means that resources cannot be shared between segments by default. By defining which modules are resources, Jitar won't duplicate the code and will share the resource between segments.

### Resource files

Jitar will search for resource definitions files in the project directory. The files are named `*.resources.json`. Each entry defines the entry point of the `module` that should be used as a resource.

The file has the following structure:

```json
// app.resources.json
[
"./integrations/authentication/entry-file",
"./integrations/database/index",
"./integrations/filestore
]
```

::: info
The `/index` part is optional. If the module is a directory, Jitar will automatically look for the index file.
:::

::: tip
It's possible to define multiple resource files within a project. This is useful in a monorepo setup where different modules might be managed by different teams.
:::
4 changes: 2 additions & 2 deletions documentation/docs/deploy/segmentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ prev:
link: /develop/debugging

next:
text: Environments
link: /deploy/environments
text: Resources
link: /deploy/resources

---

Expand Down
18 changes: 18 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,8 @@
"typescript": "^5.6.2",
"typescript-eslint": "^8.18.1",
"vitest": "^2.1.8"
},
"optionalDependencies": {
"@nrwl/nx-linux-x64-gnu": "^15.9.3"
}
}
3 changes: 2 additions & 1 deletion packages/build/src/BuildManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ export default class BuildManager
async build(): Promise<void>
{
const moduleFiles = await this.#sourceFileManager.filter(Files.MODULE_PATTERN);
const resourceFiles = await this.#projectFileManager.filter(Files.RESOURCES_PATTERN);
const segmentFiles = await this.#projectFileManager.filter(Files.SEGMENT_PATTERN);

const applicationModel = await this.#applicationReader.read(moduleFiles, segmentFiles);
const applicationModel = await this.#applicationReader.read(moduleFiles, resourceFiles, segmentFiles);

return this.#applicationBuilder.build(applicationModel);
}
Expand Down
10 changes: 7 additions & 3 deletions packages/build/src/source/application/Reader.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@

import type { FileManager } from '@jitar/sourcing';

import { ResourceReader } from '../resource';
import { ModuleReader } from '../module';
import { SegmentReader } from '../segment';

import Application from './models/Application.js';
import Application from './models/Application';

export default class Reader
{
Expand All @@ -15,14 +16,17 @@ export default class Reader
this.#fileManager = fileManager;
}

async read(moduleFiles: string[], segmentFiles: string[]): Promise<Application>
async read(moduleFiles: string[], resourceFiles: string[], segmentFiles: string[]): Promise<Application>
{
const moduleReader = new ModuleReader(this.#fileManager);
const repository = await moduleReader.readAll(moduleFiles);

const resourceReader = new ResourceReader(this.#fileManager);
const resources = await resourceReader.readAll(resourceFiles);

const segmentReader = new SegmentReader(this.#fileManager, repository);
const segmentation = await segmentReader.readAll(segmentFiles);

return new Application(repository, segmentation);
return new Application(repository, resources, segmentation);
}
}
7 changes: 6 additions & 1 deletion packages/build/src/source/application/models/Application.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@

import type { ResourcesList } from '../../resource';
import type { ModuleRepository } from '../../module';
import type { Segmentation } from '../../segment';

export default class Application
{
readonly #repository: ModuleRepository;
readonly #resources: ResourcesList;
readonly #segmentation: Segmentation;

constructor(repository: ModuleRepository, segmentation: Segmentation)
constructor(repository: ModuleRepository, resources: ResourcesList, segmentation: Segmentation)
{
this.#repository = repository;
this.#resources = resources;
this.#segmentation = segmentation;
}

get resources() { return this.#resources; }

get repository() { return this.#repository; }

get segmentation() { return this.#segmentation; }
Expand Down
1 change: 1 addition & 0 deletions packages/build/src/source/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
export { Application, ApplicationReader } from './application';
export { Module } from './module';
export { Segment, SegmentModule, SegmentImplementation, Segmentation } from './segment';
export { ResourcesList } from './resource';
53 changes: 53 additions & 0 deletions packages/build/src/source/resource/Reader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@

import type { FileManager } from '@jitar/sourcing';

import ResourcesList from './models/ResourcesList';
import FileNotLoaded from './errors/FileNotLoaded';
import type ResourceFile from './types/File';
import { FileHelper } from '../../utils';

export default class ResourceReader
{
readonly #fileManager: FileManager;
readonly #fileHelper = new FileHelper();

constructor(fileManager: FileManager)
{
this.#fileManager = fileManager;
}

async readAll(filenames: string[]): Promise<ResourcesList>
{
const resources = await Promise.all(filenames.map(filename => this.#loadResourceDefinition(filename)));

return new ResourcesList(resources.flat());
}

async #loadResourceDefinition(filename: string): Promise<ResourceFile>
{
try
{
const content = await this.#fileManager.getContent(filename);

const result = JSON.parse(content.toString()) as ResourceFile;

return result.map(resource => this.#makeResourceFilename(resource));
}
catch (error: unknown)
{
const message = error instanceof Error ? error.message : String(error);

throw new FileNotLoaded(filename, message);
}
}

#makeResourceFilename(filename: string): string
{
const fullFilename = this.#fileHelper.assureExtension(filename);

if (fullFilename.startsWith('./')) return fullFilename.substring(2);
if (fullFilename.startsWith('/')) return fullFilename.substring(1);

return fullFilename;
}
}
8 changes: 8 additions & 0 deletions packages/build/src/source/resource/errors/FileNotLoaded.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

export default class FileNotLoaded extends Error
{
constructor(filename: string, message: string)
{
super(`Failed to load resource file '${filename}' because of: ${message}`);
}
}
3 changes: 3 additions & 0 deletions packages/build/src/source/resource/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

export { default as ResourcesList } from './models/ResourcesList';
export { default as ResourceReader } from './Reader';
15 changes: 15 additions & 0 deletions packages/build/src/source/resource/models/ResourcesList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

export default class ResourcesList
{
readonly #resources: string[];

constructor(resources: string[])
{
this.#resources = resources;
}

isResourceModule(moduleFilename: string): boolean
{
return this.#resources.includes(moduleFilename);
}
}
4 changes: 4 additions & 0 deletions packages/build/src/source/resource/types/File.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

type File = string[];

export default File;
12 changes: 7 additions & 5 deletions packages/build/src/target/ExportRewriter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,14 @@ export default class ExportRewriter
return this.#rewriteToStaticExport(dependency, from); // different segments
}

// export shared (unsegmented) module
// export common (unsegmented) module

if (this.#segment !== undefined)
{
console.warn('Exporting shared module from a segmented module!');
console.warn('Exporting common module from a segmented module!');
}

const from = this.#rewriteApplicationFrom(targetModuleFilename, 'shared');
const from = this.#rewriteApplicationFrom(targetModuleFilename);

return this.#rewriteToStaticExport(dependency, from);
}
Expand All @@ -93,12 +93,14 @@ export default class ExportRewriter
return this.#rewriteToStaticExport(dependency, from);
}

#rewriteApplicationFrom(filename: string, scope: string): string
#rewriteApplicationFrom(filename: string, scope?: string): string
{
const callingModulePath = this.#fileHelper.extractPath(this.#module.filename);
const relativeFilename = this.#fileHelper.makePathRelative(filename, callingModulePath);

return this.#fileHelper.addSubExtension(relativeFilename, scope);
return scope === undefined
? relativeFilename
: this.#fileHelper.addSubExtension(relativeFilename, scope);
}

#rewriteRuntimeFrom(dependency: ESExport): string
Expand Down
Loading

0 comments on commit 95b14c1

Please sign in to comment.