Merge branch 'master' of github.com:Budibase/budibase into feature/sqs-table-cleanup

This commit is contained in:
mike12345567 2024-05-14 14:38:00 +01:00
commit 673211dfbb
28 changed files with 941 additions and 267 deletions

213
i18n/README.it.md Normal file
View File

@ -0,0 +1,213 @@
<p align="center">
<a href="https://www.budibase.com">
<img alt="Budibase" src="https://res.cloudinary.com/daog6scxm/image/upload/v1696515725/Branding/Assets/Symbol/RGB/Full%20Colour/Budibase_Symbol_RGB_FullColour_cbqvha_1_z5cwq2.svg" width="60" />
</a>
</p>
<h1 align="center">
Budibase
</h1>
<h3 align="center">
La piattaforma low-code che amerai utilizzare
</h3>
<p align="center">
Budibase è una piattaforma low-code open source ed è il modo più semplice per creare strumenti interni che migliorano la produttività.
</p>
<h3 align="center">
🤖 🎨 🚀
</h3>
<br>
<p align="center">
<img alt="Budibase design ui" src="https://res.cloudinary.com/daog6scxm/image/upload/v1633524049/ui/design-ui-wide-mobile_gdaveq.jpg">
</p>
<p align="center">
<a href="https://github.com/Budibase/budibase/releases">
<img alt="GitHub tutte le release" src="https://img.shields.io/github/downloads/Budibase/budibase/total">
</a>
<a href="https://github.com/Budibase/budibase/releases">
<img alt="GitHub release (ordine cronologico)" src="https://img.shields.io/github/v/release/Budibase/budibase">
</a>
<a href="https://twitter.com/intent/follow?screen_name=budibase">
<img src="https://img.shields.io/twitter/follow/budibase?style=social" alt="Segui @budibase" />
</a>
<img src="https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg" alt="Codice di condotta" />
<a href="https://codecov.io/gh/Budibase/budibase">
<img src="https://codecov.io/gh/Budibase/budibase/graph/badge.svg?token=E8W2ZFXQOH"/>
</a>
</p>
<h3 align="center">
<a href="https://docs.budibase.com/getting-started">Inizia</a>
<span> · </span>
<a href="https://docs.budibase.com">Documentazione</a>
<span> · </span>
<a href="https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas">Richieste di miglioramento</a>
<span> · </span>
<a href="https://github.com/Budibase/budibase/issues">Segnala un bug</a>
<span> · </span>
Supporto: <a href="https://github.com/Budibase/budibase/discussions">Discussioni</a>
</h3>
<br /><br />
## ✨ Funzionalità
### Costruisci e distribuisci software reale
A differenza di altre piattaforme, con Budibase puoi costruire e distribuire applicazioni one-page. Le applicazioni Budibase sono altamente performanti e possono essere progettate in modo responsive, offrendo ai tuoi utenti un'esperienza eccezionale.
<br /><br />
### Sorgente aperto ed estensibile
Budibase è software open source - sotto licenza GPL v3. Questo dovrebbe rassicurarti sul fatto che Budibase sarà sempre lì. Puoi anche codificare in Budibase o fare fork e apportare modifiche a tuo piacimento, rendendolo un'esperienza amichevole per gli sviluppatori.
<br /><br />
### Importa dati o inizia da zero
Budibase può estrarre i suoi dati da diverse fonti, tra cui MongoDB, CouchDB, PostgreSQL, MySQL, Airtable, S3, DynamoDB o un'API REST. E a differenza di altre piattaforme, con Budibase puoi partire da zero e creare applicazioni aziendali senza alcuna fonte di dati. [Richiedi una nuova fonte di dati](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas).
<p align="center">
<img alt="Dati Budibase" src="https://res.cloudinary.com/daog6scxm/image/upload/v1636970242/Out%20of%20beta%20launch/data_n1tlhf.png">
</p>
<br /><br />
### Progetta e crea applicazioni utilizzando componenti predefiniti.
Budibase è dotato di componenti predefiniti belli e potenti che puoi utilizzare come mattoni per costruire la tua interfaccia utente. Esporremo anche molte delle tue opzioni di stile CSS preferite in modo che tu possa esprimere una creatività maggiore. [Richiedi un nuovo componente](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas).
<p align="center">
<img alt="Design Budibase" src="https://res.cloudinary.com/daog6scxm/image/upload/v1636970243/Out%20of%20beta%20launch/design-like-a-pro_qhlfeu.gif">
</p>
<br /><br />
### Automatizza processi, integra altri strumenti e collegati a webhook
Risparmia tempo automatizzando processi manuali e flussi di lavoro. Che si tratti di connettersi a webhook o automatizzare email, basta dire a Budibase cosa fare e lasciarlo lavorare per te. Puoi facilmente [creare una nuova automazione per Budibase qui](https://github.com/Budibase/automations) o [Richiedere una nuova automazione](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas).
<p align="center">
<img alt="Automazioni Budibase" src="https://res.cloudinary.com/daog6scxm/image/upload/v1636970486/Out%20of%20beta%20launch/automation_riro7u.png">
</p>
<br /><br />
### Integrazione con i tuoi strumenti preferiti
Budibase si integra con vari strumenti popolari, consentendoti di creare applicazioni che si adattano perfettamente alla tua stack tecnologica.
<p align="center">
<img alt="Integrazioni Budibase" src="https://res.cloudinary.com/daog6scxm/image/upload/v1636970242/Out%20of%20beta%20launch/integrations_kc7dqt.png">
</p>
<br /><br />
### Paradiso degli amministratori
Budibase è progettato per crescere. Con Budibase, puoi auto-ospitarti sulla tua infrastruttura e gestire globalmente utenti, home, SMTP, applicazioni, gruppi, aspetto e altro ancora. Puoi anche fornire agli utenti/gruppi un portale delle applicazioni e affidare la gestione degli utenti al responsabile del gruppo.
- Guarda il video promozionale: https://youtu.be/xoljVpty_Kw
<br /><br /><br />
## 🏁 Inizio
<img src="https://res.cloudinary.com/daog6scxm/image/upload/v1634808888/logo/deploy_npl9za.png" />
Implementa Budibase self-hosted nella tua infrastruttura esistente, utilizzando Docker, Kubernetes e Digital Ocean.
Oppure utilizza Budibase Cloud se non hai bisogno di auto-ospitare e desideri iniziare rapidamente.
### [Inizia con Budibase](https://budibase.com)
<br /><br />
## 🎓 Imparare Budibase
La documentazione Budibase [è qui](https://docs.budibase.com).
<br />
<br /><br />
## 💬 Comunità
Se hai domande o vuoi discutere con altri utenti di Budibase e unirti alla nostra comunità, vai su: [Discussioni Github](https://github.com/Budibase/budibase/discussions)
<br /><br /><br />
## ❗ Codice di condotta
Budibase si impegna a offrire a tutti un'esperienza accogliente, diversificata e priva di molestie. Ci aspettiamo che tutti i membri della comunità Budibase rispettino i principi del nostro [**Codice di condotta**](https://github.com/Budibase/budibase/blob/HEAD/.github/CODE_OF_CONDUCT.md). Grazie per la tua attenzione.
<br />
<br /><br />
## 🙌 Contribuire a Budibase
Che tu stia aprendo un rapporto di bug o creando una Pull request, ogni contributo è apprezzato e benvenuto. Se stai pensando di implementare una nuova funzionalità o modificare l'API, crea prima un Issue. In questo modo possiamo assicurarci che il tuo lavoro non sia inutile.
### Non sai da dove cominciare ?
Un buon punto di partenza per contribuire è qui: [Progetti in corso](https://github.com/Budibase/budibase/projects/22).
### Come è organizzato il repo ?
Budibase è un monorepo gestito da lerna. Lerna gestisce la costruzione e la pubblicazione dei pacchetti di Budibase. Ecco, a grandi linee, i pacchetti che compongono Budibase.
- [packages/builder](https://github.com/Budibase/budibase/tree/HEAD/packages/builder) - contiene il codice per l'applicazione svelte lato client di budibase builder.
- [packages/client](https://github.com/Budibase/budibase/tree/HEAD/packages/client) - Un modulo che viene eseguito nel browser e che è responsabile della lettura delle definizioni JSON e della creazione di applicazioni web viventi da esse.
- [packages/server](https://github.com/Budibase/budibase/tree/HEAD/packages/server) - Il server budibase. Questa applicazione Koa è responsabile del servizio del JS per le applicazioni builder e budibase, oltre a fornire l'API per l'interazione con il database e il filesystem.
Per ulteriori informazioni, vedere [CONTRIBUTING.md](https://github.com/Budibase/budibase/blob/HEAD/.github/CONTRIBUTING.md)
<br /><br />
## 📝 Licenza
Budibase è open source, con licenza [GPL v3](https://www.gnu.org/licenses/gpl-3.0.en.html). Le librerie client e dei componenti sono con licenza [MPL](https://directory.fsf.org/wiki/License:MPL-2.0) - quindi le applicazioni che crei possono essere utilizzate con licenza come desideri.
<br /><br />
## ⭐ Stargazers nel tempo
[![Stargazers nel tempo](https://starchart.cc/Budibase/budibase.svg)](https://starchart.cc/Budibase/budibase)
Se riscontri problemi tra gli aggiornamenti del builder, utilizza la seguente guida [qui](https://github.com/Budibase/budibase/blob/HEAD/.github/CONTRIBUTING.md#troubleshooting) per pulire il tuo ambiente.
<br /><br />
## Contributeurs ✨
Grazie a queste meravigliose persone ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="http://martinmck.com"><img src="https://avatars1.githubusercontent.com/u/11256663?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Martin McKeaveney</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=shogunpurple" title="Codice">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=shogunpurple" title="Documentazione">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=shogunpurple" title="Test">⚠️</a> <a href="#infra-shogunpurple" title="Infrastruttura (Hosting, Strumenti di build, etc)">🚇</a></td>
<td align="center"><a href="http://www.michaeldrury.co.uk/"><img src="https://avatars2.githubusercontent.com/u/4407001?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Drury</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=mike12345567" title="Documentazione">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=mike12345567" title="Codice">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=mike12345567" title="Test">⚠️</a> <a href="#infra-mike12345567" title="Infrastruttura (Hosting, Strumenti di build, etc)">🚇</a></td>
<td align="center"><a href="https://github.com/aptkingston"><img src="https://avatars3.githubusercontent.com/u/9075550?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andrew Kingston</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=aptkingston" title="Documentazione">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=aptkingston" title="Codice">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=aptkingston" title="Test">⚠️</a> <a href="#design-aptkingston" title="Design">🎨</a></td>
<td align="center"><a href="https://budibase.com/"><img src="https://avatars3.githubusercontent.com/u/3524181?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Shanks</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=mjashanks" title="Documentazione">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=mjashanks" title="Codice">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=mjashanks" title="Test">⚠️</a></td>
<td align="center"><a href="https://github.com/kevmodrome"><img src="https://avatars3.githubusercontent.com/u/534488?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kevin Åberg Kultalahti</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=kevmodrome" title="Documentazione">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=kevmodrome" title="Codice">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=kevmodrome" title="Test">⚠️</a></td>
<td align="center"><a href="https://www.budibase.com/"><img src="https://avatars2.githubusercontent.com/u/49767913?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Joe</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=joebudi" title="Documentazione">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=joebudi" title="Codice">💻</a> <a href="#content-joebudi" title="Contenuto">🖋</a> <a href="#design-joebudi" title="Design">🎨</a></td>
<td align="center"><a href="https://github.com/Rory-Powell"><img src="https://avatars.githubusercontent.com/u/8755148?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rory Powell</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=Rory-Powell" title="Codice">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=Rory-Powell" title="Documentazione">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=Rory-Powell" title="Test">⚠️</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/PClmnt"><img src="https://avatars.githubusercontent.com/u/5665926?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Peter Clement</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=PClmnt" title="Codice">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=PClmnt" title="Documentazione">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=PClmnt" title="Test">⚠️</a></td>
<td align="center"><a href="https://github.com/Conor-Mack"><img src="https://avatars.githubusercontent.com/u/36074859?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Conor Mack</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=Conor-Mack" title="Codice">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=Conor-Mack" title="Documentazione">📖</a></td>
<td align="center"><a href="https://github.com/dominiccave"><img src="https://avatars.githubusercontent.com/u/17828738?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dominic Cave</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=dominiccave" title="Codice">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=dominiccave" title="Documentazione">📖</a></td>
<td align="center"><a href="https://github.com/Rabonaire"><img src="https://avatars.githubusercontent.com/u/10060936?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rabonaire</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=Rabonaire" title="Codice">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=Rabonaire" title="Documentazione">📖</a> <a href="https://github.com/Budibase/budibase/issues?q=author%3ARabonaire" title="Report di bug">🐛</a></td>
<td align="center"><a href="https://github.com/Alexsyeung"><img src="https://avatars.githubusercontent.com/u/31413823?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alex Yeung</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=Alexsyeung" title="Documentazione">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=Alexsyeung" title="Codice">💻</a></td>
<td align="center"><a href="https://github.com/SamWoodsIV"><img src="https://avatars.githubusercontent.com/u/25854138?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sam Woods</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=SamWoodsIV" title="Documentazione">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=SamWoodsIV" title="Codice">💻</a> <a href="#infra-SamWoodsIV" title="Infrastruttura (Hosting, Strumenti di build, etc)">🚇</a></td>
<td align="center"><a href="https://github.com/ScooterSwope"><img src="https://avatars.githubusercontent.com/u/21829556?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ScooterSwope</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=ScooterSwope" title="Codice">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=ScooterSwope" title="Documentazione">📖</a></td>
</tr>
</table>
<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
Questo progetto segue il [convenant del contribuente](https://github.com/Budibase/budibase/blob/master/CODE_OF_CONDUCT.md). Ogni contributo è il benvenuto!
</td>
</tr>
</table>

211
i18n/README.por.md Normal file
View File

@ -0,0 +1,211 @@
<p align="center">
<a href="https://www.budibase.com">
<img alt="Budibase" src="https://res.cloudinary.com/daog6scxm/image/upload/v1696515725/Branding/Assets/Symbol/RGB/Full%20Colour/Budibase_Symbol_RGB_FullColour_cbqvha_1_z5cwq2.svg" width="60" />
</a>
</p>
<h1 align="center">
Budibase
</h1>
<h3 align="center">
A plataforma low-code que você vai adorar usar
</h3>
<p align="center">
Budibase é uma plataforma low-code de código aberto e é a maneira mais fácil de criar ferramentas internas que melhoram a produtividade.
</p>
<h3 align="center">
🤖 🎨 🚀
</h3>
<br>
<p align="center">
<img alt="Budibase design ui" src="https://res.cloudinary.com/daog6scxm/image/upload/v1633524049/ui/design-ui-wide-mobile_gdaveq.jpg">
</p>
<p align="center">
<a href="https://github.com/Budibase/budibase/releases">
<img alt="GitHub todos os releases" src="https://img.shields.io/github/downloads/Budibase/budibase/total">
</a>
<a href="https://github.com/Budibase/budibase/releases">
<img alt="GitHub release (por ordem cronológica)" src="https://img.shields.io/github/v/release/Budibase/budibase">
</a>
<a href="https://twitter.com/intent/follow?screen_name=budibase">
<img src="https://img.shields.io/twitter/follow/budibase?style=social" alt="Siga @budibase" />
</a>
<img src="https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg" alt="Código de Conduta" />
<a href="https://codecov.io/gh/Budibase/budibase">
<img src="https://codecov.io/gh/Budibase/budibase/graph/badge.svg?token=E8W2ZFXQOH"/>
</a>
</p>
<h3 align="center">
<a href="https://docs.budibase.com/getting-started">Começar</a>
<span> · </span>
<a href="https://docs.budibase.com">Documentação</a>
<span> · </span>
<a href="https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas">Solicitar melhorias</a>
<span> · </span>
<a href="https://github.com/Budibase/budibase/issues">Reportar um bug</a>
<span> · </span>
Suporte: <a href="https://github.com/Budibase/budibase/discussions">Discussões</a>
</h3>
<br /><br />
## ✨ Recursos
### Construa e implante um software real
Ao contrário de outras plataformas, com o Budibase você constrói e implanta aplicativos de uma página. Os aplicativos Budibase são altamente performáticos e podem ser designados de forma responsiva, proporcionando uma experiência excepcional aos seus usuários.
<br /><br />
### Código-fonte livre e extensível
Budibase é software livre - sob a licença GPL v3. Isso deve lhe dar confiança de que o Budibase estará sempre disponível. Você também pode codificar no Budibase ou bifurcá-lo e fazer alterações conforme desejar, tornando-o amigável para desenvolvedores.
<br /><br />
### Importar dados ou começar do zero
Budibase pode extrair dados de várias fontes, incluindo MongoDB, CouchDB, PostgreSQL, MySQL, Airtable, S3, DynamoDB ou uma API REST. E ao contrário de outras plataformas, com o Budibase você pode começar do zero e criar aplicativos de negócios sem nenhuma fonte de dados. [Solicitar uma nova fonte de dados](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas).
<p align="center">
<img alt="Dados Budibase" src="https://res.cloudinary.com/daog6scxm/image/upload/v1636970242/Out%20of%20beta%20launch/data_n1tlhf.png">
</p>
<br /><br />
### Projetar e criar aplicativos usando componentes pré-definidos
O Budibase vem com componentes lindamente projetados e poderosos que você pode usar como blocos de construção para criar sua interface do usuário. Também oferecemos muitas das suas opções de estilo CSS favoritas para que você possa mostrar sua criatividade. [Solicitar um novo componente](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas).
<p align="center">
<img alt="Design Budibase" src="https://res.cloudinary.com/daog6scxm/image/upload/v1636970243/Out%20of%20beta%20launch/design-like-a-pro_qhlfeu.gif">
</p>
<br /><br />
### Automatizar processos, integrar outras ferramentas e conectar webhooks
Economize tempo automatizando processos manuais e fluxos de trabalho. Seja conectando-se a webhooks ou automatizando e-mails, basta dizer ao Budibase o que fazer e deixá-lo trabalhar para você. Você pode facilmente [criar uma nova automação para o Budibase aqui](https://github.com/Budibase/automations) ou [Solicitar uma nova automação](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas).
<p align="center">
<img alt="Automações Budibase" src="https://res.cloudinary.com/daog6scxm/image/upload/v1636970486/Out%20of%20beta%20launch/automation_riro7u.png">
</p>
<br /><br />
### Integração com suas ferramentas favoritas
O Budibase se integra a várias ferramentas populares, permitindo que você crie aplicativos que se encaixam perfeitamente em sua pilha tecnológica.
<p align="center">
<img alt="Integrações Budibase" src="https://res.cloudinary.com/daog6scxm/image/upload/v1636970242/Out%20of%20beta%20launch/integrations_kc7dqt.png">
</p>
<br /><br />
### Paraíso dos administradores
O Budibase é projetado para escalar. Com o Budibase, você pode se auto-hospedar em sua própria infraestrutura e gerenciar globalmente usuários, home, SMTP, aplicativos, grupos, aparência e muito mais. Você também pode fornecer aos usuários/grupos um portal de aplicativos e delegar o gerenciamento de usuários ao líder do grupo.
- Assista ao vídeo promocional: https://youtu.be/xoljVpty_Kw
<br /><br /><br />
## 🏁 Começar
<img src="https://res.cloudinary.com/daog6scxm/image/upload/v1634808888/logo/deploy_npl9za.png" />
Implante o Budibase em auto-hospedagem em sua infraestrutura existente, usando Docker, Kubernetes e Digital Ocean.
Ou use o Budibase Cloud se você não precisar se auto-hospedar e quiser começar rapidamente.
### [Começar com o Budibase](https://budibase.com)
<br /><br />
## 🎓 Aprenda Budibase
A documentação Budibase [está aqui](https://docs.budibase.com).
<br />
<br /><br />
## 💬 Comunidade
Se você tiver alguma dúvida ou quiser conversar com outros usuários do Budibase e se juntar à nossa comunidade, visite [Discussões do Github](https://github.com/Budibase/budibase/discussions)
<br /><br /><br />
## ❗ Código de Conduta
O Budibase está comprometido em oferecer a todos uma experiência acolhedora, diversificada e livre de assédio. Esperamos que todos os membros da comunidade Budibase sigam os princípios do nosso [**Código de Conduta**](https://github.com/Budibase/budibase/blob/HEAD/.github/CODE_OF_CONDUCT.md). Obrigado por ler.
<br />
<br /><br />
## 🙌 Contribuindo para o Budibase
Seja abrindo uma issue ou criando um pull request, toda contribuição é apreciada e bem-vinda. Se você está pensando em implementar uma nova funcionalidade ou alterar a API, por favor, crie primeiro uma Issue. Assim, podemos garantir que seu trabalho não seja em vão.
### Não sabe por onde começar?
Um bom lugar para começar a contribuir é aqui: [Projetos em andamento](https://github.com/Budibase/budibase/projects/22).
### Como o repositório está organizado?
O Budibase é um monorepo gerenciado pelo lerna. O Lerna cuida da construção e publicação dos pacotes do Budibase. Aqui estão, em alto nível, os pacotes que compõem o Budibase.
- [packages/builder](https://github.com/Budibase/budibase/tree/HEAD/packages/builder) - contém o código para o aplicativo svelte do lado do cliente do budibase builder.
- [packages/client](https://github.com/Budibase/budibase/tree/HEAD/packages/client) - Um módulo que roda no navegador e é responsável por ler definições JSON e criar aplicativos web dinâmicos a partir delas.
- [packages/server](https://github.com/Budibase/budibase/tree/HEAD/packages/server) - O servidor budibase. Este aplicativo Koa é responsável por servir o JS para os aplicativos builder e budibase, bem como fornecer a API para interagir com o banco de dados e o sistema de arquivos.
Para mais informações, veja [CONTRIBUTING.md](https://github.com/Budibase/budibase/blob/HEAD/.github/CONTRIBUTING.md)
<br /><br />
## 📝 Licença
O Budibase é open source, sob a licença [GPL v3](https://www.gnu.org/licenses/gpl-3.0.en.html). As bibliotecas do cliente e dos componentes estão licenciadas sob [MPL](https://directory.fsf.org/wiki/License:MPL-2.0) - para que os aplicativos que você cria possam ser usados sob licença como você desejar.
<br /><br />
## ⭐ Stargazers ao longo do tempo
[![Stargazers ao longo do tempo](https://starchart.cc/Budibase/budibase.svg)](https://starchart.cc/Budibase/budibase)
Se você tiver problemas entre as atualizações do builder, por favor, use o guia a seguir [aqui](https://github.com/Budibase/budibase/blob/HEAD/.github/CONTRIBUTING.md#troubleshooting) para limpar seu ambiente.
<br /><br />
## Contribuidores ✨
Agradecimentos a estas pessoas maravilhosas ([chave de emoji](https://allcontributors.org/docs/fr/emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="http://martinmck.com"><img src="https://avatars1.githubusercontent.com/u/11256663?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Martin McKeaveney</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=shogunpurple" title="Código">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=shogunpurple" title="Documentação">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=shogunpurple" title="Testes">⚠️</a> <a href="#infra-shogunpurple" title="Infraestrutura (Hospedagem, Ferramentas de Build, etc)">🚇</a></td>
<td align="center"><a href="http://www.michaeldrury.co.uk/"><img src="https://avatars2.githubusercontent.com/u/4407001?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Drury</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=mike12345567" title="Documentação">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=mike12345567" title="Código">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=mike12345567" title="Testes">⚠️</a> <a href="#infra-mike12345567" title="Infraestrutura (Hospedagem, Ferramentas de Build, etc)">🚇</a></td>
<td align="center"><a href="https://github.com/aptkingston"><img src="https://avatars3.githubusercontent.com/u/9075550?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andrew Kingston</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=aptkingston" title="Documentação">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=aptkingston" title="Código">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=aptkingston" title="Testes">⚠️</a> <a href="#design-aptkingston" title="Design">🎨</a></td>
<td align="center"><a href="https://budibase.com/"><img src="https://avatars3.githubusercontent.com/u/3524181?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Shanks</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=mjashanks" title="Documentação">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=mjashanks" title="Código">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=mjashanks" title="Testes">⚠️</a></td>
<td align="center"><a href="https://github.com/kevmodrome"><img src="https://avatars3.githubusercontent.com/u/534488?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kevin Åberg Kultalahti</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=kevmodrome" title="Documentação">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=kevmodrome" title="Código">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=kevmodrome" title="Testes">⚠️</a></td>
<td align="center"><a href="https://www.budibase.com/"><img src="https://avatars2.githubusercontent.com/u/49767913?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Joe</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=joebudi" title="Documentação">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=joebudi" title="Código">💻</a> <a href="#content-joebudi" title="Conteúdo">🖋</a> <a href="#design-joebudi" title="Design">🎨</a></td>
<td align="center"><a href="https://github.com/Rory-Powell"><img src="https://avatars.githubusercontent.com/u/8755148?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rory Powell</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=Rory-Powell" title="Código">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=Rory-Powell" title="Documentação">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=Rory-Powell" title="Testes">⚠️</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/PClmnt"><img src="https://avatars.githubusercontent.com/u/5665926?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Peter Clement</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=PClmnt" title="Código">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=PClmnt" title="Documentação">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=PClmnt" title="Testes">⚠️</a></td>
<td align="center"><a href="https://github.com/Conor-Mack"><img src="https://avatars.githubusercontent.com/u/36074859?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Conor Mack</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=Conor-Mack" title="Código">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=Conor-Mack" title="Documentação">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=Conor-Mack" title="Testes">⚠️</a></td>
<td align="center"><a href="https://github.com/Grays-world"><img src="https://avatars.githubusercontent.com/u/89784014?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Grays-world</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=Grays-world" title="Código">💻</a></td>
<td align="center"><a href="https://github.com/syluss"><img src="https://avatars.githubusercontent.com/u/1770743?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sylvain Galand</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=syluss" title="Código">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=syluss" title="Documentação">📖</a></td>
<td align="center"><a href="https://github.com/John-Mullins1"><img src="https://avatars.githubusercontent.com/u/89561797?v=4?s=100" width="100px;" alt=""/><br /><sub><b>John Mullins</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=John-Mullins1" title="Código">💻</a></td>
<td align="center"><a href="https://github.com/Jakeboyd"><img src="https://avatars.githubusercontent.com/u/55934414?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jakeboyd</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=Jakeboyd" title="Código">💻</a></td>
<td align="center"><a href="https://github.com/stevedoescode"><img src="https://avatars.githubusercontent.com/u/29486122?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Steve Bridle</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=stevedoescode" title="Código">💻</a></td>
</tr>
</table>
<!-- markdownlint-enable -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
<br /><br />
## Licença
Distribuído sob a licença GPL v3.0. Veja `LICENSE` para mais informações.
</p>

207
i18n/README.ru.md Normal file
View File

@ -0,0 +1,207 @@
<p align="center">
<a href="https://www.budibase.com">
<img alt="Budibase" src="https://res.cloudinary.com/daog6scxm/image/upload/v1696515725/Branding/Assets/Symbol/RGB/Full%20Colour/Budibase_Symbol_RGB_FullColour_cbqvha_1_z5cwq2.svg" width="60" />
</a>
</p>
<h1 align="center">
Budibase
</h1>
<h3 align="center">
Низкокодовая платформа, которую вы полюбите использовать
</h3>
<p align="center">
Budibase - это открытая низкокодовая платформа, которая представляет собой самый простой способ создания внутренних инструментов, повышающих производительность.
</p>
<h3 align="center">
🤖 🎨 🚀
</h3>
<br>
<p align="center">
<img alt="Budibase design ui" src="https://res.cloudinary.com/daog6scxm/image/upload/v1633524049/ui/design-ui-wide-mobile_gdaveq.jpg">
</p>
<p align="center">
<a href="https://github.com/Budibase/budibase/releases">
<img alt="GitHub все релизы" src="https://img.shields.io/github/downloads/Budibase/budibase/total">
</a>
<a href="https://github.com/Budibase/budibase/releases">
<img alt="GitHub релизы (в хронологическом порядке)" src="https://img.shields.io/github/v/release/Budibase/budibase">
</a>
<a href="https://twitter.com/intent/follow?screen_name=budibase">
<img src="https://img.shields.io/twitter/follow/budibase?style=social" alt="Подписаться на @budibase" />
</a>
<img src="https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg" alt="Кодекс поведения" />
<a href="https://codecov.io/gh/Budibase/budibase">
<img src="https://codecov.io/gh/Budibase/budibase/graph/badge.svg?token=E8W2ZFXQOH"/>
</a>
</p>
<h3 align="center">
<a href="https://docs.budibase.com/getting-started">Начать</a>
<span> · </span>
<a href="https://docs.budibase.com">Документация</a>
<span> · </span>
<a href="https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas">Запросы на улучшения</a>
<span> · </span>
<a href="https://github.com/Budibase/budibase/issues">Сообщить об ошибке</a>
<span> · </span>
Поддержка: <a href="https://github.com/Budibase/budibase/discussions">Обсуждения</a>
</h3>
<br /><br />
## ✨ Функциональные возможности
### Строим и развертываем настоящее программное обеспечение
В отличие от других платформ, с помощью Budibase вы создаете и развертываете одностраничные приложения. Приложения Budibase имеют высокую производительность и могут быть адаптированы для разных устройств, обеспечивая вашим пользователям удивительный опыт.
<br /><br />
### Открытый и расширяемый исходный код
Budibase - это свободное программное обеспечение под лицензией GPL v3. Это должно вас уверить в том, что Budibase всегда будет здесь. Вы также можете писать код в Budibase или форкнуть его и вносить изменения по своему усмотрению, что сделает его дружелюбным для разработчиков.
<br /><br />
### Импорт данных или начало с нуля
Budibase может получать данные из различных источников, включая MongoDB, CouchDB, PostgreSQL, MySQL, Airtable, S3, DynamoDB или REST API. И в отличие от других платформ, с помощью Budibase вы можете начать с нуля и создавать бизнес-приложения без каких-либо источников данных. [Запросить новый источник данных](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas).
<p align="center">
<img alt="Budibase data" src="https://res.cloudinary.com/daog6scxm/image/upload/v1636970242/Out%20of%20beta%20launch/data_n1tlhf.png">
</p>
<br /><br />
### Проектирование и создание приложений с использованием предварительно определенных компонентов.
Budibase поставляется с красиво оформленными и мощными компонентами, которые вы можете использовать как строительные блоки для создания вашего пользовательского интерфейса. Мы также предоставляем множество ваших любимых опций стилей CSS, чтобы вы могли проявить больше креативности. [Запросить новый компонент](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas).
<p align="center">
<img alt="Budibase design" src="https://res.cloudinary.com/daog6scxm/image/upload/v1636970243/Out%20of%20beta%20launch/design-like-a-pro_qhlfeu.gif">
</p>
<br /><br />
### Автоматизация процессов, интеграция с другими инструментами и подключение к вебхукам
Экономьте время, автоматизируя ручные процессы и рабочие потоки. Будь то подключение к вебхукам или автоматизация отправки электронных писем, просто скажите Budibase, что он должен делать, и позвольте ему работать за вас. Вы можете легко [создать новую автоматизацию для Budibase здесь](https://github.com/Budibase/automations) или [Запросить новую автоматизацию](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas).
<p align="center">
<img alt="Budibase automations" src="https://res.cloudinary.com/daog6scxm/image/upload/v1636970486/Out%20of%20beta%20launch/automation_riro7u.png">
</p>
<br /><br />
### Интеграция с вашими любимыми инструментами
Budibase интегрируется с рядом популярных инструментов, что позволяет вам создавать приложения, которые идеально вписываются в вашу технологическую стопку.
<p align="center">
<img alt="Budibase integrations" src="https://res.cloudinary.com/daog6scxm/image/upload/v1636970242/Out%20of%20beta%20launch/integrations_kc7dqt.png">
</p>
<br /><br />
### Рай для админов
Budibase разработан для масштабирования. С Budibase вы можете самостоятельно размещать его на своей собственной инфраструктуре и глобально управлять пользователями, доменами, SMTP, приложениями, группами, внешним видом и многим другим. Вы также можете предоставить пользователям/группам портал приложений и поручить управление пользователями руководителю группы.
- Смотрите промо-видео: https://youtu.be/xoljVpty_Kw
<br /><br /><br />
## 🏁 Начало работы
<img src="https://res.cloudinary.com/daog6scxm/image/upload/v1634808888/logo/deploy_npl9za.png" />
Разверните Budibase на своей собственной инфраструктуре с использованием Docker, Kubernetes и Digital Ocean.
Или используйте Budibase Cloud, если вам не нужно самостоятельно размещаться, и вы хотите быстро начать.
### [Начать работу с Budibase](https://budibase.com)
<br /><br />
## 🎓 Изучение Budibase
Документация Budibase [здесь](https://docs.budibase.com).
<br />
<br /><br />
## 💬 Сообщество
Если у вас есть вопросы или вы хотите обсудить что-то с другими пользователями Budibase и присоединиться к нашему сообществу, пожалуйста, перейдите по следующей ссылке: [Обсуждения на GitHub](https://github.com/Budibase/budibase/discussions)
<br /><br /><br />
## ❗ Кодекс поведения
Budibase обязуется обеспечить каждому дружелюбный, разнообразный и безопасный опыт. Мы ожидаем, что все члены сообщества Budibase будут следовать принципам нашего [**Кодекса поведения**](https://github.com/Budibase/budibase/blob/HEAD/.github/CODE_OF_CONDUCT.md). Спасибо за внимание.
<br />
<br /><br />
## 🙌 Вклад в Budibase
Будь то открытие ошибки или создание запроса на включение изменений, любой вклад приветствуется и приветствуется. Если вы планируете реализовать новую функциональность или изменить API, сначала создайте Issue. Так мы сможем убедиться, что ваша работа не напрасна.
### Не знаете, с чего начать?
Хорошее место для начала вклада - это здесь: [Текущие проекты](https://github.com/Budibase/budibase/projects/22).
### Как организован репозиторий?
Budibase - это монорепозиторий, управляемый с помощью lerna. Lerna управляет сборкой и публикацией пакетов Budibase. Вот, в общих чертах, пакеты, из которых состоит Budibase.
- [packages/builder](https://github.com/Budibase/budibase/tree/HEAD/packages/builder) - содержит код клиентского приложения Svelte для Budibase builder.
- [packages/client](https://github.com/Budibase/budibase/tree/HEAD/packages/client) - Модуль, который запускается в браузере и отвечает за чтение JSON-определений и создание веб-приложений из них.
- [packages/server](https://github.com/Budibase/budibase/tree/HEAD/packages/server) - Сервер Budibase. Это приложение Koa отвечает за предоставление JS для строителей и приложений Budibase, а также предоставляет API для взаимодействия с базой данных и файловой системой.
Для получения дополнительной информации см. [CONTRIBUTING.md](https://github.com/Budibase/budibase/blob/HEAD/.github/CONTRIBUTING.md)
<br /><br />
## 📝 Лицензия
Budibase является проектом с открытым исходным кодом, лицензированным по [GPL v3](https://www.gnu.org/licenses/gpl-3.0.en.html). Клиентские библиотеки и компоненты лицензируются по [MPL](https://directory.fsf.org/wiki/License:MPL-2.0), так что приложения, которые вы создаете, могут использоваться под любой лицензией, как вам угодно.
<br /><br />
## ⭐ Старгейзеры во времени
[![Stargazers во времени](https://starchart.cc/Budibase/budibase.svg)](https://starchart.cc/Budibase/budibase)
Если у вас возникли проблемы между обновлениями билдера, пожалуйста, используйте следующее руководство [здесь](https://github.com/Budibase/budibase/blob/HEAD/.github/CONTRIBUTING.md#troubleshooting), чтобы очистить ваше окружение.
<br /><br />
## Участники ✨
Благодарим этих замечательных людей ([ключи эмодзи](https://allcontributors.org/docs/ru/emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="http://martinmck.com"><img src="https://avatars1.githubusercontent.com/u/11256663?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Martin McKeaveney</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=shogunpurple" title="Код">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=shogunpurple" title="Документация">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=shogunpurple" title="Тесты">⚠️</a> <a href="#infra-shogunpurple" title="Инфраструктура (хостинг, средства сборки и т. д.)">🚇</a></td>
<td align="center"><a href="http://www.michaeldrury.co.uk/"><img src="https://avatars2.githubusercontent.com/u/4407001?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Drury</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=mike12345567" title="Документация">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=mike12345567" title="Код">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=mike12345567" title="Тесты">⚠️</a> <a href="#infra-mike12345567" title="Инфраструктура (хостинг, средства сборки и т. д.)">🚇</a></td>
<td align="center"><a href="https://github.com/aptkingston"><img src="https://avatars3.githubusercontent.com/u/9075550?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andrew Kingston</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=aptkingston" title="Документация">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=aptkingston" title="Код">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=aptkingston" title="Тесты">⚠️</a> <a href="#design-aptkingston" title="Дизайн">🎨</a></td>
<td align="center"><a href="https://budibase.com/"><img src="https://avatars3.githubusercontent.com/u/3524181?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Shanks</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=mjashanks" title="Документация">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=mjashanks" title="Код">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=mjashanks" title="Тесты">⚠️</a></td>
<td align="center"><a href="https://github.com/kevmodrome"><img src="https://avatars3.githubusercontent.com/u/534488?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kevin Åberg Kultalahti</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=kevmodrome" title="Документация">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=kevmodrome" title="Код">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=kevmodrome" title="Тесты">⚠️</a></td>
<td align="center"><a href="https://www.budibase.com/"><img src="https://avatars2.githubusercontent.com/u/49767913?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Joe</b></sub></a><br /><a href="#userTesting-joe14" title="User Testing">📓</a></td>
<td align="center"><a href="https://github.com/ntkleynhans"><img src="https://avatars.githubusercontent.com/u/4908235?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nico Kleynhans</b></sub></a><br /><a href="#design-ntkleynhans" title="Дизайн">🎨</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/kkeithle"><img src="https://avatars.githubusercontent.com/u/18712925?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Keith Lee</b></sub></a><br /><a href="#design-kkeithle" title="Дизайн">🎨</a></td>
<td align="center"><a href="https://github.com/Ben-Shabs"><img src="https://avatars.githubusercontent.com/u/26257661?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ben-Shabs</b></sub></a><br /><a href="#userTesting-Ben-Shabs" title="User Testing">📓</a></td>
<td align="center"><a href="https://www.reeceking.dev"><img src="https://avatars.githubusercontent.com/u/4020324?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Reece King</b></sub></a><br /><a href="#design-reeceking" title="Дизайн">🎨</a></td>
<td align="center"><a href="https://github.com/gunjan5"><img src="https://avatars.githubusercontent.com/u/6934146?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Gunjan Chhabra</b></sub></a><br /><a href="#userTesting-gunjan5" title="User Testing">📓</a></td>
<td align="center"><a href="https://github.com/stavros-liaskos"><img src="https://avatars.githubusercontent.com/u/29320217?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stavros Liaskos</b></sub></a><br /><a href="#design-stavros-liaskos" title="Дизайн">🎨</a></td>
<td align="center"><a href="https://github.com/theshu8"><img src="https://avatars.githubusercontent.com/u/28013049?v=4?s=100" width="100px;" alt=""/><br /><sub><b>theshu8</b></sub></a><br /><a href="#userTesting-theshu8" title="User Testing">📓</a></td>
<td align="center"><a href="https://github.com/Kleebster"><img src="https://avatars.githubusercontent.com/u/63757547?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kleebster</b></sub></a><br /><a href="#userTesting-Kleebster" title="User Testing">📓</a></td>
</tr>
</table>
<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
</p>

View File

@ -1,5 +1,5 @@
{ {
"version": "2.26.0", "version": "2.26.2",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*", "packages/*",

View File

@ -9,6 +9,9 @@ import {
AutomationAttachmentContent, AutomationAttachmentContent,
BucketedContent, BucketedContent,
} from "@budibase/types" } from "@budibase/types"
import stream from "stream"
import streamWeb from "node:stream/web"
/**************************************************** /****************************************************
* NOTE: When adding a new bucket - name * * NOTE: When adding a new bucket - name *
* sure that S3 usages (like budibase-infra) * * sure that S3 usages (like budibase-infra) *
@ -53,12 +56,10 @@ export const bucketTTLConfig = (
Rules: [lifecycleRule], Rules: [lifecycleRule],
} }
const params = { return {
Bucket: bucketName, Bucket: bucketName,
LifecycleConfiguration: lifecycleConfiguration, LifecycleConfiguration: lifecycleConfiguration,
} }
return params
} }
async function processUrlAttachment( async function processUrlAttachment(
@ -69,9 +70,12 @@ async function processUrlAttachment(
throw new Error(`Unexpected response ${response.statusText}`) throw new Error(`Unexpected response ${response.statusText}`)
} }
const fallbackFilename = path.basename(new URL(attachment.url).pathname) const fallbackFilename = path.basename(new URL(attachment.url).pathname)
if (!response.body) {
throw new Error("No response received for attachment")
}
return { return {
filename: attachment.filename || fallbackFilename, filename: attachment.filename || fallbackFilename,
content: response.body, content: stream.Readable.fromWeb(response.body as streamWeb.ReadableStream),
} }
} }

View File

@ -374,6 +374,16 @@
return `${value.title || (key === "row" ? "Table" : key)} ${requiredSuffix}` return `${value.title || (key === "row" ? "Table" : key)} ${requiredSuffix}`
} }
function handleAttachmentParams(keyValueObj) {
let params = {}
if (keyValueObj?.length) {
for (let param of keyValueObj) {
params[param.url] = param.filename
}
}
return params
}
onMount(async () => { onMount(async () => {
try { try {
await environment.loadVariables() await environment.loadVariables()
@ -381,15 +391,6 @@
console.error(error) console.error(error)
} }
}) })
const handleAttachmentParams = keyValuObj => {
let params = {}
if (keyValuObj?.length) {
for (let param of keyValuObj) {
params[param.url] = param.filename
}
}
return params
}
</script> </script>
<div class="fields"> <div class="fields">

View File

@ -25,21 +25,21 @@
return !!schema.constraints?.inclusion?.length return !!schema.constraints?.inclusion?.length
} }
const handleAttachmentParams = keyValuObj => { function handleAttachmentParams(keyValueObj) {
let params = {} let params = {}
if ( if (
schema.type === FieldType.ATTACHMENT_SINGLE && schema.type === FieldType.ATTACHMENT_SINGLE &&
Object.keys(keyValuObj).length === 0 Object.keys(keyValueObj).length === 0
) { ) {
return [] return []
} }
if (!Array.isArray(keyValuObj)) { if (!Array.isArray(keyValueObj) && keyValueObj) {
keyValuObj = [keyValuObj] keyValueObj = [keyValueObj]
} }
if (keyValuObj.length) { if (keyValueObj.length) {
for (let param of keyValuObj) { for (let param of keyValueObj) {
params[param.url] = param.filename params[param.url] = param.filename
} }
} }

View File

@ -49,17 +49,20 @@
}, },
] ]
$: tables = findAllMatchingComponents($selectedScreen?.props, component => $: components = findAllMatchingComponents(
component._component.endsWith("table")
)
$: tableBlocks = findAllMatchingComponents(
$selectedScreen?.props, $selectedScreen?.props,
component => component._component.endsWith("tableblock") component => {
const type = component._component
return (
type.endsWith("/table") ||
type.endsWith("/tableblock") ||
type.endsWith("/gridblock")
)
}
) )
$: components = tables.concat(tableBlocks)
$: componentOptions = components.map(table => ({ $: componentOptions = components.map(table => ({
label: table._instanceName, label: table._instanceName,
value: table._component.includes("tableblock") value: table._component.endsWith("/tableblock")
? `${table._id}-table` ? `${table._id}-table`
: table._id, : table._id,
})) }))
@ -69,6 +72,7 @@
$: selectedTable = components.find( $: selectedTable = components.find(
component => component._id === selectedTableId component => component._id === selectedTableId
) )
$: parameters.rows = `{{ literal [${parameters.tableComponentId}].[selectedRows] }}`
onMount(() => { onMount(() => {
if (!parameters.type) { if (!parameters.type) {

View File

@ -104,6 +104,10 @@
} }
onMount(async () => { onMount(async () => {
document.fonts.onloadingdone = e => {
builderStore.loadFonts(e.fontfaces)
}
if (!hasSynced && application) { if (!hasSynced && application) {
try { try {
await API.syncApp(application) await API.syncApp(application)
@ -144,17 +148,19 @@
/> />
</span> </span>
<Tabs {selected} size="M"> <Tabs {selected} size="M">
{#each $layout.children as { path, title }} {#key $builderStore?.fonts}
<TourWrap stepKeys={[`builder-${title}-section`]}> {#each $layout.children as { path, title }}
<Tab <TourWrap stepKeys={[`builder-${title}-section`]}>
quiet <Tab
selected={$isActive(path)} quiet
on:click={topItemNavigate(path)} selected={$isActive(path)}
title={capitalise(title)} on:click={topItemNavigate(path)}
id={`builder-${title}-tab`} title={capitalise(title)}
/> id={`builder-${title}-tab`}
</TourWrap> />
{/each} </TourWrap>
{/each}
{/key}
</Tabs> </Tabs>
</div> </div>
<div class="topcenternav"> <div class="topcenternav">

View File

@ -166,10 +166,16 @@ const automationActions = store => ({
await store.actions.save(newAutomation) await store.actions.save(newAutomation)
}, },
test: async (automation, testData) => { test: async (automation, testData) => {
const result = await API.testAutomation({ let result
automationId: automation?._id, try {
testData, result = await API.testAutomation({
}) automationId: automation?._id,
testData,
})
} catch (err) {
const message = err.message || err.status || JSON.stringify(err)
throw `Automation test failed - ${message}`
}
if (!result?.trigger && !result?.steps?.length) { if (!result?.trigger && !result?.steps?.length) {
if (result?.err?.code === "usage_limit_exceeded") { if (result?.err?.code === "usage_limit_exceeded") {
throw "You have exceeded your automation quota" throw "You have exceeded your automation quota"

View File

@ -14,6 +14,7 @@ export const INITIAL_BUILDER_STATE = {
tourKey: null, tourKey: null,
tourStepKey: null, tourStepKey: null,
hoveredComponentId: null, hoveredComponentId: null,
fonts: null,
} }
export class BuilderStore extends BudiStore { export class BuilderStore extends BudiStore {
@ -36,6 +37,16 @@ export class BuilderStore extends BudiStore {
this.websocket this.websocket
} }
loadFonts(fontFaces) {
const ff = fontFaces.map(
fontFace => `${fontFace.family}-${fontFace.weight}`
)
this.update(state => ({
...state,
fonts: [...(state.fonts || []), ...ff],
}))
}
init(app) { init(app) {
if (!app?.appId) { if (!app?.appId) {
console.error("BuilderStore: No appId supplied for websocket") console.error("BuilderStore: No appId supplied for websocket")

View File

@ -7017,10 +7017,22 @@
] ]
} }
], ],
"context": { "context": [
"type": "schema", {
"scope": "local" "type": "schema",
}, "scope": "local"
},
{
"type": "static",
"values": [
{
"label": "Selected rows",
"key": "selectedRows",
"type": "array"
}
]
}
],
"actions": ["RefreshDatasource"] "actions": ["RefreshDatasource"]
}, },
"bbreferencefield": { "bbreferencefield": {

View File

@ -1,8 +1,8 @@
<script> <script>
// NOTE: this is not a block - it's just named as such to avoid confusing users, // NOTE: this is not a block - it's just named as such to avoid confusing users,
// because it functions similarly to one // because it functions similarly to one
import { getContext } from "svelte" import { getContext, onMount } from "svelte"
import { get } from "svelte/store" import { get, derived, readable } from "svelte/store"
import { Grid } from "@budibase/frontend-core" import { Grid } from "@budibase/frontend-core"
// table is actually any datasource, but called table for legacy compatibility // table is actually any datasource, but called table for legacy compatibility
@ -19,7 +19,6 @@
export let columns = null export let columns = null
export let onRowClick = null export let onRowClick = null
export let buttons = null export let buttons = null
export let repeat = null
const context = getContext("context") const context = getContext("context")
const component = getContext("component") const component = getContext("component")
@ -36,17 +35,18 @@
} = getContext("sdk") } = getContext("sdk")
let grid let grid
let gridContext
$: columnWhitelist = parsedColumns $: parsedColumns = getParsedColumns(columns)
?.filter(col => col.active) $: columnWhitelist = parsedColumns.filter(x => x.active).map(x => x.field)
?.map(col => col.field)
$: schemaOverrides = getSchemaOverrides(parsedColumns) $: schemaOverrides = getSchemaOverrides(parsedColumns)
$: enrichedButtons = enrichButtons(buttons) $: enrichedButtons = enrichButtons(buttons)
$: parsedColumns = getParsedColumns(columns) $: selectedRows = deriveSelectedRows(gridContext)
$: data = { selectedRows: $selectedRows }
$: actions = [ $: actions = [
{ {
type: ActionTypes.RefreshDatasource, type: ActionTypes.RefreshDatasource,
callback: () => grid?.getContext()?.rows.actions.refreshData(), callback: () => gridContext?.rows.actions.refreshData(),
metadata: { dataSource: table }, metadata: { dataSource: table },
}, },
] ]
@ -68,12 +68,14 @@
// Parses columns to fix older formats // Parses columns to fix older formats
const getParsedColumns = columns => { const getParsedColumns = columns => {
if (!columns?.length) {
return []
}
// If the first element has an active key all elements should be in the new format // If the first element has an active key all elements should be in the new format
if (columns?.length && columns[0]?.active !== undefined) { if (columns[0].active !== undefined) {
return columns return columns
} }
return columns.map(column => ({
return columns?.map(column => ({
label: column.displayName || column.name, label: column.displayName || column.name,
field: column.name, field: column.name,
active: true, active: true,
@ -82,7 +84,7 @@
const getSchemaOverrides = columns => { const getSchemaOverrides = columns => {
let overrides = {} let overrides = {}
columns?.forEach(column => { columns.forEach(column => {
overrides[column.field] = { overrides[column.field] = {
displayName: column.label, displayName: column.label,
} }
@ -109,6 +111,23 @@
})) }))
} }
const deriveSelectedRows = gridContext => {
if (!gridContext) {
return readable([])
}
return derived(
[gridContext.selectedRows, gridContext.rowLookupMap, gridContext.rows],
([$selectedRows, $rowLookupMap, $rows]) => {
return Object.entries($selectedRows || {})
.filter(([_, selected]) => selected)
.map(([rowId]) => {
const idx = $rowLookupMap[rowId]
return gridContext.rows.actions.cleanRow($rows[idx])
})
}
)
}
const getSanitisedStyles = styles => { const getSanitisedStyles = styles => {
return { return {
...styles, ...styles,
@ -118,40 +137,44 @@
}, },
} }
} }
onMount(() => {
gridContext = grid.getContext()
})
</script> </script>
<div use:styleable={styles} class:in-builder={$builderStore.inBuilder}> <div use:styleable={styles} class:in-builder={$builderStore.inBuilder}>
<span style="--height:{height};"> <span style="--height:{height};">
<Provider {actions}> <Grid
<Grid bind:this={grid}
bind:this={grid} datasource={table}
datasource={table} {API}
{API} {stripeRows}
{stripeRows} {quiet}
{quiet} {initialFilter}
{initialFilter} {initialSortColumn}
{initialSortColumn} {initialSortOrder}
{initialSortOrder} {fixedRowHeight}
{fixedRowHeight} {columnWhitelist}
{columnWhitelist} {schemaOverrides}
{schemaOverrides} canAddRows={allowAddRows}
{repeat} canEditRows={allowEditRows}
canAddRows={allowAddRows} canDeleteRows={allowDeleteRows}
canEditRows={allowEditRows} canEditColumns={false}
canDeleteRows={allowDeleteRows} canExpandRows={false}
canEditColumns={false} canSaveSchema={false}
canExpandRows={false} canSelectRows={true}
canSaveSchema={false} showControls={false}
showControls={false} notifySuccess={notificationStore.actions.success}
notifySuccess={notificationStore.actions.success} notifyError={notificationStore.actions.error}
notifyError={notificationStore.actions.error} buttons={enrichedButtons}
buttons={enrichedButtons} on:rowclick={e => onRowClick?.({ row: e.detail })}
on:rowclick={e => onRowClick?.({ row: e.detail })} />
/>
</Provider>
</span> </span>
</div> </div>
<Provider {data} {actions} />
<style> <style>
div { div {
display: flex; display: flex;

View File

@ -15,7 +15,7 @@ const createRowSelectionStore = () => {
const componentId = Object.keys(selection).find( const componentId = Object.keys(selection).find(
componentId => componentId === tableComponentId componentId => componentId === tableComponentId
) )
return selection[componentId] || {} return selection[componentId]
} }
return { return {

View File

@ -333,31 +333,59 @@ const s3UploadHandler = async action => {
} }
} }
/**
* For new configs, "rows" is defined and enriched to be the array of rows to
* export. For old configs it will be undefined and we need to use the legacy
* row selection store in combination with the tableComponentId parameter.
*/
const exportDataHandler = async action => { const exportDataHandler = async action => {
let selection = rowSelectionStore.actions.getSelection( let { tableComponentId, rows, type, columns, delimiter, customHeaders } =
action.parameters.tableComponentId action.parameters
) let tableId
if (selection.selectedRows && selection.selectedRows.length > 0) {
// Handle legacy configs using the row selection store
if (!rows?.length) {
const selection = rowSelectionStore.actions.getSelection(tableComponentId)
if (selection?.selectedRows?.length) {
rows = selection.selectedRows
tableId = selection.tableId
}
}
// Get table ID from first row if needed
if (!tableId) {
tableId = rows?.[0]?.tableId
}
// Handle no rows selected
if (!rows?.length) {
notificationStore.actions.error("Please select at least one row")
}
// Handle case where we're not using a DS+
else if (!tableId) {
notificationStore.actions.error(
"You can only export data from table datasources"
)
}
// Happy path when we have both rows and table ID
else {
try { try {
// Flatten rows if required
if (typeof rows[0] !== "string") {
rows = rows.map(row => row._id)
}
const data = await API.exportRows({ const data = await API.exportRows({
tableId: selection.tableId, tableId,
rows: selection.selectedRows, rows,
format: action.parameters.type, format: type,
columns: action.parameters.columns?.map( columns: columns?.map(column => column.name || column),
column => column.name || column delimiter,
), customHeaders,
delimiter: action.parameters.delimiter,
customHeaders: action.parameters.customHeaders,
}) })
download( download(new Blob([data], { type: "text/plain" }), `${tableId}.${type}`)
new Blob([data], { type: "text/plain" }),
`${selection.tableId}.${action.parameters.type}`
)
} catch (error) { } catch (error) {
notificationStore.actions.error("There was an error exporting the data") notificationStore.actions.error("There was an error exporting the data")
} }
} else {
notificationStore.actions.error("Please select at least one row")
} }
} }

View File

@ -16,6 +16,8 @@
const { config, dispatch, selectedRows } = getContext("grid") const { config, dispatch, selectedRows } = getContext("grid")
const svelteDispatch = createEventDispatcher() const svelteDispatch = createEventDispatcher()
$: selectionEnabled = $config.canSelectRows || $config.canDeleteRows
const select = e => { const select = e => {
e.stopPropagation() e.stopPropagation()
svelteDispatch("select") svelteDispatch("select")
@ -52,7 +54,7 @@
<div <div
on:click={select} on:click={select}
class="checkbox" class="checkbox"
class:visible={$config.canDeleteRows && class:visible={selectionEnabled &&
(disableNumber || rowSelected || rowHovered || rowFocused)} (disableNumber || rowSelected || rowHovered || rowFocused)}
> >
<Checkbox value={rowSelected} {disabled} /> <Checkbox value={rowSelected} {disabled} />
@ -60,7 +62,7 @@
{#if !disableNumber} {#if !disableNumber}
<div <div
class="number" class="number"
class:visible={!$config.canDeleteRows || class:visible={!selectionEnabled ||
!(rowSelected || rowHovered || rowFocused)} !(rowSelected || rowHovered || rowFocused)}
> >
{row.__idx + 1} {row.__idx + 1}
@ -117,19 +119,11 @@
.expand { .expand {
margin-right: 4px; margin-right: 4px;
} }
.expand { .expand:not(.visible),
.expand:not(.visible) :global(*) {
opacity: 0; opacity: 0;
pointer-events: none !important;
} }
.expand :global(.spectrum-Icon) {
pointer-events: none;
}
.expand.visible {
opacity: 1;
}
.expand.visible :global(.spectrum-Icon) {
pointer-events: all;
}
.delete:hover { .delete:hover {
cursor: pointer; cursor: pointer;
} }

View File

@ -41,6 +41,7 @@
export let canDeleteRows = true export let canDeleteRows = true
export let canEditColumns = true export let canEditColumns = true
export let canSaveSchema = true export let canSaveSchema = true
export let canSelectRows = false
export let stripeRows = false export let stripeRows = false
export let quiet = false export let quiet = false
export let collaboration = true export let collaboration = true
@ -94,6 +95,7 @@
canDeleteRows, canDeleteRows,
canEditColumns, canEditColumns,
canSaveSchema, canSaveSchema,
canSelectRows,
stripeRows, stripeRows,
quiet, quiet,
collaboration, collaboration,

View File

@ -5,11 +5,11 @@ import { TypeIconMap } from "../../../constants"
// using something very unusual to avoid this problem // using something very unusual to avoid this problem
const JOINING_CHARACTER = "‽‽" const JOINING_CHARACTER = "‽‽"
export const parseCellID = rowId => { export const parseCellID = cellId => {
if (!rowId) { if (!cellId) {
return undefined return { id: undefined, field: undefined }
} }
const parts = rowId.split(JOINING_CHARACTER) const parts = cellId.split(JOINING_CHARACTER)
const field = parts.pop() const field = parts.pop()
return { id: parts.join(JOINING_CHARACTER), field } return { id: parts.join(JOINING_CHARACTER), field }
} }

View File

@ -110,12 +110,11 @@ export const deriveStores = context => {
} }
export const createActions = context => { export const createActions = context => {
const { focusedCellId, selectedRows, hoveredRowId } = context const { focusedCellId, hoveredRowId } = context
// Callback when leaving the grid, deselecting all focussed or selected items // Callback when leaving the grid, deselecting all focussed or selected items
const blur = () => { const blur = () => {
focusedCellId.set(null) focusedCellId.set(null)
selectedRows.set({})
hoveredRowId.set(null) hoveredRowId.set(null)
} }

View File

@ -79,8 +79,7 @@
"dotenv": "8.2.0", "dotenv": "8.2.0",
"form-data": "4.0.0", "form-data": "4.0.0",
"global-agent": "3.0.0", "global-agent": "3.0.0",
"google-auth-library": "7.12.0", "google-spreadsheet": "4.1.2",
"google-spreadsheet": "3.2.0",
"ioredis": "5.3.2", "ioredis": "5.3.2",
"isolated-vm": "^4.7.2", "isolated-vm": "^4.7.2",
"jimp": "0.22.10", "jimp": "0.22.10",
@ -125,7 +124,6 @@
"@swc/jest": "0.2.27", "@swc/jest": "0.2.27",
"@types/archiver": "6.0.2", "@types/archiver": "6.0.2",
"@types/global-agent": "2.1.1", "@types/global-agent": "2.1.1",
"@types/google-spreadsheet": "3.1.5",
"@types/jest": "29.5.5", "@types/jest": "29.5.5",
"@types/koa": "2.13.4", "@types/koa": "2.13.4",
"@types/koa-send": "^4.1.6", "@types/koa-send": "^4.1.6",

View File

@ -279,8 +279,7 @@ export async function trigger(ctx: UserCtx) {
{ {
fields: ctx.request.body.fields, fields: ctx.request.body.fields,
timeout: timeout:
ctx.request.body.timeout * 1000 || ctx.request.body.timeout * 1000 || env.AUTOMATION_THREAD_TIMEOUT,
env.getDefaults().AUTOMATION_SYNC_TIMEOUT,
}, },
{ getResponses: true } { getResponses: true }
) )

View File

@ -163,7 +163,10 @@ async function generateAttachmentRow(attachment: AutomationAttachment) {
try { try {
const { filename } = attachment const { filename } = attachment
const extension = path.extname(filename) let extension = path.extname(filename)
if (extension.startsWith(".")) {
extension = extension.substring(1, extension.length)
}
const attachmentResult = await objectStore.processAutomationAttachment( const attachmentResult = await objectStore.processAutomationAttachment(
attachment attachment
) )
@ -182,8 +185,8 @@ async function generateAttachmentRow(attachment: AutomationAttachment) {
return { return {
size, size,
name: filename,
extension, extension,
name: filename,
key: s3Key, key: s3Key,
} }
} catch (error) { } catch (error) {

View File

@ -94,18 +94,6 @@ export async function run({ inputs, appId, emitter }: AutomationStepInput) {
} }
} }
// have to clean up the row, remove the table from it
const ctx: any = buildCtx(appId, emitter, {
body: {
...inputs.row,
_id: inputs.rowId,
},
params: {
rowId: inputs.rowId,
tableId: tableId,
},
})
try { try {
if (tableId) { if (tableId) {
inputs.row = await automationUtils.cleanUpRow( inputs.row = await automationUtils.cleanUpRow(
@ -118,6 +106,17 @@ export async function run({ inputs, appId, emitter }: AutomationStepInput) {
inputs.row inputs.row
) )
} }
// have to clean up the row, remove the table from it
const ctx: any = buildCtx(appId, emitter, {
body: {
...inputs.row,
_id: inputs.rowId,
},
params: {
rowId: inputs.rowId,
tableId: tableId,
},
})
await rowController.patch(ctx) await rowController.patch(ctx)
return { return {
row: ctx.body, row: ctx.body,

View File

@ -20,7 +20,7 @@ function parseIntSafe(number?: string) {
const DEFAULTS = { const DEFAULTS = {
QUERY_THREAD_TIMEOUT: 15000, QUERY_THREAD_TIMEOUT: 15000,
AUTOMATION_THREAD_TIMEOUT: 12000, AUTOMATION_THREAD_TIMEOUT: 15000,
AUTOMATION_SYNC_TIMEOUT: 120000, AUTOMATION_SYNC_TIMEOUT: 120000,
AUTOMATION_MAX_ITERATIONS: 200, AUTOMATION_MAX_ITERATIONS: 200,
JS_PER_EXECUTION_TIME_LIMIT_MS: 1500, JS_PER_EXECUTION_TIME_LIMIT_MS: 1500,
@ -34,6 +34,10 @@ const DEFAULTS = {
const QUERY_THREAD_TIMEOUT = const QUERY_THREAD_TIMEOUT =
parseIntSafe(process.env.QUERY_THREAD_TIMEOUT) || parseIntSafe(process.env.QUERY_THREAD_TIMEOUT) ||
DEFAULTS.QUERY_THREAD_TIMEOUT DEFAULTS.QUERY_THREAD_TIMEOUT
const DEFAULT_AUTOMATION_TIMEOUT =
QUERY_THREAD_TIMEOUT > DEFAULTS.AUTOMATION_THREAD_TIMEOUT
? QUERY_THREAD_TIMEOUT
: DEFAULTS.AUTOMATION_THREAD_TIMEOUT
const environment = { const environment = {
// features // features
APP_FEATURES: process.env.APP_FEATURES, APP_FEATURES: process.env.APP_FEATURES,
@ -75,9 +79,7 @@ const environment = {
QUERY_THREAD_TIMEOUT: QUERY_THREAD_TIMEOUT, QUERY_THREAD_TIMEOUT: QUERY_THREAD_TIMEOUT,
AUTOMATION_THREAD_TIMEOUT: AUTOMATION_THREAD_TIMEOUT:
parseIntSafe(process.env.AUTOMATION_THREAD_TIMEOUT) || parseIntSafe(process.env.AUTOMATION_THREAD_TIMEOUT) ||
DEFAULTS.AUTOMATION_THREAD_TIMEOUT > QUERY_THREAD_TIMEOUT DEFAULT_AUTOMATION_TIMEOUT,
? DEFAULTS.AUTOMATION_THREAD_TIMEOUT
: QUERY_THREAD_TIMEOUT,
BB_ADMIN_USER_EMAIL: process.env.BB_ADMIN_USER_EMAIL, BB_ADMIN_USER_EMAIL: process.env.BB_ADMIN_USER_EMAIL,
BB_ADMIN_USER_PASSWORD: process.env.BB_ADMIN_USER_PASSWORD, BB_ADMIN_USER_PASSWORD: process.env.BB_ADMIN_USER_PASSWORD,
PLUGINS_DIR: process.env.PLUGINS_DIR || DEFAULTS.PLUGINS_DIR, PLUGINS_DIR: process.env.PLUGINS_DIR || DEFAULTS.PLUGINS_DIR,

View File

@ -158,12 +158,12 @@ const SCHEMA: Integration = {
class GoogleSheetsIntegration implements DatasourcePlus { class GoogleSheetsIntegration implements DatasourcePlus {
private readonly config: GoogleSheetsConfig private readonly config: GoogleSheetsConfig
private client: GoogleSpreadsheet private readonly spreadsheetId: string
private client: GoogleSpreadsheet = undefined!
constructor(config: GoogleSheetsConfig) { constructor(config: GoogleSheetsConfig) {
this.config = config this.config = config
const spreadsheetId = this.cleanSpreadsheetUrl(this.config.spreadsheetId) this.spreadsheetId = this.cleanSpreadsheetUrl(this.config.spreadsheetId)
this.client = new GoogleSpreadsheet(spreadsheetId)
} }
async testConnection(): Promise<ConnectionInfo> { async testConnection(): Promise<ConnectionInfo> {
@ -191,7 +191,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
* @param spreadsheetId - the URL or standard spreadsheetId of the google sheet * @param spreadsheetId - the URL or standard spreadsheetId of the google sheet
* @returns spreadsheet Id of the google sheet * @returns spreadsheet Id of the google sheet
*/ */
cleanSpreadsheetUrl(spreadsheetId: string) { private cleanSpreadsheetUrl(spreadsheetId: string) {
if (!spreadsheetId) { if (!spreadsheetId) {
throw new Error( throw new Error(
"You must set a spreadsheet ID in your configuration to fetch tables." "You must set a spreadsheet ID in your configuration to fetch tables."
@ -201,7 +201,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
return parts.length > 5 ? parts[5] : spreadsheetId return parts.length > 5 ? parts[5] : spreadsheetId
} }
async fetchAccessToken( private async fetchAccessToken(
payload: AuthTokenRequest payload: AuthTokenRequest
): Promise<AuthTokenResponse> { ): Promise<AuthTokenResponse> {
const response = await fetch("https://www.googleapis.com/oauth2/v4/token", { const response = await fetch("https://www.googleapis.com/oauth2/v4/token", {
@ -226,7 +226,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
return json return json
} }
async connect() { private async connect() {
try { try {
await setupCreationAuth(this.config) await setupCreationAuth(this.config)
@ -252,7 +252,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
access_token: tokenResponse.access_token, access_token: tokenResponse.access_token,
}) })
this.client.useOAuth2Client(oauthClient) this.client = new GoogleSpreadsheet(this.spreadsheetId, oauthClient)
await this.client.loadInfo() await this.client.loadInfo()
} catch (err: any) { } catch (err: any) {
// this happens for xlsx imports // this happens for xlsx imports
@ -271,7 +271,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
return sheets.map(s => s.title) return sheets.map(s => s.title)
} }
getTableSchema( private getTableSchema(
title: string, title: string,
headerValues: string[], headerValues: string[],
datasourceId: string, datasourceId: string,
@ -385,18 +385,22 @@ class GoogleSheetsIntegration implements DatasourcePlus {
} }
} }
buildRowObject(headers: string[], values: string[], rowNumber: number) { private buildRowObject(
headers: string[],
values: Record<string, string>,
rowNumber: number
) {
const rowObject: { rowNumber: number } & Row = { const rowObject: { rowNumber: number } & Row = {
rowNumber, rowNumber,
_id: rowNumber.toString(), _id: rowNumber.toString(),
} }
for (let i = 0; i < headers.length; i++) { for (let i = 0; i < headers.length; i++) {
rowObject[headers[i]] = values[i] rowObject[headers[i]] = values[headers[i]]
} }
return rowObject return rowObject
} }
async createTable(name?: string) { private async createTable(name?: string) {
if (!name) { if (!name) {
throw new Error("Must provide name for new sheet.") throw new Error("Must provide name for new sheet.")
} }
@ -409,7 +413,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
} }
} }
async updateTable(table: TableRequest) { private async updateTable(table: TableRequest) {
await this.connect() await this.connect()
const sheet = this.client.sheetsByTitle[table.name] const sheet = this.client.sheetsByTitle[table.name]
await sheet.loadHeaderRow() await sheet.loadHeaderRow()
@ -456,7 +460,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
} }
} }
async deleteTable(sheet: any) { private async deleteTable(sheet: any) {
try { try {
await this.connect() await this.connect()
const sheetToDelete = this.client.sheetsByTitle[sheet] const sheetToDelete = this.client.sheetsByTitle[sheet]
@ -475,7 +479,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
typeof query.row === "string" ? JSON.parse(query.row) : query.row typeof query.row === "string" ? JSON.parse(query.row) : query.row
const row = await sheet.addRow(rowToInsert) const row = await sheet.addRow(rowToInsert)
return [ return [
this.buildRowObject(sheet.headerValues, row._rawData, row._rowNumber), this.buildRowObject(sheet.headerValues, row.toObject(), row.rowNumber),
] ]
} catch (err) { } catch (err) {
console.error("Error writing to google sheets", err) console.error("Error writing to google sheets", err)
@ -483,7 +487,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
} }
} }
async createBulk(query: { sheet: string; rows: Row[] }) { private async createBulk(query: { sheet: string; rows: Row[] }) {
try { try {
await this.connect() await this.connect()
const sheet = this.client.sheetsByTitle[query.sheet] const sheet = this.client.sheetsByTitle[query.sheet]
@ -493,7 +497,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
} }
const rows = await sheet.addRows(rowsToInsert) const rows = await sheet.addRows(rowsToInsert)
return rows.map(row => return rows.map(row =>
this.buildRowObject(sheet.headerValues, row._rawData, row._rowNumber) this.buildRowObject(sheet.headerValues, row.toObject(), row.rowNumber)
) )
} catch (err) { } catch (err) {
console.error("Error bulk writing to google sheets", err) console.error("Error bulk writing to google sheets", err)
@ -548,7 +552,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
let response = [] let response = []
for (let row of filtered) { for (let row of filtered) {
response.push( response.push(
this.buildRowObject(headerValues, row._rawData, row._rowNumber) this.buildRowObject(headerValues, row.toObject(), row._rowNumber)
) )
} }
@ -598,10 +602,10 @@ class GoogleSheetsIntegration implements DatasourcePlus {
const updateValues = const updateValues =
typeof query.row === "string" ? JSON.parse(query.row) : query.row typeof query.row === "string" ? JSON.parse(query.row) : query.row
for (let key in updateValues) { for (let key in updateValues) {
row[key] = updateValues[key] row.set(key, updateValues[key])
if (row[key] === null) { if (row.get(key) === null) {
row[key] = "" row.set(key, "")
} }
const { type, subtype, constraints } = query.table.schema[key] const { type, subtype, constraints } = query.table.schema[key]
@ -609,13 +613,17 @@ class GoogleSheetsIntegration implements DatasourcePlus {
type === FieldType.BB_REFERENCE && type === FieldType.BB_REFERENCE &&
subtype === BBReferenceFieldSubType.USER && subtype === BBReferenceFieldSubType.USER &&
constraints?.type !== "array" constraints?.type !== "array"
if (isDeprecatedSingleUser && Array.isArray(row[key])) { if (isDeprecatedSingleUser && Array.isArray(row.get(key))) {
row[key] = row[key][0] row.set(key, row.get(key)[0])
} }
} }
await row.save() await row.save()
return [ return [
this.buildRowObject(sheet.headerValues, row._rawData, row._rowNumber), this.buildRowObject(
sheet.headerValues,
row.toObject(),
row.rowNumber
),
] ]
} else { } else {
throw new Error("Row does not exist.") throw new Error("Row does not exist.")

View File

@ -1,6 +1,12 @@
import { ObjectStoreBuckets } from "../../constants" import { ObjectStoreBuckets } from "../../constants"
import { context, db as dbCore, objectStore } from "@budibase/backend-core" import { context, db as dbCore, objectStore } from "@budibase/backend-core"
import { FieldType, RenameColumn, Row, Table } from "@budibase/types" import {
FieldType,
RenameColumn,
Row,
RowAttachment,
Table,
} from "@budibase/types"
export class AttachmentCleanup { export class AttachmentCleanup {
static async coreCleanup(fileListFn: () => string[]): Promise<void> { static async coreCleanup(fileListFn: () => string[]): Promise<void> {
@ -21,7 +27,7 @@ export class AttachmentCleanup {
private static extractAttachmentKeys( private static extractAttachmentKeys(
type: FieldType, type: FieldType,
rowData: any rowData: RowAttachment[] | RowAttachment
): string[] { ): string[] {
if ( if (
type !== FieldType.ATTACHMENTS && type !== FieldType.ATTACHMENTS &&
@ -34,10 +40,15 @@ export class AttachmentCleanup {
return [] return []
} }
if (type === FieldType.ATTACHMENTS) { if (type === FieldType.ATTACHMENTS && Array.isArray(rowData)) {
return rowData.map((attachment: any) => attachment.key) return rowData
.filter(attachment => attachment.key)
.map(attachment => attachment.key)
} else if ("key" in rowData) {
return [rowData.key]
} }
return [rowData.key]
return []
} }
private static async tableChange( private static async tableChange(

View File

@ -1,11 +1,11 @@
import * as linkRows from "../../db/linkedRows" import * as linkRows from "../../db/linkedRows"
import { processFormulas, fixAutoColumnSubType } from "./utils" import { fixAutoColumnSubType, processFormulas } from "./utils"
import { objectStore, utils } from "@budibase/backend-core" import { objectStore, utils } from "@budibase/backend-core"
import { InternalTables } from "../../db/utils" import { InternalTables } from "../../db/utils"
import { TYPE_TRANSFORM_MAP } from "./map" import { TYPE_TRANSFORM_MAP } from "./map"
import { import {
FieldType,
AutoFieldSubType, AutoFieldSubType,
FieldType,
Row, Row,
RowAttachment, RowAttachment,
Table, Table,
@ -221,27 +221,31 @@ export async function outputProcessing<T extends Row[] | Row>(
opts.squash = true opts.squash = true
} }
// process complex types: attachements, bb references... // process complex types: attachments, bb references...
for (let [property, column] of Object.entries(table.schema)) { for (let [property, column] of Object.entries(table.schema)) {
if (column.type === FieldType.ATTACHMENTS) { if (
column.type === FieldType.ATTACHMENTS ||
column.type === FieldType.ATTACHMENT_SINGLE
) {
for (let row of enriched) { for (let row of enriched) {
if (row[property] == null || !Array.isArray(row[property])) { if (row[property] == null) {
continue continue
} }
row[property].forEach((attachment: RowAttachment) => { const process = (attachment: RowAttachment) => {
if (!attachment.url) { if (!attachment.url && attachment.key) {
attachment.url = objectStore.getAppFileUrl(attachment.key) attachment.url = objectStore.getAppFileUrl(attachment.key)
} }
}) return attachment
}
} else if (column.type === FieldType.ATTACHMENT_SINGLE) {
for (let row of enriched) {
if (!row[property] || Object.keys(row[property]).length === 0) {
continue
} }
if (typeof row[property] === "string") {
if (!row[property].url) { row[property] = JSON.parse(row[property])
row[property].url = objectStore.getAppFileUrl(row[property].key) }
if (Array.isArray(row[property])) {
row[property].forEach((attachment: RowAttachment) => {
process(attachment)
})
} else {
process(row[property])
} }
} }
} else if ( } else if (

View File

@ -5272,11 +5272,6 @@
resolved "https://registry.yarnpkg.com/@types/global-agent/-/global-agent-2.1.1.tgz#3f93185e48a3a36e377a52a8301320cd162a831b" resolved "https://registry.yarnpkg.com/@types/global-agent/-/global-agent-2.1.1.tgz#3f93185e48a3a36e377a52a8301320cd162a831b"
integrity sha512-sVox8Phk1UKgP6LQPAdeRxfww6vHKt7Bf59dXzYLsQBUEMEn8S10a+ESp/yO0i4fJ3WS4+CIuz42hgJcuA+3mA== integrity sha512-sVox8Phk1UKgP6LQPAdeRxfww6vHKt7Bf59dXzYLsQBUEMEn8S10a+ESp/yO0i4fJ3WS4+CIuz42hgJcuA+3mA==
"@types/google-spreadsheet@3.1.5":
version "3.1.5"
resolved "https://registry.yarnpkg.com/@types/google-spreadsheet/-/google-spreadsheet-3.1.5.tgz#2bdc6f9f5372551e0506cb6ef3f562adcf44fc2e"
integrity sha512-7N+mDtZ1pmya2RRFPPl4KYc2TRgiqCNBLUZfyrKfER+u751JgCO+C24/LzF70UmUm/zhHUbzRZ5mtfaxekQ1ZQ==
"@types/graceful-fs@^4.1.3": "@types/graceful-fs@^4.1.3":
version "4.1.6" version "4.1.6"
resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae"
@ -6983,7 +6978,7 @@ axios-retry@^3.1.9:
"@babel/runtime" "^7.15.4" "@babel/runtime" "^7.15.4"
is-retry-allowed "^2.2.0" is-retry-allowed "^2.2.0"
axios@0.24.0, axios@1.1.3, axios@1.6.3, axios@^0.21.1, axios@^0.21.4, axios@^0.26.0, axios@^1.0.0, axios@^1.1.3, axios@^1.5.0: axios@0.24.0, axios@1.1.3, axios@1.6.3, axios@^0.21.1, axios@^0.26.0, axios@^1.0.0, axios@^1.1.3, axios@^1.4.0, axios@^1.5.0:
version "1.6.3" version "1.6.3"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.3.tgz#7f50f23b3aa246eff43c54834272346c396613f4" resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.3.tgz#7f50f23b3aa246eff43c54834272346c396613f4"
integrity sha512-fWyNdeawGam70jXSVlKl+SUNVcL6j6W79CuSIPfi6HnDUmSCH6gyUys/HrqHeA/wU0Az41rRgean494d0Jb+ww== integrity sha512-fWyNdeawGam70jXSVlKl+SUNVcL6j6W79CuSIPfi6HnDUmSCH6gyUys/HrqHeA/wU0Az41rRgean494d0Jb+ww==
@ -11071,17 +11066,6 @@ gauge@^4.0.3:
strip-ansi "^6.0.1" strip-ansi "^6.0.1"
wide-align "^1.1.5" wide-align "^1.1.5"
gaxios@^4.0.0:
version "4.3.3"
resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-4.3.3.tgz#d44bdefe52d34b6435cc41214fdb160b64abfc22"
integrity sha512-gSaYYIO1Y3wUtdfHmjDUZ8LWaxJQpiavzbF5Kq53akSzvmVg0RfyOcFDbO1KJ/KCGRFz2qG+lS81F0nkr7cRJA==
dependencies:
abort-controller "^3.0.0"
extend "^3.0.2"
https-proxy-agent "^5.0.0"
is-stream "^2.0.0"
node-fetch "^2.6.7"
gaxios@^5.0.0, gaxios@^5.0.1: gaxios@^5.0.0, gaxios@^5.0.1:
version "5.1.3" version "5.1.3"
resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-5.1.3.tgz#f7fa92da0fe197c846441e5ead2573d4979e9013" resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-5.1.3.tgz#f7fa92da0fe197c846441e5ead2573d4979e9013"
@ -11092,14 +11076,6 @@ gaxios@^5.0.0, gaxios@^5.0.1:
is-stream "^2.0.0" is-stream "^2.0.0"
node-fetch "^2.6.9" node-fetch "^2.6.9"
gcp-metadata@^4.2.0:
version "4.3.1"
resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-4.3.1.tgz#fb205fe6a90fef2fd9c85e6ba06e5559ee1eefa9"
integrity sha512-x850LS5N7V1F3UcV7PoupzGsyD6iVwTVvsh3tbXfkctZnBnjW5yu5z1/3k3SehF7TyoTIe78rJs02GMMy+LF+A==
dependencies:
gaxios "^4.0.0"
json-bigint "^1.0.0"
gcp-metadata@^5.3.0: gcp-metadata@^5.3.0:
version "5.3.0" version "5.3.0"
resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-5.3.0.tgz#6f45eb473d0cb47d15001476b48b663744d25408" resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-5.3.0.tgz#6f45eb473d0cb47d15001476b48b663744d25408"
@ -11506,36 +11482,6 @@ gonzales-pe@^4.2.3, gonzales-pe@^4.3.0:
dependencies: dependencies:
minimist "^1.2.5" minimist "^1.2.5"
google-auth-library@7.12.0:
version "7.12.0"
resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-7.12.0.tgz#7965db6bc20cb31f2df05a08a296bbed6af69426"
integrity sha512-RS/whvFPMoF1hQNxnoVET3DWKPBt1Xgqe2rY0k+Jn7TNhoHlwdnSe7Rlcbo2Nub3Mt2lUVz26X65aDQrWp6x8w==
dependencies:
arrify "^2.0.0"
base64-js "^1.3.0"
ecdsa-sig-formatter "^1.0.11"
fast-text-encoding "^1.0.0"
gaxios "^4.0.0"
gcp-metadata "^4.2.0"
gtoken "^5.0.4"
jws "^4.0.0"
lru-cache "^6.0.0"
google-auth-library@^6.1.3:
version "6.1.6"
resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-6.1.6.tgz#deacdcdb883d9ed6bac78bb5d79a078877fdf572"
integrity sha512-Q+ZjUEvLQj/lrVHF/IQwRo6p3s8Nc44Zk/DALsN+ac3T4HY/g/3rrufkgtl+nZ1TW7DNAw5cTChdVp4apUXVgQ==
dependencies:
arrify "^2.0.0"
base64-js "^1.3.0"
ecdsa-sig-formatter "^1.0.11"
fast-text-encoding "^1.0.0"
gaxios "^4.0.0"
gcp-metadata "^4.2.0"
gtoken "^5.0.4"
jws "^4.0.0"
lru-cache "^6.0.0"
google-auth-library@^8.0.1, google-auth-library@^8.0.2: google-auth-library@^8.0.1, google-auth-library@^8.0.2:
version "8.9.0" version "8.9.0"
resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-8.9.0.tgz#15a271eb2ec35d43b81deb72211bd61b1ef14dd0" resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-8.9.0.tgz#15a271eb2ec35d43b81deb72211bd61b1ef14dd0"
@ -11572,13 +11518,6 @@ google-gax@^3.5.7:
protobufjs-cli "1.1.1" protobufjs-cli "1.1.1"
retry-request "^5.0.0" retry-request "^5.0.0"
google-p12-pem@^3.1.3:
version "3.1.4"
resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-3.1.4.tgz#123f7b40da204de4ed1fbf2fd5be12c047fc8b3b"
integrity sha512-HHuHmkLgwjdmVRngf5+gSmpkyaRI6QmOg77J8tkNBHhNEI62sGHyw4/+UkgyZEI7h84NbWprXDJ+sa3xOYFvTg==
dependencies:
node-forge "^1.3.1"
google-p12-pem@^4.0.0: google-p12-pem@^4.0.0:
version "4.0.1" version "4.0.1"
resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-4.0.1.tgz#82841798253c65b7dc2a4e5fe9df141db670172a" resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-4.0.1.tgz#82841798253c65b7dc2a4e5fe9df141db670172a"
@ -11586,13 +11525,12 @@ google-p12-pem@^4.0.0:
dependencies: dependencies:
node-forge "^1.3.1" node-forge "^1.3.1"
google-spreadsheet@3.2.0: google-spreadsheet@4.1.2:
version "3.2.0" version "4.1.2"
resolved "https://registry.yarnpkg.com/google-spreadsheet/-/google-spreadsheet-3.2.0.tgz#ce8aa75c15705aa950ad52b091a6fc4d33dcb329" resolved "https://registry.yarnpkg.com/google-spreadsheet/-/google-spreadsheet-4.1.2.tgz#92e30fdba7e0d78c55d50731528df7835d58bfee"
integrity sha512-z7XMaqb+26rdo8p51r5O03u8aPLAPzn5YhOXYJPcf2hdMVr0dUbIARgdkRdmGiBeoV/QoU/7VNhq1MMCLZv3kQ== integrity sha512-HFBweDAkOcyC2qO9kmWESKbNuOcn+R7UzZN/tj5LLNxVv8FHmg113u0Ow+yaKwwIOt/NnDtPLuptAhaxTs0FYw==
dependencies: dependencies:
axios "^0.21.4" axios "^1.4.0"
google-auth-library "^6.1.3"
lodash "^4.17.21" lodash "^4.17.21"
gopd@^1.0.1: gopd@^1.0.1:
@ -11678,15 +11616,6 @@ graphemer@^1.4.0:
resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
gtoken@^5.0.4:
version "5.3.2"
resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-5.3.2.tgz#deb7dc876abe002178e0515e383382ea9446d58f"
integrity sha512-gkvEKREW7dXWF8NV8pVrKfW7WqReAmjjkMBh6lNCCGOM4ucS0r0YyXXl0r/9Yj8wcW/32ISkfc8h5mPTDbtifQ==
dependencies:
gaxios "^4.0.0"
google-p12-pem "^3.1.3"
jws "^4.0.0"
gtoken@^6.1.0: gtoken@^6.1.0:
version "6.1.2" version "6.1.2"
resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-6.1.2.tgz#aeb7bdb019ff4c3ba3ac100bbe7b6e74dce0e8bc" resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-6.1.2.tgz#aeb7bdb019ff4c3ba3ac100bbe7b6e74dce0e8bc"