diff --git a/scripts/sonarqube.sh b/scripts/sonarqube.sh new file mode 100755 index 000000000..92938c839 --- /dev/null +++ b/scripts/sonarqube.sh @@ -0,0 +1,155 @@ +# Configuration +SONAR_HOST_URL="https://sonarqube-developers.rootstrap.net" +SONAR_API_TOKEN="squ_21cf7e64993c3df2b83511539c16429a28ec1d17" +#!/bin/bash + +#!/bin/bash + +# Check if fzf is installed, and install it if it's missing +if ! command -v fzf &>/dev/null; then + echo "fzf is not installed. Installing fzf via Homebrew..." + + # Check if Homebrew is installed + if command -v brew &>/dev/null; then + brew install fzf + else + echo "Homebrew is not installed. Please install Homebrew f irst: https://brew.sh/" + exit 1 + fi +fi + +### Step 1: Retrieve the project name ### +echo "" +read -p "Please enter the project name: " project_name + +# Confirm the entered project name +if [[ -z "$project_name" ]]; then + echo "No project name provided. Exiting." + exit 1 +fi + +echo "You have entered project name: $project_name" + +### Step 2: Retrieve and select Quality Profile ### +# Fetch the list of quality profiles +quality_profiles_response=$(curl -s -X GET "$SONAR_HOST_URL/api/qualityprofiles/search" \ + -H "Authorization: Bearer $SONAR_API_TOKEN") + +# Check if the response contains any profiles +if ! echo "$quality_profiles_response" | grep -q '"profiles"'; then + echo "Failed to retrieve quality profiles. Response:" + echo "$quality_profiles_response" + exit 1 +fi + +# Extract and display the names of the quality profiles using jq and fzf for selection +selected_quality_profile=$(echo "$quality_profiles_response" | jq -r '.profiles[] | "\(.languageName) (\(.name))"' | fzf --prompt="Select a quality profile: ") + +# Check if a selection was made +if [[ -z "$selected_quality_profile" ]]; then + echo "No selection made. Exiting." + exit 1 +fi + +# Extract both key and language in a single query based on the fzf selection +profile_info=$(echo "$quality_profiles_response" | jq -r --arg selected "$selected_quality_profile" '.profiles[] | select("\(.languageName) (\(.name))" == $selected) | {key, language, name}') + +# Extract key and language from the JSON result +selected_quality_profile_name=$(echo "$profile_info" | jq -r '.name') +selected_quality_profile_language=$(echo "$profile_info" | jq -r '.language') + +# Display the selected quality profile +echo "You have selected Quality Profile: $selected_quality_profile" + +### Step 3: Retrieve and select Quality Gate ### +# Fetch the list of quality gates +quality_gates_response=$(curl -s -X GET "$SONAR_HOST_URL/api/qualitygates/list" \ + -H "Authorization: Bearer $SONAR_API_TOKEN") + +# Check if the response contains any quality gates +if ! echo "$quality_gates_response" | grep -q '"qualitygates"'; then + echo "Failed to retrieve quality gates. Response:" + echo "$quality_gates_response" + exit 1 +fi + +# Extract and display the names of the quality gates using jq and fzf for selection +selected_quality_gate=$(echo "$quality_gates_response" | jq -r '.qualitygates[].name' | fzf --prompt="Select a quality gate: ") + +# Check if a selection was made +if [[ -z "$selected_quality_gate" ]]; then + echo "No selection made. Exiting." + exit 1 +fi + +# Extract the ID of the selected quality gate for API usage +selected_quality_gate_name=$(echo "$quality_gates_response" | jq -r --arg selected "$selected_quality_gate" '.qualitygates[] | select(.name == $selected) | .name') + +# Display the selected quality gate +echo "You have selected Quality Gate: $selected_quality_gate" + +### Step 4: Show summary of selected fields ### +echo "" +echo "########################################" +echo "Project Name: $project_name" +echo "Quality Profile: $selected_quality_profile" +echo "Quality Profile Language: $selected_quality_profile_language" +echo "Quality Gate: $selected_quality_gate" +echo "########################################" +echo "" + +# Confirm choices +read -p "Are you sure you want to proceed with these selections? (y/n): " confirmation + +if [[ "$confirmation" != "y" ]]; then + echo "Project creation canceled. Exiting." + exit 1 +fi + +### Step 5: Create the project ### +create_project_response=$(curl -s -X POST "$SONAR_HOST_URL/api/projects/create" \ + -H "Authorization: Bearer $SONAR_API_TOKEN" \ + -d "name=$project_name&project=$project_name") + +if echo "$create_project_response" | grep -q '"errors"'; then + echo "Failed to create project. Response:" + echo "$create_project_response" + exit 1 +else + echo "Project $project_name created successfully." +fi + +### Step 6: Associate the Quality Profile with the project ### +assign_quality_profile_response=$(curl -s -X POST "$SONAR_HOST_URL/api/qualityprofiles/add_project" \ + -H "Authorization: Bearer $SONAR_API_TOKEN" \ + -d "project=$project_name&qualityProfile=$selected_quality_profile_name&language=$selected_quality_profile_language") + +if echo "$assign_quality_profile_response" | grep -q '"errors"'; then + echo "Failed to assign quality profile. Response:" + echo "$assign_quality_profile_response" + exit 1 +else + echo "Quality Profile $selected_quality_profile (Language: $selected_quality_profile_language) assigned to project $project_name successfully." +fi + +### Step 7: Assign the Quality Gate to the project ### +assign_quality_gate_response=$(curl -s -X POST "$SONAR_HOST_URL/api/qualitygates/select" \ + -H "Authorization: Bearer $SONAR_API_TOKEN" \ + -d "projectKey=$project_name&gateName=$selected_quality_gate_name") + +if echo "$assign_quality_gate_response" | grep -q '"errors"'; then + echo "Failed to assign quality gate. Response:" + echo "$assign_quality_gate_response" + exit 1 +else + echo "Quality Gate $selected_quality_gate assigned to project $project_name successfully." +fi + +### Final Confirmation Echo ### +echo "" +echo "########################################" +echo "SUCCESS!" +echo "Project '$project_name' was created successfully." +echo "Quality Profile '$selected_quality_profile' (Language: $selected_quality_profile_language) and Quality Gate '$selected_quality_gate' have been assigned to the project." +echo "########################################" +echo "" diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 000000000..1315a4fc1 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,6 @@ +#----- Project Configuration +# sonar.sources=. +# sonar.projectBaseDir=. +sonar.projectKey=react-native-template +sonar.token=sqp_6d89f6f7ebb165d274df00407c5f6d46df8fe5c0 +# sonar.projectVersion=1.0.0 \ No newline at end of file diff --git a/src/api/carts/query-keys.ts b/src/api/carts/query-keys.ts deleted file mode 100644 index 8b5e74025..000000000 --- a/src/api/carts/query-keys.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { createQueryKeys } from '@lukemorales/query-key-factory'; - -export const cartKeys = createQueryKeys('carts', { - list: (filters) => [filters], - detail: (id) => [id], -}); diff --git a/src/api/carts/types.ts b/src/api/carts/types.ts deleted file mode 100644 index 0d9066b36..000000000 --- a/src/api/carts/types.ts +++ /dev/null @@ -1,19 +0,0 @@ -export type Product = { - id: number; - title: string; - price: number; - quantity: number; - total: number; - discountPercentage: number; - discountedTotal: number; - thumbnail: string; -}; -export type Cart = { - id: number; - products: Product[]; - total: number; - discountedTotal: number; - userId: number; - totalProducts: number; - totalQuantity: number; -}; diff --git a/src/api/carts/use-carts.ts b/src/api/carts/use-carts.ts deleted file mode 100644 index 21d01892b..000000000 --- a/src/api/carts/use-carts.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { useInfiniteQuery } from '@tanstack/react-query'; - -import { client } from '../common'; -import { cartKeys } from './query-keys'; - -type Variables = { - limit?: number | string; - offset?: number | string; -}; - -const DEFAULT_LIMIT = 10; -const DEFAULT_OFFSET = 0; - -const getCarts = async ({ - limit = DEFAULT_LIMIT, - offset = DEFAULT_OFFSET, -}: Variables) => { - const { data } = await client.get('carts', { - params: { - limit, - offset, - }, - }); - return { data, offset: Number(offset), limit: Number(limit) }; -}; - -export const useCarts = (variables: Variables) => - useInfiniteQuery({ - ...cartKeys.list(variables), - queryFn: () => getCarts(variables), - initialPageParam: { offset: DEFAULT_OFFSET, limit: DEFAULT_LIMIT }, - getNextPageParam: (lastPage) => { - const { offset, limit } = lastPage; - return { offset: offset + limit, limit }; - }, - }); diff --git a/src/api/index.tsx b/src/api/index.tsx index deda66371..498fd62c7 100644 --- a/src/api/index.tsx +++ b/src/api/index.tsx @@ -1,3 +1,2 @@ export * from './common'; -export * from './posts'; export * from './types'; diff --git a/src/api/posts/index.ts b/src/api/posts/index.ts deleted file mode 100644 index 9c2e9287c..000000000 --- a/src/api/posts/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './types'; -export * from './use-add-post'; -export * from './use-post'; -export * from './use-post-comments'; -export * from './use-posts'; diff --git a/src/api/posts/types.ts b/src/api/posts/types.ts deleted file mode 100644 index 10d0eafa1..000000000 --- a/src/api/posts/types.ts +++ /dev/null @@ -1,18 +0,0 @@ -export type Post = { - userId: number; - id: number; - title: string; - body: string; -}; - -export type Comment = { - id: number; - body: string; - postId: number; - likes: number; - user: { - id: number; - username: string; - fullName: string; - }; -}; diff --git a/src/api/posts/use-add-post.ts b/src/api/posts/use-add-post.ts deleted file mode 100644 index 32bb338b6..000000000 --- a/src/api/posts/use-add-post.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { AxiosError } from 'axios'; -import { createMutation } from 'react-query-kit'; - -import { client } from '../common'; -import type { Post } from './types'; - -type Variables = { title: string; body: string; userId: number }; -type Response = Post; - -export const useAddPost = createMutation({ - mutationFn: async (variables) => - client({ - url: 'posts/add', - method: 'POST', - data: variables, - headers: { - 'Content-Type': 'application/json', - }, - }).then((response) => response.data), -}); diff --git a/src/api/posts/use-post-comments.ts b/src/api/posts/use-post-comments.ts deleted file mode 100644 index 03a36c86a..000000000 --- a/src/api/posts/use-post-comments.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { createQuery } from 'react-query-kit'; - -import { queryFactory } from '@/api/query-factory'; - -import { client } from '../common'; -import { type Comment } from './types'; - -type Variables = { - id?: number; -}; - -type Response = { - comments: Comment[]; -}; - -const getPostComments = async (id?: number) => { - const { data } = await client.get(`posts/${id}/comments`); - return data; -}; - -export const usePostComments = createQuery({ - ...queryFactory.posts.detail(1)._ctx.comments, - fetcher: (variables) => getPostComments(variables?.id), -}); diff --git a/src/api/posts/use-post.ts b/src/api/posts/use-post.ts deleted file mode 100644 index 6e28b9bb1..000000000 --- a/src/api/posts/use-post.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { AxiosError } from 'axios'; -import { createQuery } from 'react-query-kit'; - -import { queryFactory } from '@/api/query-factory'; - -import { client } from '../common'; -import type { Post } from './types'; - -type Variables = { id: string }; -type Response = Post; - -const getPosts = async (variables: Variables) => { - const { data } = await client.get(`posts/${variables.id}`); - return data; -}; - -export const usePost = createQuery({ - ...queryFactory.posts.detail(1), - fetcher: getPosts, -}); diff --git a/src/api/posts/use-posts.ts b/src/api/posts/use-posts.ts deleted file mode 100644 index 9358abef5..000000000 --- a/src/api/posts/use-posts.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { AxiosError } from 'axios'; -import { createQuery } from 'react-query-kit'; - -import { queryFactory } from '@/api/query-factory'; - -import { client } from '../common'; -import type { Post } from './types'; - -type Response = Post[]; -type Variables = void; // as react-query-kit is strongly typed, we need to specify the type of the variables as void in case we don't need them - -export const usePosts = createQuery({ - // old queryKey: ['posts'], - ...queryFactory.posts.list({}), // this translates to ['posts', filters] - fetcher: () => client.get(`posts`).then((response) => response.data.posts), -}); diff --git a/src/api/query-factory.ts b/src/api/query-factory.ts index 969059c90..3c9034d3e 100644 --- a/src/api/query-factory.ts +++ b/src/api/query-factory.ts @@ -3,7 +3,6 @@ import { mergeQueryKeys, } from '@lukemorales/query-key-factory'; -import { cartKeys } from './carts/query-keys'; type Filters = { limit?: number; offset?: number; @@ -25,4 +24,4 @@ const productsKeys = createQueryKeys('products', { detail: (id) => [id], }); -export const queryFactory = mergeQueryKeys(postKeys, cartKeys, productsKeys); +export const queryFactory = mergeQueryKeys(postKeys, productsKeys); diff --git a/src/app/(app)/_layout.tsx b/src/app/(app)/_layout.tsx index 8f68a1999..6be6b5cd8 100644 --- a/src/app/(app)/_layout.tsx +++ b/src/app/(app)/_layout.tsx @@ -1,4 +1,3 @@ -/* eslint-disable react/no-unstable-nested-components */ import { Link, Redirect, SplashScreen, Tabs } from 'expo-router'; import { useCallback, useEffect } from 'react'; @@ -8,7 +7,6 @@ import { Feed as FeedIcon, Settings as SettingsIcon, Style as StyleIcon, - Support as SupportIcon, } from '@/ui/icons'; export default function TabLayout() { @@ -30,7 +28,7 @@ export default function TabLayout() { return ; } if (status === 'signOut') { - return ; + return ; } return ( @@ -43,15 +41,6 @@ export default function TabLayout() { tabBarTestID: 'feed-tab', }} /> - , - tabBarTestID: 'carts-tab', - }} - /> - - {isLoading ? ( - - ) : ( - - data={data?.pages.flatMap((page) => page.data.carts)} - renderItem={({ item }) => } - ListEmptyComponent={No carts} - estimatedItemSize={30} - keyExtractor={(_, index) => `item-${index}`} - /> - )} - - ); -} diff --git a/src/app/(app)/index.tsx b/src/app/(app)/index.tsx index fcd068292..36fa0e6af 100644 --- a/src/app/(app)/index.tsx +++ b/src/app/(app)/index.tsx @@ -1,36 +1,9 @@ -import { FlashList } from '@shopify/flash-list'; -import { useCallback } from 'react'; - -import type { Post } from '@/api'; -import { usePosts } from '@/api'; -import { Card } from '@/components/card'; -import { EmptyList, FocusAwareStatusBar, Text, View } from '@/ui'; +import { FocusAwareStatusBar, View } from '@/ui'; export default function Feed() { - const { data, isPending, isError } = usePosts(); - - const renderItem = useCallback( - ({ item }: { item: Post }) => , - [], - ); - - if (isError) { - return ( - - Error Loading data - - ); - } return ( - `item-${index}`} - ListEmptyComponent={} - estimatedItemSize={300} - /> ); } diff --git a/src/app/feed/[id].tsx b/src/app/feed/[id].tsx index 4b634fcfb..9a2e4533a 100644 --- a/src/app/feed/[id].tsx +++ b/src/app/feed/[id].tsx @@ -1,69 +1,12 @@ -import { Stack, useLocalSearchParams } from 'expo-router'; -import { FlatList } from 'react-native'; +import { Stack } from 'expo-router'; -import { type Comment, usePost, usePostComments } from '@/api'; -import { ActivityIndicator, FocusAwareStatusBar, Text, View } from '@/ui'; +import { FocusAwareStatusBar, View } from '@/ui'; export default function Post() { - const local = useLocalSearchParams<{ id: string }>(); - - const { data, isPending, isError } = usePost({ - variables: { id: local.id }, - }); - - const { - data: { comments } = { comments: [] }, - isLoading: isLoadingComments, - } = usePostComments({ - variables: { id: data?.id }, - }); - - if (isPending) { - return ( - - - - - - ); - } - if (isError) { - return ( - - - - Error loading post - - ); - } - return ( - {data.title} - {data.body} - {isLoadingComments ? ( - - Loading comments... - - - ) : ( - - data={comments} - renderItem={({ item: comment }) => ( - - - {comment.body} - - )} - keyExtractor={(item) => item.id.toString()} - ListEmptyComponent={No comments yet} - ListHeaderComponent={ - Comments: - } - /> - )} ); } diff --git a/src/app/feed/add-post.tsx b/src/app/feed/add-post.tsx index 3e18ec47e..bf64a9d9f 100644 --- a/src/app/feed/add-post.tsx +++ b/src/app/feed/add-post.tsx @@ -1,78 +1,11 @@ -import { zodResolver } from '@hookform/resolvers/zod'; -import { useQueryClient } from '@tanstack/react-query'; -import { Stack, useRouter } from 'expo-router'; -import { useForm } from 'react-hook-form'; -import { showMessage } from 'react-native-flash-message'; -import { z } from 'zod'; - -import { useAddPost } from '@/api'; -import { queryFactory } from '@/api/query-factory'; -import { Button, ControlledInput, showErrorMessage, View } from '@/ui'; - -const TITLE_MIN_CHARS = 10; -const BODY_MIN_CHARS = 120; -const schema = z.object({ - title: z.string().min(TITLE_MIN_CHARS), - body: z.string().min(BODY_MIN_CHARS), -}); - -type FormType = z.infer; - +import { Stack } from 'expo-router'; export default function AddPost() { - const { control, handleSubmit } = useForm({ - resolver: zodResolver(schema), - }); - const { mutate: addPost, isPending } = useAddPost(); - const queryClient = useQueryClient(); - const router = useRouter(); - - const onSubmit = (data: FormType) => { - addPost( - { ...data, userId: 1 }, - { - onSuccess: () => { - showMessage({ - message: 'Post added successfully', - type: 'success', - }); - queryClient.invalidateQueries({ - queryKey: queryFactory.posts.list({}).queryKey, - }); - router.back(); - }, - onError: () => showErrorMessage('Error adding post'), - }, - ); - }; return ( - <> - - - - -