Browse Source

Merge pull request #9517 from nocodb/nc-fix/at-stall

fix: at import stall issue
pull/9534/head
Pranav C 2 months ago committed by GitHub
parent
commit
25352e96cc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      packages/nocodb/src/models/Column.ts
  2. 1
      packages/nocodb/src/modules/jobs/jobs.module.ts
  3. 2
      packages/nocodb/src/modules/jobs/jobs/at-import/at-import.processor.ts
  4. 135
      packages/nocodb/src/modules/jobs/jobs/at-import/helpers/fetchAT.ts

2
packages/nocodb/src/models/Column.ts

@ -816,6 +816,7 @@ export default class Column<T = any> implements ColumnType {
if (button.type === 'url') { if (button.type === 'url') {
if ( if (
button.formula &&
addFormulaErrorIfMissingColumn({ addFormulaErrorIfMissingColumn({
formula: button, formula: button,
columnId: id, columnId: id,
@ -858,6 +859,7 @@ export default class Column<T = any> implements ColumnType {
formulaCol, formulaCol,
).getColOptions<FormulaColumn>(context, ncMeta); ).getColOptions<FormulaColumn>(context, ncMeta);
if ( if (
formula.formula &&
addFormulaErrorIfMissingColumn({ addFormulaErrorIfMissingColumn({
formula, formula,
columnId: id, columnId: id,

1
packages/nocodb/src/modules/jobs/jobs.module.ts

@ -54,6 +54,7 @@ export const JobsModuleMetadata = {
name: JOBS_QUEUE, name: JOBS_QUEUE,
defaultJobOptions: { defaultJobOptions: {
removeOnComplete: true, removeOnComplete: true,
attempts: 1,
}, },
}), }),
] ]

2
packages/nocodb/src/modules/jobs/jobs/at-import/at-import.processor.ts

@ -469,7 +469,7 @@ export class AtImportProcessor {
if ( if (
options.find( options.find(
(el) => (el) =>
el.title.toLowerCase() === (value as any).name.toLowerCase(), el.title === (value as any).name,
) )
) { ) {
logWarning( logWarning(

135
packages/nocodb/src/modules/jobs/jobs/at-import/helpers/fetchAT.ts

@ -1,5 +1,8 @@
import { Logger } from '@nestjs/common'; import { Logger } from '@nestjs/common';
import axios from 'axios'; import axios from 'axios';
import { streamObject } from 'stream-json/streamers/StreamObject';
import { parser } from 'stream-json/Parser';
import { ignore } from 'stream-json/filters/Ignore';
const logger = new Logger('FetchAT'); const logger = new Logger('FetchAT');
@ -17,8 +20,7 @@ async function initialize(shareId, appId?: string) {
const url = `https://airtable.com/${appId ? `${appId}/` : ''}${shareId}`; const url = `https://airtable.com/${appId ? `${appId}/` : ''}${shareId}`;
try { try {
const hreq = await axios const hreq = await axios.get(url, {
.get(url, {
headers: { headers: {
accept: accept:
'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
@ -39,23 +41,16 @@ async function initialize(shareId, appId?: string) {
referrerPolicy: 'strict-origin-when-cross-origin', referrerPolicy: 'strict-origin-when-cross-origin',
body: null, body: null,
method: 'GET', method: 'GET',
}) });
.then((response) => {
for (const ck of response.headers['set-cookie']) { for (const ck of hreq.headers['set-cookie']) {
info.cookie += ck.split(';')[0] + '; '; info.cookie += ck.split(';')[0] + '; ';
} }
return response.data;
})
.catch((e) => {
logger.log(e);
throw {
message:
'Invalid Shared Base ID :: Ensure www.airtable.com/<SharedBaseID> is accessible. Refer https://bit.ly/3x0OdXI for details',
};
});
const headers = hreq.match(/(?<=var headers =)(.*)(?=;)/g); const data = hreq.data;
const link = hreq.match(/(?<=fetch\(")(\\.*)(?=")/g);
const headers = data.match(/(?<=var headers =)(.*)(?=;)/g);
const link = data.match(/(?<=fetch\(")(\\.*)(?=")/g);
if (!headers || !link) { if (!headers || !link) {
throw { throw {
@ -65,11 +60,11 @@ async function initialize(shareId, appId?: string) {
} }
info.headers = JSON.parse( info.headers = JSON.parse(
hreq.match(/(?<=var headers =)(.*)(?=;)/g)[0].trim(), data.match(/(?<=var headers =)(.*)(?=;)/g)[0].trim(),
); );
info.link = unicodeToChar( info.link = unicodeToChar(
hreq.match(/(?<=fetch\(")(\\.*)(?=")/g)[0].trim(), data.match(/(?<=fetch\(")(\\.*)(?=")/g)[0].trim(),
); );
info.link = info.link.replace( info.link = info.link.replace(
@ -112,6 +107,7 @@ async function initialize(shareId, appId?: string) {
async function read() { async function read() {
if (info.initialized) { if (info.initialized) {
try {
const resreq = await axios('https://airtable.com' + info.link, { const resreq = await axios('https://airtable.com' + info.link, {
headers: { headers: {
accept: '*/*', accept: '*/*',
@ -133,23 +129,46 @@ async function read() {
referrerPolicy: 'no-referrer', referrerPolicy: 'no-referrer',
body: null, body: null,
method: 'GET', method: 'GET',
}) responseType: 'stream',
.then((response) => { });
return response.data;
}) const data: any = await new Promise((resolve, reject) => {
.catch((e) => { const jsonStream = resreq.data
logger.log(e); .pipe(parser())
throw { .pipe(ignore({ filter: 'data.tableDatas' }))
message: .pipe(streamObject());
'Error Reading :: Ensure www.airtable.com/<SharedBaseID> is accessible. Refer https://bit.ly/3x0OdXI for details',
}; const fullObject = {};
jsonStream.on('data', (chunk) => {
if (chunk.key) fullObject[chunk.key] = chunk.value;
});
jsonStream.on('error', (err) => {
reject(err);
});
jsonStream.on('end', () => {
resolve(fullObject);
});
}); });
if (data?.data) {
return { return {
schema: resreq.data, schema: data?.data,
baseId: info.baseId, baseId: info.baseId,
baseInfo: info.baseInfo, baseInfo: info.baseInfo,
}; };
} else {
throw new Error('Error Reading :: Data missing');
}
} catch (e) {
logger.log(e);
throw {
message:
'Error Reading :: Ensure www.airtable.com/<SharedBaseID> is accessible. Refer https://bit.ly/3x0OdXI for details',
};
}
} else { } else {
throw { throw {
message: 'Error Initializing :: please try again !!', message: 'Error Initializing :: please try again !!',
@ -159,6 +178,7 @@ async function read() {
async function readView(viewId) { async function readView(viewId) {
if (info.initialized) { if (info.initialized) {
try {
const resreq = await axios( const resreq = await axios(
`https://airtable.com/v0.3/view/${viewId}/readData?` + `https://airtable.com/v0.3/view/${viewId}/readData?` +
`stringifiedObjectParams=${encodeURIComponent( `stringifiedObjectParams=${encodeURIComponent(
@ -199,19 +219,44 @@ async function readView(viewId) {
referrerPolicy: 'no-referrer', referrerPolicy: 'no-referrer',
body: null, body: null,
method: 'GET', method: 'GET',
responseType: 'stream',
}, },
) );
.then((response) => {
return response.data; const data: any = await new Promise((resolve, reject) => {
}) const jsonStream = resreq.data
.catch((e) => { .pipe(parser())
.pipe(ignore({ filter: 'data.rowOrder' }))
.pipe(streamObject());
const fullObject = {};
jsonStream.on('data', (chunk) => {
if (chunk.key) fullObject[chunk.key] = chunk.value;
});
jsonStream.on('error', (err) => {
reject(err);
});
jsonStream.on('end', () => {
resolve(fullObject);
});
});
if (data?.data) {
return { view: data.data };
} else {
throw new Error('Error Reading :: Data missing');
}
} catch (e) {
logger.log(e); logger.log(e);
throw { throw {
message: message:
'Error Reading View :: Ensure www.airtable.com/<SharedBaseID> is accessible. Refer https://bit.ly/3x0OdXI for details', 'Error Reading View :: Ensure www.airtable.com/<SharedBaseID> is accessible. Refer https://bit.ly/3x0OdXI for details',
}; };
}); }
return { view: resreq.data };
} else { } else {
throw { throw {
message: 'Error Initializing :: please try again !!', message: 'Error Initializing :: please try again !!',
@ -223,6 +268,8 @@ async function readTemplate(templateId) {
if (!info.initialized) { if (!info.initialized) {
await initialize('shrO8aYf3ybwSdDKn'); await initialize('shrO8aYf3ybwSdDKn');
} }
try {
const resreq = await axios( const resreq = await axios(
`https://www.airtable.com/v0.3/exploreApplications/${templateId}`, `https://www.airtable.com/v0.3/exploreApplications/${templateId}`,
{ {
@ -250,18 +297,20 @@ async function readTemplate(templateId) {
mode: 'cors', mode: 'cors',
credentials: 'include', credentials: 'include',
}, },
) );
.then((response) => {
return response.data; if (resreq?.data) {
}) return { template: resreq.data };
.catch((e) => { } else {
throw new Error('Error Reading :: Data missing');
}
} catch (e) {
logger.log(e); logger.log(e);
throw { throw {
message: message:
'Error Fetching :: Ensure www.airtable.com/templates/featured/<TemplateID> is accessible.', 'Error Fetching :: Ensure www.airtable.com/templates/featured/<TemplateID> is accessible.',
}; };
}); }
return { template: resreq };
} }
function unicodeToChar(text) { function unicodeToChar(text) {

Loading…
Cancel
Save