Skip to content

Commit

Permalink
feat: Added account setting screens (#4119)
Browse files Browse the repository at this point in the history
* feat: Done with new routing setup for account screens and started with account screen implementations

Signed-off-by: Hrishav <[email protected]>

* feat: Added support for React-Query and automatic API generation using swagger file

Signed-off-by: Hrishav <[email protected]>

* chore: Updated createAt swagger issue

Signed-off-by: Hrishav <[email protected]>

* chore: Updated invitation_state

Signed-off-by: Hrishav <[email protected]>

* chore: Updated invitation_state again

Signed-off-by: Hrishav <[email protected]>

* feat: Added swagger API generation and several account setting screens

Signed-off-by: Hrishav <[email protected]>

* feat: Added user interaction modals in settings overview

Signed-off-by: Hrishav <[email protected]>

* feat: Added new automatic API generations and account setting screens

Signed-off-by: Hrishav <[email protected]>

* chore: updated testUtils

Signed-off-by: Hrishav <[email protected]>

* chore: updated testUtils and fixed errors

Signed-off-by: Hrishav <[email protected]>

* chore: Fixed review comments

Signed-off-by: Hrishav <[email protected]>

* chore: updated user search function

Signed-off-by: Hrishav <[email protected]>

* chore: fixed request comments

Signed-off-by: Hrishav <[email protected]>

---------

Signed-off-by: Hrishav <[email protected]>
  • Loading branch information
hrishavjha authored Aug 10, 2023
1 parent fb6da08 commit aa8e9a3
Show file tree
Hide file tree
Showing 160 changed files with 5,436 additions and 2,513 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func getInvitation(service services.ApplicationService, member entities.MemberIn
func ListInvitations(service services.ApplicationService) gin.HandlerFunc {
return func(c *gin.Context) {
uID := c.MustGet("uid").(string)
invitationState := c.Param("invitation-state")
invitationState := c.Param("invitation_state")
var response []entities.ListInvitationResponse
projects, err := service.ListInvitations(uID, entities.Invitation(invitationState))
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion chaoscenter/authentication/api/routes/project_router.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func ProjectRouter(router *gin.Engine, service services.ApplicationService) {
router.GET("/get_project_role/:project_id", rest.GetProjectRole(service))
router.GET("/list_projects", rest.GetProjectsByUserID(service))
router.GET("/get_projects_stats", rest.GetProjectStats(service))
router.GET("/list_invitations_with_filters/:invitation-state", rest.ListInvitations(service))
router.GET("/list_invitations_with_filters/:invitation_state", rest.ListInvitations(service))
router.POST("/create_project", rest.CreateProject(service))
router.POST("/send_invitation", rest.SendInvitation(service))
router.POST("/accept_invitation", rest.AcceptInvitation(service))
Expand Down
52 changes: 40 additions & 12 deletions chaoscenter/authentication/pkg/project/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"errors"
"log"
"strconv"
"time"

"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
Expand Down Expand Up @@ -88,6 +87,9 @@ func (r repository) GetProjectsByUserID(userID string, isOwner bool) ([]*entitie
{"$elemMatch", bson.D{
{"user_id", userID},
{"$and", bson.A{
bson.D{{"invitation", bson.D{
{"$ne", entities.PendingInvitation},
}}},
bson.D{{"invitation", bson.D{
{"$ne", entities.DeclinedInvitation},
}}},
Expand Down Expand Up @@ -241,7 +243,7 @@ func (r repository) UpdateInvite(projectID string, userID string, invitation ent
update = bson.D{
{"$set", bson.D{
{"members.$[elem].invitation", invitation},
{"members.$[elem].joined_at", strconv.FormatInt(time.Now().Unix(), 10)},
{"members.$[elem].joined_at", time.Now().Unix()},
}}}
case entities.ExitedProject:
update = bson.D{
Expand Down Expand Up @@ -345,6 +347,13 @@ func (r repository) GetOwnerProjects(ctx context.Context, userID string) ([]*ent
pipeline := mongo.Pipeline{
bson.D{{"$match", filter}},
bson.D{{"$project", bson.D{
{"name", 1},
{"state", 1},
{"created_at", 1},
{"updated_at", 1},
{"created_by", 1},
{"updated_by", 1},
{"is_removed", 1},
{"members", bson.D{
{"$filter", bson.D{
{"input", "$members"},
Expand Down Expand Up @@ -471,17 +480,36 @@ func (r repository) GetProjectMembers(projectID string, state string) ([]*entiti
func (r repository) ListInvitations(userID string, invitationState entities.Invitation) ([]*entities.Project, error) {

var pipeline mongo.Pipeline
filter := bson.D{
{"$match", bson.D{
{"members", bson.D{
{"$elemMatch", bson.D{
{"user_id", userID},
{"invitation", bson.D{
{"$eq", invitationState},
var filter bson.D
if invitationState == entities.PendingInvitation {
filter = bson.D{
{"$match", bson.D{
{"members", bson.D{
{"$elemMatch", bson.D{
{"user_id", userID},
{"invitation", bson.D{
{"$eq", invitationState},
}},
}},
}},
}}},
},
}}},
},
}
} else if invitationState == entities.AcceptedInvitation {
filter = bson.D{
{"$match", bson.D{
{"members", bson.D{
{"$elemMatch", bson.D{
{"user_id", userID},
{"role", bson.D{
{"$ne", entities.RoleOwner},
}},
{"invitation", bson.D{
{"$eq", invitationState},
}},
}},
}}},
},
}
}
pipeline = append(pipeline, filter)

Expand Down
4 changes: 2 additions & 2 deletions chaoscenter/graphql/server/pkg/authorization/user_jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import (
// UserValidateJWT validates the cluster jwt
func UserValidateJWT(token string) (jwt.MapClaims, error) {
tkn, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
if ok := token.Method.Alg() == jwt.SigningMethodHS512.Alg(); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
if _, isValid := token.Method.(*jwt.SigningMethodHMAC); !isValid {
return nil, fmt.Errorf("invalid token %s", token.Header["alg"])
}
return []byte(utils.Config.JwtSecret), nil
})
Expand Down
33 changes: 33 additions & 0 deletions chaoscenter/web/config/oats.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { defineConfig } from '@harnessio/oats-cli';
import reactQueryPlugin from '@harnessio/oats-plugin-react-query';
import { mapKeys, omit } from 'lodash-es';

function normalizePath(url: string): string {
return url.replace(/\/{2,}/g, '/');
}

export default defineConfig({
services: {
auth: {
url: 'https://raw.githubusercontent.com/litmuschaos/litmus/master/mkdocs/docs/auth/v3.0.0/auth-api.json',
output: 'src/api/auth',
transformer(spec) {
return {
...spec,
components: {
...spec.components,
schemas: omit(spec.components?.schemas, ['OauthSettings'])
},
paths: mapKeys(spec.paths, (_val, key) => normalizePath(`/auth/${key}`))
};
},
genOnlyUsed: true,
plugins: [
reactQueryPlugin({
customFetcher: 'services/fetcher',
overrides: {}
})
]
}
}
});
9 changes: 7 additions & 2 deletions chaoscenter/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"test:watch": "yarn test --watch",
"test:coverage": "jest --coverage --colors --maxWorkers=2",
"prepare": "cd ../../ && husky install chaoscenter/web/.husky",
"services": "node scripts/services.mjs",
"strings:check": "node scripts/strings/yamlStringsCheck.js",
"strings:sort": "yaml-sort -i src/strings/strings.en.yaml",
"typecheck": "tsc",
Expand Down Expand Up @@ -59,8 +60,11 @@
"@blueprintjs/select": "3.12.3",
"@harnessio/design-system": "^1.6.0-beta.1",
"@harnessio/icons": "^2.0.0-beta.2",
"@harnessio/oats-cli": "^2.1.0",
"@harnessio/oats-plugin-react-query": "^2.1.0",
"@harnessio/uicore": "^4.0.0-beta.1",
"@popperjs/core": "^2.11.5",
"@tanstack/react-query": "4.20.4",
"@types/d3-array": "^3.0.3",
"@types/react-timeago": "^4.1.3",
"@visx/axis": "^2.10.0",
Expand Down Expand Up @@ -94,6 +98,7 @@
"masonry-layout": "^4.2.2",
"moment": "^2.25.3",
"normalize.css": "^8.0.1",
"prompts": "^2.4.2",
"qs": "^6.9.4",
"react": "^17.0.2",
"react-dom": "^17.0.2",
Expand All @@ -106,13 +111,13 @@
"react-table": "^7.1.0",
"react-timeago": "^7.1.0",
"react-to-print": "^2.14.12",
"restful-react": "15.6.0",
"subscriptions-transport-ws": "^0.11.0",
"swr": "^1.3.0",
"uuid": "^8.3.2",
"yaml": "^2.1.0",
"yaml-sort": "^1.2.1",
"yup": "^0.32.11"
"yup": "^0.32.11",
"zx": "^5.3.0"
},
"devDependencies": {
"@apollo/react-testing": "^4.0.0",
Expand Down
27 changes: 27 additions & 0 deletions chaoscenter/web/scripts/services.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import prompts from 'prompts';
import { $ } from 'zx';

(async () => {
const services = ['auth'];

services.sort();

const response = await prompts({
type: 'multiselect',
name: 'services',
message: 'Please select the services you want to generate',
choices: services.map(title => ({ title }))
});

if (!response.services || response.services.length === 0) {
console.log('No services selected. Exiting...');
process.exit(0);
}

for (const index of response.services) {
const service = services[index];
await $`npx oats import --config config/oats.config.ts --service ${service} --clean`;
}

await $`npx prettier --write \"src/services/**/*.{ts,tsx,json,scss}\"`;
})();
92 changes: 28 additions & 64 deletions chaoscenter/web/src/api/LitmusAPIProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React from 'react';
import axios from 'axios';
import {
ApolloClient,
ApolloLink,
Expand All @@ -8,17 +7,14 @@ import {
HttpLink,
InMemoryCache,
NormalizedCacheObject,
Operation,
RequestHandler,
split
RequestHandler
} from '@apollo/client';
import { WebSocketLink } from '@apollo/client/link/ws';
import { getMainDefinition } from '@apollo/client/utilities';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { getUserDetails } from '@utils';

export interface APIConfig {
gqlEndpoints: {
chaosManagerUri: string;
sockURL: string;
};
restEndpoints: {
authUri: string;
Expand All @@ -28,71 +24,35 @@ export interface APIConfig {

interface LitmusAPIProviderProps {
config: APIConfig;
token: string;
}

function createApolloClient({
config,
token
}: LitmusAPIProviderProps): ApolloClient<NormalizedCacheObject> | undefined {
function createApolloClient({ config }: LitmusAPIProviderProps): ApolloClient<NormalizedCacheObject> | undefined {
const { accessToken } = getUserDetails();
if (!config.gqlEndpoints) return undefined;

const httpLinkUri = config.gqlEndpoints.chaosManagerUri;
const wsLinkUri = config.gqlEndpoints.sockURL;

if (!httpLinkUri && !wsLinkUri) return undefined;
if (!httpLinkUri) return undefined;

let httpLink: HttpLink | null = null;
if (httpLinkUri) {
httpLink = new HttpLink({
uri: httpLinkUri
});
}

let wsLink: WebSocketLink | null = null;
if (wsLinkUri) {
wsLink = new WebSocketLink({
uri: wsLinkUri,
options: {
reconnect: true,
timeout: 30000
}
});
}
httpLink = new HttpLink({
uri: httpLinkUri
});

const authLink = new ApolloLink((operation, forward) => {
// add the authorization to the headers
operation.setContext(({ headers = {} }) => ({
headers: {
...headers,
authorization: token
authorization: `${accessToken}`
}
}));

return forward(operation);
});

const links: (ApolloLink | RequestHandler)[] = [authLink];

// The split function takes three parameters:
//
// * A function that's called for each operation to execute
// * The Link to use for an operation if the function returns a "truthy" value
// * The Link to use for an operation if the function returns a "falsy" value
const splitOperation = ({ query }: Operation): boolean => {
const definition = getMainDefinition(query);
return definition.kind === 'OperationDefinition' && definition.operation === 'subscription';
};

if (httpLink && !wsLink) {
links.push(httpLink);
} else if (!httpLink && wsLink) {
const splitLink = split(splitOperation, wsLink);
links.push(splitLink);
} else if (httpLink && wsLink) {
const splitLink = split(splitOperation, wsLink, httpLink);
links.push(splitLink);
}
links.push(httpLink);

const client = new ApolloClient({
link: from(links),
Expand All @@ -102,18 +62,22 @@ function createApolloClient({
return client;
}

export const LitmusAPIProvider: React.FC<LitmusAPIProviderProps> = ({
config,
token,
children
}): React.ReactElement => {
if (config.restEndpoints) {
axios.defaults.baseURL = config.restEndpoints.authUri;
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
}

// const scope = getScope();
const apolloClient = createApolloClient({ config, token });
export const LitmusAPIProvider: React.FC<LitmusAPIProviderProps> = ({ config, children }): React.ReactElement => {
const apolloClient = createApolloClient({ config });
const reactQueryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false
},
mutations: {
retry: false
}
}
});

return apolloClient ? <ApolloProvider client={apolloClient}>{children}</ApolloProvider> : <></>;
return (
<QueryClientProvider client={reactQueryClient}>
{apolloClient ? <ApolloProvider client={apolloClient}>{children}</ApolloProvider> : children}
</QueryClientProvider>
);
};
Loading

0 comments on commit aa8e9a3

Please sign in to comment.