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

Feat/streams api #658

Merged
merged 12 commits into from
Sep 8, 2022
1 change: 1 addition & 0 deletions .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"moralis",
"@moralisweb3/core",
"@moralisweb3/auth",
"@moralisweb3/streams",
"@moralisweb3/api-utils",
"@moralisweb3/evm-utils",
"@moralisweb3/sol-utils",
Expand Down
8 changes: 8 additions & 0 deletions .changeset/quiet-peas-shout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@moralisweb3/api-utils': minor
'@moralisweb3/core': minor
'moralis': minor
'@moralisweb3/streams': minor
---

Intergrating stream API in code base, creating a new package @moralisweb3/streams
8 changes: 4 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
id: cache
with:
path: '**/node_modules'
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}_C4
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}_C5
- name: Install packages
if: steps.cache.outputs.cache-hit != 'true'
run: yarn install
Expand All @@ -55,7 +55,7 @@ jobs:
id: cache
with:
path: '**/node_modules'
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}_C4
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}_C5
- name: ESLint
run: yarn lint
continue-on-error: true
Expand Down Expand Up @@ -87,7 +87,7 @@ jobs:
id: cache
with:
path: '**/node_modules'
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}_C4
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}_C5
- name: Prettier
run: yarn format:check

Expand All @@ -111,7 +111,7 @@ jobs:
id: cache
with:
path: '**/node_modules'
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}_C4
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}_C5
- name: Build
run: yarn build:clean
- name: Test
Expand Down
2 changes: 1 addition & 1 deletion packages/apiUtils/src/resolvers/Endpoint.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { MoralisCore } from '@moralisweb3/core';

export type EndpointMethod = 'get' | 'post' | 'put';
export type EndpointMethod = 'get' | 'post' | 'put' | 'delete';

export enum EndpointBodyType {
PROPERTY = 'property',
Expand Down
15 changes: 15 additions & 0 deletions packages/apiUtils/src/resolvers/EndpointResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,19 @@ export class EndpointResolver<ApiParams, Params, ApiResult, AdaptedResult, JSONR
return new ApiResultAdapter(result, this.endpoint.apiToResult, this.endpoint.resultToJson, params);
};

private delete = async (params: Params) => {
const url = this.createUrl(params);
const apiParams = this.endpoint.parseParams(params);

const searchParams = this.paramsReader.getSearchParams(apiParams);

const result = await this.requestController.delete<ApiResult>(url, searchParams, {
headers: this.createHeaders(),
});

return new ApiResultAdapter(result, this.endpoint.apiToResult, this.endpoint.resultToJson, params);
};

private createUrl(params: Params): string {
return this.baseUrl + this.endpoint.getUrl(params);
}
Expand Down Expand Up @@ -97,6 +110,8 @@ export class EndpointResolver<ApiParams, Params, ApiResult, AdaptedResult, JSONR
return this.post(params);
case 'put':
return this.put(params);
case 'delete':
return this.delete(params);
default:
return this.get(params);
}
Expand Down
15 changes: 15 additions & 0 deletions packages/core/src/controllers/RequestController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,19 @@ export class RequestController {
signal: abortSignal,
});
}

public async delete<Response>(
url: string,
searchParams?: Record<string, unknown>,
options?: RequestOptions,
abortSignal?: AbortController['signal'],
): Promise<Response> {
return this.request<unknown, Response>({
url,
params: searchParams,
method: 'DELETE',
headers: options?.headers,
signal: abortSignal,
});
}
}
1 change: 1 addition & 0 deletions packages/integration/mockRequests/config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export const MOCK_API_KEY = 'test-api-key';
export const EVM_API_ROOT = 'https://deep-index.moralis.io/api/v2';
export const SOL_API_ROOT = 'https://solana-gateway.moralis.io';
export const STREAM_API_ROOT = 'https://streams-api.aws-prod-streams-master-1.moralis.io';
19 changes: 19 additions & 0 deletions packages/integration/mockRequests/mockRequestsStream.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { setupServer } from 'msw/node';

import { mockCreateStream } from './streamApi/createStream';
import { mockDeleteStream } from './streamApi/deleteStream';
import { mockGetStreams } from './streamApi/getStreams';
import { mockUpdateStream } from './streamApi/updateStream';
import { mockSetSettings } from './streamApi/setSettings';
import { mockGetSettings } from './streamApi/getSettings';

const handlers = [
mockCreateStream,
mockGetStreams,
mockUpdateStream,
mockDeleteStream,
mockSetSettings,
mockGetSettings,
];

export const mockServer = setupServer(...handlers);
26 changes: 26 additions & 0 deletions packages/integration/mockRequests/streamApi/createStream.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { rest } from 'msw';
import { STREAM_API_ROOT, MOCK_API_KEY } from '../config';

export const mockCreateStreamOutput: Record<string, unknown> = {
webhookUrl: 'https://webhook.site/c76c6361-960d-4600-8498-9fecba8abb5f',
description: 'string',
tag: 'string',
tokenAddress: '0xdac17f958d2ee523a2206206994597c13d831ec7',
topic0: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
filter: {},
abi: {},
address: '0x992eCcC191D6F74E8Be187ed6B6AC196b08314f7',
chainIds: ['0x3'],
type: 'wallet',
id: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
};

export const mockCreateStream = rest.put(`${STREAM_API_ROOT}/streams`, (req, res, ctx) => {
const apiKey = req.headers.get('x-api-key');

if (apiKey !== MOCK_API_KEY) {
return res(ctx.status(401));
}

return res(ctx.status(200), ctx.json(mockCreateStreamOutput));
});
33 changes: 33 additions & 0 deletions packages/integration/mockRequests/streamApi/deleteStream.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { rest } from 'msw';
import { STREAM_API_ROOT, MOCK_API_KEY } from '../config';

export const mockDeleteStreamOutput: Record<string, unknown> = {
webhookUrl: 'https://webhook.site/c76c6361-960d-4600-8498-9fecba8abb5f',
description: 'string',
tag: 'string',
tokenAddress: '0xdac17f958d2ee523a2206206994597c13d831ec7',
topic0: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
filter: {},
abi: {},
address: '0x992eCcC191D6F74E8Be187ed6B6AC196b08314f7',
chainIds: ['0x3'],
type: 'wallet',
id: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
};

export const mockDeleteStream = rest.delete(`${STREAM_API_ROOT}/streams/:id`, (req, res, ctx) => {
const id = req.params.id as string;
const apiKey = req.headers.get('x-api-key');

if (apiKey !== MOCK_API_KEY) {
return res(ctx.status(401));
}

const value = mockDeleteStreamOutput;

if (value.id !== id) {
return res(ctx.status(404));
}

return res(ctx.status(200), ctx.json(value));
});
17 changes: 17 additions & 0 deletions packages/integration/mockRequests/streamApi/getSettings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { rest } from 'msw';
import { STREAM_API_ROOT, MOCK_API_KEY } from '../config';

export const mockGetSettingsOutput: Record<string, string> = {
secretKey: 'top_secret',
region: 'us-east-1',
};

export const mockGetSettings = rest.get(`${STREAM_API_ROOT}/settings`, (req, res, ctx) => {
const apiKey = req.headers.get('x-api-key');

if (apiKey !== MOCK_API_KEY) {
return res(ctx.status(401));
}

return res(ctx.status(200), ctx.json(mockGetSettingsOutput));
});
32 changes: 32 additions & 0 deletions packages/integration/mockRequests/streamApi/getStreams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { rest } from 'msw';
import { STREAM_API_ROOT, MOCK_API_KEY } from '../config';

export const mockGetStreamsOutput = {
result: [
{
webhookUrl: 'https://webhook.site/c76c6361-960d-4600-8498-9fecba8abb5f',
description: 'string',
tag: 'string',
tokenAddress: '0xdac17f958d2ee523a2206206994597c13d831ec7',
topic0: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
filter: {},
abi: {},
address: '0x992eCcC191D6F74E8Be187ed6B6AC196b08314f7',
chainIds: ['0x3'],
type: 'wallet',
id: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
},
],
cursor: 'string',
total: 20,
};

export const mockGetStreams = rest.get(`${STREAM_API_ROOT}/streams`, (req, res, ctx) => {
const apiKey = req.headers.get('x-api-key');

if (apiKey !== MOCK_API_KEY) {
return res(ctx.status(401));
}

return res(ctx.status(200), ctx.json(mockGetStreamsOutput));
});
14 changes: 14 additions & 0 deletions packages/integration/mockRequests/streamApi/setSettings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { rest } from 'msw';
import { STREAM_API_ROOT, MOCK_API_KEY } from '../config';

export const mockSetSettingsOutput: Record<string, string> = {};

export const mockSetSettings = rest.post(`${STREAM_API_ROOT}/settings`, (req, res, ctx) => {
const apiKey = req.headers.get('x-api-key');

if (apiKey !== MOCK_API_KEY) {
return res(ctx.status(401));
}

return res(ctx.status(200), ctx.json(mockSetSettingsOutput));
});
33 changes: 33 additions & 0 deletions packages/integration/mockRequests/streamApi/updateStream.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { rest } from 'msw';
import { STREAM_API_ROOT, MOCK_API_KEY } from '../config';

export const mockUpdateStreamOutput: Record<string, unknown> = {
webhookUrl: 'https://webhook.site/c76c6361-960d-4600-8498-9fecba8abb5f',
description: 'string',
tag: 'string',
tokenAddress: '0xdac17f958d2ee523a2206206994597c13d831ec7',
topic0: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
filter: {},
abi: {},
address: '0x992eCcC191D6F74E8Be187ed6B6AC196b08314f7',
chainIds: ['0x3'],
type: 'wallet',
id: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
};

export const mockUpdateStream = rest.post(`${STREAM_API_ROOT}/streams/:id`, (req, res, ctx) => {
const id = req.params.id as string;
const apiKey = req.headers.get('x-api-key');

if (apiKey !== MOCK_API_KEY) {
return res(ctx.status(401));
}

const value = mockUpdateStreamOutput;

if (value.id !== id) {
return res(ctx.status(404));
}

return res(ctx.status(200), ctx.json(value));
});
1 change: 1 addition & 0 deletions packages/integration/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"dependencies": {
"@moralisweb3/core": "^2.3.1",
"@moralisweb3/evm-api": "^2.3.1",
"@moralisweb3/streams": "^2.3.1",
"@moralisweb3/evm-utils": "^2.3.1",
"eventemitter3": "^4.0.7"
}
Expand Down
59 changes: 59 additions & 0 deletions packages/integration/test/streamApi/createStream.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { MoralisStreams } from '@moralisweb3/streams';
import { cleanStreamsApi, setupStreamApi } from './setup';

describe('Create stream', () => {
let StreamApi: MoralisStreams;

beforeAll(() => {
StreamApi = setupStreamApi();
});

afterAll(() => {
cleanStreamsApi();
});

it('should create a stream ', async () => {
const result = await StreamApi.add({
chains: ['0x3'],
address: '0x992eCcC191D6F74E8Be187ed6B6AC196b08314f7',
tag: 'test',
description: 'test',
type: 'wallet',
webhookUrl: 'https://webhook.site/4f1b1b1b-1b1b-4f1b-1b1b-1b1b1b1b1b1b',
});

expect(result).toBeDefined();
expect(result).toEqual(expect.objectContaining({}));
expect(result.result.chainIds).toContain('0x3');
expect(result.result.type).toEqual('wallet');
});

it('should not create stream', async () => {
const failedResult = await StreamApi.add({
chains: ['0x3'],
address: '0x992eCcC191D6F74E8Be187ed6B6AC196b08314f7',
tag: 'test',
description: 'test',
type: 'wallet',
webhookUrl: 'https://webhook.site/4f1b1b1b-1b1b-4f1b-1b1b-1b1b1b1b1b1b',
})
.then()
.catch((err: any) => {
return err;
});

expect(failedResult).toBeDefined();
expect(
StreamApi.add({
chains: ['invalid_chain'],
address: '0x992eCcC191D6F74E8Be187ed6B6AC196b08314f7',
tag: 'test',
description: 'test',
type: 'wallet',
webhookUrl: 'https://webhook.site/4f1b1b1b-1b1b-4f1b-1b1b-1b1b1b1b1b1b',
}),
).rejects.toThrowErrorMatchingInlineSnapshot(
`"[C0005] Invalid provided chain, value must be a positive number, or a hex-string starting with '0x'"`,
);
});
});
25 changes: 25 additions & 0 deletions packages/integration/test/streamApi/deleteStream.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { MoralisStreams } from '@moralisweb3/streams';
import { cleanStreamsApi, setupStreamApi } from './setup';

describe('Delete stream', () => {
let StreamApi: MoralisStreams;

beforeAll(() => {
StreamApi = setupStreamApi();
});

afterAll(() => {
cleanStreamsApi();
});

it('should delete a stream ', async () => {
const result = await StreamApi.delete({
id: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
});

expect(result).toBeDefined();
expect(result).toEqual(expect.objectContaining({}));
expect(result.result.chainIds).toContain('0x3');
expect(result.result.type).toEqual('wallet');
});
});
Loading