diff --git a/.github/workflows/ecs_deploy.yml b/.github/workflows/ecs_deploy.yml new file mode 100644 index 0000000000..0d662c9c28 --- /dev/null +++ b/.github/workflows/ecs_deploy.yml @@ -0,0 +1,85 @@ +# This workflow will build and push a new container image to Amazon ECR, +# and then will deploy a new task definition to Amazon ECS, when a release is created +# +# To use this workflow, you will need to complete the following set-up steps: +# +# 1. Create an ECR repository to store your images. +# For example: `aws ecr create-repository --repository-name my-ecr-repo --region us-east-2`. +# Replace the value of `ECR_REPOSITORY` in the workflow below with your repository's name. +# Replace the value of `aws-region` in the workflow below with your repository's region. +# +# 2. Create an ECS task definition, an ECS cluster, and an ECS service. +# For example, follow the Getting Started guide on the ECS console: +# https://us-east-2.console.aws.amazon.com/ecs/home?region=us-east-2#/firstRun +# Replace the values for `service` and `cluster` in the workflow below with your service and cluster names. +# +# 3. Store your ECS task definition as a JSON file in your repository. +# The format should follow the output of `aws ecs register-task-definition --generate-cli-skeleton`. +# Replace the value of `task-definition` in the workflow below with your JSON file's name. +# Replace the value of `container-name` in the workflow below with the name of the container +# in the `containerDefinitions` section of the task definition. +# +# 4. Store an IAM user access key in GitHub Actions secrets named `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`. +# See the documentation for each action used below for the recommended IAM policies for this IAM user, +# and best practices on handling the access key credentials. + +on: + push: + tags: + - 'v*' + +name: Deploy to Amazon ECS + +jobs: + deploy: + name: deploy + runs-on: ubuntu-16.04 + + steps: + - name: Checkout + 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: Download task definition + run: | + aws ecs describe-task-definition --task-definition ProdAppServerStackprodbudiapplbfargateserviceprodbudiappserverfargatetaskdefinition2EF7F1E7 --query taskDefinition > task-definition.json + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v1 + + - name: Build, tag, and push image to Amazon ECR + id: build-image + env: + ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + ECR_REPOSITORY: prod-budi-app-server + IMAGE_TAG: ${{ github.sha }} + run: | + # Build a docker container and + # push it to ECR so that it can + # be deployed to ECS + cd packages/server + docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . + docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG + echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" + - name: Fill in the new image ID in the Amazon ECS task definition + id: task-def + uses: aws-actions/amazon-ecs-render-task-definition@v1 + with: + task-definition: task-definition.json + container-name: prod-budi-app-server + image: ${{ steps.build-image.outputs.image }} + + - name: Deploy Amazon ECS task definition + uses: aws-actions/amazon-ecs-deploy-task-definition@v1 + with: + task-definition: ${{ steps.task-def.outputs.task-definition }} + service: prod-budi-app-server-service + cluster: prod-budi-app-server + wait-for-service-stability: true diff --git a/.gitignore b/.gitignore index 3b81fd63df..54401c6b36 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ builder/* .data/ .temp/ packages/server/runtime_apps/ +.idea/ # Logs logs diff --git a/AUTHORS.md b/AUTHORS.md index 3fe249cbc7..28e8898524 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -5,3 +5,5 @@ Contributors * Daniel Loudon - [@danbudi](https://github.com/marblekirby) * Joe - [@joebudi](https://github.com/joebudi) * Martin McKeaveney - [@shogunpurple](https://github.com/shogunpurple) +* Andrew Kingston - [@aptkingston](https://github.com/aptkingston) +* Michael Drury - [@mike12345567](https://github.com/mike12345567) \ No newline at end of file diff --git a/packages/builder/cypress/integration/createBinding.spec.js b/packages/builder/cypress/integration/createBinding.spec.js new file mode 100644 index 0000000000..971df3ed75 --- /dev/null +++ b/packages/builder/cypress/integration/createBinding.spec.js @@ -0,0 +1,18 @@ +xcontext('Create a Binding', () => { + before(() => { + cy.visit('localhost:4001/_builder') + cy.createApp('Binding App', 'Binding App Description') + cy.navigateToFrontend() + }) + + it('add an input binding', () => { + cy.get(".nav-items-container").contains('Home').click() + cy.contains("Add").click() + cy.get("[data-cy=Input]").click() + cy.get("[data-cy=Textfield]").click() + cy.contains("Heading").click() + cy.get("[data-cy=text-binding-button]").click() + cy.get("[data-cy=binding-dropdown-modal]").contains('Input 1').click() + cy.get("[data-cy=binding-dropdown-modal] textarea").should('have.value', 'Home{{ Input 1 }}') + }) +}) diff --git a/packages/builder/cypress/integration/createUser.spec.js b/packages/builder/cypress/integration/createUser.spec.js index 192cdae316..b1f737068b 100644 --- a/packages/builder/cypress/integration/createUser.spec.js +++ b/packages/builder/cypress/integration/createUser.spec.js @@ -9,7 +9,7 @@ context('Create a User', () => { // https://on.cypress.io/interacting-with-elements it('should create a user', () => { - cy.createUser('bbuser', 'test', 'ADMIN') + cy.createUser('bbuser', 'test', 'POWER_USER') // Check to make sure user was created! cy.get("input[disabled]").should('have.value', 'bbuser') diff --git a/packages/builder/cypress/integration/createView.spec.js b/packages/builder/cypress/integration/createView.spec.js index 3c13405914..3ccf4d5cfd 100644 --- a/packages/builder/cypress/integration/createView.spec.js +++ b/packages/builder/cypress/integration/createView.spec.js @@ -54,7 +54,7 @@ context('Create a View', () => { expect($headers).to.have.length(7) const headers = $headers.map((i, header) => Cypress.$(header).text()) expect(headers.get()).to.deep.eq([ - "group", + "field", "sum", "min", "max", @@ -66,7 +66,7 @@ context('Create a View', () => { cy.get("tbody td").should(($values) => { const values = $values.map((i, value) => Cypress.$(value).text()) expect(values.get()).to.deep.eq([ - "null", + "age", "155", "20", "49", diff --git a/packages/builder/cypress/integration/createWorkflow.spec.js b/packages/builder/cypress/integration/createWorkflow.spec.js index f341bf86b1..e7fe34b630 100644 --- a/packages/builder/cypress/integration/createWorkflow.spec.js +++ b/packages/builder/cypress/integration/createWorkflow.spec.js @@ -1,4 +1,4 @@ -xcontext('Create a workflow', () => { +context('Create a workflow', () => { before(() => { cy.server() @@ -9,7 +9,7 @@ xcontext('Create a workflow', () => { // https://on.cypress.io/interacting-with-elements it('should create a workflow', () => { - cy.createTable('dog', 'name', 'age') + cy.createTestTableWithData() cy.contains('workflow').click() cy.contains('Create New Workflow').click() @@ -23,21 +23,23 @@ xcontext('Create a workflow', () => { // Create action cy.get('[data-cy=SAVE_RECORD]').click() - cy.get(':nth-child(2) > .budibase__input').type('goodboy') - cy.get(':nth-child(3) > .budibase__input').type('11') + cy.get('.container input').first().type('goodboy') + cy.get('.container input').eq(1).type('11') // Save cy.contains('Save Workflow').click() // Activate Workflow cy.get('[data-cy=activate-workflow]').click() - + cy.contains("Add Record").should("be.visible") + cy.get(".stop-button.highlighted").should("be.visible") }) - xit('should add record when a new record is added', () => { + + it('should add record when a new record is added', () => { cy.contains('backend').click() - cy.addRecord('bob', '15') - + cy.addRecord(["Rover", 15]) + cy.reload() cy.contains('goodboy').should('have.text', 'goodboy') }) diff --git a/packages/builder/cypress/support/commands.js b/packages/builder/cypress/support/commands.js index da85abddb3..82f87032fa 100644 --- a/packages/builder/cypress/support/commands.js +++ b/packages/builder/cypress/support/commands.js @@ -52,17 +52,22 @@ Cypress.Commands.add("createApp", name => { .type("test") cy.contains("Submit").click() cy.contains("Create New Table", { - timeout: 10000, + timeout: 20000, }).should("be.visible") }) }) +Cypress.Commands.add("createTestTableWithData", () => { + cy.createTable("dog") + cy.addColumn("dog", "name", "Plain Text") + cy.addColumn("dog", "age", "Number") +}) + Cypress.Commands.add("createTable", tableName => { // Enter model name cy.contains("Create New Table").click() cy.get("[placeholder='Table Name']").type(tableName) - // Add 'name' field cy.contains("Save").click() cy.contains(tableName).should("be.visible") }) @@ -84,7 +89,7 @@ Cypress.Commands.add("addRecord", values => { cy.contains("Create New Row").click() for (let i = 0; i < values.length; i++) { - cy.get("input") + cy.get(".actions input") .eq(i) .type(values[i]) } @@ -93,7 +98,7 @@ Cypress.Commands.add("addRecord", values => { cy.contains("Save").click() }) -Cypress.Commands.add("createUser", (username, password) => { +Cypress.Commands.add("createUser", (username, password, accessLevel) => { // Create User cy.get(".toprightnav > .settings").click() cy.contains("Users").click() @@ -104,9 +109,12 @@ Cypress.Commands.add("createUser", (username, password) => { cy.get("[name=Password]") .first() .type(password) + cy.get("select") + .first() + .select(accessLevel) // Save - cy.get(".create-button").click() + cy.get(".create-button > button").click() }) Cypress.Commands.add("addHeadlineComponent", text => { @@ -135,7 +143,7 @@ Cypress.Commands.add("createScreen", (screenName, route) => { if (route) { cy.get("[data-cy=new-screen-dialog] input:last").type(route) } - cy.get(".uk-modal-footer").within(() => { + cy.get("[data-cy=create-screen-footer]").within(() => { cy.contains("Create Screen").click() }) cy.get(".nav-items-container").within(() => { diff --git a/packages/builder/cypress/support/cookies.js b/packages/builder/cypress/support/cookies.js index bfb470c57b..1c172f4d4c 100644 --- a/packages/builder/cypress/support/cookies.js +++ b/packages/builder/cypress/support/cookies.js @@ -1,3 +1,3 @@ Cypress.Cookies.defaults({ - whitelist: "builder:token", + preserve: "builder:token", }) diff --git a/packages/builder/cypress/videos/createApp.spec.js.mp4 b/packages/builder/cypress/videos/createApp.spec.js.mp4 new file mode 100644 index 0000000000..4e13dbe91b Binary files /dev/null and b/packages/builder/cypress/videos/createApp.spec.js.mp4 differ diff --git a/packages/builder/cypress/videos/createBinding.spec.js.mp4 b/packages/builder/cypress/videos/createBinding.spec.js.mp4 new file mode 100644 index 0000000000..6cbfc19b9d Binary files /dev/null and b/packages/builder/cypress/videos/createBinding.spec.js.mp4 differ diff --git a/packages/builder/cypress/videos/createComponents.spec.js.mp4 b/packages/builder/cypress/videos/createComponents.spec.js.mp4 new file mode 100644 index 0000000000..8b5fcbe188 Binary files /dev/null and b/packages/builder/cypress/videos/createComponents.spec.js.mp4 differ diff --git a/packages/builder/cypress/videos/createTable.spec.js.mp4 b/packages/builder/cypress/videos/createTable.spec.js.mp4 new file mode 100644 index 0000000000..162b21a8b5 Binary files /dev/null and b/packages/builder/cypress/videos/createTable.spec.js.mp4 differ diff --git a/packages/builder/cypress/videos/createUser.spec.js.mp4 b/packages/builder/cypress/videos/createUser.spec.js.mp4 new file mode 100644 index 0000000000..cae887ed6c Binary files /dev/null and b/packages/builder/cypress/videos/createUser.spec.js.mp4 differ diff --git a/packages/builder/cypress/videos/createView.spec.js.mp4 b/packages/builder/cypress/videos/createView.spec.js.mp4 new file mode 100644 index 0000000000..e24101c222 Binary files /dev/null and b/packages/builder/cypress/videos/createView.spec.js.mp4 differ diff --git a/packages/builder/cypress/videos/createWorkflow.spec.js.mp4 b/packages/builder/cypress/videos/createWorkflow.spec.js.mp4 new file mode 100644 index 0000000000..52116ef9e9 Binary files /dev/null and b/packages/builder/cypress/videos/createWorkflow.spec.js.mp4 differ diff --git a/packages/builder/cypress/videos/screens.spec.js.mp4 b/packages/builder/cypress/videos/screens.spec.js.mp4 new file mode 100644 index 0000000000..812238057d Binary files /dev/null and b/packages/builder/cypress/videos/screens.spec.js.mp4 differ diff --git a/packages/builder/package.json b/packages/builder/package.json index ace69a028a..a091b21bcb 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -57,10 +57,13 @@ "eslintConfig": { "extends": [ "plugin:cypress/recommended" - ] + ], + "rules": { + "cypress/no-unnecessary-waiting": "off" + } }, "dependencies": { - "@budibase/bbui": "^1.28.2", + "@budibase/bbui": "^1.31.0", "@budibase/client": "^0.1.19", "@budibase/colorpicker": "^1.0.1", "@sentry/browser": "5.19.1", @@ -75,9 +78,9 @@ "mustache": "^4.0.1", "posthog-js": "1.3.1", "shortid": "^2.2.15", + "svelte-loading-spinners": "^0.1.1", "svelte-portal": "^0.1.0", "svelte-simple-modal": "^0.4.2", - "uikit": "^3.1.7", "yup": "^0.29.2" }, "devDependencies": { @@ -91,7 +94,7 @@ "@testing-library/jest-dom": "^5.11.0", "@testing-library/svelte": "^3.0.0", "babel-jest": "^24.8.0", - "cypress": "^4.8.0", + "cypress": "^5.1.0", "cypress-terminal-report": "^1.4.1", "eslint-plugin-cypress": "^2.11.1", "identity-obj-proxy": "^3.0.0", @@ -114,4 +117,4 @@ "svelte-jester": "^1.0.6" }, "gitHead": "115189f72a850bfb52b65ec61d932531bf327072" -} \ No newline at end of file +} diff --git a/packages/builder/src/budibase.css b/packages/builder/src/budibase.css index 0db3e2fec7..23fd45e1e0 100644 --- a/packages/builder/src/budibase.css +++ b/packages/builder/src/budibase.css @@ -85,11 +85,6 @@ box-sizing: border-box; } -.uk-text-right { - display: flex; - justify-content: flex-start; -} - .preview-pane { grid-column: 2; margin: 80px 60px; @@ -154,3 +149,13 @@ .bb-table td > :last-child { margin-bottom: 0; } +.bb__alert { + position: relative; + margin-bottom: var(--spacing-m); + padding: var(--spacing-l); +} + +.bb__alert--danger { + background: #fef4f6; + color: #f0506e; +} \ No newline at end of file diff --git a/packages/builder/src/builderStore/store/index.js b/packages/builder/src/builderStore/store/index.js index ac13c90dbe..b64bf78624 100644 --- a/packages/builder/src/builderStore/store/index.js +++ b/packages/builder/src/builderStore/store/index.js @@ -104,6 +104,10 @@ const setPackage = (store, initial) => async pkg => { initial.pages = pkg.pages initial.hasAppPackage = true initial.screens = values(pkg.screens) + initial.allScreens = [ + ...Object.values(main_screens), + ...Object.values(unauth_screens), + ] initial.builtins = [getBuiltin("##builtin/screenslot")] initial.appInstances = pkg.application.instances initial.appId = pkg.application._id @@ -132,6 +136,7 @@ const _saveScreen = async (store, s, screen) => { innerState.pages[s.currentPageName]._screens = screens innerState.screens = screens innerState.currentPreviewItem = screen + innerState.allScreens = [...innerState.allScreens, screen] const safeProps = makePropsSafe( innerState.components[screen.props._component], screen.props diff --git a/packages/builder/src/components/common/ConfirmDialog.svelte b/packages/builder/src/components/common/ConfirmDialog.svelte index f9a4e7ec57..31e2d3ad86 100644 --- a/packages/builder/src/components/common/ConfirmDialog.svelte +++ b/packages/builder/src/components/common/ConfirmDialog.svelte @@ -1,8 +1,5 @@ -
-
- +
-
+ diff --git a/packages/builder/src/components/common/DatePicker.svelte b/packages/builder/src/components/common/DatePicker.svelte index 8533389595..8fc03decda 100644 --- a/packages/builder/src/components/common/DatePicker.svelte +++ b/packages/builder/src/components/common/DatePicker.svelte @@ -2,11 +2,10 @@ import flatpickr from "flatpickr" import "flatpickr/dist/flatpickr.css" import { onMount } from "svelte" + import { Label, Input } from "@budibase/bbui" export let value export let label - export let width = "medium" - export let size = "small" let input let fpInstance @@ -25,10 +24,9 @@
- -
- -
+ +
+ + diff --git a/packages/builder/src/components/common/ErrorsBox.svelte b/packages/builder/src/components/common/ErrorsBox.svelte index dffb383ec5..0e565a4ad8 100644 --- a/packages/builder/src/components/common/ErrorsBox.svelte +++ b/packages/builder/src/components/common/ErrorsBox.svelte @@ -5,7 +5,7 @@ {#if hasErrors} -
+
{#each errors as error}
{error.dataPath} {error.message}
{/each} diff --git a/packages/builder/src/components/common/IconButton.svelte b/packages/builder/src/components/common/IconButton.svelte deleted file mode 100644 index 6a01973181..0000000000 --- a/packages/builder/src/components/common/IconButton.svelte +++ /dev/null @@ -1,59 +0,0 @@ - - - - - diff --git a/packages/builder/src/components/common/Modal.svelte b/packages/builder/src/components/common/Modal.svelte deleted file mode 100644 index c91c30827e..0000000000 --- a/packages/builder/src/components/common/Modal.svelte +++ /dev/null @@ -1,59 +0,0 @@ - - -
- {#if isOpen} -
- {#if title} -
-

{title}

-
- {/if} -
- {#if onClosed} -
- -
- {/if} -
- - diff --git a/packages/builder/src/components/common/Spinner.svelte b/packages/builder/src/components/common/Spinner.svelte index 2a4e6c485f..a9c947915c 100644 --- a/packages/builder/src/components/common/Spinner.svelte +++ b/packages/builder/src/components/common/Spinner.svelte @@ -1,5 +1,5 @@ - + diff --git a/packages/builder/src/components/common/ValuesList.svelte b/packages/builder/src/components/common/ValuesList.svelte index 4f161247a8..19cd6c533a 100644 --- a/packages/builder/src/components/common/ValuesList.svelte +++ b/packages/builder/src/components/common/ValuesList.svelte @@ -17,9 +17,9 @@
-
-