-
Notifications
You must be signed in to change notification settings - Fork 167
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
fix: handle neynar responses + add additional information #35
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export class FetchError extends Error { | ||
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. nit. for next PR, we can probably have Fetch Error shared in a |
||
constructor(message: string) { | ||
super(message); | ||
this.name = 'FetchError'; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { neynarBulkUserLookup } from './user/neynarUserFunctions'; | ||
|
||
describe('integration tests', () => { | ||
it('bulk data lookup should find all users', async () => { | ||
const fidsToLookup = [3, 194519]; // dwr and polak.eth fids | ||
const response = await neynarBulkUserLookup(fidsToLookup); | ||
expect(response?.users.length).toEqual(2); | ||
for (const user of response?.users!) { | ||
expect(fidsToLookup).toContain(user.fid); | ||
} | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { FetchError } from '../exceptions/FetchError'; | ||
import { neynarBulkUserLookup } from './neynarUserFunctions'; | ||
|
||
describe('neynar user functions', () => { | ||
let fetchMock = jest.fn(); | ||
let status = 200; | ||
|
||
beforeEach(() => { | ||
status = 200; | ||
global.fetch = jest.fn(() => | ||
Promise.resolve({ | ||
status, | ||
json: fetchMock, | ||
}), | ||
) as jest.Mock; | ||
}); | ||
|
||
it('should return fetch response correctly', async () => { | ||
fetchMock.mockResolvedValue({ | ||
users: [{ fid: 1 }], | ||
}); | ||
|
||
const resp = await neynarBulkUserLookup([1]); | ||
expect(resp?.users[0]?.fid).toEqual(1); | ||
}); | ||
|
||
it('fails on a non-200', async () => { | ||
status = 401; | ||
const resp = neynarBulkUserLookup([1]); | ||
await expect(resp).rejects.toThrow(FetchError); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import { FetchError } from '../exceptions/FetchError'; | ||
|
||
export const NEYNAR_DEFAULT_API_KEY = 'NEYNAR_API_DOCS'; | ||
export interface NeynarUserModel { | ||
fid: number; | ||
custody_address: string; | ||
username: string; | ||
display_name: string; | ||
pfp_url: string; | ||
profile: { | ||
bio: { | ||
text: string; | ||
}; | ||
}; | ||
follower_count: number; | ||
verifications: string[]; | ||
} | ||
export interface NeynarBulkUserLookupModel { | ||
users: NeynarUserModel[]; | ||
} | ||
|
||
export async function neynarBulkUserLookup( | ||
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. nit. for follow up PR, let's have more docs in here to explain what's going on. Also let's try to keep the internal architecture as flat as possible, so that instead of having |
||
farcasterIDs: number[], | ||
apiKey: string = NEYNAR_DEFAULT_API_KEY, | ||
): Promise<NeynarBulkUserLookupModel | undefined> { | ||
const options = { | ||
method: 'GET', | ||
url: `https://api.neynar.com/v2/farcaster/user/bulk?fids=${farcasterIDs.join(',')}`, | ||
headers: { accept: 'application/json', api_key: apiKey }, | ||
}; | ||
const resp = await fetch(options.url, { headers: options.headers }); | ||
if (resp.status !== 200) { | ||
throw new FetchError(`non-200 status returned from neynar : ${resp.status}`); | ||
} | ||
const responseBody = await resp.json(); | ||
return convertToNeynarResponseModel(responseBody); | ||
} | ||
|
||
function convertToNeynarResponseModel(data: any): NeynarBulkUserLookupModel | undefined { | ||
if (!data) { | ||
return; | ||
} | ||
|
||
const response: NeynarBulkUserLookupModel = { | ||
users: [], | ||
}; | ||
|
||
for (const user of data.users) { | ||
const formattedUser = convertToNeynarUserModel(user); | ||
if (formattedUser) { | ||
response.users.push(formattedUser); | ||
} | ||
} | ||
return response; | ||
} | ||
|
||
function convertToNeynarUserModel(data: any): NeynarUserModel | undefined { | ||
if (!data) { | ||
return; | ||
} | ||
|
||
return { | ||
fid: data.fid ?? 0, | ||
custody_address: data.custody_address ?? '', | ||
username: data.username ?? '', | ||
display_name: data.display_name ?? '', | ||
pfp_url: data.pfp_url ?? '', | ||
profile: { | ||
bio: { | ||
text: data.profile?.bio?.text ?? '', | ||
}, | ||
}, | ||
follower_count: data.follower_count ?? 0, | ||
verifications: Array.isArray(data.verifications) ? data.verifications : [], | ||
}; | ||
} |
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.
Out of curiosity, is
integ
the new way folks calle2e
?I am not agaist that, I am just curios as I never saw this prefix before.
cc @robpolak @cnasc