Skip to content

Commit 86f94a5

Browse files
authored
Merge pull request #121 from Turing-Sandbox/SUMMA-19-Rebase
SUMMA-19 and SUMMA-21: Rebased
2 parents 0a55780 + bbf31eb commit 86f94a5

File tree

17 files changed

+576
-495
lines changed

17 files changed

+576
-495
lines changed

backend/src/modules/search/controllers/search.controller.ts

Lines changed: 53 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,51 +4,60 @@ import { logger } from "../../../shared/utils/logger";
44

55

66
export class SearchController {
7-
/**
8-
* @description
9-
* Fetches 5 users at a time where their username matches or starts with the text
10-
* provided to the search query. If a starting point is provided, the search query
11-
* starts from the provided starting point.
12-
*
13-
* @param req - Express request object.
14-
* @param res - Express response object.
15-
*/
16-
static async searchUsers(req: Request, res: Response) {
17-
const query: Record<string, any> = req.query;
7+
/**
8+
* @description
9+
* Fetches 5 users at a time where their username matches or starts with the text
10+
* provided to the search query. If a starting point is provided, the search query
11+
* starts from the provided starting point.
12+
*
13+
* @param req - Express request object.
14+
* @param res - Express response object.
15+
*/
16+
static async searchUsers(req: Request, res: Response) {
17+
const searchText = req.query.searchText as string;
18+
const userStartingPoint = req.query.userStartingPoint as string;
1819

19-
const searchText = query.searchText;
20-
const userStartingPoint = query.userStartingPoint;
20+
logger.info(`Searching for users that match the following: ${searchText}`);
21+
try {
22+
const response = await SearchService.searchUsers(
23+
searchText,
24+
userStartingPoint
25+
);
26+
res.status(200).json(response);
27+
} catch (error) {
28+
if (error instanceof Error) {
29+
logger.error(`Error searching users: ${error.message}`);
30+
} else {
31+
logger.error(`Error searching users: ${String(error)}`);
32+
}
33+
res.status(500).json({ error: "Failed to search users" });
34+
}
35+
}
2136

22-
logger.info(`Searching for users that match the following: ${searchText}`);
23-
try {
24-
const response = await SearchService.searchUsers(searchText, userStartingPoint);
25-
res.status(200).json(response);
26-
} catch (error: any) {
27-
logger.error(`Error searching users: ${error.message || error}`);
28-
res.status(500).json({ error: 'Failed to search users' });
29-
}
30-
}
37+
/**
38+
* @description
39+
* Fetches 5 content items at a time where their title matches or starts with the
40+
* text provided to the search query.
41+
*
42+
* @param req - Express request object.
43+
* @param res - Express response object.
44+
*/
45+
static async searchContents(req: Request, res: Response) {
46+
const searchText = req.query.searchText as string;
47+
logger.info(
48+
`Searching for content that matches the following: ${searchText}`
49+
);
3150

32-
/**
33-
* @description
34-
* Fetches 5 content items at a time where their title matches or starts with the
35-
* text provided to the search query.
36-
*
37-
* @param req - Express request object.
38-
* @param res - Express response object.
39-
*/
40-
static async searchContent(req: Request, res: Response) {
41-
const query: Record<string, any> = req.query;
42-
43-
const searchText = query.searchText;
44-
logger.info(`Searching for content that matches the following: ${searchText}`);
45-
46-
try {
47-
const response = await SearchService.searchContent(searchText);
48-
res.status(200).json(response);
49-
} catch (error: any) {
50-
logger.error(`Error searching content: ${error.message || error}`);
51-
res.status(500).json({ error: 'Failed to search content' });
52-
}
53-
}
51+
try {
52+
const response = await SearchService.searchContents(searchText);
53+
res.status(200).json(response);
54+
} catch (error) {
55+
if (error instanceof Error) {
56+
logger.error(`Error searching content: ${error.message}`);
57+
} else {
58+
logger.error(`Error searching content: ${String(error)}`);
59+
}
60+
res.status(500).json({ error: "Failed to search content" });
61+
}
62+
}
5463
}

backend/src/modules/search/routes/search.routes.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Router } from "express";
33
import { SearchController } from "../controllers/search.controller";
44
const searchRoutes = Router();
55

6-
searchRoutes.get("/users/", SearchController.searchUsers);
7-
searchRoutes.get("/content/", SearchController.searchContent);
6+
searchRoutes.get("/users", SearchController.searchUsers);
7+
searchRoutes.get("/contents", SearchController.searchContents);
88

99
export default searchRoutes;

backend/src/modules/search/services/search.service.ts

Lines changed: 51 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,20 @@ import {
1212
getDoc,
1313
} from "firebase/firestore";
1414
import { logger } from "../../../shared/utils/logger";
15-
15+
import { User } from "../../user/models/user.model";
1616

1717
export class SearchService {
1818
private static algoliaClient: ReturnType<typeof algoliasearch> | null = null;
19-
private static readonly ALGOLIA_INDEX_NAME = process.env.ALGOLIA_INDEX_NAME;
19+
private static readonly ALGOLIA_INDEX_NAME = process.env
20+
.ALGOLIA_INDEX_NAME as string;
2021

2122
private static getAlgoliaClient() {
2223
if (!SearchService.algoliaClient) {
23-
const ALGOLIA_APP_ID = process.env.ALGOLIA_APP_ID;
24-
const ALGOLIA_ADMIN_KEY = process.env.ALGOLIA_API_KEY;
24+
const ALGOLIA_APP_ID = process.env.ALGOLIA_APP_ID as string;
25+
const ALGOLIA_ADMIN_KEY = process.env.ALGOLIA_API_KEY as string;
2526
SearchService.algoliaClient = algoliasearch(
26-
ALGOLIA_APP_ID as string,
27-
ALGOLIA_ADMIN_KEY as string
27+
ALGOLIA_APP_ID,
28+
ALGOLIA_ADMIN_KEY
2829
);
2930
}
3031
return SearchService.algoliaClient;
@@ -41,70 +42,41 @@ export class SearchService {
4142
* @param startingPoint - The starting point for the search query.
4243
* @returns An array of users matching the search query.
4344
*/
44-
static async searchUsers(searchText: string, startingPoint = null) {
45+
static async searchUsers(
46+
searchText: string,
47+
startingPoint: string | null = null
48+
) {
4549
logger.info(`Searching for users that match the following: ${searchText}`);
4650
const userRef = collection(db, "users");
4751
const limitNumber: number = 5;
4852

49-
// Create the base user query (no previous query)
50-
const userQuery = query(
53+
// Build the base query
54+
const baseQuery = query(
5155
userRef,
5256
where("usernameLower", ">=", searchText.toLowerCase()),
5357
where("usernameLower", "<=", searchText.toLowerCase() + "\uf8ff"),
5458
orderBy("usernameLower"),
5559
limit(limitNumber)
5660
);
5761

58-
/**
59-
* If a starting point is provided, create a new query starting at that point
60-
* (fetch next 5 documents starting after the starting point)
61-
*/
62-
if (startingPoint) {
63-
logger.info(`Starting point: ${startingPoint}.`);
64-
logger.info("Starting point (JSON):", JSON.stringify(startingPoint, null, 3));
65-
66-
const nextUserQuery = query(
67-
userRef,
68-
where("usernameLower", ">=", searchText.toLowerCase()),
69-
where("usernameLower", "<=", searchText.toLowerCase() + "\uf8ff"),
70-
orderBy("usernameLower"),
71-
limit(limitNumber),
72-
startAfter(startingPoint)
73-
);
62+
// If a starting point is provided, add it to the query
63+
const finalQuery = startingPoint
64+
? query(baseQuery, startAfter(startingPoint))
65+
: baseQuery;
7466

75-
const results = await getDocs(nextUserQuery);
76-
const documents = results.docs.map((doc) => ({
77-
id: doc.id,
78-
...doc.data(),
79-
}));
80-
let nextStartingPoint = null;
81-
82-
if (documents.length >= limitNumber) {
83-
nextStartingPoint =
84-
results.docs[results.docs.length - 1]?.data().usernameLower;
85-
}
86-
87-
logger.info(`Setting starting point: ${nextStartingPoint}.`);
88-
return { documents, nextStartingPoint };
89-
} else {
90-
// If there's no starting point, execute base query
91-
logger.info("No starting point provided, starting from the beginning.");
92-
93-
const results = await getDocs(userQuery);
94-
const documents = results.docs.map((doc) => ({
95-
id: doc.id,
96-
...doc.data(),
97-
}));
98-
let newStartingPoint = null;
99-
100-
if (documents.length >= limitNumber) {
101-
newStartingPoint =
102-
results.docs[results.docs.length - 1]?.data().usernameLower;
103-
}
104-
105-
logger.info(`Setting starting point: ${newStartingPoint}.`);
106-
return { documents, newStartingPoint };
107-
}
67+
// Execute the query
68+
const results = await getDocs(finalQuery);
69+
70+
const users = results.docs.map((doc) => doc.data() as User);
71+
72+
// Determine the next starting point
73+
const nextStartingPoint =
74+
users.length >= limitNumber
75+
? results.docs[results.docs.length - 1]?.data().usernameLower
76+
: null;
77+
78+
logger.info(`Next starting point: ${nextStartingPoint}`);
79+
return { users, nextStartingPoint };
10880
}
10981

11082
/**
@@ -118,20 +90,25 @@ export class SearchService {
11890
* @returns - Object containing the documents and next starting point
11991
* @throws - Error if search fails, i.e if the search query fails
12092
*/
121-
static async searchContent(searchText: string) {
122-
try {
123-
if (!searchText) {
124-
return { documents: [], nextStartingPoint: null };
125-
}
93+
static async searchContents(searchText: string) {
94+
if (!searchText) {
95+
return { documents: [], nextStartingPoint: null };
96+
}
97+
98+
const client = SearchService.getAlgoliaClient();
99+
const index = client.initIndex(this.ALGOLIA_INDEX_NAME);
126100

127-
const client = this.getAlgoliaClient();
128-
const index = client.initIndex(this.ALGOLIA_INDEX_NAME as string);
101+
try {
129102
const { hits } = await index.search(searchText);
130103

131-
logger.info(`Algolia search results length: ${hits.length}, hits: ${hits}`);
104+
logger.info(
105+
`Algolia search results length: ${hits.length}, hits: ${JSON.stringify(
106+
hits
107+
)}`
108+
);
132109

133-
// Fetch corresponding Firebase documents
134-
const firebaseDocuments = await Promise.all(
110+
// Fetch corresponding Firebase content documents
111+
const firebaseContents = await Promise.all(
135112
hits.map(async (hit) => {
136113
const docRef = doc(db, "contents", hit.objectID);
137114
const docSnap = await getDoc(docRef);
@@ -148,18 +125,17 @@ export class SearchService {
148125
);
149126

150127
// Filter out any null values (in case some documents weren't found in Firebase)
151-
const documents = firebaseDocuments.filter(
152-
(doc): doc is NonNullable<typeof doc> => doc !== null
128+
const contents = firebaseContents.filter(
129+
(content): content is NonNullable<typeof content> => content !== null
153130
);
154131

155132
return {
156-
documents,
157-
nextStartingPoint: null,
133+
contents,
134+
nextStartingPoint: null, // Placeholder for future pagination logic
158135
};
159136
} catch (err) {
160-
logger.error(`Something went wrong, error: ${err}`);
161-
throw new Error(`Failed to search for content, error: ${err}`);
162-
137+
logger.error(`Failed to search for contents, error: ${err}`);
138+
throw new Error(`Failed to search for contents, error: ${err}`);
163139
}
164140
}
165141
}

backend/src/modules/subscription/services/stripe.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import dotenv from 'dotenv';
33

44
dotenv.config();
55
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY || '', {
6-
apiVersion: '2025-03-31.basil',
6+
apiVersion: '2025-04-30.basil',
77
});
88

99
/**

frontend/src/App.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ import NotFound from "./pages/error/404";
5555

5656
export default function App() {
5757
return (
58-
<AuthProvider>
59-
<Router>
58+
<Router>
59+
<AuthProvider>
6060
<Background />
6161
<NavbarWrapper />
6262

@@ -103,7 +103,7 @@ export default function App() {
103103
<Route path='*' element={<NotFound />} />
104104
</Routes>
105105
<Footer />
106-
</Router>
107-
</AuthProvider>
106+
</AuthProvider>
107+
</Router>
108108
);
109109
}

0 commit comments

Comments
 (0)