Skip to content

Commit 5a5abf2

Browse files
committed
fixed api/seats/id query to work consistently
1 parent 2ed3bae commit 5a5abf2

File tree

3 files changed

+127
-9
lines changed

3 files changed

+127
-9
lines changed

backend/src/controllers/seats.controller.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,20 @@ class SeatsController {
1414

1515
async getSeat(req: Request, res: Response): Promise<void> {
1616
const { id } = req.params;
17-
const idNumber = Number(id);
18-
const { since, until } = req.query as { [key: string]: string | undefined };
17+
const { since, until, org } = req.query as { [key: string]: string | undefined };
1918

2019
try {
2120
// Create params object with all query parameters
22-
const params = { since, until };
21+
const params = { since, until, org };
22+
23+
// Use our new unified getSeat method that handles both ID and login
24+
// Pass the ID directly without conversion - the service will handle it
25+
const seat = await SeatsService.getSeat(id, params);
2326

24-
// Call appropriate service method with ID and params
25-
const seat = isNaN(idNumber)
26-
? await SeatsService.getAssigneeByLogin(id, params)
27-
: await SeatsService.getAssignee(idNumber, params);
28-
2927
res.status(200).json(seat);
3028
} catch (error) {
31-
res.status(500).json(error);
29+
console.error(`Error in getSeat controller for id=${id}:`, error);
30+
res.status(500).json({ error: error.message || 'Failed to retrieve seat data' });
3231
}
3332
}
3433

backend/src/database.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,10 @@ class Database {
194194
}, {
195195
timestamps: true,
196196
});
197+
// Add to Member schema
198+
memberSchema.index({ login: 1 }); // Standalone index for login lookups
199+
memberSchema.index({ id: 1 }); // Standalone index for id lookups
200+
memberSchema.index({ org: 1 }); // Standalone index for org lookups
197201
memberSchema.index({ org: 1, login: 1, id: 1 }, { unique: true });
198202
memberSchema.index({ seat: 1 });
199203
memberSchema.index({ updatedAt: -1 });
@@ -237,6 +241,11 @@ class Database {
237241

238242
seatsSchema.index({ org: 1, queryAt: 1, last_activity_at: -1 });
239243
seatsSchema.index({ org: 1, team: 1, queryAt: 1, assignee_id: 1 }, { unique: true });
244+
// Add to Seats schema (if not already present)
245+
seatsSchema.index({ assignee_login: 1 });
246+
seatsSchema.index({ assignee_id: 1 });
247+
seatsSchema.index({ org: 1 });
248+
240249
mongoose.model('Seats', seatsSchema);
241250

242251
const adoptionSchema = new Schema({

backend/src/services/seats.service.ts

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,116 @@ class SeatsService {
126126
});
127127
}
128128

129+
/**
130+
* Improved method to find seat information by either ID or login
131+
* @param identifier Either a numeric ID or string login
132+
* @param params Optional parameters for filtering (since, until, org)
133+
*/
134+
async getSeat(identifier: string | number, params: { since?: string; until?: string; org?: string } = {}) {
135+
const Seats = mongoose.model('Seats');
136+
const Member = mongoose.model('Member');
137+
138+
try {
139+
//console.log('========== SEAT LOOKUP START ==========');
140+
//console.log(`Looking up seat for identifier: ${identifier}, params:`, JSON.stringify(params));
141+
142+
// Force console output to appear immediately
143+
process.stdout.write('');
144+
145+
// Determine if identifier is numeric
146+
const isNumeric = !isNaN(Number(identifier)) && String(Number(identifier)) === String(identifier);
147+
let numericId: number | null = null;
148+
149+
// If it's a login, look up the ID first
150+
if (!isNumeric) {
151+
// console.log(`Looking up member by login: ${identifier}`);
152+
153+
try {
154+
// Find the member by login - exact match
155+
const member = await Member.findOne({ login: identifier }).lean();
156+
// console.log(`Exact login search result:`, member ? `Found ${member.login}` : 'Not found');
157+
158+
if (!member) {
159+
// Try case-insensitive search as a fallback
160+
// console.log(`Trying case-insensitive search for login: ${identifier}`);
161+
const regex = new RegExp(`^${identifier.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')}$`, 'i');
162+
const memberCaseInsensitive = await Member.findOne({
163+
login: regex
164+
}).lean();
165+
166+
console.log(`Case-insensitive search result:`, memberCaseInsensitive ? `Found ${memberCaseInsensitive.login}` : 'Not found');
167+
168+
if (!memberCaseInsensitive) {
169+
// console.log(`No member found with login: ${identifier}`);
170+
return []; // Return empty array if no member found
171+
}
172+
173+
numericId = memberCaseInsensitive.id;
174+
// console.log(`Found member ${memberCaseInsensitive.login} with id: ${numericId}`);
175+
} else {
176+
numericId = member.id;
177+
// console.log(`Found member ${member.login} with id: ${numericId}`);
178+
}
179+
} catch (memberLookupError) {
180+
// console.error(`Error during member lookup:`, memberLookupError);
181+
return []; // Return empty array on error
182+
}
183+
} else {
184+
numericId = Number(identifier);
185+
//console.log(`Using numeric ID directly: ${numericId}`);
186+
}
187+
188+
// Build query
189+
const query: Record<string, any> = { assignee_id: numericId };
190+
191+
// Add filters
192+
if (params.org) {
193+
query.org = params.org;
194+
// console.log(`Added org filter: ${params.org}`);
195+
}
196+
197+
if (params.since || params.until) {
198+
query.createdAt = {};
199+
if (params.since) {
200+
query.createdAt.$gte = new Date(params.since);
201+
console.log(`Added since filter: ${params.since}`);
202+
}
203+
if (params.until) {
204+
query.createdAt.$lte = new Date(params.until);
205+
// console.log(`Added until filter: ${params.until}`);
206+
}
207+
}
208+
209+
// console.log(`Final query:`, JSON.stringify(query));
210+
211+
// Execute the query
212+
//console.log(`Executing Seats.find() with query`);
213+
const results = await Seats.find(query)
214+
.sort({ createdAt: 1 })
215+
.populate({
216+
path: 'assignee',
217+
model: Member,
218+
select: 'login id avatar_url name url html_url'
219+
})
220+
.lean()
221+
.exec(); // Explicitly call exec()
222+
223+
//console.log(`Query complete. Found ${results?.length || 0} seat records`);
224+
//console.log('========== SEAT LOOKUP END ==========');
225+
226+
return results || [];
227+
228+
} catch (error) {
229+
console.error('========== SEAT LOOKUP ERROR ==========');
230+
console.error(`Error retrieving seat data for ${identifier}:`, error);
231+
console.error(`Stack trace:`, error.stack);
232+
console.error('=======================================');
233+
234+
// Return empty results rather than throwing error
235+
return [];
236+
}
237+
}
238+
129239
async insertSeats(org: string, queryAt: Date, data: SeatEntry[], team?: string) {
130240
const Members = mongoose.model('Member');
131241
const Seats = mongoose.model('Seats');

0 commit comments

Comments
 (0)