Skip to content

Commit 1bd6df8

Browse files
feat: Implement Athena Classifier Service with gRPC support
- Added athena.ts to define protobuf messages and service for classification. - Introduced empty.ts for handling empty messages in gRPC. - Developed main.ts to compute image hashes and send classification requests to the Athena ClassifierService. - Created ClassifyImageOptions interface for better request handling. - Implemented classifyImage function to manage image classification requests and responses.
1 parent 9265511 commit 1bd6df8

9 files changed

Lines changed: 2501 additions & 207 deletions

File tree

README.md

Lines changed: 24 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,121 +1,31 @@
1-
# node-typescript-boilerplate
1+
## Regenerating the TypeScript gRPC Client
22

3-
[![Sponsor][sponsor-badge]][sponsor]
4-
[![TypeScript version][ts-badge]][typescript-5-7]
5-
[![Node.js version][nodejs-badge]][nodejs]
6-
[![APLv2][license-badge]][license]
7-
[![Build Status - GitHub Actions][gha-badge]][gha-ci]
3+
This project uses [`@protobuf-ts/plugin`](https://github.com/timostamm/protobuf-ts) to generate TypeScript-native gRPC clients and message types for use with `@grpc/grpc-js`.
84

9-
👩🏻‍💻 Developer Ready: A comprehensive template. Works out of the box for most [Node.js][nodejs] projects.
5+
### How to update the generated client code
106

11-
🏃🏽 Instant Value: All basic tools included and configured:
7+
1. **Ensure dependencies are installed:**
8+
```sh
9+
npm install --save-dev @protobuf-ts/plugin
10+
npm install --save @grpc/grpc-js
11+
```
1212

13-
- [TypeScript][typescript] [5.7][typescript-5-7]
14-
- [ESM][esm]
15-
- [ESLint][eslint] with some initial rules recommendation
16-
- [Vitest][vitest] for fast unit testing and code coverage
17-
- Type definitions for Node.js
18-
- [Prettier][prettier] to enforce consistent code style
19-
- NPM [scripts](#available-scripts) for common operations
20-
- [EditorConfig][editorconfig] for consistent coding style
21-
- Reproducible environments thanks to [Volta][volta]
22-
- Example configuration for [GitHub Actions][gh-actions]
23-
- Simple example of TypeScript code and unit test
13+
2. **Run the following command to regenerate the client and types:**
14+
```sh
15+
npx protoc \
16+
--plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts \
17+
--ts_out=client_grpc1:./src/athena \
18+
--proto_path=./athena-protobufs/athena \
19+
./athena-protobufs/athena/athena.proto
20+
```
21+
- This will generate `.ts` files in `src/athena/` including a gRPC client compatible with `@grpc/grpc-js`.
2422

25-
🤲 Free as in speech: available under the APLv2 license.
23+
3. **Update your imports in your code as needed:**
24+
- The main client will be in `src/athena/athena.grpc-client.ts`.
25+
- Message types and enums are in `src/athena/athena.ts`.
2626

27-
## Getting Started
27+
### Notes
28+
- If you add or change proto files, rerun the above command to keep the TypeScript client in sync.
29+
- If you see TypeScript errors about missing modules, ensure your `tsconfig.json` includes the `src/athena` directory and restart your IDE/tsserver.
2830

29-
This project is intended to be used with the latest Active LTS release of [Node.js][nodejs].
30-
31-
### Use as a repository template
32-
33-
To start, just click the **[Use template][repo-template-action]** link (or the green button). Start adding your code in the `src` and unit tests in the `__tests__` directories.
34-
35-
### Clone repository
36-
37-
To clone the repository, use the following commands:
38-
39-
```sh
40-
git clone https://github.com/jsynowiec/node-typescript-boilerplate
41-
cd node-typescript-boilerplate
42-
npm install
43-
```
44-
45-
### Download latest release
46-
47-
Download and unzip the current **main** branch or one of the tags:
48-
49-
```sh
50-
wget https://github.com/jsynowiec/node-typescript-boilerplate/archive/main.zip -O node-typescript-boilerplate.zip
51-
unzip node-typescript-boilerplate.zip && rm node-typescript-boilerplate.zip
52-
```
53-
54-
## Available Scripts
55-
56-
- `clean` - remove coverage data, cache and transpiled files,
57-
- `prebuild` - lint source files and tests before building,
58-
- `build` - transpile TypeScript to ES6,
59-
- `build:watch` - interactive watch mode to automatically transpile source files,
60-
- `lint` - lint source files and tests,
61-
- `prettier` - reformat files,
62-
- `test` - run tests,
63-
- `test:watch` - interactive watch mode to automatically re-run tests
64-
- `test:coverage` - run test and print out test coverage
65-
66-
## Additional Information
67-
68-
### Why include Volta
69-
70-
I recommend to [install][volta-getting-started] Volta and use it to manage your project's toolchain.
71-
72-
[Volta][volta]’s toolchain always keeps track of where you are, it makes sure the tools you use always respect the settings of the project you’re working on. This means you don’t have to worry about changing the state of your installed software when switching between projects. For example, it's [used by engineers at LinkedIn][volta-tomdale] to standardize tools and have reproducible development environments.
73-
74-
### Why Vitest instead of Jest
75-
76-
I recommend using [Vitest][vitest] for unit and integration testing of your TypeScript code.
77-
78-
In 2023, my team and I gradually switched from Jest to [Vitest][vitest] in all the projects. We've found out that generally, Vitest is faster than Jest, especially for large test suits. Furthermore, Vitest has native support for ES modules, is easier to configure, and has a much nicer developer experience when used with TypeScript. For example, when working with mocks, spies and types.
79-
80-
Nevertheless, the choice of specific tooling always depends on the specific requirements and characteristics of the project.
81-
82-
### ES Modules
83-
84-
This template uses native [ESM][esm]. Make sure to read [this][nodejs-esm], and [this][ts47-esm] first.
85-
86-
If your project requires CommonJS, you will have to [convert to ESM][sindresorhus-esm].
87-
88-
Please do not open issues for questions regarding CommonJS or ESM on this repo.
89-
90-
## Backers & Sponsors
91-
92-
Support this project by becoming a [sponsor][sponsor].
93-
94-
## License
95-
96-
Licensed under the APLv2. See the [LICENSE](https://github.com/jsynowiec/node-typescript-boilerplate/blob/main/LICENSE) file for details.
97-
98-
[ts-badge]: https://img.shields.io/badge/TypeScript-5.7-blue.svg
99-
[nodejs-badge]: https://img.shields.io/badge/Node.js-22-blue.svg
100-
[nodejs]: https://nodejs.org/dist/latest-v22.x/docs/api/
101-
[gha-badge]: https://github.com/jsynowiec/node-typescript-boilerplate/actions/workflows/nodejs.yml/badge.svg
102-
[gha-ci]: https://github.com/jsynowiec/node-typescript-boilerplate/actions/workflows/nodejs.yml
103-
[typescript]: https://www.typescriptlang.org/
104-
[typescript-5-7]: https://devblogs.microsoft.com/typescript/announcing-typescript-5-7/
105-
[license-badge]: https://img.shields.io/badge/license-APLv2-blue.svg
106-
[license]: https://github.com/jsynowiec/node-typescript-boilerplate/blob/main/LICENSE
107-
[sponsor-badge]: https://img.shields.io/badge/♥-Sponsor-fc0fb5.svg
108-
[sponsor]: https://github.com/sponsors/jsynowiec
109-
[eslint]: https://github.com/eslint/eslint
110-
[prettier]: https://prettier.io
111-
[volta]: https://volta.sh
112-
[volta-getting-started]: https://docs.volta.sh/guide/getting-started
113-
[volta-tomdale]: https://twitter.com/tomdale/status/1162017336699838467
114-
[gh-actions]: https://github.com/features/actions
115-
[repo-template-action]: https://github.com/jsynowiec/node-typescript-boilerplate/generate
116-
[esm]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules
117-
[sindresorhus-esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
118-
[nodejs-esm]: https://nodejs.org/docs/latest-v16.x/api/esm.html
119-
[ts47-esm]: https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/#esm-nodejs
120-
[editorconfig]: https://editorconfig.org
121-
[vitest]: https://vitest.dev
31+
---
24.7 KB
Loading

__tests__/unit/main.test.ts

Lines changed: 30 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,33 @@
11
import { describe, it, afterEach, beforeEach, vi, expect } from 'vitest';
2-
import { Delays, greeter } from '../../src/main.js';
3-
4-
describe('greeter function', () => {
5-
const name = 'John';
6-
7-
beforeEach(() => {
8-
// Read more about fake timers
9-
// https://vitest.dev/api/vi.html#vi-usefaketimers
10-
vi.useFakeTimers();
11-
});
12-
13-
afterEach(() => {
14-
vi.useRealTimers();
15-
vi.restoreAllMocks();
16-
});
17-
18-
// Assert if setTimeout was called properly
19-
it('delays the greeting by 2 seconds', async () => {
20-
vi.spyOn(global, 'setTimeout');
21-
const p = greeter(name);
22-
23-
await vi.runAllTimersAsync();
24-
await p;
25-
26-
expect(setTimeout).toHaveBeenCalledTimes(1);
27-
expect(setTimeout).toHaveBeenLastCalledWith(
28-
expect.any(Function),
29-
Delays.Long,
30-
);
31-
});
32-
33-
// Assert greeter result
34-
it('greets a user with `Hello, {name}` message', async () => {
35-
const p = greeter(name);
36-
await vi.runAllTimersAsync();
37-
38-
expect(await p).toBe(`Hello, ${name}`);
2+
import { classifyImage, computeHashesFromStream, ClassifyImageOptions } from '../../src/main.js';
3+
4+
5+
import fs from 'fs';
6+
7+
describe('classifyImage function', () => {
8+
it('should classify Steamboat-willie.jpg and return responses (integration smoke test)', async () => {
9+
// This is a smoke test. You must have a running gRPC server at localhost:50051 for this to pass.
10+
// You may want to mock the gRPC client for true unit testing.
11+
const imagePath = __dirname + '/Steamboat-willie.jpg';
12+
const imageStream = fs.createReadStream(imagePath);
13+
const options: ClassifyImageOptions = {
14+
imageStream,
15+
deploymentId: 'test-deployment',
16+
affiliate: 'test-affiliate',
17+
correlationId: 'test-correlation',
18+
// encoding, format, grpcAddress use defaults
19+
};
20+
// This will fail if no server is running, but will exercise the code path.
21+
let error: any = null;
22+
try {
23+
const responses = await classifyImage(options);
24+
expect(Array.isArray(responses)).toBe(true);
25+
} catch (err) {
26+
error = err;
27+
}
28+
// Accept either a successful call or a connection error (for CI/dev convenience)
29+
if (error) {
30+
expect(error.message).toMatch(/connect|unavailable|ECONNREFUSED|14/);
31+
}
3932
});
4033
});

0 commit comments

Comments
 (0)