Skip to content

Commit

Permalink
Update notification channels not working in SDK + refactor (#291)
Browse files Browse the repository at this point in the history
* move sdk monitors update logic to backend

* update monitors update example

* remove unused function

* format
  • Loading branch information
MCarlomagno authored Mar 22, 2024
1 parent 338c440 commit 5343aec
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 191 deletions.
5 changes: 4 additions & 1 deletion examples/update-monitor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ async function main() {

const blockRequestParameters = {
type: 'BLOCK', // BLOCK or FORTA
network: 'goerli',
network: 'sepolia',
// optional
confirmLevel: 1, // if not set, we pick the blockwatcher for the chosen network with the lowest offset
name: 'My Monitor',
Expand Down Expand Up @@ -70,7 +70,10 @@ async function main() {
console.log('created monitor id', created.monitorId);

const updated = await client.monitor.update(created.monitorId, {
// updates monitor name
name: 'Monitor name updated!',
// removes notification channels
notificationChannels: [],
});

console.log('new name:', updated.name);
Expand Down
179 changes: 26 additions & 153 deletions packages/monitor/src/api/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ describe('MonitorClient', () => {
await monitor.list();
await monitor.list();

expect(createAuthenticatedApi).toBeCalledTimes(1);
expect(createAuthenticatedApi).toHaveBeenCalledTimes(1);
});

it('throws an init exception at the correct context', async () => {
Expand All @@ -170,16 +170,16 @@ describe('MonitorClient', () => {
});

await monitor.list();
expect(monitor.api.get).toBeCalledWith('/monitors');
expect(createAuthenticatedApi).toBeCalledTimes(2); // First time and renewal
expect(monitor.api.get).toHaveBeenCalledWith('/monitors');
expect(createAuthenticatedApi).toHaveBeenCalledTimes(2); // First time and renewal
});
});

describe('list', () => {
it('calls API correctly', async () => {
await monitor.list();
expect(monitor.api.get).toBeCalledWith('/monitors');
expect(createAuthenticatedApi).toBeCalled();
expect(monitor.api.get).toHaveBeenCalledWith('/monitors');
expect(createAuthenticatedApi).toHaveBeenCalled();
});
});

Expand Down Expand Up @@ -222,8 +222,8 @@ describe('MonitorClient', () => {
};

await monitor.create(createBlockPayload);
expect(monitor.api.post).toBeCalledWith('/monitors', expectedApiRequest);
expect(createAuthenticatedApi).toBeCalled();
expect(monitor.api.post).toHaveBeenCalledWith('/monitors', expectedApiRequest);
expect(createAuthenticatedApi).toHaveBeenCalled();
});

it('passes correct FORTA type arguments to the API', async () => {
Expand All @@ -249,8 +249,8 @@ describe('MonitorClient', () => {
};

await monitor.create(createFortaPayload);
expect(monitor.api.post).toBeCalledWith('/monitors', expectedApiRequest);
expect(createAuthenticatedApi).toBeCalled();
expect(monitor.api.post).toHaveBeenCalledWith('/monitors', expectedApiRequest);
expect(createAuthenticatedApi).toHaveBeenCalled();
});
it('passes correct Private FORTA type arguments to the API', async () => {
const { name, paused, type, addresses, fortaConditions, network } = createFortaPayload;
Expand All @@ -276,185 +276,58 @@ describe('MonitorClient', () => {
};

await monitor.create({ ...createFortaPayload, privateFortaNodeId: '0x123' });
expect(monitor.api.post).toBeCalledWith('/monitors', expectedApiRequest);
expect(createAuthenticatedApi).toBeCalled();
expect(monitor.api.post).toHaveBeenCalledWith('/monitors', expectedApiRequest);
expect(createAuthenticatedApi).toHaveBeenCalled();
});
});

describe('get', () => {
it('passes correct arguments to the API', async () => {
await monitor.get('i-am-the-watcher');
expect(monitor.api.get).toBeCalledWith('/monitors/i-am-the-watcher');
expect(createAuthenticatedApi).toBeCalled();
expect(createAuthenticatedApi).toHaveBeenCalled();
});
});

describe('update', () => {
it('passes correct BLOCK type arguments to the API', async () => {
jest.spyOn(monitor, 'get').mockImplementation(async () => oldBlockMonitor);

const { name, network, paused, type, addresses, abi, txCondition, eventConditions, functionConditions } =
createBlockPayload;

const expectedApiRequest = {
paused,
type,
name,
network,
addressRules: [
{
abi,
addresses: addresses,
actionCondition: undefined,
conditions: [
{
eventConditions,
txConditions: [{ expression: txCondition, status: 'any' }],
functionConditions: [],
},
{
eventConditions: [],
txConditions: [{ expression: txCondition, status: 'any' }],
functionConditions,
},
],
},
],
alertThreshold: undefined,
blockWatcherId: 'i-am-the-watcher',
notifyConfig: {
actionId: undefined,
notifications: [],
timeoutMs: 0,
},
};

const monitorId = 'i-am-the-BLOCK-watcher';
await monitor.update(monitorId, { monitorId, ...createBlockPayload });
expect(monitor.api.put).toBeCalledWith(`/monitors/${monitorId}`, expectedApiRequest);
expect(createAuthenticatedApi).toBeCalled();
expect(monitor.api.put).toHaveBeenCalledWith(`/monitors/${monitorId}`, { monitorId, ...createBlockPayload });
expect(createAuthenticatedApi).toHaveBeenCalled();
});

it('passes correct FORTA type arguments to the API', async () => {
const oldMonitor: CreateMonitorResponse = {
type: 'FORTA',
monitorId: 'old-subscriber-id',
name: 'Previous monitor',
paused: false,
network: 'goerli',
fortaRule: {
addresses: ['0xdead'],
conditions: {
minimumScannerCount: 100,
},
},
};
jest.spyOn(monitor, 'get').mockImplementation(async () => oldMonitor);

const { name, paused, type, addresses, fortaConditions, network } = createFortaPayload;

const expectedApiRequest = {
paused,
type,
name,
network,
alertThreshold: undefined,
notifyConfig: {
actionId: undefined,
notifications: [],
timeoutMs: 0,
},
fortaRule: {
addresses: addresses,
agentIDs: undefined,
actionCondition: undefined,
conditions: fortaConditions,
},
};

const monitorId = 'i-am-the-FORTA-watcher';
await monitor.update(monitorId, { monitorId, ...createFortaPayload });
expect(monitor.api.put).toBeCalledWith(`/monitors/${monitorId}`, expectedApiRequest);
expect(createAuthenticatedApi).toBeCalled();
});

it('does not override with nulls or undefined when only passing one argument', async () => {
jest.spyOn(monitor, 'get').mockImplementation(async () => oldBlockMonitor);

const name = 'some random new name';

if (!oldBlockMonitor?.addressRules[0]) throw new Error('oldBlockMonitor.addressRules is empty');

const expectedApiRequest = {
type: oldBlockMonitor.type,
name,
addressRules: [
{
abi: oldBlockMonitor.addressRules[0].abi,
addresses: oldBlockMonitor.addressRules[0].addresses,
actionCondition: undefined,
conditions: [],
},
],
blockWatcherId: oldBlockMonitor.blockWatcherId,
network: oldBlockMonitor.network,
notifyConfig: {
actionId: undefined,
notifications: [],
timeoutMs: 0,
},
alertThreshold: undefined,
paused: oldBlockMonitor.paused,
};

const monitorId = 'i-am-the-BLOCK-watcher';
await monitor.update(monitorId, {
monitorId,
type: 'BLOCK',
name,
});
expect(monitor.api.put).toBeCalledWith(`/monitors/${monitorId}`, expectedApiRequest);
expect(createAuthenticatedApi).toBeCalled();
expect(monitor.api.put).toHaveBeenCalledWith(`/monitors/${monitorId}`, { monitorId, ...createFortaPayload });
expect(createAuthenticatedApi).toHaveBeenCalled();
});
});

describe('pause', () => {
it('passes correct arguments to the API', async () => {
jest.spyOn(monitor, 'get').mockImplementation(async () => oldBlockMonitor);

const monitorId = 'i-am-the-BLOCK-watcher';
await monitor.pause(monitorId);
expect(monitor.api.put).toBeCalledWith(
`/monitors/${monitorId}`,
expect.objectContaining({
paused: true,
}),
);
expect(createAuthenticatedApi).toBeCalled();
expect(monitor.api.put).toHaveBeenCalledWith(`/monitors/${monitorId}`, { paused: true });
expect(createAuthenticatedApi).toHaveBeenCalled();
});
});

describe('unpause', () => {
it('passes correct arguments to the API', async () => {
jest.spyOn(monitor, 'get').mockImplementation(async () => oldBlockMonitor);

const monitorId = 'i-am-the-BLOCK-watcher';
await monitor.unpause(monitorId);
expect(monitor.api.put).toBeCalledWith(
`/monitors/${monitorId}`,
expect.objectContaining({
paused: false,
}),
);
expect(createAuthenticatedApi).toBeCalled();
expect(monitor.api.put).toHaveBeenCalledWith(`/monitors/${monitorId}`, { paused: false });
expect(createAuthenticatedApi).toHaveBeenCalled();
});
});

describe('delete', () => {
it('passes correct arguments to the API', async () => {
await monitor.delete('i-am-the-watcher');
expect(monitor.api.delete).toBeCalledWith('/monitors/i-am-the-watcher');
expect(createAuthenticatedApi).toBeCalled();
expect(monitor.api.delete).toHaveBeenCalledWith('/monitors/i-am-the-watcher');
expect(createAuthenticatedApi).toHaveBeenCalled();
});
});

Expand Down Expand Up @@ -537,17 +410,17 @@ describe('MonitorClient', () => {
it('calls API correctly', async () => {
listBlockwatchersSpy.mockRestore();
await monitor.listBlockwatchers();
expect(monitor.api.get).toBeCalledWith('/blockwatchers');
expect(createAuthenticatedApi).toBeCalled();
expect(monitor.api.get).toHaveBeenCalledWith('/blockwatchers');
expect(createAuthenticatedApi).toHaveBeenCalled();
});
});

describe('listTenantBlockwatchers', () => {
it('calls API correctly', async () => {
listBlockwatchersSpy.mockRestore();
await monitor.listTenantBlockwatchers();
expect(monitor.api.get).toBeCalledWith('/blockwatchers/tenant');
expect(createAuthenticatedApi).toBeCalled();
expect(monitor.api.get).toHaveBeenCalledWith('/blockwatchers/tenant');
expect(createAuthenticatedApi).toHaveBeenCalled();
});
});

Expand Down
40 changes: 3 additions & 37 deletions packages/monitor/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,8 @@ export class MonitorClient extends BaseApiClient {
}

public async update(id: string, params: UpdateMonitorRequest): Promise<CreateMonitorResponse> {
const currentMonitor = await this.get(id);

return this.apiCall(async (api) => {
return await api.put(`/monitors/${id}`, await this.mergeApiMonitorWithUpdateMonitor(currentMonitor, params));
return await api.put(`/monitors/${id}`, params);
});
}

Expand All @@ -74,30 +72,14 @@ export class MonitorClient extends BaseApiClient {
}

public async pause(id: string): Promise<ExternalCreateMonitorRequest> {
const monitor = await this.get(id);
return this.apiCall(async (api) => {
return await api.put(
`/monitors/${id}`,
await this.mergeApiMonitorWithUpdateMonitor(monitor, {
monitorId: id,
type: monitor.type,
paused: true,
}),
);
return await api.put(`/monitors/${id}`, { paused: true });
});
}

public async unpause(id: string): Promise<ExternalCreateMonitorRequest> {
const monitor = await this.get(id);
return this.apiCall(async (api) => {
return await api.put(
`/monitors/${id}`,
await this.mergeApiMonitorWithUpdateMonitor(monitor, {
monitorId: id,
type: monitor.type,
paused: false,
}),
);
return await api.put(`/monitors/${id}`, { paused: false });
});
}

Expand Down Expand Up @@ -380,20 +362,4 @@ export class MonitorClient extends BaseApiClient {

throw new Error(`Invalid monitor type. Type must be FORTA or BLOCK`);
}

private mergeApiMonitorWithUpdateMonitor(
apiMonitor: CreateMonitorResponse,
monitor: UpdateMonitorRequest,
): Promise<CreateMonitorRequest> {
const newMonitor: ExternalCreateMonitorRequest = this.toCreateMonitorRequest(apiMonitor);

const updatedProperties = Object.keys(monitor) as Array<keyof typeof monitor>;
for (const prop of updatedProperties) {
if (prop !== 'monitorId') {
(newMonitor[prop] as any) = monitor[prop];
}
}

return this.constructMonitorRequest(newMonitor);
}
}

0 comments on commit 5343aec

Please sign in to comment.