-
Notifications
You must be signed in to change notification settings - Fork 729
Serp linkedin finder enrichment source #2664
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
Changes from 2 commits
2976359
e0f8860
604e9a8
d4f4611
0cf254b
b7ce392
9b7d434
93c416b
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,209 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import axios from 'axios' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { Logger, LoggerBase } from '@crowd/logging' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { MemberEnrichmentSource, MemberIdentityType, PlatformType } from '@crowd/types' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| IEnrichmentService, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| IEnrichmentSourceInput, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| IMemberEnrichmentDataNormalized, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from '../../types' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { IMemberEnrichmentDataSerp, IMemberEnrichmentSerpApiResponse } from './types' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export default class EnrichmentServiceSerpApi extends LoggerBase implements IEnrichmentService { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public source: MemberEnrichmentSource = MemberEnrichmentSource.SERP | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public platform = `enrichment-${this.source}` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public enrichableBySql = ` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (members."displayName" like '% %') AND | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
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. What's the idea? People with a space in their name?
Collaborator
Author
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. yeah, people with multiple-word names. Single-word names are not good enough when searching in google. But now I'm thinking, since we're already using other stuff to distinguish (like website, location w/e), maybe we can remove this
Collaborator
Author
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. the test results with one-word display names aren't great.
I'm keeping this for now, since it's more important to have more correct answers than more enrichable members |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (members.attributes->'location'->>'default' is not null and members.attributes->'location'->>'default' <> '') AND | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ((members.attributes->'websiteUrl'->>'default' is not null and members.attributes->'websiteUrl'->>'default' <> '') OR | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (mi.verified AND mi.type = 'username' and mi.platform = 'github') OR | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (mi.verified AND mi.type = 'email') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
epipav marked this conversation as resolved.
Comment on lines
+19
to
+26
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. Fix undefined SQL alias 'mi' The SQL condition uses the Consider adding the necessary JOIN clause: public enrichableBySql = `
("activitySummary".total_count > ${this.enrichMembersWithActivityMoreThan}) AND
(members."displayName" like '% %') AND
(members.attributes->'location'->>'default' is not null and members.attributes->'location'->>'default' <> '') AND
((members.attributes->'websiteUrl'->>'default' is not null and members.attributes->'websiteUrl'->>'default' <> '') OR
+ EXISTS (SELECT 1 FROM member_identities mi WHERE mi.member_id = members.id AND
(mi.verified AND mi.type = 'username' and mi.platform = 'github') OR
(mi.verified AND mi.type = 'email')
+ )
)`📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // bust cache after 60 days | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public cacheObsoleteAfterSeconds = 60 * 60 * 24 * 60 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| constructor(public readonly log: Logger) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| super(log) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isEnrichableBySource(input: IEnrichmentSourceInput): boolean { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const displayNameSplit = input.displayName?.split(' ') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| displayNameSplit?.length > 1 && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| !!input.location && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ((!!input.email && input.email.verified) || | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (!!input.github && input.github.verified) || | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| !!input.website) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async getData(input: IEnrichmentSourceInput): Promise<IMemberEnrichmentDataSerp | null> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let enriched: IMemberEnrichmentDataSerp = null | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (input.displayName && input.location && input.website) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| enriched = await this.queryWithWebsite(input.displayName, input.location, input.website) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!enriched && input.displayName && input.location && input.github) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| enriched = await this.queryWithGithubIdentity( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| input.displayName, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| input.location, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| input.github.value, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
epipav marked this conversation as resolved.
Outdated
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!enriched && input.displayName && input.location && input.email) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| enriched = await this.queryWithEmail(input.displayName, input.location, input.email.value) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
epipav marked this conversation as resolved.
Outdated
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return enriched | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private async queryWithWebsite( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| displayName: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| location: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| website: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ): Promise<IMemberEnrichmentDataSerp> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const url = `https://serpapi.com/search.json` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const config = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| method: 'get', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| url, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| params: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| api_key: process.env['CROWD_SERP_API_KEY'], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
epipav marked this conversation as resolved.
Outdated
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| q: `"${displayName}" ${location} "${website}" site:linkedin.com/in`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| num: 3, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| engine: 'google', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const response: IMemberEnrichmentSerpApiResponse = (await axios(config)).data | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
epipav marked this conversation as resolved.
Outdated
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (response.search_information.total_results > 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| response.organic_results.length > 0 && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| response.organic_results[0].link && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| !response.search_information.spelling_fix && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| !response.search_information.spelling_fix_type | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| linkedinUrl: response.organic_results[0].link, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return null | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
epipav marked this conversation as resolved.
Outdated
epipav marked this conversation as resolved.
Outdated
epipav marked this conversation as resolved.
Outdated
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private async queryWithGithubIdentity( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| displayName: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| location: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| githubIdentity: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ): Promise<IMemberEnrichmentDataSerp> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const url = `https://serpapi.com/search.json` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const config = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| method: 'get', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| url, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| params: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| api_key: process.env['CROWD_SERP_API_KEY'], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| q: `"${displayName}" ${location} "${githubIdentity}" site:linkedin.com/in`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| num: 3, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| engine: 'google', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const response: IMemberEnrichmentSerpApiResponse = (await axios(config)).data | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (response.search_information.total_results > 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| response.organic_results.length > 0 && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| response.organic_results[0].link && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| !response.search_information.spelling_fix && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| !response.search_information.spelling_fix_type | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| linkedinUrl: response.organic_results[0].link, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return null | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private async queryWithEmail( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| displayName: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| location: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| email: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ): Promise<IMemberEnrichmentDataSerp> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const url = `https://serpapi.com/search.json` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const config = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| method: 'get', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| url, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| params: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| api_key: process.env['CROWD_SERP_API_KEY'], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| q: `"${displayName}" ${location} "${email}" site:linkedin.com/in`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| num: 3, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| engine: 'google', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const response: IMemberEnrichmentSerpApiResponse = (await axios(config)).data | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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. Add error handling for Axios request to prevent unhandled exceptions The Axios request in Apply this diff to add error handling: try {
const response: IMemberEnrichmentSerpApiResponse = (await axios(config)).data
if (response.search_information.total_results > 0) {
// existing logic
}
return null
+ } catch (error) {
+ this.log.error('Error fetching data from SerpAPI', error)
+ return null
}
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (response.search_information.total_results > 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| response.organic_results.length > 0 && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| response.organic_results[0].link && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| !response.search_information.spelling_fix && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| !response.search_information.spelling_fix_type | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| linkedinUrl: response.organic_results[0].link, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return null | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| normalize(data: IMemberEnrichmentDataSerp): IMemberEnrichmentDataNormalized { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const normalized: IMemberEnrichmentDataNormalized = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| identities: [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| platform: PlatformType.LINKEDIN, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: MemberIdentityType.USERNAME, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| verified: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: this.normalizeLinkedUrl(data.linkedinUrl), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return normalized | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+97
to
+109
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. Add error handling in normalize method The Add error handling: normalize(data: IMemberEnrichmentDataSerp): IMemberEnrichmentDataNormalized {
+ let normalizedUrl: string;
+ try {
+ normalizedUrl = this.normalizeLinkedUrl(data.linkedinUrl);
+ } catch (error) {
+ this.log.warn('Failed to normalize LinkedIn URL, using original', { url: data.linkedinUrl });
+ normalizedUrl = data.linkedinUrl;
+ }
const normalized: IMemberEnrichmentDataNormalized = {
identities: [
{
platform: PlatformType.LINKEDIN,
type: MemberIdentityType.USERNAME,
verified: false,
- value: this.normalizeLinkedUrl(data.linkedinUrl),
+ value: normalizedUrl,
},
],
}
return normalized
}📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private normalizeLinkedUrl(url: string): string { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const parsedUrl = new URL(url) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (parsedUrl.hostname.endsWith('linkedin.com')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parsedUrl.hostname = 'linkedin.com' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parsedUrl.search = '' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let path = parsedUrl.pathname | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (path.endsWith('/')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| path = path.slice(0, -1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return parsedUrl.origin + path | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return url | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.log.error(`Error while normalizing linkedin url: ${url}`, error) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
epipav marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw error | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| export interface IMemberEnrichmentDataSerp { | ||
| linkedinUrl: string | ||
| } | ||
|
epipav marked this conversation as resolved.
|
||
|
|
||
| export interface IMemberEnrichmentSerpApiResponse { | ||
| organic_results: IMemberEnrichmentSerpApiResponseOrganicResult[] | ||
| search_information: IMemberEnrichmentSerpApiResponseSearchInformation | ||
| } | ||
|
epipav marked this conversation as resolved.
|
||
|
|
||
| export interface IMemberEnrichmentSerpApiResponseSearchInformation { | ||
| query_displayed: string | ||
| total_results: number | ||
| time_taken_displayed: number | ||
| organic_results_state: string | ||
| spelling_fix?: string | ||
| spelling_fix_type?: string | ||
| } | ||
|
epipav marked this conversation as resolved.
|
||
|
|
||
| export interface IMemberEnrichmentSerpApiResponseOrganicResult { | ||
| position: number | ||
| title: string | ||
| link: string | ||
| redirect_link: string | ||
| displayed_link: string | ||
| favicon: string | ||
| snippet: string | ||
| snippet_highlighted_words: string[] | ||
| sitelinks: { | ||
| inline: IMemberEnrichmentSerpApiResponseOrganicResultSitelinkInline[] | ||
| } | ||
| source: string | ||
| } | ||
|
epipav marked this conversation as resolved.
epipav marked this conversation as resolved.
|
||
|
|
||
| export interface IMemberEnrichmentSerpApiResponseOrganicResultSitelinkInline { | ||
| title: string | ||
| link: string | ||
| } | ||
|
epipav marked this conversation as resolved.
|
||
Uh oh!
There was an error while loading. Please reload this page.