-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fetch people from the Runn API and attempt to match them by their first name #7
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import axios from 'axios' | ||
|
||
import { RUNN_API_BASE_URL, RUNN_API_TOKEN } from 'src/constants.js' | ||
|
||
export const API_CONFIG = { | ||
headers: { | ||
accept: 'application/json', | ||
'accept-version': '1.0.0', | ||
Authorization: `Bearer ${RUNN_API_TOKEN}`, | ||
}, | ||
} | ||
|
||
const apiClient = axios.create({ | ||
baseURL: RUNN_API_BASE_URL, | ||
headers: API_CONFIG.headers, | ||
}) | ||
|
||
export const api = async (endpoint: string, params = {}) => { | ||
try { | ||
const response = await apiClient.get(endpoint, { params }) | ||
return response.data | ||
} catch (error) { | ||
console.error(`Error fetching data from ${endpoint}:`, error) | ||
throw error | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { api } from './index.js' | ||
|
||
export interface Person { | ||
id: number | ||
firstName: string | ||
lastName: string | ||
email: string | ||
archived: boolean | ||
references: Reference[] | ||
teamId: number | null | ||
Check failure on line 10 in src/api/people.ts GitHub Actions / Handover Bot
|
||
tags: Tag[] | ||
holidaysGroupId: number | null | ||
Check failure on line 12 in src/api/people.ts GitHub Actions / Handover Bot
|
||
} | ||
|
||
interface Reference { | ||
referenceName: string | ||
externalId: string | ||
} | ||
|
||
interface Tag { | ||
id: number | ||
name: string | ||
} | ||
|
||
export interface People { | ||
values: Person[] | ||
nextCursor: string | ||
} | ||
|
||
export const getPeople = async (cursor?: string) => { | ||
const endpoint = '/people/' | ||
const params = { limit: 50, cursor } | ||
|
||
try { | ||
const data = await api(endpoint, params) | ||
return data | ||
} catch (error) { | ||
console.error('Error fetching people:', error) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,8 @@ const schema = z.object({ | |
.regex(/^\d\d:\d\d$/) | ||
.default('17:00'), | ||
OPENAI_API_KEY: z.optional(z.string()), | ||
RUNN_API_BASE_URL: z.string(), | ||
RUNN_API_TOKEN: z.string(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TODO: I need to get an API token for production and add it to the .env file of the production environment |
||
}) | ||
|
||
const config = schema.parse(process.env) | ||
|
@@ -28,3 +30,5 @@ export const HANDOVER_CHANNEL = config.HANDOVER_CHANNEL | |
export const HANDOVER_TITLE = config.HANDOVER_TITLE | ||
export const HANDOVER_DAILY_REMINDER_TIME = config.HANDOVER_DAILY_REMINDER_TIME | ||
export const OPENAI_API_KEY = config.OPENAI_API_KEY | ||
export const RUNN_API_BASE_URL = config.RUNN_API_BASE_URL | ||
export const RUNN_API_TOKEN = config.RUNN_API_TOKEN |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { test, expect, describe } from 'vitest' | ||
import { findPerson } from './people.js' | ||
import { Person } from 'src/api/people.js' | ||
|
||
const peopleResponse: Person[] = [ | ||
{ | ||
id: 2832, | ||
firstName: 'Andria', | ||
lastName: 'Hibe', | ||
email: '[email protected]', | ||
archived: false, | ||
references: [], | ||
teamId: null, | ||
tags: [], | ||
holidaysGroupId: null, | ||
}, | ||
{ | ||
id: 25032, | ||
firstName: 'Zehavit', | ||
lastName: 'Zaslansky', | ||
email: '[email protected]', | ||
archived: false, | ||
references: [], | ||
teamId: 63, | ||
tags: [ | ||
{ | ||
id: 21, | ||
name: 'Powlowskicester', | ||
}, | ||
], | ||
holidaysGroupId: null, | ||
}, | ||
{ | ||
id: 19859, | ||
firstName: 'Aaron', | ||
lastName: 'Carlino', | ||
email: '[email protected]', | ||
archived: false, | ||
references: [], | ||
teamId: 63, | ||
tags: [], | ||
holidaysGroupId: null, | ||
}, | ||
] | ||
|
||
describe('findPerson', () => { | ||
test('finds a person by full name', async () => { | ||
const person = findPerson('Aaron Carlino', peopleResponse) | ||
expect(person?.firstName).toEqual('Aaron') | ||
}) | ||
|
||
test('finds a person by first name in lowercase', async () => { | ||
const person = findPerson('andria', peopleResponse) | ||
expect(person?.firstName).toEqual('Andria') | ||
}) | ||
|
||
test('finds a person by first name in uppercase', async () => { | ||
const person = findPerson('Zehavit', peopleResponse) | ||
expect(person?.firstName).toEqual('Zehavit') | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { Person, getPeople } from 'src/api/people.js' | ||
|
||
interface PersonFound { | ||
id: number | ||
firstName?: string | ||
} | ||
|
||
export const findPerson = ( | ||
userName: string, | ||
people: Person[], | ||
): PersonFound | null => { | ||
const [firstName, _] = userName.split(' ') | ||
const person = people.find((person) => { | ||
return person.firstName.toLowerCase() === firstName?.toLowerCase() | ||
}) | ||
return person | ||
? { | ||
id: person.id, | ||
firstName: person.firstName, | ||
} | ||
: null | ||
} | ||
|
||
export const findPeopleFromList = async ( | ||
userName: string, | ||
cursor?: string, | ||
count: number = 1, | ||
): Promise<PersonFound | null> => { | ||
console.log(`findPeopleFromList called ${count} times`) | ||
|
||
const response = await getPeople(cursor) | ||
|
||
if (!response || !response.values) { | ||
console.error('Failed to fetch people or response is malformed.') | ||
return null | ||
} | ||
|
||
const personFound = findPerson(userName, response.values) | ||
|
||
if (personFound) { | ||
return personFound | ||
} else if (response.nextCursor) { | ||
return findPeopleFromList(userName, response.nextCursor, count + 1) | ||
} | ||
|
||
// the person is not found and there is no next cursor | ||
return null | ||
} | ||
|
||
// 1. findPeopleFromList(userName) | ||
// 2-a. could not find person -> enter Runn ID manually | ||
// 2-b. found person -> fetch timeOffs with the person ID | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ☝️ This is the plan! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could consider using OpenAPI to auto-generate types in the future, but for now, I thought it's not necessary. These have been added manually.