Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TM-1531] Add site name in entity and return entityname in delayed job #31

Merged
merged 12 commits into from
Dec 24, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 39 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,36 @@
# terramatch-microservices

Repository for the Microservices API backend of the TerraMatch service

# Requirements:
* Node v20.11.1. Using [NVM](https://github.com/nvm-sh/nvm?tab=readme-ov-file) is recommended.
* [Docker](https://www.docker.com/)
* [CDK CLI](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html) (install globally)
* [AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html)
* [NX](https://nx.dev/getting-started/installation#installing-nx-globally) (install globally)
* [NestJS](https://docs.nestjs.com/) (install globally, useful for development)

- Node v20.11.1. Using [NVM](https://github.com/nvm-sh/nvm?tab=readme-ov-file) is recommended.
- [Docker](https://www.docker.com/)
- [CDK CLI](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html) (install globally)
- [AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html)
- [NX](https://nx.dev/getting-started/installation#installing-nx-globally) (install globally)
- [NestJS](https://docs.nestjs.com/) (install globally, useful for development)

# Building and starting the apps
* Copy `.env.local.sample` to `.env`
* On Linux systems, the DOCKER_HOST value should be `unix:///var/run/docker.sock` instead of what's in the sample.
* To run all services:
* `nx run-many -t serve`
* The default maximum number of services it can run in parallel is 3. To run all of the services at once, use something like
`nx run-many --parallel=100 -t serve`, or you can cherry-pick which services you want to run instead with
`nx run-many -t serve --projects user-service jobs-service`.
* Some useful targets have been added to the root `package.json` for service sets. For instance, to run just the services needed
by the TM React front end, use `npm run fe-services`, or to run all use `npm run all`.
* In `.env` in your `wri-terramatch-website` repository, set your BE connection URL correctly by noting the config
in `.env.local.sample` for local development.
* The `NEXT_PUBLIC_API_BASE_URL` still points at the PHP BE directly
* New `NEXT_PUBLIC_<SERVICE>_URL` values are needed for each service you're running locally. This will typically match
the services defined in `V3_NAMESPACES` in `src/generated/v3/utils.ts`.

- Copy `.env.local.sample` to `.env`
- On Linux systems, the DOCKER_HOST value should be `unix:///var/run/docker.sock` instead of what's in the sample.
- To run all services:
- `nx run-many -t serve`
- The default maximum number of services it can run in parallel is 3. To run all of the services at once, use something like
`nx run-many --parallel=100 -t serve`, or you can cherry-pick which services you want to run instead with
`nx run-many -t serve --projects user-service job-service`.
- Some useful targets have been added to the root `package.json` for service sets. For instance, to run just the services needed
by the TM React front end, use `npm run fe-services`, or to run all use `npm run all`.
- In `.env` in your `wri-terramatch-website` repository, set your BE connection URL correctly by noting the config
in `.env.local.sample` for local development.
- The `NEXT_PUBLIC_API_BASE_URL` still points at the PHP BE directly
- New `NEXT_PUBLIC_<SERVICE>_URL` values are needed for each service you're running locally. This will typically match
the services defined in `V3_NAMESPACES` in `src/generated/v3/utils.ts`.

# Deployment
Deployment is handled via manual trigger of GitHub actions. There is one for services, and one for the ApiGateway. The

Deployment is handled via manual trigger of GitHub actions. There is one for services, and one for the ApiGateway. The
ApiGateway only needs to be redeployed if its code changes; it does not need to be redeployed for updates to individual services
to take effect.

Expand Down Expand Up @@ -62,10 +66,12 @@ and main branches.
need to remain a manual process.

# Database work
For now, Laravel is the source of truth for all things related to the DB schema. As such, TypeORM is not allowed to modify the
schema, and is expected to interface with exactly the schema that is managed by Laravel. This note is included in user.entity.ts,

For now, Laravel is the source of truth for all things related to the DB schema. As such, TypeORM is not allowed to modify the
schema, and is expected to interface with exactly the schema that is managed by Laravel. This note is included in user.entity.ts,
and should hold true for all models created in this codebase until this codebase can take over as the source of truth for DB
schema:

```
// Note: this has some additional typing information (like width: 1 on bools and type: timestamps on
// CreateDateColumn) to make the types generated here match what is generated by Laravel exactly.
Expand All @@ -78,25 +84,29 @@ This codebase connects to the database running in the `wri-terramatch-api` docke
file included in this repo is used only for setting up the database needed for running unit tests in Github Actions.

# Testing

To set up the local testing database, run the `./bin/setup-test-database.sh` script. This script assumes that the
`wri-terramatch-api` project is checked out in the same parent directory as this one. The script may be run
`wri-terramatch-api` project is checked out in the same parent directory as this one. The script may be run
again at any time to clear out the test database records and schema.

`setup-jest.ts` is responsible for creating the Sequelize connection for all tests. Via the `sync` command, it also
creates database tables according to the schema declared in the `entity.ts` files in this codebase. Care should be
taken to make sure that the schema is set up in this codebase such that the database tables are created with the same
types and indices as in the primary database controlled by the Laravel backend.
types and indices as in the primary database controlled by the Laravel backend.

Factories may be used to create entries in the database for testing. See `user.factory.ts`, and uses of `UserFactory` for
Factories may be used to create entries in the database for testing. See `user.factory.ts`, and uses of `UserFactory` for
an example.

To run the tests for a single app/library:
* `nx test user-service` or `nx test common`

- `nx test user-service` or `nx test common`

To run the tests for the whole codebase:
* `nx run-many -t test --passWithNoTests`

- `nx run-many -t test --passWithNoTests`

For checking coverage, simply pass the `--coverage` flag:
* `nx test user-service --coverage` or `nx run-many -t test --passWithNoTests --coverage`

- `nx test user-service --coverage` or `nx run-many -t test --passWithNoTests --coverage`

For apps/libraries that have tests defined, the coverage thresholds are set for the whole project in `jest.preset.js`
35 changes: 25 additions & 10 deletions apps/job-service/src/jobs/delayed-jobs.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,19 @@ export class DelayedJobsController {
order: [["createdAt", "DESC"]]
});
roguenet marked this conversation as resolved.
Show resolved Hide resolved

const jobsWithEntityNames = await Promise.all(
runningJobs.map(async job => {
const entityName = job.metadata?.entity_name;
return { ...job.toJSON(), entityName };
})
);

const document = buildJsonApi();
runningJobs.forEach(job => {
jobsWithEntityNames.forEach(job => {
document.addData(job.uuid, new DelayedJobDto(job));
});
return document.serialize();
}

@Get(":uuid")
@ApiOperation({
operationId: "delayedJobsFind",
Expand Down Expand Up @@ -90,25 +96,34 @@ export class DelayedJobsController {
uuid: { [Op.in]: jobUpdates.map(({ uuid }) => uuid) },
createdBy: authenticatedUserId,
status: { [Op.ne]: "pending" }
}
},
logging: console.log,
roguenet marked this conversation as resolved.
Show resolved Hide resolved
order: [["createdAt", "DESC"]]
});
if (jobs.length !== jobUpdates.length) {

if (!jobs.length) {
roguenet marked this conversation as resolved.
Show resolved Hide resolved
throw new NotFoundException("Some jobs in the request could not be updated");
}

const updatePromises = jobUpdates.map(async ({ uuid, attributes }) => {
const job = jobs.find(job => job.uuid === uuid);
job.isAcknowledged = attributes.isAcknowledged;
await job.save();
return job;
});
const updatePromises = jobUpdates
.filter(({ uuid }) => jobs.some(job => job.uuid === uuid))
.map(async ({ uuid, attributes }) => {
const job = jobs.find(job => job.uuid === uuid);
job.isAcknowledged = attributes.isAcknowledged;
await job.save();

const entityName = job.metadata ? job.metadata.entity_name : null;

return { ...job.toJSON(), entityName };
});

const updatedJobs = await Promise.all(updatePromises);

const jsonApiBuilder = buildJsonApi();
updatedJobs.forEach(job => {
jsonApiBuilder.addData(job.uuid, new DelayedJobDto(job));
});

return jsonApiBuilder.serialize();
}
}
13 changes: 13 additions & 0 deletions apps/job-service/src/jobs/dto/delayed-job.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,17 @@ export class DelayedJobDto extends JsonApiAttributes<DelayedJobDto> {
nullable: true
})
isAcknowledged: boolean | null;

@ApiProperty({
description: "The name of the delayedJob",
nullable: true
})
name: string | null;

@ApiProperty({
description: "The name of the related entity (e.g., Kerrawarra, New Site, etc).",
nullable: true,
required: false
})
entityName?: string | null;
roguenet marked this conversation as resolved.
Show resolved Hide resolved
}
28 changes: 25 additions & 3 deletions libs/database/src/lib/entities/delayed-job.entity.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
import { AllowNull, AutoIncrement, Column, Default, ForeignKey, Index, Model, PrimaryKey, Table } from "sequelize-typescript";
import {
AllowNull,
AutoIncrement,
Column,
Default,
ForeignKey,
Index,
Model,
PrimaryKey,
Table
} from "sequelize-typescript";
import { BIGINT, BOOLEAN, INTEGER, JSON, STRING, UUID } from "sequelize";
import { User } from "./user.entity";

interface JobMetadata {
entity_id: string; // or number, depending on your model's entity ID type
entity_type: string;
entity_name: string; // Name of the related entity
}
@Table({ tableName: "delayed_jobs", underscored: true })
roguenet marked this conversation as resolved.
Show resolved Hide resolved
roguenet marked this conversation as resolved.
Show resolved Hide resolved
export class DelayedJob extends Model<DelayedJob> {
@PrimaryKey
Expand Down Expand Up @@ -35,7 +50,7 @@ export class DelayedJob extends Model<DelayedJob> {

@AllowNull
@Column(STRING)
progressMessage: string | null
progressMessage: string | null;

@ForeignKey(() => User)
@AllowNull
Expand All @@ -44,5 +59,12 @@ export class DelayedJob extends Model<DelayedJob> {

@Column(BOOLEAN)
isAcknowledged: boolean;


@AllowNull
@Column(STRING)
name: string | null;

@AllowNull
@Column(JSON)
metadata: JobMetadata | null;
}
4 changes: 2 additions & 2 deletions libs/database/src/lib/entities/project.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ export class Project extends Model<Project> {

@AllowNull
@Column(TEXT)
projectCountryDistrict: string | null;
projectCountyDistrict: string | null;

@AllowNull
@Column(TEXT)
Expand Down Expand Up @@ -221,7 +221,7 @@ export class Project extends Model<Project> {
pctEmployeesWomen: number | null;

@AllowNull
@Column({ type: TINYINT, field: "pct_employees_18t35" })
@Column({ type: TINYINT, field: "pct_employees_18to35" })
pctEmployees18To35: number | null;

@AllowNull
Expand Down
14 changes: 14 additions & 0 deletions libs/database/src/lib/entities/site-name.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { AutoIncrement, Column, Model, PrimaryKey, Table } from "sequelize-typescript";
import { BIGINT } from "sequelize";

// A quick stub for the research endpoints
@Table({ tableName: "v2_sites", underscored: true, paranoid: true })
roguenet marked this conversation as resolved.
Show resolved Hide resolved
export class SiteName extends Model<SiteName> {
@PrimaryKey
@AutoIncrement
@Column(BIGINT.UNSIGNED)
override id: number;

@Column
name: string;
}
3 changes: 3 additions & 0 deletions libs/database/src/lib/entities/site.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ export class Site extends Model<Site> {
@Column(BIGINT.UNSIGNED)
override id: number;

@Column
name: string;

@Index
@Column(UUID)
uuid: string;
Expand Down
Loading