Skip to content

Commit

Permalink
solution: sierra project and org api
Browse files Browse the repository at this point in the history
  • Loading branch information
kanazirsky committed Dec 3, 2024
1 parent 61756db commit 6b0d536
Show file tree
Hide file tree
Showing 14 changed files with 372 additions and 38 deletions.
2 changes: 1 addition & 1 deletion api-definitions
95 changes: 84 additions & 11 deletions packages/core/src/typesSierra.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,40 @@
import * as sierra_stat_message_pb from './generated/sierra.stat.message_pb';
import * as sierra_message_pb from './generated/sierra.message_pb';
import { DataMapper } from './Publisher';
import { MessageFactory } from './typesConvert';

export interface Project {
orgId: string;
projectId: string;
name: string;
description: string;
createdAt: Date;
}

export interface Org {
orgId: string;
name: string;
description: string;
createdAt: Date;
}

export interface CreateProjectRequest {
orgId: string;
name: string;
description: string;
}

export interface CreateProjectResponse {
projectId: string;
}

export interface ListProjectsRequest {
orgId: string;
}

export interface GetOrgRequest {
orgId: string;
}

export enum Granularity {
UNSPECIFIED = 0,
SECOND = 1,
Expand All @@ -17,14 +50,15 @@ export enum GroupBy {
SERVICE = 1,
PROJECT = 2,
}

export interface GetRequestCountRequest {
orgId: string;
timestampFrom?: Date;
timestampTo?: Date;
projectIds?: string[];
services?: string[];
granularity?: Granularity;
GroupBy?: GroupBy;
groupBy?: GroupBy;
}

export interface RequestCount {
Expand Down Expand Up @@ -62,8 +96,28 @@ export class ConvertSierra {
this.factory = factory;
}

public getRequestCountRequest(request: GetRequestCountRequest): sierra_stat_message_pb.GetRequestCountRequest {
const result: sierra_stat_message_pb.GetRequestCountRequest = this.factory('sierra_stat_message_pb.GetRequestCountRequest');
public createProjectRequest(request: CreateProjectRequest): sierra_message_pb.CreateProjectRequest {
const result: sierra_message_pb.CreateProjectRequest = this.factory('sierra_message_pb.CreateProjectRequest');
result.setOrgId(request.orgId);
result.setName(request.name);
result.setDescription(request.description);
return result;
}

public listProjectsRequest(request: ListProjectsRequest): sierra_message_pb.ListProjectsRequest {
const result: sierra_message_pb.ListProjectsRequest = this.factory('sierra_message_pb.ListProjectsRequest');
result.setOrgId(request.orgId);
return result;
}

public getOrgRequest(request: GetOrgRequest): sierra_message_pb.GetOrgRequest {
const result: sierra_message_pb.GetOrgRequest = this.factory('sierra_message_pb.GetOrgRequest');
result.setOrgId(request.orgId);
return result;
}

public getRequestCountRequest(request: GetRequestCountRequest): sierra_message_pb.GetRequestCountRequest {
const result: sierra_message_pb.GetRequestCountRequest = this.factory('sierra_message_pb.GetRequestCountRequest');
result.setOrgId(request.orgId)
if (request.timestampFrom) {
result.setTimestampFrom(request.timestampFrom.getTime());
Expand All @@ -80,20 +134,39 @@ export class ConvertSierra {
if (request.granularity) {
result.setGranularity(request.granularity.valueOf())
}
if (request.GroupBy) {
result.setGroupBy(request.GroupBy.valueOf());
if (request.groupBy) {
result.setGroupBy(request.groupBy.valueOf());
}
return result;
}

public requestCount(): DataMapper<sierra_stat_message_pb.RequestCount, RequestCount> {
public project(): DataMapper<sierra_message_pb.Project, Project> {
return (response) => ({
orgId: response.getOrgId(),
projectId: response.getProjectId(),
name: response.getName(),
description: response.getDescription(),
createdAt: new Date(response.getCreatedAt()),
});
}

public org(): DataMapper<sierra_message_pb.Org, Org> {
return (response) => ({
orgId: response.getOrgId(),
name: response.getName(),
description: response.getDescription(),
createdAt: new Date(response.getCreatedAt()),
});
}

public requestCount(): DataMapper<sierra_message_pb.RequestCount, RequestCount> {
return (response) => ({
timestamp: new Date(response.getTimestamp()),
count: response.getCount(),
});
}

public groupRequestCount(): DataMapper<sierra_stat_message_pb.GroupRequestCount, GroupRequestCount> {
public groupRequestCount(): DataMapper<sierra_message_pb.GroupRequestCount, GroupRequestCount> {
return (response) => {
let group: GroupService | GroupProjectId;
if (response.hasService()) {
Expand All @@ -112,16 +185,16 @@ export class ConvertSierra {
};
}

public getTokenStatRequest(request: GetTokenStatRequest): sierra_stat_message_pb.GetTokenStatRequest {
const result: sierra_stat_message_pb.GetTokenStatRequest = this.factory('sierra_stat_message_pb.GetTokenStatRequest');
public getTokenStatRequest(request: GetTokenStatRequest): sierra_message_pb.GetTokenStatRequest {
const result: sierra_message_pb.GetTokenStatRequest = this.factory('sierra_message_pb.GetTokenStatRequest');
result.setOrgId(request.orgId);
if (request.tokenIds) {
result.setTokenIdsList(request.tokenIds);
}
return result;
}

public tokenStat(): DataMapper<sierra_stat_message_pb.TokenStat, TokenStat> {
public tokenStat(): DataMapper<sierra_message_pb.TokenStat, TokenStat> {
return (response) => {
return {
tokenId: response.getTokenId(),
Expand Down
28 changes: 17 additions & 11 deletions packages/node/src/EmeraldApi.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { ChannelCredentials } from '@grpc/grpc-js';
import { emeraldCredentials } from './credentials';
import { AddressClient } from './wrapped/AddressClient';
import { BlockchainClient } from './wrapped/BlockchainClient';
import { MarketClient } from './wrapped/MarketClient';
import { MonitoringClient } from './wrapped/MonitoringClient';
import { TokenClient } from './wrapped/TokenClient';
import { TransactionClient } from './wrapped/TransactionClient';
import {SecretToken} from "@emeraldpay/api/lib/typesAuth";
import {SecretToken} from "@emeraldpay/api";
import {ChannelCredentials} from '@grpc/grpc-js';
import {emeraldCredentials} from './credentials';
import {AddressClient} from './wrapped/AddressClient';
import {AuthClient} from "./wrapped/Auth";
import {BlockchainClient} from './wrapped/BlockchainClient';
import {MarketClient} from './wrapped/MarketClient';
import {MonitoringClient} from './wrapped/MonitoringClient';
import {SierraProjectClient} from "./wrapped/SierraProjectClient";
import {TokenClient} from './wrapped/TokenClient';
import {TransactionClient} from './wrapped/TransactionClient';

export class EmeraldApi {
private readonly agents: string[] = ['test-client/0.0.0'];
Expand All @@ -22,13 +23,13 @@ export class EmeraldApi {

static devApi(token?: SecretToken | undefined, credentials?: ChannelCredentials): EmeraldApi {
// a dev token with access only from the internal network
let devToken = token ?? 'emrld_8ntrHbZN67DF8TWKgCMO1I9nSaMG0cpoMhj3GP';
const devToken = token ?? 'emrld_8ntrHbZN67DF8TWKgCMO1I9nSaMG0cpoMhj3GP';
return new EmeraldApi('api.emeraldpay.dev:443', devToken, credentials);
}

static localApi(port = 50051, credentials?: ChannelCredentials): EmeraldApi {
// just a random token, doesn't exist
let noToken = 'emrld_yKb3jXMKRJLUWFzL7wPrktkherocZCBy7W6qZH';
const noToken = 'emrld_yKb3jXMKRJLUWFzL7wPrktkherocZCBy7W6qZH';
return new EmeraldApi(`localhost:${port}`, noToken, credentials);
}

Expand Down Expand Up @@ -63,4 +64,9 @@ export class EmeraldApi {
auth(): AuthClient {
return new AuthClient(this.hostname, this.credentials, this.agents);
}

sierraProject(): SierraProjectClient {
return new SierraProjectClient(this.hostname, this.credentials, this.agents);
}

}
45 changes: 45 additions & 0 deletions packages/node/src/__integration-tests__/sierraProject.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {EmeraldApi} from "../EmeraldApi";

jest.setTimeout(35000);

describe("SierraProjectClient", () => {
let api: EmeraldApi;

beforeAll(() => {
// a dev token with user id: bada55a1-0000-4000-a000-000000000000
api = EmeraldApi.devApi("emrld_fU88aIafXsCClerhyWtflBp1hH6h112ckzpSfP");
});

test('createProject permission denied for the user', async () => {
const client = api.sierraProject();

try {
await client.createProject({
orgId: "cafe0000-0000-4000-a000-000000000000",
name: "Test Project",
description: "Project for using in tests",
})
} catch (e) {
console.log("createProject error: ", e)
expect(e.code).toEqual(7); // 7: PERMISSION_DENIED
}
});

test('listProjects', (done) => {
const client = api.sierraProject();

const call = client.listProjects({
orgId: "cafe0000-0000-4000-a000-000000000000",
})
call
.onData((data) => {
console.log("listProjects", data);
done();
})
.onError((error) => {
console.log("cancel: ", error.message);
done(error);
})
});

});
11 changes: 9 additions & 2 deletions packages/node/src/wrapped/Factory.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { MessageFactory } from '@emeraldpay/api';
import {MessageFactory} from '@emeraldpay/api';
import * as address_message_pb from '../generated/address.message_pb';
import * as auth_message_pb from '../generated/auth_pb';
import * as blockchain_pb from '../generated/blockchain_pb';
import * as common_pb from '../generated/common_pb';
import * as market_pb from '../generated/market_pb';
import * as sierra_message_pb from "../generated/sierra.message_pb";
import * as token_message_pb from '../generated/token.message_pb';
import * as transaction_message_pb from '../generated/transaction.message_pb';
import * as auth_message_pb from '../generated/auth_pb';

export const classFactory: MessageFactory = (id: string) => {
switch (id) {
Expand Down Expand Up @@ -70,6 +71,12 @@ export const classFactory: MessageFactory = (id: string) => {
return new transaction_message_pb.GetTransactionsRequest();
case 'transaction_message_pb.SubscribeTransactionsRequest':
return new transaction_message_pb.SubscribeTransactionsRequest();
// Sierra
case 'sierra_message_pb.CreateProjectRequest':
return new sierra_message_pb.CreateProjectRequest();
case 'sierra_message_pb.ListProjectsRequest':
return new sierra_message_pb.ListProjectsRequest();

default:
throw Error(`Unsupported type: ${id}`);
}
Expand Down
46 changes: 46 additions & 0 deletions packages/node/src/wrapped/SierraProjectClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {ConnectionListener, Publisher, publishToPromise, readOnce, sierra,} from '@emeraldpay/api';
import {ChannelCredentials} from '@grpc/grpc-js';
import {NativeChannel, callSingle, callStream} from '../channel';
import {ProjectClient} from '../generated/sierra_grpc_pb';
import {classFactory} from './Factory';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { version: clientVersion } = require('../../package.json');

export class SierraProjectClient {
readonly client: ProjectClient;
readonly channel: NativeChannel;
readonly credentials: ChannelCredentials;
readonly retries: number;

private readonly convert: sierra.ConvertSierra = new sierra.ConvertSierra(classFactory);

constructor(address: string, credentials: ChannelCredentials, agents: string[], retries = 3) {
const agent = [...agents, `emerald-client-node/${clientVersion}`].join(' ');

this.client = new ProjectClient(address, credentials, { 'grpc.primary_user_agent': agent });
this.channel = new NativeChannel(this.client);
this.credentials = credentials;
this.retries = retries;
}

public setConnectionListener(listener: ConnectionListener): void {
this.channel.setListener(listener);
}

public createProject(request: sierra.CreateProjectRequest): Promise<sierra.Project> {
const req = this.convert.createProjectRequest(request);
const mapper = this.convert.project();

const call = callSingle(this.client.createProject.bind(this.client), mapper);
// disable retries for create
return publishToPromise(readOnce(this.channel, call, req, 1));
}

public listProjects(request: sierra.ListProjectsRequest): Publisher<sierra.Project> {
const req = this.convert.listProjectsRequest(request);
const mapper = this.convert.project();

const call = callStream(this.client.listProjects.bind(this.client), mapper);
return readOnce(this.channel, call, req, this.retries);
}
}
12 changes: 11 additions & 1 deletion packages/web/src/EmeraldApi.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import {SecretToken} from "@emeraldpay/api";
import {WebChannel} from "./channel";
import {CredentialsContext, emeraldCredentials} from "./credentials";
import {AuthClient} from "./wrapped/AuthClient";
import {BlockchainClient} from "./wrapped/BlockchainClient";
import {InsightsClient} from "./wrapped/InsightsClient";
import {MarketClient} from "./wrapped/MarketClient";
import {SierraOrgClient} from "./wrapped/SierraOrgClient";
import {SierraProjectClient} from "./wrapped/SierraProjectClient";
import {SierraStatClient} from "./wrapped/SierraStatClient";
import {AuthClient} from "./wrapped/AuthClient";

export class EmeraldApi {
private readonly hostname: string;
Expand Down Expand Up @@ -43,6 +45,14 @@ export class EmeraldApi {
return new MarketClient(this.hostname, this.channel, this.credentials);
}

get sierraProject(): SierraProjectClient {
return new SierraProjectClient(this.hostname, this.channel, this.credentials);
}

get sierraOrg(): SierraOrgClient {
return new SierraOrgClient(this.hostname, this.channel, this.credentials);
}

get sierraStat(): SierraStatClient {
return new SierraStatClient(this.hostname, this.channel, this.credentials);
}
Expand Down
27 changes: 27 additions & 0 deletions packages/web/src/__integration-tests__/sierraOrg.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {EmeraldApi} from "../EmeraldApi";

jest.setTimeout(35000);

describe("SierraOrgClient", () => {
let api: EmeraldApi;

beforeAll(() => {
// a dev token with user id: bada55a1-0000-4000-a000-000000000000
api = EmeraldApi.devApi("emrld_fU88aIafXsCClerhyWtflBp1hH6h112ckzpSfP");
});

test('getOrg', async () => {
const client = api.sierraOrg;

const actual = await client.getOrg({
orgId: "cafe0000-0000-4000-a000-000000000000",
})
expect(actual).toEqual({
orgId: "cafe0000-0000-4000-a000-000000000000",
name: "Test Org",
description: "Organization for using in tests",
createdAt: new Date("2024-11-21T22:32:41.173Z"),
});
});

});
Loading

0 comments on commit 6b0d536

Please sign in to comment.