From f041ed3bae6c275ea26d2ac93061e59209e89cfd Mon Sep 17 00:00:00 2001 From: Harsh Mishra Date: Mon, 24 Oct 2022 23:26:41 +0530 Subject: [PATCH 1/9] adding minor doc updates to mention docker desktop --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c44bf6d..34f7dde 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ LocalStack empowers developers to use over 75+ AWS services locally while helpin You can install the LocalStack Extension for Docker Desktop via pulling our public Docker image from Docker Hub: ```bash -docker extension install localstack/localstack-docker-extension:main +docker extension install localstack/localstack-docker-desktop:0.1.0 ``` To setup the Docker Extension by building the image locally, you can run the following commands: @@ -46,13 +46,13 @@ To contribute, check out our [issue tracker](https://github.com/localstack/local 2. Open the Developer Tools or create new features: ```bash - $ docker extension dev debug localstack/localstack-docker-extension + $ docker extension dev debug localstack/localstack-docker-desktop ``` 3. Start the Extension on Docker Desktop and enable hot-reload using the following command: ```bash $ npm start - $ docker extension dev ui-source localstack/localstack-docker-extension http://localhost:3000 + $ docker extension dev ui-source localstack/localstack-docker-desktop http://localhost:3000 ``` ## License From f904211bfd61d8e93e046483d8c767e59f821652 Mon Sep 17 00:00:00 2001 From: Luca Pivetta <36865043+Pive01@users.noreply.github.com> Date: Fri, 18 Nov 2022 15:08:50 +0100 Subject: [PATCH 2/9] Settings persistence (#9) * Added volume to extension with r/w functions * Fixed config auto-deletion problem * Removed newline * changed error status returned * Update docker-compose.yaml Co-authored-by: Waldemar Hummer * Modified eslint configs and fixed relative errors * lint updates * Fixed problem on first-tme running extension Co-authored-by: Waldemar Hummer --- docker-compose.yaml | 5 + ui/.eslintrc.js | 118 ++++++++++++------ ui/package-lock.json | 88 +++++++++++++ ui/package.json | 11 +- ui/src/components/Configs/ConfigOptions.tsx | 20 +-- ui/src/components/Configs/StartConfigs.tsx | 14 +-- ui/src/components/Configs/UpsertConfig.tsx | 18 +-- ui/src/components/Header/Controller.tsx | 9 +- ui/src/components/Header/Header.tsx | 40 +++--- .../components/SystemStatus/SystemStatus.tsx | 88 +++++++------ ui/src/components/index.ts | 1 - ui/src/components/provider.tsx | 11 +- ui/src/services/context/GlobalDDContext.ts | 4 +- ui/src/services/hooks/api.ts | 27 ++-- ui/src/services/hooks/utils.ts | 6 +- vm/main.go | 45 ++++++- 16 files changed, 342 insertions(+), 163 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index af81c25..4ab495c 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,3 +1,8 @@ services: LocalStack: image: ${DESKTOP_PLUGIN_IMAGE} + volumes: + - ls_app_config:/saved_config + +volumes: + ls_app_config: diff --git a/ui/.eslintrc.js b/ui/.eslintrc.js index b5e9a24..79685eb 100644 --- a/ui/.eslintrc.js +++ b/ui/.eslintrc.js @@ -1,46 +1,88 @@ -// eslint-disable-next-line no-undef module.exports = { - "root": true, - 'env': { - 'browser': true, - 'es2021': true, + root: true, + env: { + browser: true, + es2021: true, }, - 'extends': [ + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + ecmaVersion: 12, + sourceType: 'module', + requireConfigFile: false, + }, + parser: '@babel/eslint-parser', + extends: [ + 'plugin:react/recommended', + 'airbnb', + 'prettier', + 'plugin:import/recommended', + 'plugin:import/typescript', 'eslint:recommended', 'plugin:react/recommended', 'plugin:@typescript-eslint/recommended', ], - 'overrides': [ - ], - 'parser': '@typescript-eslint/parser', - 'parserOptions': { - 'ecmaVersion': 'latest', - 'sourceType': 'module', - }, - 'plugins': [ - 'react', - '@typescript-eslint', - ], - 'rules': { - '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], - 'indent': ['error', 2], - 'eol-last': ['error', 'always'], - 'no-param-reassign': 'off', - 'no-console': 'off', - 'curly': 'error', - 'no-unused-vars': 'warn', - 'key-spacing': ['error', { 'afterColon': true }], - 'class-methods-use-this': 'off', - 'consistent-return': 'off', - 'semi': 'error', - 'comma-dangle': ['error', 'always-multiline'], - 'max-len': [ - 'error', - { - code: 120, - tabWidth: 2, - ignoreComments: true, + overrides: [ + { + parser: '@typescript-eslint/parser', + plugins: [ + 'react', + '@typescript-eslint', + ], + + files: ['*.ts', '*.tsx'], + rules: { + quotes: ['error', 'single'], + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], + 'react/require-default-props': 'off', + 'react/function-component-definition': 'off', + 'react/jsx-props-no-spreading': 'off', + 'indent': ['error', 2], + 'react/jsx-filename-extension': [1, { extensions: ['.tsx', '.jsx'] }], + 'eol-last': ['error', 'always'], + 'no-param-reassign': 'off', + 'no-console': 'off', + 'import/no-extraneous-dependencies': 'off', + 'import/prefer-default-export': 'off', + 'import/no-named-as-default': 'off', + 'no-use-before-define': 'off', + 'no-shadow': 'off', + 'curly': 'error', + 'no-unused-vars': 'warn', + 'key-spacing': ['error', { 'afterColon': true }], + 'class-methods-use-this': 'off', + 'consistent-return': 'off', + 'semi': 'error', + 'comma-dangle': ['error', 'always-multiline'], + 'max-len': [ + 'error', + { + code: 120, + tabWidth: 2, + ignoreComments: true, + }, + ], + 'import/extensions': [ + 'error', + 'ignorePackages', + { + js: 'never', + jsx: 'never', + ts: 'never', + tsx: 'never', + }, + ], }, - ], - }, + extends: [ + 'plugin:react/recommended', + 'airbnb', + 'prettier', + 'plugin:import/recommended', + 'plugin:import/typescript', + 'plugin:@typescript-eslint/eslint-recommended', + 'plugin:@typescript-eslint/recommended', + ], + }, + ], }; diff --git a/ui/package-lock.json b/ui/package-lock.json index 048f5a5..f82bd3f 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -24,17 +24,23 @@ "swr": "^1.3.0" }, "devDependencies": { + "@babel/eslint-parser": "^7.19.1", "@docker/extension-api-client-types": "^0.3.0", "@types/jest": "^27.5.1", "@types/node": "^17.0.35", "@types/react-dom": "^17.0.2", "@typescript-eslint/eslint-plugin": "^5.38.1", "eslint": "^8.24.0", + "eslint-config-airbnb": "^19.0.4", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-config-prettier": "^8.5.0", "eslint-config-standard-with-typescript": "^23.0.0", "eslint-plugin-import": "^2.23.4", + "eslint-plugin-jsx-a11y": "^6.6.1", "eslint-plugin-n": "^15.2.5", "eslint-plugin-promise": "^6.0.1", "eslint-plugin-react": "^7.31.0", + "eslint-plugin-react-hooks": "^4.6.0", "typescript": "^4.6.4" } }, @@ -7239,6 +7245,58 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-config-airbnb": { + "version": "19.0.4", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz", + "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==", + "dev": true, + "dependencies": { + "eslint-config-airbnb-base": "^15.0.0", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5" + }, + "engines": { + "node": "^10.12.0 || ^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^7.32.0 || ^8.2.0", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.28.0", + "eslint-plugin-react-hooks": "^4.3.0" + } + }, + "node_modules/eslint-config-airbnb-base": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", + "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", + "dev": true, + "dependencies": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5", + "semver": "^6.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "peerDependencies": { + "eslint": "^7.32.0 || ^8.2.0", + "eslint-plugin-import": "^2.25.2" + } + }, + "node_modules/eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, "node_modules/eslint-config-react-app": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", @@ -22410,6 +22468,36 @@ } } }, + "eslint-config-airbnb": { + "version": "19.0.4", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz", + "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==", + "dev": true, + "requires": { + "eslint-config-airbnb-base": "^15.0.0", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5" + } + }, + "eslint-config-airbnb-base": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", + "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", + "dev": true, + "requires": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5", + "semver": "^6.3.0" + } + }, + "eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "dev": true, + "requires": {} + }, "eslint-config-react-app": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", diff --git a/ui/package.json b/ui/package.json index af152af..e6ad123 100644 --- a/ui/package.json +++ b/ui/package.json @@ -22,7 +22,8 @@ "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", - "eject": "react-scripts eject" + "eject": "react-scripts eject", + "lint": "eslint --ext js,ts,tsx src" }, "eslintConfig": { "extends": [ @@ -42,6 +43,12 @@ "eslint-plugin-n": "^15.2.5", "eslint-plugin-promise": "^6.0.1", "eslint-plugin-react": "^7.31.0", - "typescript": "^4.6.4" + "typescript": "^4.6.4", + "@babel/eslint-parser": "^7.19.1", + "eslint-config-airbnb": "^19.0.4", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-jsx-a11y": "^6.6.1", + "eslint-plugin-react-hooks": "^4.6.0" } } diff --git a/ui/src/components/Configs/ConfigOptions.tsx b/ui/src/components/Configs/ConfigOptions.tsx index 8befad5..139d5f4 100644 --- a/ui/src/components/Configs/ConfigOptions.tsx +++ b/ui/src/components/Configs/ConfigOptions.tsx @@ -1,9 +1,9 @@ -import { Box, Card, IconButton, List, ListItem, TextField } from "@mui/material"; -import { AddCircleOutline, RemoveCircleOutline } from "@mui/icons-material"; -import React, { ReactElement, useState } from "react"; +import { Box, Card, IconButton, List, ListItem, TextField } from '@mui/material'; +import { AddCircleOutline, RemoveCircleOutline } from '@mui/icons-material'; +import React, { ReactElement, useState } from 'react'; import { v4 as uuid } from 'uuid'; -import { useRunConfig } from "../../services/hooks"; -import { RunConfig } from "../../types"; +import { useRunConfig } from '../../services/hooks'; +import { RunConfig } from '../../types'; const DEFAULT_COLUMN_WIDTH = 2000; @@ -28,11 +28,11 @@ export const ConfigOptions = ({ config }: Props): ReactElement => { }; const handleRemoveButtonPress = (id: string) => { - setRunConfig(runConfig.map(config1 => { - return config1.id !== config.id ? - config1 : - { id: config1.id, name: config1.name, vars: config1.vars?.filter(item => item?.id !== id) || [] } as RunConfig; - })); + setRunConfig(runConfig.map(config1 => config1.id !== config.id ? + config1 : { + id: config1.id, name: config1.name, + vars: config1.vars?.filter(item => item?.id !== id) || [], + } as RunConfig)); }; return ( diff --git a/ui/src/components/Configs/StartConfigs.tsx b/ui/src/components/Configs/StartConfigs.tsx index 520ed9b..cc83ea2 100644 --- a/ui/src/components/Configs/StartConfigs.tsx +++ b/ui/src/components/Configs/StartConfigs.tsx @@ -1,10 +1,10 @@ -import { Add as AddIcon, Edit } from "@mui/icons-material"; -import { Box, Button, Card, IconButton, TextField, Theme } from "@mui/material"; -import React, { ReactElement, useState } from "react"; -import { useRunConfig } from "../../services/hooks"; +import { Add as AddIcon, Edit } from '@mui/icons-material'; +import { Box, Button, Card, IconButton, TextField, Theme } from '@mui/material'; +import React, { ReactElement, useState } from 'react'; import { createStyles, makeStyles } from '@mui/styles'; -import { UpsertConfig } from "./UpsertConfig"; -import { Optional, RunConfig } from "../../types"; +import { useRunConfig } from '../../services/hooks'; +import { UpsertConfig } from './UpsertConfig'; +import { Optional, RunConfig } from '../../types'; const useStyles = makeStyles((theme: Theme) => createStyles({ root: { @@ -43,7 +43,7 @@ export const StartConfigs = (): ReactElement => { runConfig.map(item => ( - + {item.id !== '0' && openModalSetup(item)} > diff --git a/ui/src/components/Configs/UpsertConfig.tsx b/ui/src/components/Configs/UpsertConfig.tsx index 572d81e..bf53a2d 100644 --- a/ui/src/components/Configs/UpsertConfig.tsx +++ b/ui/src/components/Configs/UpsertConfig.tsx @@ -9,12 +9,12 @@ import { List, ListItem, TextField, -} from "@mui/material"; -import { AddCircleOutline, RemoveCircleOutline } from "@mui/icons-material"; -import React, { ReactElement, useState } from "react"; +} from '@mui/material'; +import { AddCircleOutline, RemoveCircleOutline } from '@mui/icons-material'; +import React, { ReactElement, useState } from 'react'; import { v4 as uuid } from 'uuid'; -import { useRunConfig } from "../../services/hooks"; -import { RunConfig } from "../../types"; +import { useRunConfig } from '../../services/hooks'; +import { RunConfig } from '../../types'; const DEFAULT_COLUMN_WIDTH = 2000; @@ -42,7 +42,7 @@ export const UpsertConfig = ({ config, open, onClose }: Props): ReactElement => }; const handleSaveButtonPress = () => { - setRunConfig([...runConfig.filter(config_1 => config_1.id !== newConfig.id), + setRunConfig([...runConfig.filter(config1 => config1.id !== newConfig.id), { name: configName, id: newConfig.id, vars: newConfig.vars, }]); @@ -57,7 +57,7 @@ export const UpsertConfig = ({ config, open, onClose }: Props): ReactElement => const handleDeleteButtonPress = () => { if (newConfig.id) { - setRunConfig(runConfig.filter(config_1 => config_1.id !== newConfig.id)); + setRunConfig(runConfig.filter(config1 => config1.id !== newConfig.id)); } onClose(); }; @@ -79,8 +79,8 @@ export const UpsertConfig = ({ config, open, onClose }: Props): ReactElement => {newConfig?.vars.map(item => ( - - + + handleRemoveButtonPress(item.id)} > diff --git a/ui/src/components/Header/Controller.tsx b/ui/src/components/Header/Controller.tsx index 1a775da..f1ef676 100644 --- a/ui/src/components/Header/Controller.tsx +++ b/ui/src/components/Header/Controller.tsx @@ -1,21 +1,21 @@ import React, { ReactElement, useEffect, useState } from 'react'; import { Chip, Button, ButtonGroup, Select, MenuItem, FormControl } from '@mui/material'; +import { PlayArrow, Stop } from '@mui/icons-material'; +import { v4 as uuid } from 'uuid'; import { START_ARGS, STOP_ARGS } from '../../constants'; import { DockerImage } from '../../types'; import { useDDClient, useRunConfig, useLocalStack } from '../../services/hooks'; -import { PlayArrow, Stop } from '@mui/icons-material'; -import { v4 as uuid } from 'uuid'; export const Controller = (): ReactElement => { const ddClient = useDDClient(); - const { runConfig, setRunConfig } = useRunConfig(); + const { runConfig, isLoading, setRunConfig } = useRunConfig(); const { data, mutate } = useLocalStack(); const [runningConfig, setRunningConfig] = useState('Default'); const isRunning = data && data.State === 'running'; useEffect(() => { - if (!runConfig.find(item => item.name === 'Default')) { + if (!isLoading && !runConfig.find(item => item.name === 'Default')) { setRunConfig([...runConfig, { name: 'Default', id: '0', vars: @@ -40,7 +40,6 @@ export const Controller = (): ReactElement => { ddClient.docker.cli.exec('run', STOP_ARGS).then(() => mutate()); }; - return ( <> { const ddClient = useDDClient(); return ( - <> - - - - + + + + LocalStack - - - - - - - + + + ); }; diff --git a/ui/src/components/SystemStatus/SystemStatus.tsx b/ui/src/components/SystemStatus/SystemStatus.tsx index 62c807a..1699785 100644 --- a/ui/src/components/SystemStatus/SystemStatus.tsx +++ b/ui/src/components/SystemStatus/SystemStatus.tsx @@ -4,13 +4,13 @@ import { Button, Card, CardActions, CardContent, List, ListItem, ListItemText, Theme, Typography, } from '@mui/material'; +import { createStyles, makeStyles } from '@mui/styles'; +import { Refresh } from '@mui/icons-material'; import { useLocalStackHealth } from '../../services/hooks/health'; import { HealthState } from '../../types'; import { Capitalize } from '../../services/generic/utils'; -import { createStyles, makeStyles } from '@mui/styles'; import { Status as SystemStatusIcon } from './Status'; import { useLocalStack } from '../../services/hooks'; -import { Refresh } from '@mui/icons-material'; const ORDER = [ HealthState.RUNNING, @@ -53,50 +53,48 @@ export const SystemStatus = (): ReactElement => { return ( - <> - - - - { - ORDER.map((status) => ( - - {statusesMap[status] && ( - - - {Capitalize(status)} - - - {Object.entries(statusesMap[status] ?? {}).map(([k, v]) => ( - - } - /> - - ))} - - - )} - - )) - } - - - {health ? - <>version : {health?.version} - : - <>No data available - } + + { + ORDER.map((status) => ( + + {statusesMap[status] && ( + + + {Capitalize(status)} + + + {Object.entries(statusesMap[status] ?? {}).map(([k, v]) => ( + + } + /> + + ))} + + + )} + + )) + } + + + {health ? + <>version : {health?.version} + : + <>No data available + } - - - + + ); }; diff --git a/ui/src/components/index.ts b/ui/src/components/index.ts index 2df47dd..0968564 100644 --- a/ui/src/components/index.ts +++ b/ui/src/components/index.ts @@ -3,4 +3,3 @@ export * from './ControlledTabPanel'; export * from './Header'; export * from './Logs/Logs'; export * from './SystemStatus'; -export * from './Header'; diff --git a/ui/src/components/provider.tsx b/ui/src/components/provider.tsx index a2a37c4..a6815c9 100644 --- a/ui/src/components/provider.tsx +++ b/ui/src/components/provider.tsx @@ -1,6 +1,7 @@ -import { createDockerDesktopClient } from "@docker/extension-api-client"; -import React, { ReactNode } from "react"; -import { GlobalDDContext } from "../services/context/GlobalDDContext"; +// eslint-disable-next-line import/no-unresolved +import { createDockerDesktopClient } from '@docker/extension-api-client'; +import React, { ReactNode, useMemo } from 'react'; +import { GlobalDDContext } from '../services/context/GlobalDDContext'; interface Props { children: ReactNode; @@ -8,8 +9,10 @@ interface Props { const client = createDockerDesktopClient(); export const GlobalDDProvider = ({ children }: Props) => { + const GlobalDDContextValue = useMemo(() => ({ client }), [client]); + return ( - + {children} ); diff --git a/ui/src/services/context/GlobalDDContext.ts b/ui/src/services/context/GlobalDDContext.ts index 7230792..7a80d17 100644 --- a/ui/src/services/context/GlobalDDContext.ts +++ b/ui/src/services/context/GlobalDDContext.ts @@ -1,5 +1,5 @@ -import { DockerDesktopClient } from "@docker/extension-api-client-types/dist/v1"; -import React from "react"; +import { DockerDesktopClient } from '@docker/extension-api-client-types/dist/v1'; +import React from 'react'; export type GlobalDDContextInterface = { client: DockerDesktopClient; diff --git a/ui/src/services/hooks/api.ts b/ui/src/services/hooks/api.ts index 640ba3e..d420f89 100644 --- a/ui/src/services/hooks/api.ts +++ b/ui/src/services/hooks/api.ts @@ -1,27 +1,33 @@ -import useSWR from "swr"; -import { STORAGE_KEY_ENVVARS, STORAGE_KEY_LOCALSTACK } from "../../constants"; -import { DockerContainer, RunConfig } from "../../types"; -import { useDDClient } from "./utils"; +import useSWR from 'swr'; +import { STORAGE_KEY_ENVVARS, STORAGE_KEY_LOCALSTACK } from '../../constants'; +import { DockerContainer, RunConfig } from '../../types'; +import { useDDClient } from './utils'; interface useRunConfigReturn { runConfig: RunConfig[], + isLoading: boolean, setRunConfig: (data: RunConfig[]) => unknown; } +interface HTTPMessageBody { + Message: string, +} + export const useRunConfig = (): useRunConfigReturn => { const cacheKey = STORAGE_KEY_ENVVARS; - - const { data, mutate } = useSWR( + const ddClient = useDDClient(); + const { data, mutate, isValidating, error } = useSWR( cacheKey, - () => JSON.parse(localStorage.getItem(STORAGE_KEY_ENVVARS) as string), + () => (ddClient.extension.vm.service.get('/getConfig') as Promise), ); - const mutateRunConfig = (newData: RunConfig[]) => { - localStorage.setItem(cacheKey, JSON.stringify(newData)); + const mutateRunConfig = async (newData: RunConfig[]) => { + await ddClient.extension.vm.service.post('/setConfig', { Data: JSON.stringify(newData) }); mutate(); }; return { - runConfig: data || [], + runConfig: (!data || data?.Message === '' || data?.Message as string === 'Failed') ? [] : JSON.parse(data?.Message), + isLoading: isValidating || (!error && !data), setRunConfig: mutateRunConfig, }; }; @@ -31,7 +37,6 @@ interface useLocalStackReturn { mutate: () => void; } - export const useLocalStack = (): useLocalStackReturn => { const ddClient = useDDClient(); const cacheKey = STORAGE_KEY_LOCALSTACK; diff --git a/ui/src/services/hooks/utils.ts b/ui/src/services/hooks/utils.ts index 90b525e..79b2e25 100644 --- a/ui/src/services/hooks/utils.ts +++ b/ui/src/services/hooks/utils.ts @@ -1,6 +1,6 @@ -import { DockerDesktopClient } from "@docker/extension-api-client-types/dist/v1"; -import { useContext } from "react"; -import { GlobalDDContext } from "../context/GlobalDDContext"; +import { DockerDesktopClient } from '@docker/extension-api-client-types/dist/v1'; +import { useContext } from 'react'; +import { GlobalDDContext } from '../context/GlobalDDContext'; export const useDDClient = (): DockerDesktopClient => { const { client } = useContext(GlobalDDContext); diff --git a/vm/main.go b/vm/main.go index c8a4fc4..347443a 100644 --- a/vm/main.go +++ b/vm/main.go @@ -1,6 +1,7 @@ package main import ( + "errors" "flag" "log" "net" @@ -17,7 +18,7 @@ func main() { flag.Parse() os.RemoveAll(socketPath) - + logrus.New().Infof("Starting listening on %s\n", socketPath) router := echo.New() router.HideBanner = true @@ -29,8 +30,9 @@ func main() { log.Fatal(err) } router.Listener = ln - - router.GET("/hello", hello) + os.Chdir("/saved_config") + router.GET("/getConfig", getSettings) + router.POST("/setConfig", setSettings) log.Fatal(router.Start(startURL)) } @@ -39,8 +41,41 @@ func listen(path string) (net.Listener, error) { return net.Listen("unix", path) } -func hello(ctx echo.Context) error { - return ctx.JSON(http.StatusOK, HTTPMessageBody{Message: "hello"}) +func getSettings(ctx echo.Context) error { + + _, err := os.Stat("data.json") + + if errors.Is(err, os.ErrNotExist) { + logrus.New().Infof("File not exist, creating") + file, err := os.Create("data.json") + file.Close() + if err != nil { + logrus.New().Infof("Errors while creating file") + logrus.New().Infof(err.Error()) + } + } + + content, err := os.ReadFile("data.json") + if err != nil { + return ctx.JSON(http.StatusConflict, HTTPMessageBody{Message: "Failed"}) + } + return ctx.JSON(http.StatusOK, HTTPMessageBody{Message: string(content[:])}) +} + +type Payload struct { + Data string `json:"data"` +} + +func setSettings(ctx echo.Context) error { + var payload Payload + ctx.Bind(&payload) + err := os.WriteFile("data.json", []byte(payload.Data), 0644) + if err != nil { + logrus.New().Infof(err.Error()) + return ctx.NoContent(http.StatusInternalServerError) + } + + return ctx.NoContent(http.StatusOK) } type HTTPMessageBody struct { From ba9fdbf4c9e8824ad0432b1a0472c5a64030d1d4 Mon Sep 17 00:00:00 2001 From: Luca Pivetta <36865043+Pive01@users.noreply.github.com> Date: Wed, 23 Nov 2022 09:11:03 +0100 Subject: [PATCH 3/9] Fixed Logs not correctly displayed and start button (#10) * Fixed logs display * polish * removed whitespace --- ui/src/components/Configs/StartConfigs.tsx | 5 +-- ui/src/components/Logs/Logs.tsx | 36 +++++++++++++--------- ui/src/constants/docker.ts | 2 +- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/ui/src/components/Configs/StartConfigs.tsx b/ui/src/components/Configs/StartConfigs.tsx index cc83ea2..87572bb 100644 --- a/ui/src/components/Configs/StartConfigs.tsx +++ b/ui/src/components/Configs/StartConfigs.tsx @@ -7,9 +7,6 @@ import { UpsertConfig } from './UpsertConfig'; import { Optional, RunConfig } from '../../types'; const useStyles = makeStyles((theme: Theme) => createStyles({ - root: { - margin: theme.spacing(2), - }, addButton: { margin: theme.spacing(2), }, @@ -29,7 +26,7 @@ export const StartConfigs = (): ReactElement => { }; return ( - + + + (row).id as string || uuid()} + disableSelectionOnClick + /> + {openModal && setOpenModal(false)} />} - { - runConfig.map(item => ( - - - - {item.id !== '0' && - openModalSetup(item)} > - - - } - - - )) - } ); }; diff --git a/ui/src/components/Configs/UpsertConfig.tsx b/ui/src/components/Configs/UpsertConfig.tsx index bf53a2d..93298ec 100644 --- a/ui/src/components/Configs/UpsertConfig.tsx +++ b/ui/src/components/Configs/UpsertConfig.tsx @@ -1,7 +1,6 @@ import { Box, Button, - Card, Dialog, DialogActions, DialogContent, @@ -9,10 +8,13 @@ import { List, ListItem, TextField, + Typography, + Theme, } from '@mui/material'; -import { AddCircleOutline, RemoveCircleOutline } from '@mui/icons-material'; +import { Add, Remove, Settings } from '@mui/icons-material'; import React, { ReactElement, useState } from 'react'; import { v4 as uuid } from 'uuid'; +import { createStyles, makeStyles } from '@mui/styles'; import { useRunConfig } from '../../services/hooks'; import { RunConfig } from '../../types'; @@ -24,13 +26,24 @@ type Props = { onClose: () => void; }; +const useStyles = makeStyles((theme: Theme) => createStyles({ + emptyBox: { + height: theme.spacing(5), + }, + textField: { + marginRight: theme.spacing(1), + marginTop: theme.spacing(1), + }, +})); + export const UpsertConfig = ({ config, open, onClose }: Props): ReactElement => { const { runConfig, setRunConfig } = useRunConfig(); const [newVar, setNewVar] = useState(''); const [newValue, setNewValue] = useState(''); const [configName, setConfigName] = useState(config?.name || ''); - const [newConfig, setNewConfig] = useState(config || { name: '', vars: [] } as RunConfig); + const [newConfig, setNewConfig] = useState(config || { name: '', id: uuid(), vars: [] } as RunConfig); + const classes = useStyles(); const handleAddButtonPress = () => { setNewConfig({ @@ -65,57 +78,64 @@ export const UpsertConfig = ({ config, open, onClose }: Props): ReactElement => return ( - - - setConfigName(e.target.value)} - /> - - - {newConfig?.vars.map(item => ( - - - - - handleRemoveButtonPress(item.id)} > - - - - - ))} - - - setNewVar(e.target.value)} - style={{ margin: 5 }} - value={newVar} - /> - setNewValue(e.target.value)} - style={{ margin: 5 }} - value={newValue} - /> - - + + + + setConfigName(e.target.value)} + /> + + + Environment Variables + } + > + {newConfig?.vars.map(item => ( + + + + + handleRemoveButtonPress(item.id)} > + - - + ))} + + + setNewVar(e.target.value)} + className={classes.textField} + value={newVar} + /> + + setNewValue(e.target.value)} + className={classes.textField} + value={newValue} + /> + + + + + + - + setRunningConfig(target.value)} + > + { + runConfig?.map(config => ( + {config.name} + )) + } + + + + + } + - - - - - - - ); From e1751f2404e0590faadef1c6b0f4790a5f032668 Mon Sep 17 00:00:00 2001 From: HarshCasper Date: Fri, 16 Dec 2022 23:35:21 +0530 Subject: [PATCH 9/9] update the documentation for 0.2.0 release --- CHANGELOG.md | 31 +++++++++++++++++++++++++++++++ README.md | 14 +++----------- 2 files changed, 34 insertions(+), 11 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..fd60533 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,31 @@ +# Change Log + +All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). + +## [0.2.0] - 2022-12-16 + +### Added + +- You can now update your LocalStack images from the UI. + +### Changed + +- We have made some changes in the UI: + - Updates in the control section + - Moved to a table to display saved configurations + - Improved UI for inserting a new configuration + +### Fixed + +- Made configuration persistent +- Logs are correctly displayed + +## [0.1.0] - 2022-10-27 + +### Added + +- Initial version of extension +- Environment variables addition +- UI and files hierarchy changes +- Add `docker` build and publish action +- Refactor the `README` page diff --git a/README.md b/README.md index f272c38..1619658 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ LocalStack empowers developers to use over 75+ AWS services locally while helpin You can install the LocalStack Extension for Docker Desktop via pulling our public Docker image from Docker Hub: ```bash -docker extension install localstack/localstack-docker-desktop:0.1.0 +docker extension install localstack/localstack-docker-desktop:0.2.0 ``` To setup the Docker Extension by building the image locally, you can run the following commands: @@ -53,17 +53,9 @@ To contribute, check out our [issue tracker](https://github.com/localstack/local $ docker extension dev ui-source localstack/localstack-docker-desktop http://localhost:3000 ``` -## Changelog -- v0.2.0: - - You can now update your LocalStack images from the UI - - We have made some changes in the UI: - - Updates in the control section - - Moved to a table to display saved configurations - - Improved UI for inserting a new configuration - - Bug fixes: - - Made configuration persistent - - Logs are correctly displayed +## Releases +Please refer to [`CHANGELOG`](CHANGELOG.md) to see the complete list of changes for each release. ## License