Skip to content

Commit e7758fd

Browse files
authored
Merge pull request #2260 from hirosystems/develop
release to master
2 parents c2b4c49 + e69f9e4 commit e7758fd

File tree

6 files changed

+128
-22
lines changed

6 files changed

+128
-22
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/* eslint-disable camelcase */
2+
3+
exports.shorthands = undefined;
4+
5+
exports.up = pgm => {
6+
pgm.dropIndex('contract_logs', 'contract_identifier');
7+
pgm.dropIndex('contract_logs', [
8+
{ name: 'block_height', sort: 'DESC' },
9+
{ name: 'microblock_sequence', sort: 'DESC' },
10+
{ name: 'tx_index', sort: 'DESC' },
11+
{ name: 'event_index', sort: 'DESC' },
12+
]);
13+
14+
pgm.createIndex(
15+
'contract_logs',
16+
[
17+
'contract_identifier',
18+
{ name: 'block_height', sort: 'DESC' },
19+
{ name: 'microblock_sequence', sort: 'DESC' },
20+
{ name: 'tx_index', sort: 'DESC' },
21+
{ name: 'event_index', sort: 'DESC' },
22+
],
23+
{ where: 'canonical = TRUE AND microblock_canonical = TRUE' }
24+
);
25+
};
26+
27+
exports.down = pgm => {
28+
pgm.dropIndex(
29+
'contract_logs',
30+
[
31+
'contract_identifier',
32+
{ name: 'block_height', sort: 'DESC' },
33+
{ name: 'microblock_sequence', sort: 'DESC' },
34+
{ name: 'tx_index', sort: 'DESC' },
35+
{ name: 'event_index', sort: 'DESC' },
36+
],
37+
{ where: 'canonical = TRUE AND microblock_canonical = TRUE' }
38+
);
39+
40+
pgm.createIndex('contract_logs', 'contract_identifier');
41+
pgm.createIndex('contract_logs', [
42+
{ name: 'block_height', sort: 'DESC' },
43+
{ name: 'microblock_sequence', sort: 'DESC' },
44+
{ name: 'tx_index', sort: 'DESC' },
45+
{ name: 'event_index', sort: 'DESC' },
46+
]);
47+
};

src/api/deprecation-plugin.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import fp from 'fastify-plugin';
2+
import { FastifyPluginAsync } from 'fastify';
3+
4+
declare module 'fastify' {
5+
interface FastifySchema {
6+
deprecatedMessage?: string;
7+
}
8+
}
9+
10+
const pluginCb: FastifyPluginAsync<{ defaultDeprecatedMessage: string }> = async (
11+
fastify,
12+
options
13+
) => {
14+
fastify.addHook('onSend', async (request, reply) => {
15+
if (request.routeOptions.schema?.deprecated) {
16+
const warningMessage =
17+
request.routeOptions.schema.deprecatedMessage ??
18+
options.defaultDeprecatedMessage ??
19+
'Endpoint is deprecated';
20+
const warning = `299 - "Deprecated: ${warningMessage}"`;
21+
if (!reply.getHeader('Warning')) {
22+
void reply.header('Warning', warning);
23+
}
24+
}
25+
});
26+
await Promise.resolve();
27+
};
28+
29+
/**
30+
* Fastify plugin that adds deprecation warnings to HTTP responses.
31+
*
32+
* If a route's schema has `deprecated: true`, a `Warning` header will be added to the response.
33+
* - If the schema includes a `deprecatedMessage`, it will be used in the warning.
34+
* - If not, the plugin uses the `defaultDeprecatedMessage` provided in the plugin options.
35+
* - If neither is available, a generic warning message `299 - "Deprecated"` is used.
36+
*/
37+
const DeprecationPlugin = fp(pluginCb);
38+
39+
export default DeprecationPlugin;

src/api/init.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import FastifyMetrics from 'fastify-metrics';
5757
import FastifyCors from '@fastify/cors';
5858
import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox';
5959
import * as promClient from 'prom-client';
60+
import DeprecationPlugin from './deprecation-plugin';
6061

6162
export interface ApiServer {
6263
fastifyApp: FastifyInstance;
@@ -271,6 +272,11 @@ export async function startApiServer(opts: {
271272
// Setup direct proxy to core-node RPC endpoints (/v2)
272273
await fastify.register(CoreNodeRpcProxyRouter, { prefix: '/v2' });
273274

275+
// Middleware to annotate http responses with deprecation warnings
276+
await fastify.register(DeprecationPlugin, {
277+
defaultDeprecatedMessage: 'See https://docs.hiro.so/stacks/api for more information',
278+
});
279+
274280
// Wait for all routes and middleware to be ready before starting the server
275281
await fastify.ready();
276282

src/datastore/pg-store-v2.ts

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -335,38 +335,37 @@ export class PgStoreV2 extends BasePgStoreModule {
335335
return await this.sqlTransaction(async sql => {
336336
const limit = args.limit ?? TransactionLimitParamSchema.default;
337337
const offset = args.offset ?? 0;
338-
const filter =
339-
args.block.type === 'latest'
340-
? sql`index_block_hash = (SELECT index_block_hash FROM blocks WHERE canonical = TRUE ORDER BY block_height DESC LIMIT 1)`
341-
: args.block.type === 'hash'
342-
? sql`(
343-
block_hash = ${normalizeHashString(args.block.hash)}
344-
OR index_block_hash = ${normalizeHashString(args.block.hash)}
345-
)`
346-
: sql`block_height = ${args.block.height}`;
347-
const blockCheck = await sql`SELECT index_block_hash FROM blocks WHERE ${filter} LIMIT 1`;
348-
if (blockCheck.count === 0)
349-
throw new InvalidRequestError(`Block not found`, InvalidRequestErrorType.invalid_param);
350338
const txsQuery = await sql<(TxQueryResult & { total: number })[]>`
351-
WITH tx_count AS (
352-
SELECT tx_count AS total FROM blocks WHERE canonical = TRUE AND ${filter}
339+
WITH block_ptr AS (
340+
SELECT index_block_hash FROM blocks
341+
WHERE ${
342+
args.block.type === 'latest'
343+
? sql`canonical = TRUE ORDER BY block_height DESC`
344+
: args.block.type === 'hash'
345+
? sql`(
346+
block_hash = ${normalizeHashString(args.block.hash)}
347+
OR index_block_hash = ${normalizeHashString(args.block.hash)}
348+
) AND canonical = TRUE`
349+
: sql`block_height = ${args.block.height} AND canonical = TRUE`
350+
}
351+
LIMIT 1
352+
),
353+
tx_count AS (
354+
SELECT tx_count AS total
355+
FROM blocks
356+
WHERE index_block_hash = (SELECT index_block_hash FROM block_ptr)
353357
)
354358
SELECT ${sql(TX_COLUMNS)}, (SELECT total FROM tx_count)::int AS total
355359
FROM txs
356360
WHERE canonical = true
357361
AND microblock_canonical = true
358-
AND ${filter}
362+
AND index_block_hash = (SELECT index_block_hash FROM block_ptr)
359363
ORDER BY microblock_sequence ASC, tx_index ASC
360364
LIMIT ${limit}
361365
OFFSET ${offset}
362366
`;
363367
if (txsQuery.count === 0)
364-
return {
365-
limit,
366-
offset,
367-
results: [],
368-
total: 0,
369-
};
368+
throw new InvalidRequestError(`Block not found`, InvalidRequestErrorType.invalid_param);
370369
return {
371370
limit,
372371
offset,

tests/api/address.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1547,6 +1547,9 @@ describe('address tests', () => {
15471547
);
15481548
expect(fetchAddrBalance1.status).toBe(200);
15491549
expect(fetchAddrBalance1.type).toBe('application/json');
1550+
expect(fetchAddrBalance1.headers['warning']).toBe(
1551+
'299 - "Deprecated: See https://docs.hiro.so/stacks/api for more information"'
1552+
);
15501553
const expectedResp1 = {
15511554
stx: {
15521555
balance: '88679',

tests/api/tx.test.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4198,7 +4198,7 @@ describe('tx tests', () => {
41984198
);
41994199
expect(result.status).toBe(200);
42004200
expect(result.type).toBe('application/json');
4201-
const json = JSON.parse(result.text);
4201+
let json = JSON.parse(result.text);
42024202
expect(json.total).toBe(2);
42034203
expect(json.results[0]).toStrictEqual({
42044204
anchor_mode: 'any',
@@ -4244,6 +4244,18 @@ describe('tx tests', () => {
42444244
tx_type: 'coinbase',
42454245
});
42464246

4247+
result = await supertest(api.server).get(`/extended/v2/blocks/latest/transactions`);
4248+
expect(result.status).toBe(200);
4249+
expect(result.type).toBe('application/json');
4250+
json = JSON.parse(result.text);
4251+
expect(json.total).toBe(2);
4252+
4253+
result = await supertest(api.server).get(`/extended/v2/blocks/1/transactions`);
4254+
expect(result.status).toBe(200);
4255+
expect(result.type).toBe('application/json');
4256+
json = JSON.parse(result.text);
4257+
expect(json.total).toBe(2);
4258+
42474259
// Try a non-existent block
42484260
result = await supertest(api.server).get(
42494261
`/extended/v2/blocks/0x00000000000000000001e2ee7f0c6bd5361b5e7afd76156ca7d6f524ee999999/transactions?limit=20&offset=0`

0 commit comments

Comments
 (0)