Skip to content

Commit 7d2dd8a

Browse files
authored
Release v1.44.0 (#1046)
* Fix Excel get table rows function * Improve editor interactions in published pipes * Retry Postman SMS on Cloudflare errors * Remove manually uploaded attachments when duplicating Pipe
2 parents b580d3e + 5c7dee1 commit 7d2dd8a

File tree

22 files changed

+576
-111
lines changed

22 files changed

+576
-111
lines changed

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/backend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,5 +106,5 @@
106106
"tsconfig-paths": "^4.2.0",
107107
"type-fest": "4.10.3"
108108
},
109-
"version": "1.43.1"
109+
"version": "1.44.0"
110110
}

packages/backend/src/apps/m365-excel/actions/get-table-row/implementation/get-top-n-table-rows.ts

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { type IGlobalVariable } from '@plumber/types'
22

33
import z from 'zod'
44

5+
import HttpError from '@/errors/http'
56
import StepError from '@/errors/step'
67

78
import WorkbookSession from '../../../common/workbook-session'
@@ -16,13 +17,116 @@ const msGraphResponseSchema = z
1617
headerSheetRowIndex: response.rowIndex,
1718
}))
1819

20+
// Can get more info e.g. formulas, numberFormat, text for possible date manipulation but too complex for now
21+
const msGraphRangeResponseSchema = z
22+
.object({
23+
rowCount: z.number(),
24+
rowIndex: z.number(),
25+
values: z.array(z.array(z.coerce.string())), // first row is the header
26+
})
27+
.transform((response) => {
28+
if (response.values.length === 0) {
29+
throw new Error('Excel table is missing the header row')
30+
}
31+
return {
32+
rowCount: response.rowCount,
33+
headerSheetRowIndex: response.rowIndex,
34+
headerRowValues: response.values[0],
35+
rowValues: response.values.slice(1),
36+
}
37+
})
38+
1939
interface GetTopNTableRowsResult {
2040
columns: string[]
2141
rows: string[][]
2242
headerSheetRowIndex: number
2343
}
2444

25-
export default async function getTopNTableRows(
45+
/**
46+
* Using range endpoint to get both the header row and data rows
47+
* instead of headerRowRange and rows to avoid extra API call
48+
* Range endpoint API time taken (similar to the previous resized range method)
49+
* - Around 0.5s for 50k rows with 5 columns of data
50+
* - Around 0.25s for 25k rows with 3 columns of empty data
51+
* - Around 0.15s for 10k rows with 3 columns of empty data
52+
*
53+
* Also, rows endpoint uses pagination so the query takes longer for large tables
54+
* Rows endpoint API time taken:
55+
* - Around 5s for 50k rows with 5 columns of data
56+
* - Around 2s for 25k rows with 3 columns of empty data
57+
* - Around 1s for 10k rows with 3 columns of empty data
58+
*
59+
* Considered using columns API but there is no headerSheetRowIndex returned so
60+
* an extra API call has to be made to retrieve it
61+
* Reference: https://learn.microsoft.com/en-us/graph/api/table-range?view=graph-rest-1.0&tabs=http
62+
*/
63+
export async function getTopNTableRows(
64+
$: IGlobalVariable,
65+
session: WorkbookSession,
66+
tableId: string,
67+
n: number,
68+
): Promise<GetTopNTableRowsResult> {
69+
try {
70+
const rangeParseResult = msGraphRangeResponseSchema.safeParse(
71+
(
72+
await session.request(
73+
`/tables/${tableId}/range?$select=values,rowCount,rowIndex`,
74+
'get',
75+
)
76+
).data,
77+
)
78+
79+
if (rangeParseResult.success === false) {
80+
throw new StepError(
81+
'Invalid table range',
82+
'Check your Excel file and try again',
83+
$.step.position,
84+
$.app.name,
85+
)
86+
}
87+
88+
const { rowCount, headerSheetRowIndex, headerRowValues, rowValues } =
89+
rangeParseResult.data
90+
91+
// rowCount includes the header row
92+
if (rowCount > n + 1) {
93+
throw new StepError(
94+
`Your Excel table has more than ${n.toLocaleString()} rows.`,
95+
`Reduce the number of rows and try again.`,
96+
$.step.position,
97+
$.app.name,
98+
)
99+
}
100+
101+
return {
102+
columns: headerRowValues,
103+
rows: rowValues,
104+
headerSheetRowIndex,
105+
}
106+
} catch (error) {
107+
if (error instanceof HttpError) {
108+
if (
109+
error.response.status === 400 &&
110+
error.response.data.error.code === 'RangeExceedsLimit'
111+
) {
112+
throw new StepError(
113+
'Your Excel table is too large',
114+
'Reduce the number of rows or columns and try again.',
115+
$.step.position,
116+
$.app.name,
117+
error,
118+
)
119+
}
120+
}
121+
throw error
122+
}
123+
}
124+
125+
/**
126+
* @deprecated Use getTopNTableRows instead
127+
*/
128+
// Old method in case we need to rollback fast
129+
export async function getTopNTableRowsOld(
26130
// Typically, we should avoid making $ viral though the codebase, but this is
27131
// an exception because getTopNTableRows is not a common helper function.
28132
$: IGlobalVariable,
@@ -86,3 +190,5 @@ export default async function getTopNTableRows(
86190
headerSheetRowIndex: tableRows.headerSheetRowIndex,
87191
}
88192
}
193+
194+
export default getTopNTableRows

packages/backend/src/apps/postman-sms/__tests__/request-error-handler.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ describe('Postman SMS request error handler', () => {
7373
})
7474

7575
describe('Other errors', () => {
76-
it.each([500, 502, 503])('should retry on %s', async (status) => {
76+
it.each([500, 502, 503, 520, 524])('should retry on %s', async (status) => {
7777
const axiosError = {
7878
isAxiosError: true,
7979
name: 'AxiosError',

packages/backend/src/apps/postman-sms/common/request-error-handler.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const handle429: ThrowingHandler = (_, error): never => {
2121
})
2222
}
2323

24-
const handle500and502and503: ThrowingHandler = (_, error): never => {
24+
const handle5xxErrors: ThrowingHandler = (_, error): never => {
2525
const status = error.response.status
2626
throw new RetriableError({
2727
error: `Retrying HTTP ${status} from Postman SMS`,
@@ -40,7 +40,9 @@ const requestErrorHandler: IApp['requestErrorHandler'] = async function (
4040
case 500:
4141
case 502:
4242
case 503:
43-
return handle500and502and503($, error)
43+
case 520: // Cloudflare-specific error
44+
case 524: // Cloudflare-specific error
45+
return handle5xxErrors($, error)
4446
default:
4547
if (error.message === 'read ETIMEDOUT') {
4648
throw new RetriableError({

0 commit comments

Comments
 (0)