Merge branch 'develop' into merge_create_user_modals

# Conflicts:
#	packages/builder/src/pages/builder/portal/manage/users/_components/BasicOnboardingModal.svelte
This commit is contained in:
Maurits Lourens 2022-06-21 13:10:27 +02:00
commit 657ee6289c
48 changed files with 1171 additions and 1063 deletions

View File

@ -1,76 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at community@budibase.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

1
.github/CODE_OF_CONDUCT.md vendored Symbolic link
View File

@ -0,0 +1 @@
../docs/CODE_OF_CONDUCT.md

View File

@ -1,208 +0,0 @@
# Contributing
From opening a bug report to creating a pull request: every contribution is appreciated and welcome. If you're planning to implement a new feature or change the api please create an issue first. This way we can ensure that your precious work is not in vain.
## Not Sure Where to Start?
Budibase is a low-code web application builder that creates svelte based web applications.
Budibase is a monorepo managed by [lerna](https://github.com/lerna/lerna). Lerna manages the building and publishing of the budibase packages. At a high level, here are the packages that make up budibase.
- **packages/builder** - contains code for the budibase builder client side svelte application.
- **packages/client** - A module that runs in the browser responsible for reading JSON definition and creating living, breathing web apps from it.
- **packages/server** - The budibase server. This [Koa](https://koajs.com/) app is responsible for serving the JS for the builder and budibase apps, as well as providing the API for interaction with the database and file system.
- **packages/worker** - This [Koa](https://koajs.com/) app is responsible for providing global apis for managing your budibase installation. Authentication, Users, Email, Org and Auth configs are all provided by the worker.
## Contributor License Agreement (CLA)
In order to accept your pull request, we need you to submit a CLA. You only need to do this once. If you are submitting a pull request for the first time, just submit a Pull Request and our CLA Bot will give you instructions on how to sign the CLA before merging your Pull Request.
All contributors must sign an [Individual Contributor License Agreement](https://github.com/budibase/budibase/blob/next/.github/cla/individual-cla.md).
If contributing on behalf of your company, your company must sign a [Corporate Contributor License Agreement](https://github.com/budibase/budibase/blob/next/.github/cla/corporate-cla.md). If so, please contact us via community@budibase.com.
## Glossary of Terms
To understand the budibase API, it can be helpful to understand the top level entities that make up Budibase.
### Client
A client represents a single budibase customer. Each budibase client will have 1 or more budibase servers. Every client is assigned a unique ID.
### App
A client can have one or more budibase applications. Budibase applications would be things like "Developer Inventory Management" or "Goat Herder CRM". Think of a budibase application as a tree.
### Database
An App can have one or more databases. Keeping with our [dendrology](https://en.wikipedia.org/wiki/Dendrology) analogy - think of an database as a branch on the tree. Databases are used to keep data separate for different instances of your app. For example, if you had a CRM app, you may create a database for your US office, and a database for your Australian office. Databases allow us to support [multitenancy](https://www.gartner.com/en/information-technology/glossary/multitenancy) in budibase applications.
### Table
Tables in budibase are almost akin to tables in relational databases. A table may be a "Car" or an "Employee". They are the main building blocks for the creation and management of backend data in budibase.
### View
A View is an advanced feature in budibase that allows you to write a custom query using [MapReduce](https://pouchdb.com/guides/queries.html) queries. Views enable powerful query functionality and calculations, allowing you to do more with your data.
### Page
A page in budibase is actually a single, self contained svelte web app. There are only 2 pages in budibase. The **login** page and the **main** page.
### Screen
A screen is a component within a single page. Generally, screens represent client side routes, and can be switched without refreshing the page.
### Component
A component is the basic frontend building block of a budibase app.
### Component Library
Component libraries are collections of components as well as the definition of their props contained in a file called `components.json`.
## Contributing to Budibase
* Please maintain the existing code style.
* Please try to keep your commits small and focused.
* Please write tests.
* If the project diverges from your branch, please rebase instead of merging. This makes the commit graph easier to read.
* Once your work is completed, please raise a PR against the `develop` branch with some information about what has changed and why.
### Getting Started For Contributors
#### 1. Prerequisites
NodeJS Version `14.x.x`
*yarn -* `npm install -g yarn`
*jest* - `npm install -g jest`
#### 2. Clone this repository
`git clone https://github.com/Budibase/budibase.git`
then `cd ` into your local copy.
#### 3. Install and Build
| **NOTE**: On Windows, all yarn commands must be executed on a bash shell (e.g. git bash)
To develop the Budibase platform you'll need [Docker](https://www.docker.com/) and [Docker Compose](https://docs.docker.com/compose/) installed.
##### Quick method
`yarn setup` will check that all necessary components are installed and setup the repo for usage.
##### Manual method
The following commands can be executed to manually get Budibase up and running (assuming Docker/Docker Compose has been installed).
`yarn` to install project dependencies
`yarn bootstrap` will install all budibase modules and symlink them together using lerna.
`yarn build` will build all budibase packages.
#### 4. Running
To run the budibase server and builder in dev mode (i.e. with live reloading):
1. Open a new console
2. `yarn dev` (from root)
3. Access the builder on http://localhost:10000/builder
This will enable watch mode for both the builder app, server, client library and any component libraries.
#### 5. Debugging using VS Code
To debug the budibase server and worker a VS Code launch configuration has been provided.
Visit the debug window and select `Budibase Server` or `Budibase Worker` to debug the respective component.
Alternatively to start both components simultaneously select `Start Budibase`.
In addition to the above, the remaining budibase components may be ran in dev mode using: `yarn dev:noserver`.
#### 6. Cleanup
If you wish to delete all the apps created in development and reset the environment then run the following:
1. `yarn nuke:docker` will wipe all the Budibase services
2. `yarn dev` will restart all the services
### Backend
For the backend we run [Redis](https://redis.io/), [CouchDB](https://couchdb.apache.org/), [MinIO](https://min.io/) and [NGINX](https://www.nginx.com/) in Docker compose. This means that to develop Budibase you will need Docker and Docker compose installed. The backend services are then ran separately as Node services with nodemon so that they can be debugged outside of Docker.
### Data Storage
When you are running locally, budibase stores data on disk using docker volumes. The volumes and the types of data associated with each are:
- `redis_data`
- Sessions, email tokens
- `couchdb3_data`
- Global and app databases
- `minio_data`
- App manifest, budibase client, static assets
### Devlopment Modes
A combination of environment variables controls the mode that budibase runs in.
Yarn commands can be used to mimic the different modes that budibase can be ran in
#### Self Hosted
The default mode. A single tenant installation with no usage restrictions.
To enable this mode, use:
```
yarn mode:self
```
#### Cloud
The cloud mode, with account portal turned off.
To enable this mode, use:
```
yarn mode:cloud
```
#### Cloud & Account
The cloud mode, with account portal turned on. This is a replica of the mode that runs at https://budibase.app
To enable this mode, use:
```
yarn mode:account
```
### CI
An overview of the CI pipelines can be found [here](./workflows/README.md)
### Troubleshooting
Sometimes, things go wrong. This can be due to incompatible updates on the budibase platform. To clear down your development environment and start again follow **Step 6. Cleanup**, then proceed from **Step 3. Install and Build** in the setup guide above. You should have a fresh Budibase installation.
### Running tests
#### End-to-end Tests
Budibase uses Cypress to run a number of E2E tests. To run the tests execute the following command in the root folder:
```
yarn test:e2e
```
Or if you are in the builder you can run `yarn cy:test`.
### Other Useful Information
* The contributors are listed in [AUTHORS.md](https://github.com/Budibase/budibase/blob/master/.github/AUTHORS.md) (add yourself).
* This project uses a modified version of the MPLv2 license, see [LICENSE](https://github.com/budibase/server/blob/master/LICENSE).
* We use the [C4 (Collective Code Construction Contract)](https://rfc.zeromq.org/spec:42/C4/) process for contributions.
Please read this if you are unfamiliar with it.

1
.github/CONTRIBUTING.md vendored Symbolic link
View File

@ -0,0 +1 @@
../docs/CONTRIBUTING.md

View File

@ -24,14 +24,14 @@ The standard CI Build job is what runs when you raise a PR to develop or master.
Triggers: Triggers:
- Push to develop - Push to develop
The job responsible for building, tagging and pushing docker images out to the test and staging environments. The job responsible for building, tagging and pushing docker images out to the test and release environments.
- Installs all dependencies - Installs all dependencies
- builds the project - builds the project
- run the unit tests - run the unit tests
- publish the budibase JS packages under a prerelease tag to NPM - publish the budibase JS packages under a prerelease tag to NPM
- build, tag and push docker images under the `develop` tag to docker hub - build, tag and push docker images under the `develop` tag to docker hub
These images will then be pulled by the test and staging environments, updating the latest automatically. Discord notifications are sent to the #infra channel when this occurs. These images will then be pulled by the test and release environments, updating the latest automatically. Discord notifications are sent to the #infra channel when this occurs.
### Release Job (release.yml) ### Release Job (release.yml)
Triggers: Triggers:
@ -57,8 +57,33 @@ This job relies on the release job to have run first, so the latest image is pus
- Build and release the budibase helm chart for kubernetes users - Build and release the budibase helm chart for kubernetes users
- Perform a github release with the latest version. You can see previous releases here (https://github.com/Budibase/budibase/releases) - Perform a github release with the latest version. You can see previous releases here (https://github.com/Budibase/budibase/releases)
### Deploy Release (deploy-release.yml)
Triggers:
- Manual Workflow Dispatch Trigger
### Cloud Deploy (deploy-cloud.yml) This job is responsible for deploying to our release, cloud kubernetes environment. You must run the release job first, to ensure that the latest images have been built and pushed to docker hub. After kicking off this job, the following will occur:
- Checks out the release branch
- Pulls the latest `values.yaml` from budibase infra, a private repo containing budibases infrastructure configuration
- Gets the latest budibase version from `lerna.json`, if it hasn't been specified in the workflow when you kicked it off
- Configures AWS Credentials
- Deploys the helm chart in the budibase repo to our preproduction EKS cluster, injecting the `values.yaml` we pulled from budibase-infra
- Fires off a discord webhook in the #infra channel to show that the deployment completely successfully.
### Deploy Preprod (deploy-preprod.yml)
Triggers:
- Manual Workflow Dispatch Trigger
This job is responsible for deploying to our preprod, cloud kubernetes environment. You must run the release job first, to ensure that the latest images have been built and pushed to docker hub. After kicking off this job, the following will occur:
- Checks out the master branch
- Pulls the latest `values.yaml` from budibase infra, a private repo containing budibases infrastructure configuration
- Gets the latest budibase version from `lerna.json`, if it hasn't been specified in the workflow when you kicked it off
- Configures AWS Credentials
- Deploys the helm chart in the budibase repo to our preprod EKS cluster, injecting the `values.yaml` we pulled from budibase-infra
- Fires off a discord webhook in the #infra channel to show that the deployment completely successfully.
### Deploy Production (deploy-cloud.yml)
Triggers: Triggers:
- Manual Workflow Dispatch Trigger - Manual Workflow Dispatch Trigger

View File

@ -1,4 +1,4 @@
name: Budibase Cloud Deploy name: Budibase Deploy Production
on: on:
workflow_dispatch: workflow_dispatch:

View File

@ -1,4 +1,4 @@
name: Budibase Release Preprod name: Budibase Deploy Preprod
on: on:
workflow_dispatch: workflow_dispatch:

77
.github/workflows/deploy-release.yml vendored Normal file
View File

@ -0,0 +1,77 @@
name: Budibase Deploy Release
on:
workflow_dispatch:
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: eu-west-1
- name: Fail if branch is not develop
if: github.ref != 'refs/heads/develop'
run: |
echo "Ref is not develop, you must run this job from develop."
exit 1
- name: Get the latest budibase release version
id: version
run: |
release_version=$(cat lerna.json | jq -r '.version')
echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV
- name: Tag and release Proxy service docker image
run: |
docker login -u $DOCKER_USER -p $DOCKER_PASSWORD
yarn build:docker:proxy:release
docker tag proxy-service budibase/proxy:$RELEASE_TAG
docker push budibase/proxy:$RELEASE_TAG
env:
DOCKER_USER: ${{ secrets.DOCKER_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_API_KEY }}
RELEASE_TAG: k8s-release
- name: Pull values.yaml from budibase-infra
run: |
curl -H "Authorization: token ${{ secrets.GH_PERSONAL_TOKEN }}" \
-H 'Accept: application/vnd.github.v3.raw' \
-o values.release.yaml \
-L https://api.github.com/repos/budibase/budibase-infra/contents/kubernetes/budibase-release/values.yaml
wc -l values.release.yaml
- name: Deploy to Release Environment
uses: glopezep/helm@v1.7.1
with:
release: budibase-release
namespace: budibase
chart: charts/budibase
token: ${{ github.token }}
helm: helm3
values: |
globals:
appVersion: develop
ingress:
enabled: true
nginx: true
value-files: >-
[
"values.release.yaml"
]
env:
KUBECONFIG_FILE: '${{ secrets.RELEASE_KUBECONFIG }}'
- name: Discord Webhook Action
uses: tsickert/discord-webhook@v4.0.0
with:
webhook-url: ${{ secrets.PROD_DEPLOY_WEBHOOK_URL }}
content: "Release Env Deployment Complete: ${{ env.RELEASE_VERSION }} deployed to Budibase Release Env."
embed-title: ${{ env.RELEASE_VERSION }}

View File

@ -1,10 +1,10 @@
name: Budibase Release Staging name: Budibase Prerelease
concurrency: release-develop concurrency: release-prerelease
on: on:
push: push:
branches: branches:
- release - develop
paths: paths:
- '.aws/**' - '.aws/**'
- '.github/**' - '.github/**'
@ -28,11 +28,11 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
# - name: Fail if branch is not develop - name: Fail if branch is not develop
# if: github.ref != 'refs/heads/develop' if: github.ref != 'refs/heads/develop'
# run: | run: |
# echo "Ref is not develop, you must run this job from develop." echo "Ref is not develop, you must run this job from develop."
# exit 1 exit 1
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-node@v1 - uses: actions/setup-node@v1
with: with:

View File

@ -174,6 +174,7 @@ Budibase is dedicated to providing a welcoming, diverse, and harrassment-free ex
## 🙌 Contributing to Budibase ## 🙌 Contributing to Budibase
From opening a bug report to creating a pull request: every contribution is appreciated and welcomed. If you're planning to implement a new feature or change the API please create an issue first. This way we can ensure your work is not in vain. From opening a bug report to creating a pull request: every contribution is appreciated and welcomed. If you're planning to implement a new feature or change the API please create an issue first. This way we can ensure your work is not in vain.
Environment setup instructions are available for [Debian](https://github.com/Budibase/budibase/tree/HEAD/docs/DEV-SETUP-DEBIAN.md) and [MacOSX](https://github.com/Budibase/budibase/tree/HEAD/docs/DEV-SETUP-MACOSX.md)
### Not Sure Where to Start? ### Not Sure Where to Start?
A good place to start contributing, is the [First time issues project](https://github.com/Budibase/budibase/projects/22). A good place to start contributing, is the [First time issues project](https://github.com/Budibase/budibase/projects/22).
@ -187,7 +188,7 @@ Budibase is a monorepo managed by lerna. Lerna manages the building and publishi
- [packages/server](https://github.com/Budibase/budibase/tree/HEAD/packages/server) - The budibase server. This Koa app is responsible for serving the JS for the builder and budibase apps, as well as providing the API for interaction with the database and file system. - [packages/server](https://github.com/Budibase/budibase/tree/HEAD/packages/server) - The budibase server. This Koa app is responsible for serving the JS for the builder and budibase apps, as well as providing the API for interaction with the database and file system.
For more information, see [CONTRIBUTING.md](https://github.com/Budibase/budibase/blob/HEAD/.github/CONTRIBUTING.md) For more information, see [CONTRIBUTING.md](https://github.com/Budibase/budibase/blob/HEAD/docs/CONTRIBUTING.md)
<br /><br /> <br /><br />
@ -202,7 +203,7 @@ Budibase is open-source, licensed as [GPL v3](https://www.gnu.org/licenses/gpl-3
[![Stargazers over time](https://starchart.cc/Budibase/budibase.svg)](https://starchart.cc/Budibase/budibase) [![Stargazers over time](https://starchart.cc/Budibase/budibase.svg)](https://starchart.cc/Budibase/budibase)
If you are having issues between updates of the builder, please use the guide [here](https://github.com/Budibase/budibase/blob/HEAD/.github/CONTRIBUTING.md#troubleshooting) to clear down your environment. If you are having issues between updates of the builder, please use the guide [here](https://github.com/Budibase/budibase/blob/HEAD/docs/CONTRIBUTING.md#troubleshooting) to clear down your environment.
<br /><br /> <br /><br />

View File

@ -123,6 +123,8 @@ spec:
value: {{ .Values.globals.google.clientId | quote }} value: {{ .Values.globals.google.clientId | quote }}
- name: GOOGLE_CLIENT_SECRET - name: GOOGLE_CLIENT_SECRET
value: {{ .Values.globals.google.secret | quote }} value: {{ .Values.globals.google.secret | quote }}
- name: TENANT_FEATURE_FLAGS
value: {{ .Values.globals.tenantFeatureFlags | quote }}
image: budibase/worker:{{ .Values.globals.appVersion }} image: budibase/worker:{{ .Values.globals.appVersion }}
imagePullPolicy: Always imagePullPolicy: Always
livenessProbe: livenessProbe:

76
docs/CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at community@budibase.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

216
docs/CONTRIBUTING.md Normal file
View File

@ -0,0 +1,216 @@
# Contributing
From opening a bug report to creating a pull request: every contribution is appreciated and welcome. If you're planning to implement a new feature or change the api please [create an issue](https://github.com/Budibase/budibase/issues/new/choose) first. This way we can ensure that your precious work is not in vain.
## Table of contents
- [Quick start](#quick-start)
- [Status](#status)
- [What's included](#whats-included)
- [Bugs and feature requests](#bugs-and-feature-requests)
## Not Sure Where to Start?
Budibase is a low-code web application builder that creates svelte-based web applications.
Budibase is a monorepo managed by [lerna](https://github.com/lerna/lerna). Lerna manages the building and publishing of the budibase packages. At a high level, here are the packages that make up budibase.
- **packages/builder** - contains code for the budibase builder client side svelte application.
- **packages/client** - A module that runs in the browser responsible for reading JSON definition and creating living, breathing web apps from it.
- **packages/server** - The budibase server. This [Koa](https://koajs.com/) app is responsible for serving the JS for the builder and budibase apps, as well as providing the API for interaction with the database and file system.
- **packages/worker** - This [Koa](https://koajs.com/) app is responsible for providing global apis for managing your budibase installation. Authentication, Users, Email, Org and Auth configs are all provided by the worker.
## Contributor License Agreement (CLA)
In order to accept your pull request, we need you to submit a CLA. You only need to do this once. If you are submitting a pull request for the first time, just submit a Pull Request and our CLA Bot will give you instructions on how to sign the CLA before merging your Pull Request.
All contributors must sign an [Individual Contributor License Agreement](https://github.com/budibase/budibase/blob/next/.github/cla/individual-cla.md).
If contributing on behalf of your company, your company must sign a [Corporate Contributor License Agreement](https://github.com/budibase/budibase/blob/next/.github/cla/corporate-cla.md). If so, please contact us via community@budibase.com.
## Glossary of Terms
To understand the budibase API, it can be helpful to understand the top level entities that make up Budibase.
### Client
A client represents a single budibase customer. Each budibase client will have 1 or more budibase servers. Every client is assigned a unique ID.
### App
A client can have one or more budibase applications. Budibase applications would be things like "Developer Inventory Management" or "Goat Herder CRM". Think of a budibase application as a tree.
### Database
An App can have one or more databases. Keeping with our [dendrology](https://en.wikipedia.org/wiki/Dendrology) analogy - think of an database as a branch on the tree. Databases are used to keep data separate for different instances of your app. For example, if you had a CRM app, you may create a database for your US office, and a database for your Australian office. Databases allow us to support [multitenancy](https://www.gartner.com/en/information-technology/glossary/multitenancy) in budibase applications.
### Table
Tables in budibase are almost akin to tables in relational databases. A table may be a "Car" or an "Employee". They are the main building blocks for the creation and management of backend data in budibase.
### View
A View is an advanced feature in budibase that allows you to write a custom query using [MapReduce](https://pouchdb.com/guides/queries.html) queries. Views enable powerful query functionality and calculations, allowing you to do more with your data.
### Page
A page in budibase is actually a single, self contained svelte web app. There are only 2 pages in budibase. The **login** page and the **main** page.
### Screen
A screen is a component within a single page. Generally, screens represent client side routes, and can be switched without refreshing the page.
### Component
A component is the basic frontend building block of a budibase app.
### Component Library
Component libraries are collections of components as well as the definition of their props contained in a file called `components.json`.
## Contributing to Budibase
* Please maintain the existing code style.
* Please try to keep your commits small and focused.
* Please write tests.
* If the project diverges from your branch, please rebase instead of merging. This makes the commit graph easier to read.
* Once your work is completed, please raise a PR against the `develop` branch with some information about what has changed and why.
### Getting Started For Contributors
#### 1. Prerequisites
NodeJS Version `14.x.x`
*yarn -* `npm install -g yarn`
*jest* - `npm install -g jest`
#### 2. Clone this repository
`git clone https://github.com/Budibase/budibase.git`
then `cd ` into your local copy.
#### 3. Install and Build
| **NOTE**: On Windows, all yarn commands must be executed on a bash shell (e.g. git bash)
To develop the Budibase platform you'll need [Docker](https://www.docker.com/) and [Docker Compose](https://docs.docker.com/compose/) installed.
##### Quick method
`yarn setup` will check that all necessary components are installed and setup the repo for usage.
##### Manual method
The following commands can be executed to manually get Budibase up and running (assuming Docker/Docker Compose has been installed).
`yarn` to install project dependencies
`yarn bootstrap` will install all budibase modules and symlink them together using lerna.
`yarn build` will build all budibase packages.
#### 4. Running
To run the budibase server and builder in dev mode (i.e. with live reloading):
1. Open a new console
2. `yarn dev` (from root)
3. Access the builder on http://localhost:10000/builder
This will enable watch mode for both the builder app, server, client library and any component libraries.
#### 5. Debugging using VS Code
To debug the budibase server and worker a VS Code launch configuration has been provided.
Visit the debug window and select `Budibase Server` or `Budibase Worker` to debug the respective component.
Alternatively to start both components simultaneously select `Start Budibase`.
In addition to the above, the remaining budibase components may be run in dev mode using: `yarn dev:noserver`.
#### 6. Cleanup
If you wish to delete all the apps created in development and reset the environment then run the following:
1. `yarn nuke:docker` will wipe all the Budibase services
2. `yarn dev` will restart all the services
### Backend
For the backend we run [Redis](https://redis.io/), [CouchDB](https://couchdb.apache.org/), [MinIO](https://min.io/) and [NGINX](https://www.nginx.com/) in Docker compose. This means that to develop Budibase you will need Docker and Docker compose installed. The backend services are then run separately as Node services with nodemon so that they can be debugged outside of Docker.
### Data Storage
When you are running locally, budibase stores data on disk using docker volumes. The volumes and the types of data associated with each are:
- `redis_data`
- Sessions, email tokens
- `couchdb3_data`
- Global and app databases
- `minio_data`
- App manifest, budibase client, static assets
### Development Modes
A combination of environment variables controls the mode budibase runs in.
Yarn commands can be used to mimic the different modes as described in the sections below:
#### Self Hosted
The default mode. A single tenant installation with no usage restrictions.
To enable this mode, use:
```
yarn mode:self
```
#### Cloud
The cloud mode, with account portal turned off.
To enable this mode, use:
```
yarn mode:cloud
```
#### Cloud & Account
The cloud mode, with account portal turned on. This is a replica of the mode that runs at https://budibase.app
To enable this mode, use:
```
yarn mode:account
```
### CI
An overview of the CI pipelines can be found [here](./workflows/README.md)
### Troubleshooting
Sometimes, things go wrong. This can be due to incompatible updates on the budibase platform. To clear down your development environment and start again follow **Step 6. Cleanup**, then proceed from **Step 3. Install and Build** in the setup guide above to create a fresh Budibase installation.
### Running tests
#### End-to-end Tests
Budibase uses Cypress to run a number of E2E tests. To run the tests execute the following command in the root folder:
```
yarn test:e2e
```
Or if you are in the builder you can run `yarn cy:test`.
### Other Useful Information
* The contributors are listed in [AUTHORS.md](https://github.com/Budibase/budibase/blob/master/.github/AUTHORS.md) (add yourself).
* This project uses a modified version of the MPLv2 license, see [LICENSE](https://github.com/budibase/server/blob/master/LICENSE).
* We use the [C4 (Collective Code Construction Contract)](https://rfc.zeromq.org/spec:42/C4/) process for contributions.
Please read this if you are unfamiliar with it.

52
docs/DEV-SETUP-DEBIAN.md Normal file
View File

@ -0,0 +1,52 @@
## Dev Environment on Debian 11
### Install Node
Budibase requires a recent version of node (14+):
```
curl -sL https://deb.nodesource.com/setup_16.x | sudo bash -
apt -y install nodejs
node -v
```
### Install npm requirements
```
npm install -g yarn jest lerna
```
### Install Docker and Docker Compose
```
apt install docker.io
pip3 install docker-compose
```
### Clone the repo
```
git clone https://github.com/Budibase/budibase.git
```
### Check Versions
This setup process was tested on Debian 11 (bullseye) with version numbers show below. Your mileage may vary using anything else.
- Docker: 20.10.5
- Docker-Compose: 1.29.2
- Node: v16.15.1
- Yarn: 1.22.19
- Lerna: 5.1.4
### Build
```
cd budibase
yarn setup
```
The yarn setup command runs several build steps i.e.
```
node ./hosting/scripts/setup.js && yarn && yarn bootstrap && yarn build && yarn dev
```
So this command will actually run the application in dev mode. It creates .env files under `./packages/server` and `./packages/worker` and runs docker containers for each service via docker-compose.
The dev version will be available on port 10000 i.e.
http://127.0.0.1:10000/builder/admin

54
docs/DEV-SETUP-MACOSX.md Normal file
View File

@ -0,0 +1,54 @@
## Dev Environment on MAC OSX 12 (Monterey)
### Install Homebrew
Install instructions [here](https://brew.sh/)
### Install Node
Budibase requires a recent version of node (14+):
```
brew install node npm
node -v
```
### Install npm requirements
```
npm install -g yarn jest lerna
```
### Install Docker and Docker Compose
```
brew install docker docker-compose
```
### Clone the repo
```
git clone https://github.com/Budibase/budibase.git
```
### Check Versions
This setup process was tested on Mac OSX 12 (Monterey) with version numbers shown below. Your mileage may vary using anything else.
- Docker: 20.10.14
- Docker-Compose: 2.6.0
- Node: 18.3.0
- Yarn: 1.22.19
- Lerna: 5.1.4
### Build
```
cd budibase
yarn setup
```
The yarn setup command runs several build steps i.e.
```
node ./hosting/scripts/setup.js && yarn && yarn bootstrap && yarn build && yarn dev
```
So this command will actually run the application in dev mode. It creates .env files under `./packages/server` and `./packages/worker` and runs docker containers for each service via docker-compose.
The dev version will be available on port 10000 i.e.
http://127.0.0.1:10000/builder/admin

View File

@ -1,79 +1,84 @@
FROM couchdb FROM node:14-slim as build
ENV DEPLOYMENT_ENVIRONMENT=docker # install node-gyp dependencies
ENV POSTHOG_TOKEN=phc_fg5I3nDOf6oJVMHSaycEhpPdlgS8rzXG2r6F2IpxCHS RUN apt-get update && apt-get install -y --no-install-recommends g++ make python
ENV COUCHDB_PASSWORD=budibase
ENV COUCHDB_USER=budibase
ENV COUCH_DB_URL=http://budibase:budibase@localhost:5984
ENV BUDIBASE_ENVIRONMENT=PRODUCTION
ENV MINIO_URL=http://localhost:9000
ENV REDIS_URL=localhost:6379
ENV WORKER_URL=http://localhost:4002
ENV INTERNAL_API_KEY=budibase
ENV JWT_SECRET=testsecret
ENV MINIO_ACCESS_KEY=budibase
ENV MINIO_SECRET_KEY=budibase
ENV SELF_HOSTED=1
ENV CLUSTER_PORT=10000
ENV REDIS_PASSWORD=budibase
ENV ARCHITECTURE=amd
ENV APP_PORT=4001
ENV WORKER_PORT=4002
RUN apt-get update # add pin script
RUN apt-get install software-properties-common wget nginx -y WORKDIR /
RUN apt-add-repository 'deb http://security.debian.org/debian-security stretch/updates main' ADD scripts/pinVersions.js scripts/cleanup.sh ./
RUN apt-get update RUN chmod +x /cleanup.sh
# build server
WORKDIR /app
ADD packages/server .
RUN node /pinVersions.js && yarn && yarn build && /cleanup.sh
# build worker
WORKDIR /worker
ADD packages/worker .
RUN node /pinVersions.js && yarn && yarn build && /cleanup.sh
FROM couchdb:3.2.1
COPY --from=build /app /app
COPY --from=build /worker /worker
ENV DEPLOYMENT_ENVIRONMENT=docker \
POSTHOG_TOKEN=phc_fg5I3nDOf6oJVMHSaycEhpPdlgS8rzXG2r6F2IpxCHS \
COUCHDB_PASSWORD=budibase \
COUCHDB_USER=budibase \
COUCH_DB_URL=http://budibase:budibase@localhost:5984 \
BUDIBASE_ENVIRONMENT=PRODUCTION \
MINIO_URL=http://localhost:9000 \
REDIS_URL=localhost:6379 \
WORKER_URL=http://localhost:4002 \
INTERNAL_API_KEY=budibase \
JWT_SECRET=testsecret \
MINIO_ACCESS_KEY=budibase \
MINIO_SECRET_KEY=budibase \
SELF_HOSTED=1 \
CLUSTER_PORT=10000 \
REDIS_PASSWORD=budibase \
ARCHITECTURE=amd \
APP_PORT=4001 \
WORKER_PORT=4002
# install base dependencies
RUN apt-get update && \
apt-get install software-properties-common wget -y && \
apt-add-repository 'deb http://security.debian.org/debian-security stretch/updates main' && \
apt-get update
# install other dependencies, nodejs, oracle requirements, jdk8, redis, nginx
WORKDIR /nodejs
RUN curl -sL https://deb.nodesource.com/setup_16.x -o /tmp/nodesource_setup.sh && \
bash /tmp/nodesource_setup.sh && \
apt-get install libaio1 nodejs nginx openjdk-8-jdk redis-server unzip -y && \
npm install --global yarn pm2
# setup nginx # setup nginx
ADD hosting/single/nginx.conf /etc/nginx ADD hosting/single/nginx.conf /etc/nginx
RUN mkdir /etc/nginx/logs RUN mkdir /etc/nginx/logs && \
RUN useradd www useradd www && \
RUN touch /etc/nginx/logs/error.log touch /etc/nginx/logs/error.log && \
RUN touch /etc/nginx/logs/nginx.pid touch /etc/nginx/logs/nginx.pid
# install java WORKDIR /
RUN apt-get install openjdk-8-jdk -y RUN mkdir -p scripts/integrations/oracle
ADD packages/server/scripts/integrations/oracle scripts/integrations/oracle
# setup nodejs RUN /bin/bash -e ./scripts/integrations/oracle/instantclient/linux/x86-64/install.sh
WORKDIR /nodejs
RUN curl -sL https://deb.nodesource.com/setup_16.x -o /tmp/nodesource_setup.sh
RUN bash /tmp/nodesource_setup.sh
RUN apt-get install nodejs
RUN npm install --global yarn
RUN npm install --global pm2
# setup redis
RUN apt install redis-server -y
# setup server
WORKDIR /app
ADD packages/server .
RUN ls -al
RUN yarn
RUN yarn build
# Install client for oracle datasource
RUN apt-get install unzip libaio1
RUN /bin/bash -e scripts/integrations/oracle/instantclient/linux/x86-64/install.sh
# setup worker
WORKDIR /worker
ADD packages/worker .
RUN yarn
RUN yarn build
# setup clouseau # setup clouseau
WORKDIR / WORKDIR /
RUN wget https://github.com/cloudant-labs/clouseau/releases/download/2.21.0/clouseau-2.21.0-dist.zip RUN wget https://github.com/cloudant-labs/clouseau/releases/download/2.21.0/clouseau-2.21.0-dist.zip && \
RUN unzip clouseau-2.21.0-dist.zip unzip clouseau-2.21.0-dist.zip && \
RUN mv clouseau-2.21.0 /opt/clouseau mv clouseau-2.21.0 /opt/clouseau && \
RUN rm clouseau-2.21.0-dist.zip rm clouseau-2.21.0-dist.zip
WORKDIR /opt/clouseau WORKDIR /opt/clouseau
RUN mkdir ./bin RUN mkdir ./bin
ADD hosting/single/clouseau ./bin/ ADD hosting/single/clouseau ./bin/
ADD hosting/single/log4j.properties . ADD hosting/single/log4j.properties hosting/single/clouseau.ini ./
ADD hosting/single/clouseau.ini .
RUN chmod +x ./bin/clouseau RUN chmod +x ./bin/clouseau
# setup CouchDB # setup CouchDB
@ -82,18 +87,22 @@ ADD hosting/single/vm.args ./etc/
# setup minio # setup minio
WORKDIR /minio WORKDIR /minio
RUN wget https://dl.min.io/server/minio/release/linux-${ARCHITECTURE}64/minio RUN wget https://dl.min.io/server/minio/release/linux-${ARCHITECTURE}64/minio && chmod +x minio
RUN chmod +x minio
# setup runner file # setup runner file
WORKDIR / WORKDIR /
ADD hosting/single/runner.sh . ADD hosting/single/runner.sh .
RUN chmod +x ./runner.sh RUN chmod +x ./runner.sh
# cleanup cache
RUN yarn cache clean -f
EXPOSE 10000 EXPOSE 10000
VOLUME /opt/couchdb/data VOLUME /opt/couchdb/data
VOLUME /minio VOLUME /minio
# must set this just before running # must set this just before running
ENV NODE_ENV=production ENV NODE_ENV=production
WORKDIR /
CMD ["./runner.sh"] CMD ["./runner.sh"]

4
hosting/single/test.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
id=$(docker run -t -d -p 10000:10000 budibase:latest)
docker exec -it $id bash
docker kill $id

View File

@ -1,5 +1,5 @@
{ {
"version": "1.0.206", "version": "1.0.207-alpha.5",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*" "packages/*"

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/backend-core", "name": "@budibase/backend-core",
"version": "1.0.206", "version": "1.0.207-alpha.5",
"description": "Budibase backend core libraries used in server and worker", "description": "Budibase backend core libraries used in server and worker",
"main": "dist/src/index.js", "main": "dist/src/index.js",
"types": "dist/src/index.d.ts", "types": "dist/src/index.d.ts",
@ -20,6 +20,7 @@
"test:watch": "jest --watchAll" "test:watch": "jest --watchAll"
}, },
"dependencies": { "dependencies": {
"@budibase/types": "^1.0.207-alpha.5",
"@techpass/passport-openidconnect": "0.3.2", "@techpass/passport-openidconnect": "0.3.2",
"aws-sdk": "2.1030.0", "aws-sdk": "2.1030.0",
"bcrypt": "5.0.1", "bcrypt": "5.0.1",
@ -57,7 +58,6 @@
] ]
}, },
"devDependencies": { "devDependencies": {
"@budibase/types": "^1.0.206",
"@shopify/jest-koa-mocks": "3.1.5", "@shopify/jest-koa-mocks": "3.1.5",
"@types/jest": "27.5.1", "@types/jest": "27.5.1",
"@types/koa": "2.0.52", "@types/koa": "2.0.52",

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/bbui", "name": "@budibase/bbui",
"description": "A UI solution used in the different Budibase projects.", "description": "A UI solution used in the different Budibase projects.",
"version": "1.0.206", "version": "1.0.207-alpha.5",
"license": "MPL-2.0", "license": "MPL-2.0",
"svelte": "src/index.js", "svelte": "src/index.js",
"module": "dist/bbui.es.js", "module": "dist/bbui.es.js",
@ -38,7 +38,7 @@
], ],
"dependencies": { "dependencies": {
"@adobe/spectrum-css-workflow-icons": "^1.2.1", "@adobe/spectrum-css-workflow-icons": "^1.2.1",
"@budibase/string-templates": "^1.0.206", "@budibase/string-templates": "^1.0.207-alpha.5",
"@spectrum-css/actionbutton": "^1.0.1", "@spectrum-css/actionbutton": "^1.0.1",
"@spectrum-css/actiongroup": "^1.0.1", "@spectrum-css/actiongroup": "^1.0.1",
"@spectrum-css/avatar": "^3.0.2", "@spectrum-css/avatar": "^3.0.2",

View File

@ -14,6 +14,7 @@
export let active = false export let active = false
export let tooltip = undefined export let tooltip = undefined
export let dataCy export let dataCy
export let newStyles = false
let showTooltip = false let showTooltip = false
</script> </script>
@ -25,6 +26,7 @@
class:spectrum-Button--warning={warning} class:spectrum-Button--warning={warning}
class:spectrum-Button--overBackground={overBackground} class:spectrum-Button--overBackground={overBackground}
class:spectrum-Button--quiet={quiet} class:spectrum-Button--quiet={quiet}
class:new-styles={newStyles}
class:active class:active
class="spectrum-Button spectrum-Button--size{size.toUpperCase()}" class="spectrum-Button spectrum-Button--size{size.toUpperCase()}"
{disabled} {disabled}
@ -93,4 +95,20 @@
padding-left: var(--spacing-m); padding-left: var(--spacing-m);
line-height: 0; line-height: 0;
} }
.spectrum-Button--primary.new-styles {
background: var(--spectrum-global-color-gray-800);
border-color: transparent;
color: var(--spectrum-global-color-gray-50);
}
.spectrum-Button--primary.new-styles:hover {
background: var(--spectrum-global-color-gray-900);
}
.spectrum-Button--secondary.new-styles {
background: var(--spectrum-global-color-gray-200);
border-color: transparent;
color: var(--spectrum-global-color-gray-900);
}
.spectrum-Button--secondary.new-styles:hover {
background: var(--spectrum-global-color-gray-300);
}
</style> </style>

View File

@ -112,4 +112,8 @@
.spectrum-Textfield { .spectrum-Textfield {
width: 100%; width: 100%;
} }
input:disabled {
color: var(--spectrum-global-color-gray-600) !important;
-webkit-text-fill-color: var(--spectrum-global-color-gray-600) !important;
}
</style> </style>

View File

@ -1,7 +1,7 @@
import filterTests from "../support/filterTests" import filterTests from "../support/filterTests"
import clientPackage from "@budibase/client/package.json" import clientPackage from "@budibase/client/package.json"
filterTests(['all'], () => { filterTests(["all"], () => {
context("Application Overview screen", () => { context("Application Overview screen", () => {
before(() => { before(() => {
cy.login() cy.login()
@ -10,31 +10,19 @@ filterTests(['all'], () => {
it("Should be accessible from the applications list", () => { it("Should be accessible from the applications list", () => {
cy.visit(`${Cypress.config().baseUrl}/builder`) cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.get(".appTable .title")
cy.get(".appTable .title").eq(0) .eq(0)
.invoke('attr', 'data-cy') .invoke("attr", "data-cy")
.then(($dataCy) => { .then($dataCy => {
const dataCy = $dataCy; const dataCy = $dataCy
cy.get(".appTable .name").eq(0).click() cy.get(".appTable .app-row-actions button")
.contains("Manage")
.click({ force: true })
cy.location().should((loc) => { cy.location().should(loc => {
expect(loc.pathname).to.eq('/builder/portal/overview/' + dataCy) expect(loc.pathname).to.eq("/builder/portal/overview/" + dataCy)
})
}) })
})
cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.get(".appTable .title").eq(0)
.invoke('attr', 'data-cy')
.then(($dataCy) => {
const dataCy = $dataCy;
cy.get(".appTable .app-row-actions button").contains("View").click({force: true})
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/builder/portal/overview/' + dataCy)
})
})
}) })
// Find a more suitable place for this. // Find a more suitable place for this.
@ -43,24 +31,28 @@ filterTests(['all'], () => {
cy.get(".appTable .lock-status").eq(0).contains("Locked by you").click() cy.get(".appTable .lock-status").eq(0).contains("Locked by you").click()
cy.unlockApp({ owned : true }) cy.unlockApp({ owned: true })
cy.get(".appTable").should("exist") cy.get(".appTable").should("exist")
cy.get(".lock-status").should('not.be.visible') cy.get(".lock-status").should("not.be.visible")
}) })
it("Should allow unlocking in the app overview screen", () => { it("Should allow unlocking in the app overview screen", () => {
cy.visit(`${Cypress.config().baseUrl}/builder`) cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.get(".appTable .app-row-actions button").contains("Edit").eq(0).click({force: true}) cy.get(".appTable .app-row-actions button")
.contains("Edit")
.eq(0)
.click({ force: true })
cy.wait(1000) cy.wait(1000)
cy.visit(`${Cypress.config().baseUrl}/builder`) cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.get(".appTable .app-row-actions button")
cy.get(".appTable .name").eq(0).click() .contains("Manage")
.eq(0)
.click({ force: true })
cy.get(".lock-status").eq(0).contains("Locked by you").click() cy.get(".lock-status").eq(0).contains("Locked by you").click()
cy.unlockApp({ owned : true }) cy.unlockApp({ owned: true })
cy.get(".lock-status").should("not.be.visible") cy.get(".lock-status").should("not.be.visible")
}) })
@ -68,107 +60,149 @@ filterTests(['all'], () => {
it("Should reflect the deploy state of an app that hasn't been published.", () => { it("Should reflect the deploy state of an app that hasn't been published.", () => {
cy.visit(`${Cypress.config().baseUrl}/builder`) cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.get(".appTable .name").eq(0).click() cy.get(".appTable .app-row-actions button")
.contains("Manage")
cy.get(".header-right button.spectrum-Button[data-cy='view-app']").should("be.disabled") .eq(0)
.click({ force: true })
cy.get(".header-right button.spectrum-Button[data-cy='view-app']").should(
"be.disabled"
)
cy.get(".spectrum-Tabs-item.is-selected").contains("Overview") cy.get(".spectrum-Tabs-item.is-selected").contains("Overview")
cy.get(".overview-tab").should("be.visible") cy.get(".overview-tab").should("be.visible")
cy.get(".overview-tab [data-cy='app-status']").within(() => { cy.get(".overview-tab [data-cy='app-status']").within(() => {
cy.get(".status-display").contains("Unpublished") cy.get(".status-display").contains("Unpublished")
cy.get(".status-display .icon svg[aria-label='GlobeStrike']").should("exist") cy.get(".status-display .icon svg[aria-label='GlobeStrike']").should(
"exist"
)
cy.get(".status-text").contains("-") cy.get(".status-text").contains("-")
}) })
}) })
it("Should reflect the app deployment state", () => { it("Should reflect the app deployment state", () => {
cy.visit(`${Cypress.config().baseUrl}/builder`) cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.get(".appTable .app-row-actions button").contains("Edit").eq(0).click({force: true}) cy.get(".appTable .app-row-actions button")
.contains("Edit")
cy.get(".toprightnav button.spectrum-Button").contains("Publish").click({ force : true }) .eq(0)
cy.get(".spectrum-Modal [data-cy='deploy-app-modal']").should("be.visible") .click({ force: true })
.within(() => {
cy.get(".spectrum-Button").contains("Publish").click({ force : true }) cy.get(".toprightnav button.spectrum-Button")
cy.wait(1000) .contains("Publish")
}); .click({ force: true })
cy.get(".spectrum-Modal [data-cy='deploy-app-modal']")
.should("be.visible")
.within(() => {
cy.get(".spectrum-Button").contains("Publish").click({ force: true })
cy.wait(1000)
})
cy.visit(`${Cypress.config().baseUrl}/builder`) cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.get(".appTable .name").eq(0).click() cy.get(".appTable .app-row-actions button")
.contains("Manage")
cy.get(".header-right button.spectrum-Button[data-cy='view-app']").should("not.be.disabled") .eq(0)
.click({ force: true })
cy.get(".header-right button.spectrum-Button[data-cy='view-app']").should(
"not.be.disabled"
)
cy.get(".overview-tab [data-cy='app-status']").within(() => { cy.get(".overview-tab [data-cy='app-status']").within(() => {
cy.get(".status-display").contains("Published") cy.get(".status-display").contains("Published")
cy.get(".status-display .icon svg[aria-label='GlobeCheck']").should("exist") cy.get(".status-display .icon svg[aria-label='GlobeCheck']").should(
"exist"
)
cy.get(".status-text").contains("Last published a few seconds ago") cy.get(".status-text").contains("Last published a few seconds ago")
}) })
}) })
it("Should reflect an application that has been unpublished", () => { it("Should reflect an application that has been unpublished", () => {
cy.visit(`${Cypress.config().baseUrl}/builder`) cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.get(".appTable .app-row-actions button").contains("Edit").eq(0).click({force: true}) cy.get(".appTable .app-row-actions button")
.contains("Edit")
.eq(0)
.click({ force: true })
cy.get(".deployment-top-nav svg[aria-label='Globe']") cy.get(".deployment-top-nav svg[aria-label='Globe']").click({
.click({ force: true }) force: true,
})
cy.get("[data-cy='publish-popover-menu']").should("be.visible") cy.get("[data-cy='publish-popover-menu']").should("be.visible")
cy.get("[data-cy='publish-popover-menu'] [data-cy='publish-popover-action']") cy.get(
.click({ force : true }) "[data-cy='publish-popover-menu'] [data-cy='publish-popover-action']"
).click({ force: true })
cy.get("[data-cy='unpublish-modal']").should("be.visible")
.within(() => { cy.get("[data-cy='unpublish-modal']")
cy.get(".confirm-wrap button").click({ force: true } .should("be.visible")
)}) .within(() => {
cy.get(".confirm-wrap button").click({ force: true })
})
cy.wait(1000) cy.wait(1000)
cy.visit(`${Cypress.config().baseUrl}/builder`) cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.get(".appTable .name").eq(0).click() cy.get(".appTable .app-row-actions button")
.contains("Manage")
.eq(0)
.click({ force: true })
cy.get(".overview-tab [data-cy='app-status']").within(() => { cy.get(".overview-tab [data-cy='app-status']").within(() => {
cy.get(".status-display").contains("Unpublished") cy.get(".status-display").contains("Unpublished")
cy.get(".status-display .icon svg[aria-label='GlobeStrike']").should("exist") cy.get(".status-display .icon svg[aria-label='GlobeStrike']").should(
"exist"
)
cy.get(".status-text").contains("Last published a few seconds ago") cy.get(".status-text").contains("Last published a few seconds ago")
}) })
}) })
it("Should allow the editing of the application icon and colour", () => { it("Should allow the editing of the application icon and colour", () => {
cy.visit(`${Cypress.config().baseUrl}/builder`) cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.get(".appTable", { timeout: 2000}) cy.get(".appTable .app-row-actions button")
.within(() => { .contains("Manage")
cy.get(".app-row-actions-icon").eq(0).click() .eq(0)
}) .click({ force: true })
cy.wait(1000)
cy.get(".app-overview-actions-icon").within(() => {
cy.get(".spectrum-Icon").click({ force: true })
})
cy.get(".spectrum-Menu").contains("Edit icon").click() cy.get(".spectrum-Menu").contains("Edit icon").click()
// Select random icon // Select random icon
cy.get(".grid").within(() => { cy.get(".grid").within(() => {
cy.get(".icon-item").eq(Math.floor(Math.random() * 23) + 1).click() cy.get(".icon-item")
.eq(Math.floor(Math.random() * 23) + 1)
.click()
}) })
// Select random colour // Select random colour
cy.get(".fill").click() cy.get(".fill").click()
cy.get(".colors").within(() => { cy.get(".colors").within(() => {
cy.get(".color").eq(Math.floor(Math.random() * 33) + 1).click() cy.get(".color")
.eq(Math.floor(Math.random() * 33) + 1)
.click()
}) })
cy.intercept('**/applications/**').as('iconChange') cy.intercept("**/applications/**").as("iconChange")
cy.get(".spectrum-Button").contains("Save").click({ force: true }) cy.get(".spectrum-Button").contains("Save").click({ force: true })
cy.wait("@iconChange") cy.wait("@iconChange")
cy.get("@iconChange").its('response.statusCode') cy.get("@iconChange").its("response.statusCode").should("eq", 200)
.should('eq', 200)
// Confirm icon has changed from default // Confirm icon has changed from default
// Confirm colour has been applied // Confirm colour has been applied
cy.get(".appTable", { timeout: 2000}) cy.get(".appTable", { timeout: 2000 }).within(() => {
.within(() => { cy.get("[aria-label]")
cy.get('[aria-label]').eq(0).children() .eq(0)
.should('have.attr', 'xlink:href').and('not.contain', '#spectrum-icon-18-Apps') .children()
cy.get(".title").children().children() .should("have.attr", "xlink:href")
.should('have.attr', 'style').and('contains', 'color') .and("not.contain", "#spectrum-icon-18-Apps")
}) cy.get(".title")
.children()
.children()
.should("have.attr", "style")
.and("contains", "color")
})
}) })
it("Should reflect the last time the application was edited", () => { it("Should reflect the last time the application was edited", () => {
cy.visit(`${Cypress.config().baseUrl}/builder`) cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.get(".appTable .name").eq(0).click() cy.get(".appTable .app-row-actions button")
.contains("Manage")
cy.get(".header-right button").contains("Edit").click({ force: true }); .eq(0)
.click({ force: true })
cy.get(".header-right button").contains("Edit").click({ force: true })
cy.navigateToFrontend() cy.navigateToFrontend()
@ -177,41 +211,51 @@ filterTests(['all'], () => {
}) })
cy.visit(`${Cypress.config().baseUrl}/builder`) cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.get(".appTable .name").eq(0).click() cy.get(".appTable .app-row-actions button")
.contains("Manage")
.eq(0)
.click({ force: true })
cy.get(".overview-tab [data-cy='edited-by']").within(() => { cy.get(".overview-tab [data-cy='edited-by']").within(() => {
cy.get(".editor-name").contains("You") cy.get(".editor-name").contains("You")
cy.get(".last-edit-text").contains("Last edited a few seconds ago") cy.get(".last-edit-text").contains("Last edited a few seconds ago")
}) })
}); })
it("Should reflect application version is up-to-date", () => { it("Should reflect application version is up-to-date", () => {
cy.visit(`${Cypress.config().baseUrl}/builder`) cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.get(".appTable .name").eq(0).click() cy.get(".appTable .app-row-actions button")
.contains("Manage")
.eq(0)
.click({ force: true })
cy.get(".overview-tab [data-cy='app-version']").within(() => { cy.get(".overview-tab [data-cy='app-version']").within(() => {
cy.get(".version-status").contains("You're running the latest!") cy.get(".version-status").contains("You're running the latest!")
}) })
}); })
it("Should navigate to the settings tab when clicking the App Version card header", () => { it("Should navigate to the settings tab when clicking the App Version card header", () => {
cy.visit(`${Cypress.config().baseUrl}/builder`) cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.get(".appTable .name").eq(0).click() cy.get(".appTable .app-row-actions button")
.contains("Manage")
.eq(0)
.click({ force: true })
cy.get(".spectrum-Tabs-item.is-selected").contains("Overview") cy.get(".spectrum-Tabs-item.is-selected").contains("Overview")
cy.get(".overview-tab").should("be.visible") cy.get(".overview-tab").should("be.visible")
cy.get(".overview-tab [data-cy='app-version'] .dash-card-header").click({ force : true }) cy.get(".overview-tab [data-cy='app-version'] .dash-card-header").click({
force: true,
})
cy.get(".spectrum-Tabs-item.is-selected").contains("Settings") cy.get(".spectrum-Tabs-item.is-selected").contains("Settings")
cy.get(".settings-tab").should("be.visible") cy.get(".settings-tab").should("be.visible")
cy.get(".overview-tab").should("not.exist") cy.get(".overview-tab").should("not.exist")
})
});
it("Should allow the upgrading of an application, if available.", () => { it("Should allow the upgrading of an application, if available.", () => {
cy.visit(`${Cypress.config().baseUrl}/builder`) cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.get(".appTable .name").eq(0).click() cy.get(".appTable .app-row-actions button")
.contains("Manage")
.eq(0)
.click({ force: true })
cy.wait(500) cy.wait(500)
cy.location().then(loc => { cy.location().then(loc => {
@ -219,8 +263,7 @@ filterTests(['all'], () => {
const appId = params[params.length - 1] const appId = params[params.length - 1]
cy.log(appId) cy.log(appId)
//Downgrade the app for the test //Downgrade the app for the test
cy.alterAppVersion(appId, "0.0.1-alpha.0") cy.alterAppVersion(appId, "0.0.1-alpha.0").then(() => {
.then(()=>{
cy.reload() cy.reload()
cy.wait(1000) cy.wait(1000)
cy.log("Current deployment version: " + clientPackage.version) cy.log("Current deployment version: " + clientPackage.version)
@ -228,115 +271,163 @@ filterTests(['all'], () => {
cy.get(".version-status a").contains("Update").click() cy.get(".version-status a").contains("Update").click()
cy.get(".spectrum-Tabs-item.is-selected").contains("Settings") cy.get(".spectrum-Tabs-item.is-selected").contains("Settings")
cy.get(".version-section .page-action button").contains("Update").click({ force: true }) cy.get(".version-section .page-action button")
.contains("Update")
cy.intercept('POST', '**/applications/**/client/update').as('updateVersion') .click({ force: true })
cy.get(".spectrum-Modal.is-open button").contains("Update").click({ force: true })
cy.intercept("POST", "**/applications/**/client/update").as(
"updateVersion"
)
cy.get(".spectrum-Modal.is-open button")
.contains("Update")
.click({ force: true })
cy.wait("@updateVersion") cy.wait("@updateVersion")
.its('response.statusCode').should('eq', 200) .its("response.statusCode")
.then(() => { .should("eq", 200)
cy.visit(`${Cypress.config().baseUrl}/builder`) .then(() => {
cy.get(".appTable .name").eq(0).click() cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.get(".appTable .app-row-actions button")
cy.get(".spectrum-Tabs-item").contains("Overview").click({ force: true }) .contains("Manage")
cy.get(".overview-tab [data-cy='app-version']").within(() => { .eq(0)
cy.get(".spectrum-Heading").contains(clientPackage.version) .click({ force: true })
cy.get(".version-status").contains("You're running the latest!") cy.get(".spectrum-Tabs-item")
.contains("Overview")
.click({ force: true })
cy.get(".overview-tab [data-cy='app-version']").within(() => {
cy.get(".spectrum-Heading").contains(clientPackage.version)
cy.get(".version-status").contains("You're running the latest!")
})
}) })
})
}) })
}); })
}) })
it("Should allow editing of the app details.", () => { it("Should allow editing of the app details.", () => {
cy.visit(`${Cypress.config().baseUrl}/builder`) cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.get(".appTable .name").eq(0).click() cy.get(".appTable .app-row-actions button")
.contains("Manage")
.eq(0)
.click({ force: true })
cy.get(".spectrum-Tabs-item").contains("Settings").click() cy.get(".spectrum-Tabs-item").contains("Settings").click()
cy.get(".spectrum-Tabs-item.is-selected").contains("Settings") cy.get(".spectrum-Tabs-item.is-selected").contains("Settings")
cy.get(".settings-tab").should("be.visible") cy.get(".settings-tab").should("be.visible")
cy.get(".details-section .page-action button").contains("Edit").click({ force: true }) cy.get(".details-section .page-action button")
.contains("Edit")
.click({ force: true })
cy.updateAppName("sample name") cy.updateAppName("sample name")
//publish and check its disabled //publish and check its disabled
cy.visit(`${Cypress.config().baseUrl}/builder`) cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.get(".appTable .app-row-actions button").contains("Edit").eq(0).click({force: true}) cy.get(".appTable .app-row-actions button")
.contains("Edit")
.eq(0)
.click({ force: true })
cy.get(".toprightnav button.spectrum-Button").contains("Publish").click({ force : true }) cy.get(".toprightnav button.spectrum-Button")
cy.get(".spectrum-Modal [data-cy='deploy-app-modal']").should("be.visible") .contains("Publish")
.within(() => { .click({ force: true })
cy.get(".spectrum-Button").contains("Publish").click({ force : true }) cy.get(".spectrum-Modal [data-cy='deploy-app-modal']")
cy.wait(1000) .should("be.visible")
}); .within(() => {
cy.get(".spectrum-Button").contains("Publish").click({ force: true })
cy.wait(1000)
})
cy.visit(`${Cypress.config().baseUrl}/builder`) cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.wait(1000) cy.wait(1000)
cy.get(".appTable .name").eq(0).click() cy.get(".appTable .app-row-actions button")
.contains("Manage")
.eq(0)
.click({ force: true })
cy.get(".spectrum-Tabs-item").contains("Settings").click() cy.get(".spectrum-Tabs-item").contains("Settings").click()
cy.get(".spectrum-Tabs-item.is-selected").contains("Settings") cy.get(".spectrum-Tabs-item.is-selected").contains("Settings")
cy.get(".details-section .page-action .spectrum-Button").scrollIntoView() cy.get(".details-section .page-action .spectrum-Button").scrollIntoView()
cy.wait(1000) cy.wait(1000)
cy.get(".details-section .page-action .spectrum-Button").should("be.disabled") cy.get(".details-section .page-action .spectrum-Button").should(
"be.disabled"
)
}) })
xit("Should allow copying of the published application Id", () => { xit("Should allow copying of the published application Id", () => {
cy.visit(`${Cypress.config().baseUrl}/builder`) cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.get(".appTable .app-row-actions").eq(0) cy.get(".appTable .app-row-actions")
.within(() => { .eq(0)
cy.get(".spectrum-Button").contains("Edit").click({ force: true }) .within(() => {
}) cy.get(".spectrum-Button").contains("Edit").click({ force: true })
})
cy.publishApp("sample-name") cy.publishApp("sample-name")
cy.visit(`${Cypress.config().baseUrl}/builder`) cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.get(".appTable .name").eq(0).click() cy.get(".appTable .app-row-actions button")
.contains("Manage")
.eq(0)
.click({ force: true })
cy.get(".app-overview-actions-icon > .icon").click({ force: true })
cy.get(".app-overview-actions-icon > .icon").click({ force : true }) cy.get("[data-cy='app-overview-menu-popover']")
.eq(0)
.within(() => {
cy.get(".spectrum-Menu-item")
.contains("Copy App ID")
.click({ force: true })
})
cy.get("[data-cy='app-overview-menu-popover']").eq(0).within(() => { cy.get(".spectrum-Toast-content")
cy.get(".spectrum-Menu-item").contains("Copy App ID").click({ force: true }) .contains("App ID copied to clipboard.")
}) .should("be.visible")
cy.get(".spectrum-Toast-content").contains("App ID copied to clipboard.").should("be.visible")
}) })
it("Should allow unpublishing of the application", () => { it("Should allow unpublishing of the application", () => {
cy.visit(`${Cypress.config().baseUrl}/builder`) cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.get(".appTable .name").eq(0).click() cy.get(".appTable .app-row-actions button")
.contains("Manage")
.eq(0)
.click({ force: true })
cy.get(".app-overview-actions-icon > .icon").click({ force: true })
cy.get(".app-overview-actions-icon > .icon").click({ force : true }) cy.get("[data-cy='app-overview-menu-popover']")
.eq(0)
.within(() => {
cy.get(".spectrum-Menu-item")
.contains("Unpublish")
.click({ force: true })
cy.wait(500)
})
cy.get("[data-cy='app-overview-menu-popover']").eq(0).within(() => { cy.get("[data-cy='unpublish-modal']")
cy.get(".spectrum-Menu-item").contains("Unpublish").click({ force: true }) .should("be.visible")
cy.wait(500) .within(() => {
}) cy.get(".confirm-wrap button").click({ force: true })
})
cy.get("[data-cy='unpublish-modal']").should("be.visible")
.within(() => {
cy.get(".confirm-wrap button").click({ force: true }
)})
cy.get(".overview-tab [data-cy='app-status']").within(() => { cy.get(".overview-tab [data-cy='app-status']").within(() => {
cy.get(".status-display").contains("Unpublished") cy.get(".status-display").contains("Unpublished")
cy.get(".status-display .icon svg[aria-label='GlobeStrike']").should("exist") cy.get(".status-display .icon svg[aria-label='GlobeStrike']").should(
"exist"
)
}) })
}) })
it("Should allow deleting of the application", () => { it("Should allow deleting of the application", () => {
cy.visit(`${Cypress.config().baseUrl}/builder`) cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.get(".appTable .name").eq(0).click() cy.get(".appTable .app-row-actions button")
.contains("Manage")
.eq(0)
.click({ force: true })
cy.get(".app-overview-actions-icon > .icon").click({ force: true })
cy.get(".app-overview-actions-icon > .icon").click({ force : true }) cy.get("[data-cy='app-overview-menu-popover']")
.eq(0)
cy.get("[data-cy='app-overview-menu-popover']").eq(0).within(() => { .within(() => {
cy.get(".spectrum-Menu-item").contains("Delete").click({ force: true }) cy.get(".spectrum-Menu-item")
cy.wait(500) .contains("Delete")
}) .click({ force: true })
cy.wait(500)
})
//The test application was renamed earlier in the spec //The test application was renamed earlier in the spec
cy.get(".spectrum-Dialog-grid").within(() => { cy.get(".spectrum-Dialog-grid").within(() => {
@ -344,18 +435,17 @@ filterTests(['all'], () => {
cy.get(".spectrum-Button--warning").click() cy.get(".spectrum-Button--warning").click()
}) })
cy.location().should((loc) => { cy.location().should(loc => {
expect(loc.pathname).to.eq('/builder/portal/apps') expect(loc.pathname).to.eq("/builder/portal/apps")
}) })
cy.get(".appTable").should("not.exist") cy.get(".appTable").should("not.exist")
cy.get(".welcome .container h1").contains("Let's create your first app!") cy.get(".welcome .container h1").contains("Let's create your first app!")
}) })
after(() => { after(() => {
cy.deleteAllApps() cy.deleteAllApps()
}) })
}) })
}) })

View File

@ -1,7 +1,7 @@
import filterTests from "../support/filterTests" import filterTests from "../support/filterTests"
const interact = require('../support/interact') const interact = require("../support/interact")
filterTests(['all'], () => { filterTests(["all"], () => {
context("Create Components", () => { context("Create Components", () => {
let headlineId let headlineId
@ -19,17 +19,15 @@ filterTests(['all'], () => {
//Use the tree to delete a selected component //Use the tree to delete a selected component
const deleteSelectedComponent = () => { const deleteSelectedComponent = () => {
cy.get(".nav-items-container .nav-item.selected .actions > div > .icon").click({ cy.get(
".nav-items-container .nav-item.selected .actions > div > .icon"
).click({
force: true,
})
cy.get(".spectrum-Popover.is-open li").contains("Delete").click()
cy.get(".spectrum-Modal button").contains("Delete Component").click({
force: true, force: true,
}) })
cy.get(".spectrum-Popover.is-open li")
.contains("Delete")
.click()
cy.get(".spectrum-Modal button")
.contains("Delete Component")
.click({
force: true,
})
} }
it("should add a container", () => { it("should add a container", () => {
@ -47,18 +45,18 @@ filterTests(['all'], () => {
it("should change the text of the headline", () => { it("should change the text of the headline", () => {
const text = "Lorem ipsum dolor sit amet." const text = "Lorem ipsum dolor sit amet."
cy.get("[data-cy=setting-text] input") cy.get("[data-cy=setting-text] input").type(text).blur()
.type(text)
.blur()
cy.getComponent(headlineId).should("have.text", text) cy.getComponent(headlineId).should("have.text", text)
}) })
it("should change the size of the headline", () => { it("should change the size of the headline", () => {
cy.get("[data-cy=setting-size]").scrollIntoView().click() cy.get("[data-cy=setting-size]").scrollIntoView().click()
cy.get("[data-cy=setting-size]").within(() => { cy.get("[data-cy=setting-size]").within(() => {
cy.get(".spectrum-Form-item li.spectrum-Menu-item").contains("3XL").click() cy.get(".spectrum-Form-item li.spectrum-Menu-item")
.contains("3XL")
.click()
}) })
cy.getComponent(headlineId).within(() => { cy.getComponent(headlineId).within(() => {
cy.get(".spectrum-Heading").should("have.css", "font-size", "60px") cy.get(".spectrum-Heading").should("have.css", "font-size", "60px")
}) })
@ -66,13 +64,9 @@ filterTests(['all'], () => {
it("should create a form and reset to match schema", () => { it("should create a form and reset to match schema", () => {
cy.addComponent("Form", "Form").then(() => { cy.addComponent("Form", "Form").then(() => {
cy.get("[data-cy=setting-dataSource]") cy.get("[data-cy=setting-dataSource]").contains("Custom").click()
.contains("Custom") cy.get(interact.DROPDOWN).contains("dog").click()
.click() cy.wait(500)
cy.get(interact.DROPDOWN)
.contains("dog")
.click()
cy.wait(500)
cy.addComponent("Form", "Field Group").then(fieldGroupId => { cy.addComponent("Form", "Field Group").then(fieldGroupId => {
cy.contains("Update form fields").click() cy.contains("Update form fields").click()
cy.get(".spectrum-Modal") cy.get(".spectrum-Modal")
@ -83,11 +77,9 @@ filterTests(['all'], () => {
cy.contains("name").should("exist") cy.contains("name").should("exist")
cy.contains("age").should("exist") cy.contains("age").should("exist")
cy.contains("breed").should("exist") cy.contains("breed").should("exist")
// cy.contains("image").should("exist") // cy.contains("image").should("exist")
}) })
cy.getComponent(fieldGroupId) cy.getComponent(fieldGroupId).find("input").should("have.length", 2)
.find("input")
.should("have.length", 2)
cy.getComponent(fieldGroupId) cy.getComponent(fieldGroupId)
.find(interact.SPECTRUM_PICKER) .find(interact.SPECTRUM_PICKER)
.should("have.length", 1) .should("have.length", 1)
@ -97,191 +89,102 @@ filterTests(['all'], () => {
it("deletes a component", () => { it("deletes a component", () => {
cy.addComponent("Elements", "Paragraph").then(componentId => { cy.addComponent("Elements", "Paragraph").then(componentId => {
cy.get("[data-cy=setting-_instanceName] input") cy.get("[data-cy=setting-_instanceName] input").type(componentId).blur()
.type(componentId) cy.get(
.blur() ".nav-items-container .nav-item.selected .actions > div > .icon"
cy.get(".nav-items-container .nav-item.selected .actions > div > .icon").click({ ).click({
force: true,
})
cy.get(".spectrum-Popover.is-open li").contains("Delete").click()
cy.get(".spectrum-Modal button").contains("Delete Component").click({
force: true, force: true,
}) })
cy.get(".spectrum-Popover.is-open li")
.contains("Delete")
.click()
cy.get(".spectrum-Modal button")
.contains("Delete Component")
.click({
force: true,
})
cy.getComponent(componentId).should("not.exist") cy.getComponent(componentId).should("not.exist")
}) })
}) })
it("should set focus to the field setting when fields are added to a form", () => {
cy.addComponent("Form", "Form").then((formId) => {
//For deletion
cy.get("[data-cy=setting-_instanceName] input")
.clear()
.type(formId)
.blur()
const componentTypeLabels = ["Text Field", "Number Field", "Password Field",
"Options Picker", "Checkbox", "Long Form Field", "Date Picker", "Attachment",
"JSON Field", "Multi-select Picker", "Relationship Picker"]
const refocusTest = (componentId) => {
cy.getComponent(componentId)
.find(".showMe").should("exist").click({ force: true })
cy.get("[data-cy=setting-field] .spectrum-InputGroup")
.should("have.class", "is-focused")
}
const testFieldFocusOnCreate = (componentLabel) => {
cy.log("Adding: " + componentLabel)
return cy.addComponent("Form", componentLabel).then((componentId) => {
refocusTest(componentId)
cy.get("[data-cy=setting-field] .spectrum-InputGroup")
.should("have.class", "is-focused")
})
}
cy.wait(1000)
cy.wrap(componentTypeLabels).each((label) => {
return testFieldFocusOnCreate(label)
}).then(()=>{
cy.get(".nav-items-container .nav-item").contains(formId).click({ force: true })
deleteSelectedComponent()
})
})
})
it("should clear the iframe place holder when a form field has been set", () => { it("should clear the iframe place holder when a form field has been set", () => {
cy.addComponent("Form", "Form").then((formId) => { cy.addComponent("Form", "Form").then(formId => {
//For deletion //For deletion
cy.get("[data-cy=setting-_instanceName] input") cy.get("[data-cy=setting-_instanceName] input")
.clear() .clear()
.type(formId) .type(formId)
.blur() .blur()
cy.get("[data-cy=setting-dataSource]").contains("Custom").click()
cy.get("[data-cy=setting-dataSource]") cy.get(".dropdown").contains("dog").click()
.contains("Custom")
.click()
cy.get(".dropdown")
.contains("dog")
.click()
const fieldTypeToColumnName = { const fieldTypeToColumnName = {
"Text Field" : "name", "Text Field": "name",
"Number Field": "age", "Number Field": "age",
"Options Picker": "breed" "Options Picker": "breed",
} }
const componentTypeLabels = Object.keys(fieldTypeToColumnName) const componentTypeLabels = Object.keys(fieldTypeToColumnName)
const testFieldFocusOnCreate = (componentLabel) => { const testFieldFocusOnCreate = componentLabel => {
cy.log("Adding: " + componentLabel) cy.log("Adding: " + componentLabel)
return cy.addComponent("Form", componentLabel).then((componentId) => { return cy.addComponent("Form", componentLabel).then(componentId => {
cy.getComponent(componentId) cy.getComponent(componentId)
.find(".placeholder_wrap").should("exist") .find(".component-placeholder")
.should("exist")
cy.get("[data-cy=setting-field] .spectrum-InputGroup")
.should("have.class", "is-focused")
cy.get("[data-cy=setting-field] button.spectrum-Picker").click() cy.get("[data-cy=setting-field] button.spectrum-Picker").click()
//Click the first appropriate field. They are filtered by type //Click the first appropriate field. They are filtered by type
cy.get("[data-cy=setting-field] .spectrum-Popover.is-open li.spectrum-Menu-item") cy.get(
.contains(fieldTypeToColumnName[componentLabel]).click() "[data-cy=setting-field] .spectrum-Popover.is-open li.spectrum-Menu-item"
)
.contains(fieldTypeToColumnName[componentLabel])
.click()
cy.wait(500) cy.wait(500)
cy.getComponent(componentId) cy.getComponent(componentId)
.find(".placeholder_wrap").should("not.exist") .find(".component-placeholder")
.should("not.exist")
}) })
} }
cy.wait(500) cy.wait(500)
cy.wrap(componentTypeLabels).each((label) => { cy.wrap(componentTypeLabels)
return testFieldFocusOnCreate(label) .each(label => {
}).then(()=>{ return testFieldFocusOnCreate(label)
cy.get(".nav-items-container .nav-item").contains(formId).click({ force: true }) })
deleteSelectedComponent() .then(() => {
}) cy.get(".nav-items-container .nav-item")
}) .contains(formId)
}) .click({ force: true })
deleteSelectedComponent()
it("should focus a charts settings on data provider if not nested in provider ", () => {
cy.addComponent("Layout", "Container").then((containerId) => {
//For deletion
cy.get("[data-cy=setting-_instanceName] input")
.clear()
.type(containerId)
.blur()
const chartTypeLabels = ["Bar Chart", "Line Chart", "Area Chart", "Pie Chart",
"Donut Chart", "Candlestick Chart"]
const refocusTest = (componentId) => {
cy.getComponent(componentId)
.find(".showMe").should("exist").click({ force: true })
cy.get("[data-cy=dataProvider-prop-control] .spectrum-Picker")
.should("have.class", "is-focused")
}
const testFocusOnCreate = (chartLabel) => {
cy.log("Adding: " + chartLabel)
cy.addComponent("Chart", chartLabel).then((componentId) => {
refocusTest(componentId)
cy.get("[data-cy=dataProvider-prop-control] .spectrum-Picker")
.should("have.class", "is-focused")
}) })
}
cy.wait(1000)
cy.wrap(chartTypeLabels).each((label) => {
return testFocusOnCreate(label)
})
.then(()=>{
cy.get(".nav-items-container .nav-item").contains(containerId).click({ force: true })
deleteSelectedComponent()
})
}) })
}) })
it("should populate the provider for charts with a data provider in its path", () => { it("should populate the provider for charts with a data provider in its path", () => {
cy.addComponent("Data", "Data Provider").then((providerId) => { cy.addComponent("Data", "Data Provider").then(providerId => {
//For deletion //For deletion
cy.get("[data-cy=setting-_instanceName] input") cy.get("[data-cy=setting-_instanceName] input")
.clear() .clear()
.type(providerId) .type(providerId)
.blur() .blur()
cy.get("[data-cy=setting-dataSource]") cy.get("[data-cy=setting-dataSource]")
.contains("Choose an option") .contains("Choose an option")
.click() .click()
cy.get(`[data-cy=dataSource-popover-${providerId}] ul li`) cy.get(`[data-cy=dataSource-popover-${providerId}] ul li`)
.contains("dog") .contains("dog")
.click() .click()
const chartTypeLabels = ["Bar Chart", "Line Chart", "Area Chart", "Pie Chart", const chartTypeLabels = [
"Donut Chart", "Candlestick Chart"] "Bar Chart",
"Line Chart",
"Area Chart",
"Pie Chart",
"Donut Chart",
"Candlestick Chart",
]
const testFocusOnCreate = (chartLabel) => { const testFocusOnCreate = chartLabel => {
cy.log("Adding: " + chartLabel) cy.log("Adding: " + chartLabel)
cy.addComponent("Chart", chartLabel).then((componentId) => { cy.addComponent("Chart", chartLabel).then(componentId => {
cy.get(
cy.get("[data-cy=dataProvider-prop-control] .spectrum-Picker") "[data-cy=dataProvider-prop-control] .spectrum-Picker"
.should("not.have.class", "is-focused") ).should("not.have.class", "is-focused")
// Pre populated. // Pre populated.
cy.get("[data-cy=dataProvider-prop-control] .spectrum-Picker-label") cy.get("[data-cy=dataProvider-prop-control] .spectrum-Picker-label")
@ -289,114 +192,86 @@ filterTests(['all'], () => {
.should("exist") .should("exist")
}) })
} }
cy.wait(1000) cy.wait(1000)
cy.wrap(chartTypeLabels).each((label) => { cy.wrap(chartTypeLabels)
return testFocusOnCreate(label) .each(label => {
}) return testFocusOnCreate(label)
.then(()=>{ })
cy.get(".nav-items-container .nav-item").contains(providerId).click({ force: true }) .then(() => {
deleteSelectedComponent() cy.get(".nav-items-container .nav-item")
}) .contains(providerId)
.click({ force: true })
deleteSelectedComponent()
})
}) })
}) })
it("should replace the placeholder when a url is set on an image", () => { it("should replace the placeholder when a url is set on an image", () => {
cy.addComponent("Elements", "Image").then((imageId) => { cy.addComponent("Elements", "Image").then(imageId => {
cy.get("[data-cy=url-prop-control] .spectrum-InputGroup")
.should("have.class", "is-focused")
cy.get("[data-cy=setting-_instanceName] input") cy.get("[data-cy=setting-_instanceName] input")
.clear() .clear()
.type(imageId) .type(imageId)
.blur() .blur()
//return $("New Data Provider.Rows")[0]["Attachment"][0]["url"] //return $("New Data Provider.Rows")[0]["Attachment"][0]["url"]
//No minio, so just enter something local that will not reslove //No minio, so just enter something local that will not reslove
cy.get("[data-cy=url-prop-control] input[type=text]") cy.get("[data-cy=url-prop-control] input[type=text]")
.type("cypress/fixtures/ghost.png") .type("cypress/fixtures/ghost.png")
.blur() .blur()
cy.getComponent(imageId) cy.getComponent(imageId)
.find(".placeholder_wrap").should("not.exist") .find(".component-placeholder")
.should("not.exist")
cy.getComponent(imageId) cy.getComponent(imageId).find(`img[alt=${imageId}]`).should("exist")
.find(`img[alt=${imageId}]`).should("exist")
cy.get(".nav-items-container .nav-item") cy.get(".nav-items-container .nav-item")
.contains(imageId) .contains(imageId)
.click({ force: true }) .click({ force: true })
deleteSelectedComponent() deleteSelectedComponent()
}) })
}) })
it("should add a markdown component.", () => { it("should add a markdown component.", () => {
cy.addComponent("Elements", "Markdown Viewer").then((markdownId) => { cy.addComponent("Elements", "Markdown Viewer").then(markdownId => {
cy.get("[data-cy=value-prop-control] .spectrum-InputGroup")
.should("have.class", "is-focused")
cy.get("[data-cy=setting-_instanceName] input") cy.get("[data-cy=setting-_instanceName] input")
.clear() .clear()
.type(markdownId) .type(markdownId)
.blur() .blur()
cy.get(
cy.get("[data-cy=value-prop-control] input[type=text].spectrum-Textfield-input") "[data-cy=value-prop-control] input[type=text].spectrum-Textfield-input"
.type("# Hi").blur() )
.type("# Hi")
.blur()
cy.getComponent(markdownId) cy.getComponent(markdownId)
.find(".placeholder_wrap").should("not.exist") .find(".component-placeholder")
.should("not.exist")
cy.getComponent(markdownId) cy.getComponent(markdownId)
.find(".editor-preview-full h1").contains("Hi") .find(".editor-preview-full h1")
.contains("Hi")
cy.get(".nav-items-container .nav-item") cy.get(".nav-items-container .nav-item")
.contains(markdownId) .contains(markdownId)
.click({ force: true }) .click({ force: true })
deleteSelectedComponent() deleteSelectedComponent()
}) })
}) })
it("should direct the user when adding an Icon component.", () => { it("should direct the user when adding an Icon component.", () => {
cy.addComponent("Elements", "Icon").then((iconId) => { cy.addComponent("Elements", "Icon").then(iconId => {
cy.getComponent(iconId).find(".component-placeholder").should("exist")
cy.get("[data-cy=icon-prop-control] .spectrum-ActionButton")
.should("have.class", "is-focused")
cy.getComponent(iconId)
.find(".placeholder_wrap").should("exist")
cy.get("[data-cy=setting-_instanceName] input") cy.get("[data-cy=setting-_instanceName] input")
.clear() .clear()
.type(iconId) .type(iconId)
.blur() .blur()
cy.get("[data-cy=icon-prop-control] .spectrum-ActionButton").click() cy.get("[data-cy=icon-prop-control] .spectrum-ActionButton").click()
cy.get("[data-cy=icon-popover].spectrum-Popover.is-open").within(() => { cy.get("[data-cy=icon-popover].spectrum-Popover.is-open").within(() => {
cy.get(".search-input input") cy.get(".search-input input").type("save").blur()
.type("save")
.blur()
cy.get(".search-input button").click({ force: true }) cy.get(".search-input button").click({ force: true })
cy.get(".icon-area .icon-container").eq(0).click({ force: true }) cy.get(".icon-area .icon-container").eq(0).click({ force: true })
}) })
cy.getComponent(iconId) cy.getComponent(iconId)
.find(".placeholder_wrap").should("not.exist") .find(".component-placeholder")
.should("not.exist")
cy.getComponent(iconId) cy.getComponent(iconId).find("i.ri-save-fill").should("exist")
.find("i.ri-save-fill").should("exist")
cy.get(".nav-items-container .nav-item") cy.get(".nav-items-container .nav-item")
.contains(iconId) .contains(iconId)
.click({ force: true }) .click({ force: true })
deleteSelectedComponent() deleteSelectedComponent()
}) })
}) })

View File

@ -1,7 +1,7 @@
import filterTests from "../support/filterTests" import filterTests from "../support/filterTests"
const interact = require('../support/interact') const interact = require("../support/interact")
filterTests(['all'], () => { filterTests(["all"], () => {
context("Rename an App", () => { context("Rename an App", () => {
beforeEach(() => { beforeEach(() => {
cy.login() cy.login()
@ -32,12 +32,15 @@ filterTests(['all'], () => {
const appRename = "Cypress Renamed" const appRename = "Cypress Renamed"
// Publish the app // Publish the app
cy.get(interact.TOP_RIGHT_NAV) cy.get(interact.TOP_RIGHT_NAV)
cy.get(interact.SPECTRUM_BUTTON).contains("Publish").click({ force: true }) cy.get(interact.SPECTRUM_BUTTON)
cy.get(interact.SPECTRUM_DIALOG_GRID) .contains("Publish")
.within(() => { .click({ force: true })
// Click publish again within the modal cy.get(interact.SPECTRUM_DIALOG_GRID).within(() => {
cy.get(interact.SPECTRUM_BUTTON).contains("Publish").click({ force: true }) // Click publish again within the modal
}) cy.get(interact.SPECTRUM_BUTTON)
.contains("Publish")
.click({ force: true })
})
// Rename app, Search for app, Confirm name was changed // Rename app, Search for app, Confirm name was changed
cy.visit(`${Cypress.config().baseUrl}/builder`) cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.wait(500) cy.wait(500)
@ -64,14 +67,20 @@ filterTests(['all'], () => {
const appName = "Cypress Tests" const appName = "Cypress Tests"
cy.visit(`${Cypress.config().baseUrl}/builder`) cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.wait(500) cy.wait(500)
cy.get(interact.SPECTRUM_BUTTON).contains("Create app").click({ force: true }) cy.get(interact.SPECTRUM_BUTTON)
.contains("Create app")
.click({ force: true })
cy.contains(/Start from scratch/).click() cy.contains(/Start from scratch/).click()
cy.get(interact.SPECTRUM_MODAL) cy.get(interact.SPECTRUM_MODAL).within(() => {
.within(() => { cy.get("input").eq(0).type(appName)
cy.get("input").eq(0).type(appName) cy.get(interact.SPECTRUM_BUTTON_GROUP)
cy.get(interact.SPECTRUM_BUTTON_GROUP).contains("Create app").click({ force: true }) .contains("Create app")
cy.get(interact.ERROR).should("have.text", "Another app with the same name already exists") .click({ force: true })
}) cy.get(interact.ERROR).should(
"have.text",
"Another app with the same name already exists"
)
})
}) })
it("should validate application names", () => { it("should validate application names", () => {
@ -89,7 +98,10 @@ filterTests(['all'], () => {
cy.reload() cy.reload()
cy.wait(1000) cy.wait(1000)
renameApp(numberName, specialCharName) renameApp(numberName, specialCharName)
cy.get(interact.ERROR).should("have.text", "App name must be letters, numbers and spaces only") cy.get(interact.ERROR).should(
"have.text",
"App name must be letters, numbers and spaces only"
)
// Set app name back to Cypress Tests // Set app name back to Cypress Tests
cy.reload() cy.reload()
cy.wait(1000) cy.wait(1000)
@ -98,24 +110,19 @@ filterTests(['all'], () => {
const renameApp = (originalName, changedName, published, noName) => { const renameApp = (originalName, changedName, published, noName) => {
cy.searchForApplication(originalName) cy.searchForApplication(originalName)
cy.get(interact.APP_TABLE) cy.get(interact.APP_TABLE).within(() => {
.within(() => { cy.get(".app-row-actions button")
cy.get(interact.AREA_LABEL_MORE).eq(0).click() .contains("Manage")
}) .eq(0)
// Check for when an app is published .click({ force: true })
if (published == true) {
// Should not have Edit as option, will unpublish app
cy.should("not.have.value", "Edit")
cy.get(interact.SPECTRUM_MENU).contains("Unpublish").click()
cy.get(interact.SPECTRUM_DIALOG_GRID).contains("Unpublish app").click()
cy.get(".appTable > :nth-child(5) > :nth-child(2) > .spectrum-Icon").click()
}
cy.get(interact.APP_ROW_ACTION_MENU_POPOVER).eq(0).within(() => {
cy.get(interact.SPECTRUM_MENU_ITEMM).contains("Edit").click({ force: true })
}) })
cy.get(".spectrum-Tabs-item").contains("Settings").click()
cy.get(".spectrum-Tabs-item.is-selected").contains("Settings")
cy.get(".settings-tab").should("be.visible")
cy.get(".details-section .page-action button")
.contains("Edit")
.click({ force: true })
cy.updateAppName(changedName, noName) cy.updateAppName(changedName, noName)
} }
}) })
}) })

View File

@ -144,20 +144,27 @@ Cypress.Commands.add("deleteApp", name => {
return return
} }
// Go to app overview
const appIdParsed = appId.split("_").pop() const appIdParsed = appId.split("_").pop()
const actionEleId = `[data-cy=row_actions_${appIdParsed}]` const actionEleId = `[data-cy=row_actions_${appIdParsed}]`
cy.get(actionEleId).within(() => { cy.get(actionEleId).within(() => {
cy.get(".spectrum-Icon").eq(0).click({ force: true }) cy.contains("Manage").click({ force: true })
}) })
cy.get(".spectrum-Menu").then($menu => { cy.wait(1000)
if ($menu.text().includes("Unpublish")) {
cy.get(".spectrum-Menu").contains("Unpublish").click() // Unpublish first if needed
cy.get(".spectrum-Dialog-grid").contains("Unpublish app").click() cy.get(`[data-cy="app-status"]`).then($status => {
if ($status.text().includes("Last published")) {
cy.contains("Unpublish").click()
cy.get(".spectrum-Modal").within(() => {
cy.contains("Unpublish app").click()
})
} }
}) })
cy.get(actionEleId).within(() => { // Delete app
cy.get(".spectrum-Icon").eq(0).click({ force: true }) cy.get(".app-overview-actions-icon").within(() => {
cy.get(".spectrum-Icon").click({ force: true })
}) })
cy.get(".spectrum-Menu").contains("Delete").click() cy.get(".spectrum-Menu").contains("Delete").click()
cy.get(".spectrum-Dialog-grid").within(() => { cy.get(".spectrum-Dialog-grid").within(() => {
@ -180,17 +187,7 @@ Cypress.Commands.add("deleteAllApps", () => {
.its("body") .its("body")
.then(val => { .then(val => {
for (let i = 0; i < val.length; i++) { for (let i = 0; i < val.length; i++) {
const appIdParsed = val[i].appId.split("_").pop() cy.deleteApp(val[i].name)
const actionEleId = `[data-cy=row_actions_${appIdParsed}]`
cy.get(actionEleId).within(() => {
cy.get(".spectrum-Icon").eq(0).click({ force: true })
})
cy.get(".spectrum-Menu").contains("Delete").click()
cy.get(".spectrum-Dialog-grid").within(() => {
cy.get("input").type(val[i].name)
cy.get(".spectrum-Button--warning").click()
})
cy.reload() cy.reload()
} }
}) })

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/builder", "name": "@budibase/builder",
"version": "1.0.206", "version": "1.0.207-alpha.5",
"license": "GPL-3.0", "license": "GPL-3.0",
"private": true, "private": true,
"scripts": { "scripts": {
@ -69,10 +69,10 @@
} }
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "^1.0.206", "@budibase/bbui": "^1.0.207-alpha.5",
"@budibase/client": "^1.0.206", "@budibase/client": "^1.0.207-alpha.5",
"@budibase/frontend-core": "^1.0.206", "@budibase/frontend-core": "^1.0.207-alpha.5",
"@budibase/string-templates": "^1.0.206", "@budibase/string-templates": "^1.0.207-alpha.5",
"@sentry/browser": "5.19.1", "@sentry/browser": "5.19.1",
"@spectrum-css/page": "^3.0.1", "@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1", "@spectrum-css/vars": "^3.0.1",

View File

@ -87,7 +87,7 @@
{#if lockedByYou && getExpiryDuration(app) > 0} {#if lockedByYou && getExpiryDuration(app) > 0}
<span class="lock-expiry-body"> <span class="lock-expiry-body">
{processStringSync( {processStringSync(
"This lock will expire in {{ duration time 'millisecond' }} from now", "This lock will expire in {{ duration time 'millisecond' }} from now.",
{ {
time: getExpiryDuration(app), time: getExpiryDuration(app),
} }
@ -141,4 +141,8 @@
gap: var(--spacing-s); gap: var(--spacing-s);
max-width: 175px; max-width: 175px;
} }
.lock-status-text {
font-weight: 400;
color: var(--spectrum-global-color-gray-800);
}
</style> </style>

View File

@ -54,8 +54,9 @@
} }
const onFieldChange = (expression, field) => { const onFieldChange = (expression, field) => {
// Update the field type // Update the field types
expression.type = enrichedSchemaFields.find(x => x.name === field)?.type expression.type = enrichedSchemaFields.find(x => x.name === field)?.type
expression.externalType = getSchema(expression)?.externalType
// Ensure a valid operator is set // Ensure a valid operator is set
const validOperators = LuceneUtils.getValidOperatorsForType( const validOperators = LuceneUtils.getValidOperatorsForType(

View File

@ -1,18 +1,11 @@
<script> <script>
import { Heading, Button, Icon, ActionMenu, MenuItem } from "@budibase/bbui" import { Heading, Button, Icon } from "@budibase/bbui"
import AppLockModal from "../common/AppLockModal.svelte" import AppLockModal from "../common/AppLockModal.svelte"
import { processStringSync } from "@budibase/string-templates" import { processStringSync } from "@budibase/string-templates"
export let app export let app
export let exportApp
export let editApp export let editApp
export let updateApp
export let deleteApp
export let unpublishApp
export let appOverview export let appOverview
export let releaseLock
export let editIcon
export let copyAppId
</script> </script>
<div class="title" data-cy={`${app.devId}`}> <div class="title" data-cy={`${app.devId}`}>
@ -20,7 +13,7 @@
<div class="app-icon" style="color: {app.icon?.color || ''}"> <div class="app-icon" style="color: {app.icon?.color || ''}">
<Icon size="XL" name={app.icon?.name || "Apps"} /> <Icon size="XL" name={app.icon?.name || "Apps"} />
</div> </div>
<div class="name" data-cy="app-name-link" on:click={() => appOverview(app)}> <div class="name" data-cy="app-name-link" on:click={() => editApp(app)}>
<Heading size="XS"> <Heading size="XS">
{app.name} {app.name}
</Heading> </Heading>
@ -37,7 +30,7 @@
{/if} {/if}
</div> </div>
<div class="desktop"> <div class="desktop">
<AppLockModal {app} buttonSize="S" /> <AppLockModal {app} buttonSize="M" />
</div> </div>
<div class="desktop"> <div class="desktop">
<div class="app-status"> <div class="app-status">
@ -52,47 +45,27 @@
</div> </div>
<div data-cy={`row_actions_${app.appId}`}> <div data-cy={`row_actions_${app.appId}`}>
<div class="app-row-actions"> <div class="app-row-actions">
<Button size="S" secondary newStyles on:click={() => appOverview(app)}>
Manage
</Button>
<Button <Button
size="S" size="S"
secondary primary
quiet newStyles
disabled={app.lockedOther} disabled={app.lockedOther}
on:click={() => editApp(app)} on:click={() => editApp(app)}
>Edit >
Edit
</Button> </Button>
<Button size="S" cta on:click={() => appOverview(app)}>View</Button>
</div> </div>
<ActionMenu align="right" dataCy="app-row-actions-menu-popover">
<span slot="control" class="app-row-actions-icon">
<Icon hoverable name="More" />
</span>
{#if app.lockedYou}
<MenuItem on:click={() => releaseLock(app)} icon="LockOpen">
Release lock
</MenuItem>
{/if}
<MenuItem on:click={() => exportApp(app)} icon="Download">Export</MenuItem>
{#if app.deployed}
<MenuItem on:click={() => unpublishApp(app)} icon="GlobeRemove">
Unpublish
</MenuItem>
<MenuItem on:click={() => copyAppId(app)} icon="Copy">
Copy App ID
</MenuItem>
{/if}
{#if !app.deployed}
<MenuItem on:click={() => updateApp(app)} icon="Edit">Edit</MenuItem>
<MenuItem on:click={() => deleteApp(app)} icon="Delete">Delete</MenuItem>
{/if}
<MenuItem on:click={() => editIcon(app)} icon="Brush">Edit icon</MenuItem>
</ActionMenu>
</div> </div>
<style> <style>
.app-row-actions { .app-row-actions {
grid-gap: var(--spacing-s); grid-gap: var(--spacing-s);
display: grid; display: flex;
grid-template-columns: 75px 75px; flex-direction: row;
justify-content: flex-end;
} }
.app-status { .app-status {
display: grid; display: grid;

View File

@ -3,20 +3,17 @@
Heading, Heading,
Layout, Layout,
Button, Button,
Input,
Select, Select,
Modal, Modal,
Page, Page,
notifications, notifications,
Body, Body,
Search, Search,
Helpers,
} from "@budibase/bbui" } from "@budibase/bbui"
import TemplateDisplay from "components/common/TemplateDisplay.svelte" import TemplateDisplay from "components/common/TemplateDisplay.svelte"
import Spinner from "components/common/Spinner.svelte" import Spinner from "components/common/Spinner.svelte"
import CreateAppModal from "components/start/CreateAppModal.svelte" import CreateAppModal from "components/start/CreateAppModal.svelte"
import UpdateAppModal from "components/start/UpdateAppModal.svelte" import UpdateAppModal from "components/start/UpdateAppModal.svelte"
import ChooseIconModal from "components/start/ChooseIconModal.svelte"
import ExportAppModal from "components/start/ExportAppModal.svelte" import ExportAppModal from "components/start/ExportAppModal.svelte"
import { store, automationStore } from "builderStore" import { store, automationStore } from "builderStore"
@ -25,10 +22,8 @@
import { apps, auth, admin, templates } from "stores/portal" import { apps, auth, admin, templates } from "stores/portal"
import download from "downloadjs" import download from "downloadjs"
import { goto } from "@roxi/routify" import { goto } from "@roxi/routify"
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
import AppRow from "components/start/AppRow.svelte" import AppRow from "components/start/AppRow.svelte"
import { AppStatus } from "constants" import { AppStatus } from "constants"
import analytics, { Events, EventSource } from "analytics"
import Logo from "assets/bb-space-man.svg" import Logo from "assets/bb-space-man.svg"
let sortBy = "name" let sortBy = "name"
@ -36,15 +31,11 @@
let selectedApp let selectedApp
let creationModal let creationModal
let updatingModal let updatingModal
let deletionModal
let unpublishModal
let exportModal let exportModal
let iconModal
let creatingApp = false let creatingApp = false
let loaded = $apps?.length || $templates?.length let loaded = $apps?.length || $templates?.length
let searchTerm = "" let searchTerm = ""
let cloud = $admin.cloud let cloud = $admin.cloud
let appName = ""
let creatingFromTemplate = false let creatingFromTemplate = false
const resolveWelcomeMessage = (auth, apps) => { const resolveWelcomeMessage = (auth, apps) => {
@ -164,18 +155,6 @@
creatingApp = false creatingApp = false
} }
const viewApp = app => {
analytics.captureEvent(Events.APP_VIEW_PUBLISHED, {
appId: app.appId,
eventSource: EventSource.PORTAL,
})
if (app.url) {
window.open(`/app${app.url}`)
} else {
window.open(`/${app.prodId}`)
}
}
const appOverview = app => { const appOverview = app => {
$goto(`../overview/${app.devId}`) $goto(`../overview/${app.devId}`)
} }
@ -190,76 +169,6 @@
$goto(`../../app/${app.devId}`) $goto(`../../app/${app.devId}`)
} }
const editIcon = app => {
selectedApp = app
iconModal.show()
}
const exportApp = app => {
exportModal.show()
selectedApp = app
}
const unpublishApp = app => {
selectedApp = app
unpublishModal.show()
}
const confirmUnpublishApp = async () => {
if (!selectedApp) {
return
}
try {
await API.unpublishApp(selectedApp.prodId)
await apps.load()
notifications.success("App unpublished successfully")
} catch (err) {
notifications.error("Error unpublishing app")
}
}
const deleteApp = app => {
selectedApp = app
deletionModal.show()
}
const confirmDeleteApp = async () => {
if (!selectedApp) {
return
}
try {
await API.deleteApp(selectedApp?.devId)
await apps.load()
// Get checklist, just in case that was the last app
await admin.init()
notifications.success("App deleted successfully")
} catch (err) {
notifications.error("Error deleting app")
}
selectedApp = null
appName = null
}
const updateApp = async app => {
selectedApp = app
updatingModal.show()
}
const releaseLock = async app => {
try {
await API.releaseAppLock(app.devId)
await apps.load()
notifications.success("Lock released successfully")
} catch (err) {
notifications.error("Error releasing lock")
}
}
const copyAppId = async app => {
await Helpers.copyToClipboard(app.prodId)
notifications.success("App ID copied to clipboard.")
}
function createAppFromTemplateUrl(templateKey) { function createAppFromTemplateUrl(templateKey) {
// validate the template key just to make sure // validate the template key just to make sure
const templateParts = templateKey.split("/") const templateParts = templateKey.split("/")
@ -407,19 +316,7 @@
<div class="appTable" class:unlocked> <div class="appTable" class:unlocked>
{#each filteredApps as app (app.appId)} {#each filteredApps as app (app.appId)}
<AppRow <AppRow {app} {editApp} {appOverview} />
{copyAppId}
{releaseLock}
{editIcon}
{app}
{unpublishApp}
{viewApp}
{editApp}
{exportApp}
{deleteApp}
{updateApp}
{appOverview}
/>
{/each} {/each}
</div> </div>
</Layout> </Layout>
@ -453,35 +350,6 @@
<ExportAppModal app={selectedApp} /> <ExportAppModal app={selectedApp} />
</Modal> </Modal>
<ConfirmDialog
bind:this={deletionModal}
title="Confirm deletion"
okText="Delete app"
onOk={confirmDeleteApp}
onCancel={() => (appName = null)}
disabled={appName !== selectedApp?.name}
>
Are you sure you want to delete the app <b>{selectedApp?.name}</b>?
<p>Please enter the app name below to confirm.</p>
<Input
bind:value={appName}
data-cy="delete-app-confirmation"
placeholder={selectedApp?.name}
/>
</ConfirmDialog>
<ConfirmDialog
bind:this={unpublishModal}
title="Confirm unpublish"
okText="Unpublish app"
onOk={confirmUnpublishApp}
dataCy={"unpublish-modal"}
>
Are you sure you want to unpublish the app <b>{selectedApp?.name}</b>?
</ConfirmDialog>
<ChooseIconModal app={selectedApp} bind:this={iconModal} />
<style> <style>
.appTable { .appTable {
border-top: var(--border-light); border-top: var(--border-light);
@ -538,12 +406,9 @@
height: 70px; height: 70px;
display: grid; display: grid;
align-items: center; align-items: center;
grid-gap: var(--spacing-xl);
grid-template-columns: auto 1fr;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
padding: 0 var(--spacing-s);
} }
.appTable :global(> div) { .appTable :global(> div) {
border-bottom: var(--border-light); border-bottom: var(--border-light);

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/cli", "name": "@budibase/cli",
"version": "1.0.206", "version": "1.0.207-alpha.5",
"description": "Budibase CLI, for developers, self hosting and migrations.", "description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "src/index.js", "main": "src/index.js",
"bin": { "bin": {

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/client", "name": "@budibase/client",
"version": "1.0.206", "version": "1.0.207-alpha.5",
"license": "MPL-2.0", "license": "MPL-2.0",
"module": "dist/budibase-client.js", "module": "dist/budibase-client.js",
"main": "dist/budibase-client.js", "main": "dist/budibase-client.js",
@ -19,9 +19,9 @@
"dev:builder": "rollup -cw" "dev:builder": "rollup -cw"
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "^1.0.206", "@budibase/bbui": "^1.0.207-alpha.5",
"@budibase/frontend-core": "^1.0.206", "@budibase/frontend-core": "^1.0.207-alpha.5",
"@budibase/string-templates": "^1.0.206", "@budibase/string-templates": "^1.0.207-alpha.5",
"@spectrum-css/button": "^3.0.3", "@spectrum-css/button": "^3.0.3",
"@spectrum-css/card": "^3.0.3", "@spectrum-css/card": "^3.0.3",
"@spectrum-css/divider": "^1.0.3", "@spectrum-css/divider": "^1.0.3",

View File

@ -1,12 +1,12 @@
{ {
"name": "@budibase/frontend-core", "name": "@budibase/frontend-core",
"version": "1.0.206", "version": "1.0.207-alpha.5",
"description": "Budibase frontend core libraries used in builder and client", "description": "Budibase frontend core libraries used in builder and client",
"author": "Budibase", "author": "Budibase",
"license": "MPL-2.0", "license": "MPL-2.0",
"svelte": "src/index.js", "svelte": "src/index.js",
"dependencies": { "dependencies": {
"@budibase/bbui": "^1.0.206", "@budibase/bbui": "^1.0.207-alpha.5",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"svelte": "^3.46.2" "svelte": "^3.46.2"
} }

View File

@ -63,3 +63,25 @@ export const TableNames = {
* - Coerce types for search endpoint * - Coerce types for search endpoint
*/ */
export const ApiVersion = "1" export const ApiVersion = "1"
/**
* Maximum minimum range for SQL number values
*/
export const SqlNumberTypeRangeMap = {
integer: {
max: 2147483647,
min: -2147483648,
},
int: {
max: 2147483647,
min: -2147483648,
},
smallint: {
max: 32767,
min: -32768,
},
mediumint: {
max: 8388607,
min: -8388608,
},
}

View File

@ -1,5 +1,5 @@
import { Helpers } from "@budibase/bbui" import { Helpers } from "@budibase/bbui"
import { OperatorOptions } from "../constants" import { OperatorOptions, SqlNumberTypeRangeMap } from "../constants"
/** /**
* Returns the valid operator options for a certain data type * Returns the valid operator options for a certain data type
@ -94,7 +94,7 @@ export const buildLuceneQuery = filter => {
} }
if (Array.isArray(filter)) { if (Array.isArray(filter)) {
filter.forEach(expression => { filter.forEach(expression => {
let { operator, field, type, value } = expression let { operator, field, type, value, externalType } = expression
// Parse all values into correct types // Parse all values into correct types
if (type === "datetime" && value) { if (type === "datetime" && value) {
value = new Date(value).toISOString() value = new Date(value).toISOString()
@ -106,16 +106,14 @@ export const buildLuceneQuery = filter => {
value = `${value}`?.toLowerCase() === "true" value = `${value}`?.toLowerCase() === "true"
} }
if (operator.startsWith("range")) { if (operator.startsWith("range")) {
const minint =
SqlNumberTypeRangeMap[externalType]?.min || Number.MIN_SAFE_INTEGER
const maxint =
SqlNumberTypeRangeMap[externalType]?.max || Number.MAX_SAFE_INTEGER
if (!query.range[field]) { if (!query.range[field]) {
query.range[field] = { query.range[field] = {
low: low: type === "number" ? minint : "0000-00-00T00:00:00.000Z",
type === "number" high: type === "number" ? maxint : "9999-00-00T00:00:00.000Z",
? Number.MIN_SAFE_INTEGER
: "0000-00-00T00:00:00.000Z",
high:
type === "number"
? Number.MAX_SAFE_INTEGER
: "9999-00-00T00:00:00.000Z",
} }
} }
if (operator === "rangeLow" && value != null && value !== "") { if (operator === "rangeLow" && value != null && value !== "") {

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/server", "name": "@budibase/server",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "1.0.206", "version": "1.0.207-alpha.5",
"description": "Budibase Web Server", "description": "Budibase Web Server",
"main": "src/index.ts", "main": "src/index.ts",
"repository": { "repository": {
@ -77,10 +77,11 @@
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@apidevtools/swagger-parser": "10.0.3", "@apidevtools/swagger-parser": "10.0.3",
"@budibase/backend-core": "^1.0.206", "@budibase/backend-core": "^1.0.207-alpha.5",
"@budibase/client": "^1.0.206", "@budibase/client": "^1.0.207-alpha.5",
"@budibase/pro": "1.0.206", "@budibase/pro": "1.0.207-alpha.5",
"@budibase/string-templates": "^1.0.206", "@budibase/string-templates": "^1.0.207-alpha.5",
"@budibase/types": "^1.0.207-alpha.5",
"@bull-board/api": "3.7.0", "@bull-board/api": "3.7.0",
"@bull-board/koa": "3.9.4", "@bull-board/koa": "3.9.4",
"@elastic/elasticsearch": "7.10.0", "@elastic/elasticsearch": "7.10.0",
@ -151,7 +152,7 @@
"@babel/core": "7.17.4", "@babel/core": "7.17.4",
"@babel/preset-env": "7.16.11", "@babel/preset-env": "7.16.11",
"@budibase/standard-components": "^0.9.139", "@budibase/standard-components": "^0.9.139",
"@budibase/types": "^1.0.206", "@budibase/types": "^1.0.207-alpha.3",
"@jest/test-sequencer": "24.9.0", "@jest/test-sequencer": "24.9.0",
"@types/apidoc": "0.50.0", "@types/apidoc": "0.50.0",
"@types/bson": "4.2.0", "@types/bson": "4.2.0",

View File

@ -155,27 +155,22 @@ exports.validate = async () => {
} }
exports.exportRows = async ctx => { exports.exportRows = async ctx => {
const { datasourceId, tableName } = breakExternalTableId(ctx.params.tableId) const { datasourceId } = breakExternalTableId(ctx.params.tableId)
const db = getAppDB() const db = getAppDB()
let format = ctx.query.format let format = ctx.query.format
const datasource = await db.get(datasourceId) const datasource = await db.get(datasourceId)
if (!datasource || !datasource.entities) { if (!datasource || !datasource.entities) {
ctx.throw(400, "Datasource has not been configured for plus API.") ctx.throw(400, "Datasource has not been configured for plus API.")
} }
const tables = datasource.entities
const table = tables[tableName]
ctx.request.body = { ctx.request.body = {
query: { query: {
oneOf: { oneOf: {
[table.primaryDisplay]: ctx.request.body.rows.map( _id: ctx.request.body.rows.map(row => JSON.parse(decodeURI(row))[0]),
id => breakRowIdField(id)[0]
),
}, },
}, },
} }
let result = await exports.search(ctx) let result = await exports.search(ctx)
let headers = Object.keys(result.rows[0]) let headers = Object.keys(result.rows[0])
const exporter = exporters[format] const exporter = exporters[format]
const filename = `export.${format}` const filename = `export.${format}`

View File

@ -97,7 +97,7 @@ exports.externalTrigger = async function (
// values are likely to be submitted as strings, so we shall convert to correct type // values are likely to be submitted as strings, so we shall convert to correct type
const coercedFields = {} const coercedFields = {}
const fields = automation.definition.trigger.inputs.fields const fields = automation.definition.trigger.inputs.fields
for (let key of Object.keys(fields)) { for (let key of Object.keys(fields || {})) {
coercedFields[key] = coerce(params.fields[key], fields[key]) coercedFields[key] = coerce(params.fields[key], fields[key])
} }
params.fields = coercedFields params.fields = coercedFields

View File

@ -246,6 +246,7 @@ module MSSQLModule {
autocolumn: !!autoColumns.find((col: string) => col === name), autocolumn: !!autoColumns.find((col: string) => col === name),
name: name, name: name,
...convertSqlType(def.DATA_TYPE), ...convertSqlType(def.DATA_TYPE),
externalType: def.DATA_TYPE,
} }
} }
tables[tableName] = { tables[tableName] = {

View File

@ -232,6 +232,7 @@ module MySQLModule {
autocolumn: isAuto, autocolumn: isAuto,
constraints, constraints,
...convertSqlType(column.Type), ...convertSqlType(column.Type),
externalType: column.Type,
} }
} }
if (!tables[tableName]) { if (!tables[tableName]) {

View File

@ -271,6 +271,7 @@ module PostgresModule {
autocolumn: isAuto, autocolumn: isAuto,
name: columnName, name: columnName,
...convertSqlType(column.data_type), ...convertSqlType(column.data_type),
externalType: column.data_type,
} }
} }

View File

@ -1094,10 +1094,10 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/backend-core@1.0.201-alpha.4": "@budibase/backend-core@1.0.207-alpha.3":
version "1.0.201-alpha.4" version "1.0.207-alpha.3"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.201-alpha.4.tgz#51fda5838e9c51c88c85204ad867811e49b7d34c" resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.207-alpha.3.tgz#98bced0575ec4e2b158239a8c73b39ca2d816719"
integrity sha512-5oQMfKPDMpB4x5MzGgOtWFIPzc7RG/uu+rSe65PVwWfp77bDa3kxtCtC1p6vtJuPlb4GYrHqGLtr7WePbJf4fA== integrity sha512-DU4X6jJ+DfhzOv4TTa1w4Dk5ZEdlK/z1joCTruT+SGM5qI75bXrGeol5OX2OaEbNKtXFKJ1zeVTmBCYcu7OFUg==
dependencies: dependencies:
"@techpass/passport-openidconnect" "0.3.2" "@techpass/passport-openidconnect" "0.3.2"
aws-sdk "2.1030.0" aws-sdk "2.1030.0"
@ -1175,12 +1175,12 @@
svelte-flatpickr "^3.2.3" svelte-flatpickr "^3.2.3"
svelte-portal "^1.0.0" svelte-portal "^1.0.0"
"@budibase/pro@1.0.201-alpha.4": "@budibase/pro@1.0.207-alpha.3":
version "1.0.201-alpha.4" version "1.0.207-alpha.3"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.201-alpha.4.tgz#5b92dc7a4bb2004bcd43339c863430ea640e0b74" resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.207-alpha.3.tgz#9bde845ceb685f1b43286a124620c21fdf891a01"
integrity sha512-fsDlE8O4Y4JpZI3NwBoN8udX4cEx7/ebZ41Q7qD5VwA6Q2niapRcX0MvDmGdjDSVECG+NJU1uqCdg5cDq1Odxw== integrity sha512-WFEMujpKTVAMvAgLBnMdw8ou9PxsbM4Oa9Dq+DAUsWpPACsMWOProyHLsdRxJyvHlgGfwVjo5MEusvStjI4j6g==
dependencies: dependencies:
"@budibase/backend-core" "1.0.201-alpha.4" "@budibase/backend-core" "1.0.207-alpha.3"
node-fetch "^2.6.1" node-fetch "^2.6.1"
"@budibase/standard-components@^0.9.139": "@budibase/standard-components@^0.9.139":

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/string-templates", "name": "@budibase/string-templates",
"version": "1.0.206", "version": "1.0.207-alpha.5",
"description": "Handlebars wrapper for Budibase templating.", "description": "Handlebars wrapper for Budibase templating.",
"main": "src/index.cjs", "main": "src/index.cjs",
"module": "dist/bundle.mjs", "module": "dist/bundle.mjs",

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/types", "name": "@budibase/types",
"version": "1.0.206", "version": "1.0.207-alpha.5",
"description": "Budibase types", "description": "Budibase types",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/index.d.ts", "types": "dist/index.d.ts",

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/worker", "name": "@budibase/worker",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "1.0.206", "version": "1.0.207-alpha.5",
"description": "Budibase background service", "description": "Budibase background service",
"main": "src/index.ts", "main": "src/index.ts",
"repository": { "repository": {
@ -34,9 +34,10 @@
"author": "Budibase", "author": "Budibase",
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@budibase/backend-core": "^1.0.206", "@budibase/backend-core": "^1.0.207-alpha.5",
"@budibase/pro": "1.0.206", "@budibase/pro": "1.0.207-alpha.5",
"@budibase/string-templates": "^1.0.206", "@budibase/string-templates": "^1.0.207-alpha.5",
"@budibase/types": "^1.0.207-alpha.5",
"@koa/router": "8.0.8", "@koa/router": "8.0.8",
"@sentry/node": "6.17.7", "@sentry/node": "6.17.7",
"@techpass/passport-openidconnect": "0.3.2", "@techpass/passport-openidconnect": "0.3.2",
@ -66,7 +67,7 @@
"server-destroy": "1.0.1" "server-destroy": "1.0.1"
}, },
"devDependencies": { "devDependencies": {
"@budibase/types": "^1.0.206", "@budibase/types": "^1.0.207-alpha.3",
"@types/jest": "26.0.23", "@types/jest": "26.0.23",
"@types/koa": "2.13.4", "@types/koa": "2.13.4",
"@types/koa-router": "7.4.4", "@types/koa-router": "7.4.4",

View File

@ -291,10 +291,10 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/backend-core@1.0.201-alpha.4": "@budibase/backend-core@1.0.207-alpha.3":
version "1.0.201-alpha.4" version "1.0.207-alpha.3"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.201-alpha.4.tgz#51fda5838e9c51c88c85204ad867811e49b7d34c" resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.207-alpha.3.tgz#98bced0575ec4e2b158239a8c73b39ca2d816719"
integrity sha512-5oQMfKPDMpB4x5MzGgOtWFIPzc7RG/uu+rSe65PVwWfp77bDa3kxtCtC1p6vtJuPlb4GYrHqGLtr7WePbJf4fA== integrity sha512-DU4X6jJ+DfhzOv4TTa1w4Dk5ZEdlK/z1joCTruT+SGM5qI75bXrGeol5OX2OaEbNKtXFKJ1zeVTmBCYcu7OFUg==
dependencies: dependencies:
"@techpass/passport-openidconnect" "0.3.2" "@techpass/passport-openidconnect" "0.3.2"
aws-sdk "2.1030.0" aws-sdk "2.1030.0"
@ -322,12 +322,12 @@
uuid "8.3.2" uuid "8.3.2"
zlib "1.0.5" zlib "1.0.5"
"@budibase/pro@1.0.201-alpha.4": "@budibase/pro@1.0.207-alpha.3":
version "1.0.201-alpha.4" version "1.0.207-alpha.3"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.201-alpha.4.tgz#5b92dc7a4bb2004bcd43339c863430ea640e0b74" resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.207-alpha.3.tgz#9bde845ceb685f1b43286a124620c21fdf891a01"
integrity sha512-fsDlE8O4Y4JpZI3NwBoN8udX4cEx7/ebZ41Q7qD5VwA6Q2niapRcX0MvDmGdjDSVECG+NJU1uqCdg5cDq1Odxw== integrity sha512-WFEMujpKTVAMvAgLBnMdw8ou9PxsbM4Oa9Dq+DAUsWpPACsMWOProyHLsdRxJyvHlgGfwVjo5MEusvStjI4j6g==
dependencies: dependencies:
"@budibase/backend-core" "1.0.201-alpha.4" "@budibase/backend-core" "1.0.207-alpha.3"
node-fetch "^2.6.1" node-fetch "^2.6.1"
"@cspotcode/source-map-consumer@0.8.0": "@cspotcode/source-map-consumer@0.8.0":

11
scripts/cleanup.sh Normal file
View File

@ -0,0 +1,11 @@
#!/bin/bash
dir=$(pwd)
mv dist /
mv package.json /
cd /
rm -r $dir
mkdir $dir
mv /dist $dir
mv /package.json $dir
cd $dir
NODE_ENV=production yarn

View File

@ -3,35 +3,44 @@ const path = require("path")
const MONOREPO_ROOT = "packages" const MONOREPO_ROOT = "packages"
const packages = fs.readdirSync(MONOREPO_ROOT) const packages = getPackages()
function getPackages() {
if (fs.existsSync(MONOREPO_ROOT)) {
return fs.readdirSync(MONOREPO_ROOT).map(pkg => path.join(MONOREPO_ROOT, pkg))
} else {
return ["./"]
}
}
function pinDeps(dependencies) { function pinDeps(dependencies) {
for (let dependency in dependencies) { for (let dependency in dependencies) {
if (dependency.startsWith("@budibase")) { if (dependency.startsWith("@budibase")) {
dependencies[dependency] = dependencies[dependency].replace("^", "") dependencies[dependency] = dependencies[dependency].replace("^", "")
} }
} }
return dependencies
} }
// iterate over the monorepo packages // iterate over the monorepo packages
for (let pkg of packages) { for (let pkgPath of packages) {
const pkgPath = path.join(MONOREPO_ROOT, pkg) // only directories
if (fs.statSync(pkgPath).isDirectory()) {
// get the package JSON file
const pkgJsonPath = path.join(pkgPath, "package.json")
if (!fs.existsSync(pkgJsonPath)) {
continue
}
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath))
// only directories // find any budibase dependencies, and pin them
if (fs.statSync(pkgPath).isDirectory()) { pkgJson.dependencies = pinDeps(pkgJson.dependencies)
pkgJson.devDependencies = pinDeps(pkgJson.devDependencies)
// update the package JSON files
// get the package JSON file fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2))
const pkgJsonPath = path.join(pkgPath, "package.json") }
const pkgJson = require(`../${pkgJsonPath}`)
// find any budibase dependencies, and pin them
pinDeps(pkgJson.dependencies)
pinDeps(pkgJson.devDependencies)
// update the package JSON files
fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2))
}
} }
console.log("Pinned dev versions for budibase packages successfully.") console.log("Pinned dev versions for budibase packages successfully.")