From 702a654279e1971f4db5674f21ce7ac43ee3fa95 Mon Sep 17 00:00:00 2001 From: kira-pilot Date: Tue, 28 Jun 2022 14:40:01 +0000 Subject: [PATCH 1/4] debounced search on type --- site/package.json | 5 ++- .../SearchBarWithFilter.test.tsx | 38 +++++++++++++++++++ .../SearchBarWithFilter.tsx | 20 +++++++++- .../pages/WorkspacesPage/WorkspacesPage.tsx | 22 ++++------- site/yarn.lock | 9 ++++- 5 files changed, 74 insertions(+), 20 deletions(-) create mode 100644 site/src/components/SearchBarWithFilter/SearchBarWithFilter.test.tsx diff --git a/site/package.json b/site/package.json index ca1a388e08028..ffe816e7939e3 100644 --- a/site/package.json +++ b/site/package.json @@ -42,9 +42,10 @@ "formik": "2.2.9", "front-matter": "4.0.2", "history": "5.3.0", + "just-debounce-it": "3.0.1", "react": "17.0.2", "react-dom": "17.0.2", - "react-helmet": "^6.1.0", + "react-helmet": "6.1.0", "react-markdown": "8.0.3", "react-router-dom": "6.3.0", "sourcemapped-stacktrace": "1.1.11", @@ -74,7 +75,7 @@ "@types/node": "14.18.16", "@types/react": "17.0.44", "@types/react-dom": "17.0.16", - "@types/react-helmet": "^6.1.5", + "@types/react-helmet": "6.1.5", "@types/superagent": "4.1.15", "@types/uuid": "8.3.4", "@typescript-eslint/eslint-plugin": "5.27.0", diff --git a/site/src/components/SearchBarWithFilter/SearchBarWithFilter.test.tsx b/site/src/components/SearchBarWithFilter/SearchBarWithFilter.test.tsx new file mode 100644 index 0000000000000..48061e1d9b369 --- /dev/null +++ b/site/src/components/SearchBarWithFilter/SearchBarWithFilter.test.tsx @@ -0,0 +1,38 @@ +import { fireEvent, screen } from "@testing-library/react" +import userEvent from "@testing-library/user-event" +import { render } from "../../testHelpers/renderHelpers" +import { SearchBarWithFilter } from "./SearchBarWithFilter" + +// mock the debounce utility +jest.mock("just-debounce-it", () => + jest.fn((fn) => { + fn.cancel = jest.fn() + return fn + }), +) + +describe("SearchBarWithFilter", () => { + it("calls the onFilter handler on keystroke", async () => { + // When + const onFilter = jest.fn() + render() + + const searchInput = screen.getByRole("textbox") + await userEvent.type(searchInput, "workspace") // 9 characters + + // Then + expect(onFilter).toBeCalledTimes(10) // 9 characters + 1 on component mount + }) + + it("calls the onFilter handler on submit", async () => { + // When + const onFilter = jest.fn() + render() + + const searchInput = screen.getByRole("textbox") + await fireEvent.keyDown(searchInput, { key: "Enter", code: "Enter", charCode: 13 }) + + // Then + expect(onFilter).toBeCalledTimes(1) + }) +}) diff --git a/site/src/components/SearchBarWithFilter/SearchBarWithFilter.tsx b/site/src/components/SearchBarWithFilter/SearchBarWithFilter.tsx index b44218dd8454c..4abf0239aa093 100644 --- a/site/src/components/SearchBarWithFilter/SearchBarWithFilter.tsx +++ b/site/src/components/SearchBarWithFilter/SearchBarWithFilter.tsx @@ -7,7 +7,8 @@ import { makeStyles } from "@material-ui/core/styles" import TextField from "@material-ui/core/TextField" import SearchIcon from "@material-ui/icons/Search" import { FormikErrors, useFormik } from "formik" -import { useState } from "react" +import debounce from "just-debounce-it" +import { useCallback, useEffect, useState } from "react" import { getFormHelpers, onChangeTrimmed } from "../../util/formUtils" import { CloseDropdown, OpenDropdown } from "../DropdownArrows/DropdownArrows" import { Stack } from "../Stack/Stack" @@ -50,6 +51,23 @@ export const SearchBarWithFilter: React.FC = ({ }, }) + // debounce query string entry by user + // we want the dependency array empty here + // as we don't need to redefine the function + // eslint-disable-next-line react-hooks/exhaustive-deps + const debouncedOnFilter = useCallback( + debounce((debouncedQueryString: string) => { + onFilter(debouncedQueryString) + }, 300), + [], + ) + + // update the query params while typing + useEffect(() => { + debouncedOnFilter(form.values.query) + return () => debouncedOnFilter.cancel() + }, [debouncedOnFilter, form.values.query]) + const getFieldHelpers = getFormHelpers(form) const [anchorEl, setAnchorEl] = useState(null) diff --git a/site/src/pages/WorkspacesPage/WorkspacesPage.tsx b/site/src/pages/WorkspacesPage/WorkspacesPage.tsx index 1f742967e5310..a85101039eee5 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesPage.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesPage.tsx @@ -1,27 +1,16 @@ import { useMachine } from "@xstate/react" -import { FC, useEffect } from "react" +import { FC } from "react" import { Helmet } from "react-helmet" import { useSearchParams } from "react-router-dom" import { pageTitle } from "../../util/page" -import { workspaceFilterQuery } from "../../util/workspace" import { workspacesMachine } from "../../xServices/workspaces/workspacesXService" import { WorkspacesPageView } from "./WorkspacesPageView" const WorkspacesPage: FC = () => { const [workspacesState, send] = useMachine(workspacesMachine) - const [searchParams, setSearchParams] = useSearchParams() + const [_, setSearchParams] = useSearchParams() const { workspaceRefs } = workspacesState.context - useEffect(() => { - const filter = searchParams.get("filter") - const query = filter !== null ? filter : workspaceFilterQuery.me - - send({ - type: "GET_WORKSPACES", - query, - }) - }, [searchParams, send]) - return ( <> @@ -33,8 +22,11 @@ const WorkspacesPage: FC = () => { loading={workspacesState.hasTag("loading")} workspaceRefs={workspaceRefs} onFilter={(query) => { - searchParams.set("filter", query) - setSearchParams(searchParams) + setSearchParams({ filter: query }) + send({ + type: "GET_WORKSPACES", + query, + }) }} /> diff --git a/site/yarn.lock b/site/yarn.lock index 23a265d0d434b..76e1f3fd78860 100644 --- a/site/yarn.lock +++ b/site/yarn.lock @@ -3041,7 +3041,7 @@ dependencies: "@types/react" "^17" -"@types/react-helmet@^6.1.5": +"@types/react-helmet@6.1.5": version "6.1.5" resolved "https://registry.yarnpkg.com/@types/react-helmet/-/react-helmet-6.1.5.tgz#35f89a6b1646ee2bc342a33a9a6c8777933f9083" integrity sha512-/ICuy7OHZxR0YCAZLNg9r7I9aijWUWvxaPR6uTuyxe8tAj5RL4Sw1+R6NhXUtOsarkGYPmaHdBDvuXh2DIN/uA== @@ -9122,6 +9122,11 @@ junk@^3.1.0: resolved "https://registry.yarnpkg.com/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1" integrity sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ== +just-debounce-it@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/just-debounce-it/-/just-debounce-it-3.0.1.tgz#8c8a4c9327c9523366ec79ac9a959a938153bd2f" + integrity sha512-6EQWOpRV8fm/ame6XvGBSxvsjoMbqj7JS9TV/4Q9aOXt9DQw22GBfTGP6gTAqcBNN/PbzlwtwH7jtM0k9oe9pg== + kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" @@ -11487,7 +11492,7 @@ react-helmet-async@^1.0.7: react-fast-compare "^3.2.0" shallowequal "^1.1.0" -react-helmet@^6.1.0: +react-helmet@6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-6.1.0.tgz#a750d5165cb13cf213e44747502652e794468726" integrity sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw== From 4f3202d9485c2f8642f564d64d6294ee018d941a Mon Sep 17 00:00:00 2001 From: kira-pilot Date: Tue, 28 Jun 2022 19:36:38 +0000 Subject: [PATCH 2/4] loading workspaces on page entry --- site/src/pages/WorkspacesPage/WorkspacesPage.tsx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/site/src/pages/WorkspacesPage/WorkspacesPage.tsx b/site/src/pages/WorkspacesPage/WorkspacesPage.tsx index a85101039eee5..aca09bb29b7ee 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesPage.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesPage.tsx @@ -1,8 +1,9 @@ import { useMachine } from "@xstate/react" -import { FC } from "react" +import { FC, useEffect } from "react" import { Helmet } from "react-helmet" import { useSearchParams } from "react-router-dom" import { pageTitle } from "../../util/page" +import { workspaceFilterQuery } from "../../util/workspace" import { workspacesMachine } from "../../xServices/workspaces/workspacesXService" import { WorkspacesPageView } from "./WorkspacesPageView" @@ -11,6 +12,17 @@ const WorkspacesPage: FC = () => { const [_, setSearchParams] = useSearchParams() const { workspaceRefs } = workspacesState.context + // On page load, populate the table with workspaces + useEffect(() => { + const query = workspaceFilterQuery.me + + send({ + type: "GET_WORKSPACES", + query, + }) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + return ( <> From 766f03e5bc785ec98d33ff414ea9f1eef552a949 Mon Sep 17 00:00:00 2001 From: kira-pilot Date: Wed, 29 Jun 2022 16:15:47 +0000 Subject: [PATCH 3/4] fixing e2e test --- site/e2e/pom/WorkspacesPage.ts | 4 ++-- site/e2e/tests/login.spec.ts | 2 +- site/src/pages/WorkspacesPage/WorkspacesPage.tsx | 6 ++++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/site/e2e/pom/WorkspacesPage.ts b/site/e2e/pom/WorkspacesPage.ts index eecdea38c89c2..33f2dd38f1b52 100644 --- a/site/e2e/pom/WorkspacesPage.ts +++ b/site/e2e/pom/WorkspacesPage.ts @@ -2,7 +2,7 @@ import { Page } from "@playwright/test" import { BasePom } from "./BasePom" export class WorkspacesPage extends BasePom { - constructor(baseURL: string | undefined, page: Page) { - super(baseURL, "/workspaces", page) + constructor(baseURL: string | undefined, page: Page, params?: string) { + super(baseURL, `/workspaces${params && params}`, page) } } diff --git a/site/e2e/tests/login.spec.ts b/site/e2e/tests/login.spec.ts index 7127f84e9562e..0e6482e602072 100644 --- a/site/e2e/tests/login.spec.ts +++ b/site/e2e/tests/login.spec.ts @@ -10,7 +10,7 @@ test("Login takes user to /workspaces", async ({ baseURL, page }) => { const signInPage = new SignInPage(baseURL, page) await signInPage.submitBuiltInAuthentication(email, password) - const workspacesPage = new WorkspacesPage(baseURL, page) + const workspacesPage = new WorkspacesPage(baseURL, page, "?filter=owner%3Ame") await waitForClientSideNavigation(page, { to: workspacesPage.url }) await page.waitForSelector("text=Workspaces") diff --git a/site/src/pages/WorkspacesPage/WorkspacesPage.tsx b/site/src/pages/WorkspacesPage/WorkspacesPage.tsx index aca09bb29b7ee..5392d9568c079 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesPage.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesPage.tsx @@ -9,12 +9,14 @@ import { WorkspacesPageView } from "./WorkspacesPageView" const WorkspacesPage: FC = () => { const [workspacesState, send] = useMachine(workspacesMachine) - const [_, setSearchParams] = useSearchParams() + const [searchParams, setSearchParams] = useSearchParams() const { workspaceRefs } = workspacesState.context // On page load, populate the table with workspaces useEffect(() => { - const query = workspaceFilterQuery.me + const filter = searchParams.get("filter") + const query = filter !== null ? filter : workspaceFilterQuery.me + // const query = workspaceFilterQuery.me send({ type: "GET_WORKSPACES", From 5637ca52b06b59f59f80bc82dd524a8aed520daf Mon Sep 17 00:00:00 2001 From: kira-pilot Date: Wed, 29 Jun 2022 16:31:11 +0000 Subject: [PATCH 4/4] removing boilerplate --- aws-linux/README.md | 70 ------------ aws-linux/main.tf | 141 ------------------------- cmd/coder/docker-code-server/README.md | 11 -- cmd/coder/docker-code-server/main.tf | 45 -------- 4 files changed, 267 deletions(-) delete mode 100644 aws-linux/README.md delete mode 100644 aws-linux/main.tf delete mode 100644 cmd/coder/docker-code-server/README.md delete mode 100644 cmd/coder/docker-code-server/main.tf diff --git a/aws-linux/README.md b/aws-linux/README.md deleted file mode 100644 index 9333d41a355b9..0000000000000 --- a/aws-linux/README.md +++ /dev/null @@ -1,70 +0,0 @@ ---- -name: Develop in Linux on AWS EC2 -description: Get started with Linux development on AWS EC2. -tags: [cloud, aws] ---- - -# aws-linux - -To get started, run `coder templates init`. When prompted, select this template. -Follow the on-screen instructions to proceed. - -## Authentication - -This template assumes that coderd is run in an environment that is authenticated -with AWS. For example, run `aws configure import` to import credentials on the -system and user running coderd. For other ways to authenticate [consult the -Terraform docs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#authentication-and-configuration). - -## Required permissions / policy - -The following sample policy allows Coder to create EC2 instances and modify -instances provisioned by Coder: - -```json -{ - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "VisualEditor0", - "Effect": "Allow", - "Action": [ - "ec2:GetDefaultCreditSpecification", - "ec2:DescribeIamInstanceProfileAssociations", - "ec2:DescribeTags", - "ec2:CreateTags", - "ec2:RunInstances", - "ec2:DescribeInstanceCreditSpecifications", - "ec2:DescribeImages", - "ec2:ModifyDefaultCreditSpecification", - "ec2:DescribeVolumes" - ], - "Resource": "*" - }, - { - "Sid": "CoderResouces", - "Effect": "Allow", - "Action": [ - "ec2:DescribeInstances", - "ec2:DescribeInstanceAttribute", - "ec2:UnmonitorInstances", - "ec2:TerminateInstances", - "ec2:StartInstances", - "ec2:StopInstances", - "ec2:DeleteTags", - "ec2:MonitorInstances", - "ec2:CreateTags", - "ec2:RunInstances", - "ec2:ModifyInstanceAttribute", - "ec2:ModifyInstanceCreditSpecification" - ], - "Resource": "arn:aws:ec2:*:*:instance/*", - "Condition": { - "StringEquals": { - "aws:ResourceTag/Coder_Provisioned": "true" - } - } - } - ] -} -``` diff --git a/aws-linux/main.tf b/aws-linux/main.tf deleted file mode 100644 index 5377f2702d022..0000000000000 --- a/aws-linux/main.tf +++ /dev/null @@ -1,141 +0,0 @@ -terraform { - required_providers { - coder = { - source = "coder/coder" - version = "0.3.4" - } - } -} - -# Last updated 2022-05-31 -# aws ec2 describe-regions | jq -r '[.Regions[].RegionName] | sort' -variable "region" { - description = "What region should your workspace live in?" - default = "us-east-1" - validation { - condition = contains([ - "ap-northeast-1", - "ap-northeast-2", - "ap-northeast-3", - "ap-south-1", - "ap-southeast-1", - "ap-southeast-2", - "ca-central-1", - "eu-central-1", - "eu-north-1", - "eu-west-1", - "eu-west-2", - "eu-west-3", - "sa-east-1", - "us-east-1", - "us-east-2", - "us-west-1", - "us-west-2" - ], var.region) - error_message = "Invalid region!" - } -} - -provider "aws" { - region = var.region -} - -data "coder_workspace" "me" { -} - -data "aws_ami" "ubuntu" { - most_recent = true - filter { - name = "name" - values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] - } - filter { - name = "virtualization-type" - values = ["hvm"] - } - owners = ["099720109477"] # Canonical -} - -resource "coder_agent" "dev" { - arch = "amd64" - auth = "aws-instance-identity" - os = "linux" -} - -locals { - - # User data is used to stop/start AWS instances. See: - # https://github.com/hashicorp/terraform-provider-aws/issues/22 - - user_data_start = <