From 2fd5f2c66a5de747b2224b2dfa398bb2d5495b54 Mon Sep 17 00:00:00 2001 From: Julien Stroheker Date: Thu, 30 Jan 2020 16:47:49 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=80=20(#17)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Well let's try this ! (#16) * typo into release pipeline * Feat/add username in header (#14) * feat: add user in redux * feat: update app and topbar components * feat: avoid user to autolike his comment (#15) * feat: add user in redux * feat: update app and topbar components * feat: avoid user to autolike his comment Co-authored-by: Yann RENAUDIN <4748419+emyann@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- .github/workflows/cd-dev.yml | 39 +++++++++++++++++++++++ .github/workflows/cd.yml | 1 + Makefile | 18 +++++++++++ charts/api/templates/deployment.yaml | 2 +- charts/api/templates/ingress.yaml | 41 ------------------------- charts/api/values.yaml | 16 ++-------- charts/client/templates/deployment.yaml | 2 +- charts/client/values.yaml | 2 +- charts/ingress/ingress.yaml | 14 +++++++++ charts/worker/templates/deployment.yaml | 2 +- charts/worker/templates/ingress.yaml | 41 ------------------------- charts/worker/values.yaml | 3 ++ cmd/web/src/App.tsx | 22 +++++++++---- cmd/web/src/Questions.tsx | 9 ++++-- cmd/web/src/TopBar.tsx | 31 +++++++++++++------ cmd/web/src/services/users.service.ts | 21 +++++++------ cmd/web/src/store/rootReducer.ts | 4 ++- cmd/web/src/store/user/index.ts | 2 ++ cmd/web/src/store/user/user.selector.ts | 5 +++ cmd/web/src/store/user/user.slice.ts | 21 +++++++++++++ 21 files changed, 170 insertions(+), 128 deletions(-) create mode 100644 .github/workflows/cd-dev.yml delete mode 100644 charts/api/templates/ingress.yaml delete mode 100644 charts/worker/templates/ingress.yaml create mode 100644 cmd/web/src/store/user/index.ts create mode 100644 cmd/web/src/store/user/user.selector.ts create mode 100644 cmd/web/src/store/user/user.slice.ts diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 41d64b6..d3593b0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Build Docker image - env: # Or as an environment variable + env: FTH_REGISTRY_PASSWORD: ${{ secrets.FTH_REGISTRY_PASSWORD }} FTH_REGISTRY_USERNAME: ${{ secrets.FTH_REGISTRY_USERNAME }} run: | diff --git a/.github/workflows/cd-dev.yml b/.github/workflows/cd-dev.yml new file mode 100644 index 0000000..78ef447 --- /dev/null +++ b/.github/workflows/cd-dev.yml @@ -0,0 +1,39 @@ +on: + push: + branches: + - dev +jobs: + build: + name: Releasing + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Build Docker image + env: # Or as an environment variable + FTH_REGISTRY_PASSWORD: ${{ secrets.FTH_REGISTRY_PASSWORD }} + FTH_REGISTRY_USERNAME: ${{ secrets.FTH_REGISTRY_USERNAME }} + run: | + make build-images + make push-images + - name: Azure Login + uses: Azure/login@v1 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + - name: Helm tool installer + uses: Azure/setup-helm@v1 + - name: Kubectl tool installer + uses: Azure/setup-kubectl@v1 + - name: Get Kubeconfig + env: # Or as an environment variable + AKS_CLUSTER_NAME: ${{ secrets.AKS_CLUSTER_NAME }} + AKS_CLUSTER_RG_NAME: ${{ secrets.AKS_CLUSTER_RG_NAME }} + run: | + az aks get-credentials -n $AKS_CLUSTER_NAME -g $AKS_CLUSTER_RG_NAME + kubectl get pods --all-namespaces + - name: Update Charts + run: | + make helm-upgrade-dev-publisher + make helm-upgrade-dev-worker + make helm-upgrade-dev-client + kubectl get pods --all-namespaces + kubectl apply -f charts/ingress/ingress.yaml diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 75a6502..bb128ca 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -28,3 +28,4 @@ jobs: make helm-upgrade-worker make helm-upgrade-client kubectl get pods --all-namespaces + kubectl apply -f charts/ingress/ingress.yaml diff --git a/Makefile b/Makefile index c61c45e..e6b935b 100644 --- a/Makefile +++ b/Makefile @@ -148,6 +148,12 @@ helm-upgrade-publisher: # Dirty hack to bypass redis auth for demo helm upgrade ihaq-api charts/api --set image.tag=$(IHAQ_VERSION) --set redis.usePassword=false +# Yea.... this can be combined with helm-upgrade-client ! Rush hour ! +.PHONY: helm-upgrade-dev-publisher +helm-upgrade-dev-publisher: + # Well presentation is tonight... hardcoding values but needs to be cleaned ! + helm upgrade ihaq-dev-api charts/api --set image.tag=$(IHAQ_VERSION) --set redis.usePassword=false --set params.redisSvcName=ihaq-dev-api-redis-master + ############################################################################### # WORKER ############################################################################### @@ -178,6 +184,12 @@ push-mutable-worker-image: registry-login helm-upgrade-worker: helm upgrade ihaq-worker charts/worker --set image.tag=$(IHAQ_VERSION) +# Yea.... this can be combined with helm-upgrade-client ! Rush hour ! +.PHONY: helm-upgrade-dev-worker +helm-upgrade-dev-worker: + # Well presentation is tonight... hardcoding values but needs to be cleaned ! + helm upgrade ihaq-dev-worker charts/worker --set image.tag=$(IHAQ_VERSION) --set params.redisSvcName=ihaq-dev-api-redis-master + ############################################################################### # CLIENT ############################################################################### @@ -202,3 +214,9 @@ push-mutable-client-image: registry-login .PHONY: helm-upgrade-client helm-upgrade-client: helm upgrade ihaq-client charts/client --set image.tag=$(IHAQ_VERSION) + +# Yea.... this can be combined with helm-upgrade-client ! Rush hour ! +.PHONY: helm-upgrade-dev-client +helm-upgrade-dev-client: + # Well presentation is tonight... hardcoding values but needs to be cleaned ! + helm upgrade ihaq-dev-client charts/client --set image.tag=$(IHAQ_VERSION) --set params.apiUrl=dev.api.ihaq.juin.me diff --git a/charts/api/templates/deployment.yaml b/charts/api/templates/deployment.yaml index 46a4a42..0c95913 100644 --- a/charts/api/templates/deployment.yaml +++ b/charts/api/templates/deployment.yaml @@ -27,7 +27,7 @@ spec: {{- toYaml .Values.securityContext | nindent 12 }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" imagePullPolicy: {{ .Values.image.pullPolicy }} - args: ["-redis-server-name", "ihaq-api-redis-master"] + args: ["-redis-server-name", "{{ .Values.params.redisSvcName }}"] ports: - name: http containerPort: 8080 diff --git a/charts/api/templates/ingress.yaml b/charts/api/templates/ingress.yaml deleted file mode 100644 index 26fef4b..0000000 --- a/charts/api/templates/ingress.yaml +++ /dev/null @@ -1,41 +0,0 @@ -{{- if .Values.ingress.enabled -}} -{{- $fullName := include "api.fullname" . -}} -{{- $svcPort := .Values.service.port -}} -{{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1beta1 -{{- else -}} -apiVersion: extensions/v1beta1 -{{- end }} -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - {{- include "api.labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: -{{- if .Values.ingress.tls }} - tls: - {{- range .Values.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} -{{- end }} - rules: - {{- range .Values.ingress.hosts }} - - host: {{ .host | quote }} - http: - paths: - {{- range .paths }} - - path: {{ . }} - backend: - serviceName: {{ $fullName }} - servicePort: {{ $svcPort }} - {{- end }} - {{- end }} -{{- end }} diff --git a/charts/api/values.yaml b/charts/api/values.yaml index c90d843..8d603cc 100644 --- a/charts/api/values.yaml +++ b/charts/api/values.yaml @@ -9,6 +9,9 @@ image: tag: production pullPolicy: Always +params: + redisSvcName: ihaq-api-redis-master + imagePullSecrets: [] nameOverride: "" fullnameOverride: "" @@ -35,19 +38,6 @@ service: type: NodePort port: 8080 -ingress: - enabled: false - annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - hosts: - - host: chart-example.local - paths: [] - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local - resources: {} # We usually recommend not to specify default resources and to leave this as a conscious # choice for the user. This also increases chances charts run on environments with little diff --git a/charts/client/templates/deployment.yaml b/charts/client/templates/deployment.yaml index 03ce68f..e93efea 100644 --- a/charts/client/templates/deployment.yaml +++ b/charts/client/templates/deployment.yaml @@ -27,7 +27,7 @@ spec: {{- toYaml .Values.securityContext | nindent 12 }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" imagePullPolicy: {{ .Values.image.pullPolicy }} - args: ["-i", "api.ihaq.juin.me"] + args: ["-i", "{{ .Values.params.apiUrl }}"] ports: - name: http containerPort: 80 diff --git a/charts/client/values.yaml b/charts/client/values.yaml index 7c7d95f..83f1ed2 100644 --- a/charts/client/values.yaml +++ b/charts/client/values.yaml @@ -10,7 +10,7 @@ image: pullPolicy: Always params: - API_SVC: publisher-svc + apiUrl: api.ihaq.juin.me imagePullSecrets: [] nameOverride: "" diff --git a/charts/ingress/ingress.yaml b/charts/ingress/ingress.yaml index 375aa70..8915ec0 100644 --- a/charts/ingress/ingress.yaml +++ b/charts/ingress/ingress.yaml @@ -20,3 +20,17 @@ spec: backend: serviceName: ihaq-client servicePort: http + - host: dev.api.ihaq.juin.me + http: + paths: + - path: / + backend: + serviceName: ihaq-dev-api + servicePort: 8080 + - host: dev.ihaq.juin.me + http: + paths: + - path: / + backend: + serviceName: ihaq-dev-client + servicePort: http diff --git a/charts/worker/templates/deployment.yaml b/charts/worker/templates/deployment.yaml index 94092db..3118120 100644 --- a/charts/worker/templates/deployment.yaml +++ b/charts/worker/templates/deployment.yaml @@ -27,7 +27,7 @@ spec: {{- toYaml .Values.securityContext | nindent 12 }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" imagePullPolicy: {{ .Values.image.pullPolicy }} - args: ["-redis-server-name", "ihaq-api-redis-master"] + args: ["-redis-server-name", "{{ .Values.params.redisSvcName }}"] resources: {{- toYaml .Values.resources | nindent 12 }} {{- with .Values.nodeSelector }} diff --git a/charts/worker/templates/ingress.yaml b/charts/worker/templates/ingress.yaml deleted file mode 100644 index 26fef4b..0000000 --- a/charts/worker/templates/ingress.yaml +++ /dev/null @@ -1,41 +0,0 @@ -{{- if .Values.ingress.enabled -}} -{{- $fullName := include "api.fullname" . -}} -{{- $svcPort := .Values.service.port -}} -{{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1beta1 -{{- else -}} -apiVersion: extensions/v1beta1 -{{- end }} -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - {{- include "api.labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: -{{- if .Values.ingress.tls }} - tls: - {{- range .Values.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} -{{- end }} - rules: - {{- range .Values.ingress.hosts }} - - host: {{ .host | quote }} - http: - paths: - {{- range .paths }} - - path: {{ . }} - backend: - serviceName: {{ $fullName }} - servicePort: {{ $svcPort }} - {{- end }} - {{- end }} -{{- end }} diff --git a/charts/worker/values.yaml b/charts/worker/values.yaml index 1e56994..75959e0 100644 --- a/charts/worker/values.yaml +++ b/charts/worker/values.yaml @@ -9,6 +9,9 @@ image: tag: production pullPolicy: Always +params: + redisSvcName: ihaq-api-redis-master + imagePullSecrets: [] nameOverride: "" fullnameOverride: "" diff --git a/cmd/web/src/App.tsx b/cmd/web/src/App.tsx index 78e3b81..3b59c48 100644 --- a/cmd/web/src/App.tsx +++ b/cmd/web/src/App.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import Container from '@material-ui/core/Container'; import ProTip from './ProTip'; @@ -9,17 +9,27 @@ import Questions from './Questions'; import { userService } from './services/users.service'; import { configService } from './services/config.service'; import { Leaderboard } from './Leaderboard'; +import { useDispatch } from 'react-redux'; +import { AppDispatch } from './store/store'; +import { saveUser } from './store/user'; export const API_SVC = configService.API_URL; console.log('API Endpoint =', API_SVC); export const socket = new WebSocket('ws://' + API_SVC + '/ws'); -socket.onopen = () => { - userService.saveUsernameLocally(); - console.log('WS Successfully Connected'); -}; - export default function App() { + const dispatch = useDispatch(); + + useEffect(() => { + socket.onopen = () => { + userService.saveUsernameLocally(); + const userId = userService.getUsername(); + console.log('WS Successfully Connected'); + if (userId) { + dispatch(saveUser({ userId })); + } + }; + }, []); return ( diff --git a/cmd/web/src/Questions.tsx b/cmd/web/src/Questions.tsx index c0dbb2d..fb36292 100644 --- a/cmd/web/src/Questions.tsx +++ b/cmd/web/src/Questions.tsx @@ -16,6 +16,7 @@ import { socket } from './App'; // @ts-ignore import { Rings as Identicon } from 'react-identicon-variety-pack'; import { Button, Grid } from '@material-ui/core'; +import { getUser } from './store/user'; const useStyles = makeStyles((theme: Theme) => createStyles({ @@ -32,6 +33,8 @@ const useStyles = makeStyles((theme: Theme) => export default function AlignItemsList() { const classes = useStyles(); const dispatch = useDispatch(); + const user = useSelector(getUser); + // Messages is updated everytime the store is updated const messages = useSelector(getMessagesWithUser); useEffect(() => { @@ -63,6 +66,7 @@ export default function AlignItemsList() { authorName={item.author.name} likes={item.message.likes} onLike={() => dispatch(likeMessage(item.message.id))} + disableLikeButton={item.author.id === user.id} /> @@ -77,15 +81,16 @@ interface MessageSubtextProps { likes: number; authorName: string; onLike: () => void; + disableLikeButton: boolean; } -const MessageSubtext: FC = ({ authorName, likes, onLike }) => { +const MessageSubtext: FC = ({ authorName, likes, onLike, disableLikeButton }) => { return ( - by {authorName} - diff --git a/cmd/web/src/TopBar.tsx b/cmd/web/src/TopBar.tsx index eaad82f..b7851a8 100644 --- a/cmd/web/src/TopBar.tsx +++ b/cmd/web/src/TopBar.tsx @@ -8,6 +8,8 @@ import useScrollTrigger from '@material-ui/core/useScrollTrigger'; import Fab from '@material-ui/core/Fab'; import KeyboardArrowUpIcon from '@material-ui/icons/KeyboardArrowUp'; import Zoom from '@material-ui/core/Zoom'; +import { useSelector } from 'react-redux'; +import { getUser } from './store/user'; interface Props { /** @@ -23,9 +25,9 @@ const useStyles = makeStyles((theme: Theme) => root: { position: 'fixed', bottom: theme.spacing(2), - right: theme.spacing(2), - }, - }), + right: theme.spacing(2) + } + }) ); function ScrollTop(props: Props) { @@ -37,13 +39,11 @@ function ScrollTop(props: Props) { const trigger = useScrollTrigger({ target: window ? window() : undefined, disableHysteresis: true, - threshold: 100, + threshold: 100 }); const handleClick = (event: React.MouseEvent) => { - const anchor = ((event.target as HTMLDivElement).ownerDocument || document).querySelector( - '#back-to-top-anchor', - ); + const anchor = ((event.target as HTMLDivElement).ownerDocument || document).querySelector('#back-to-top-anchor'); if (anchor) { anchor.scrollIntoView({ behavior: 'smooth', block: 'center' }); @@ -59,13 +59,26 @@ function ScrollTop(props: Props) { ); } +const useStyles2 = makeStyles((theme: Theme) => + createStyles({ + title: { + flexGrow: 1 + } + }) +); + export default function BackToTop(props: Props) { + const classes = useStyles2(); + const user = useSelector(getUser); return ( - I Have A Question ?! + + I Have A Question ?! + + {user.id} @@ -76,4 +89,4 @@ export default function BackToTop(props: Props) { ); -} \ No newline at end of file +} diff --git a/cmd/web/src/services/users.service.ts b/cmd/web/src/services/users.service.ts index 62695d0..16b8b4b 100644 --- a/cmd/web/src/services/users.service.ts +++ b/cmd/web/src/services/users.service.ts @@ -1,10 +1,11 @@ -import Cookies from "js-cookie"; +import Cookies from 'js-cookie'; export class UserService { - username: any; + username: string; constructor() { - if (Cookies.get("ihaq_username") !== undefined) { - this.username = Cookies.get("ihaq_username"); + const username = Cookies.get('ihaq_username'); + if (username !== undefined) { + this.username = username; } else { this.username = generateRandomUsername(); } @@ -13,10 +14,10 @@ export class UserService { return this.username; } saveUsernameLocally() { - if (Cookies.get("ihaq_username") === undefined) { - console.log("Cookie not set, creating one"); - Cookies.set("ihaq_username", this.username); - window.localStorage.setItem("ihaq_username", this.username); + if (Cookies.get('ihaq_username') === undefined) { + console.log('Cookie not set, creating one'); + Cookies.set('ihaq_username', this.username); + window.localStorage.setItem('ihaq_username', this.username); } } } @@ -24,9 +25,9 @@ export class UserService { export const userService = new UserService(); function generateRandomUsername() { - return "xxxxxxxxxxxxx".replace(/[xy]/g, function(c) { + return 'xxxxxxxxxxxxx'.replace(/[xy]/g, function(c) { var r = (Math.random() * 16) | 0, - v = c === "x" ? r : (r & 0x3) | 0x8; + v = c === 'x' ? r : (r & 0x3) | 0x8; return v.toString(16); }); } diff --git a/cmd/web/src/store/rootReducer.ts b/cmd/web/src/store/rootReducer.ts index 7a5e102..d98acd5 100644 --- a/cmd/web/src/store/rootReducer.ts +++ b/cmd/web/src/store/rootReducer.ts @@ -2,11 +2,13 @@ import { combineReducers } from '@reduxjs/toolkit'; import { authors } from './authors'; import { messages } from './messages'; import { authorMessages } from './authorMessages'; +import { user } from './user'; export const rootReducer = combineReducers({ authors, messages, - authorMessages + authorMessages, + user }); export type AppState = ReturnType; diff --git a/cmd/web/src/store/user/index.ts b/cmd/web/src/store/user/index.ts new file mode 100644 index 0000000..76bcaec --- /dev/null +++ b/cmd/web/src/store/user/index.ts @@ -0,0 +1,2 @@ +export * from './user.slice'; +export * from './user.selector'; diff --git a/cmd/web/src/store/user/user.selector.ts b/cmd/web/src/store/user/user.selector.ts new file mode 100644 index 0000000..ee162ca --- /dev/null +++ b/cmd/web/src/store/user/user.selector.ts @@ -0,0 +1,5 @@ +import { AppState } from '../rootReducer'; +import { createSelector } from '@reduxjs/toolkit'; + +export const userSelector = (state: AppState) => state.user; +export const getUser = createSelector(userSelector, user => user); diff --git a/cmd/web/src/store/user/user.slice.ts b/cmd/web/src/store/user/user.slice.ts new file mode 100644 index 0000000..64ba761 --- /dev/null +++ b/cmd/web/src/store/user/user.slice.ts @@ -0,0 +1,21 @@ +import { createSlice, PayloadAction } from '@reduxjs/toolkit'; + +interface User { + id: string; +} + +const initialState: User = { id: '' }; + +const userSlice = createSlice({ + name: 'user', + initialState, + reducers: { + saveUser(state, action: PayloadAction<{ userId: string }>) { + const { userId } = action.payload; + state.id = userId; + } + } +}); + +export const { saveUser } = userSlice.actions; +export const user = userSlice.reducer;