Skip to content

Commit

Permalink
Merge pull request #37 from joshuatz/dev
Browse files Browse the repository at this point in the history
Bugfix: Exporting your own multi-lingual profile, overriding defaultLocale
  • Loading branch information
joshuatz committed Dec 7, 2020
2 parents a48c93d + ad20002 commit 0b25a9a
Show file tree
Hide file tree
Showing 7 changed files with 418 additions and 128 deletions.
2 changes: 2 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"browser": true
},
"rules": {
"no-self-assign": "off",
"no-param-reassign": "off",
"no-underscore-dangle": "off",
"no-plusplus": "off",
"no-useless-escape": "off",
Expand Down
33 changes: 31 additions & 2 deletions LinkedIn-Notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ Back to main README: [click here](./README.md)
- V2 Docs:
- https://docs.microsoft.com/en-us/linkedin/
- https://developer.linkedin.com/docs/guide/v2
- Another project that uses the unofficial Voyager API: [tomquirk/linkedin-api](https://github.com/tomquirk/linkedin-api)
- Other projects that use the unofficial Voyager API:
- [tomquirk/linkedin-api](https://github.com/tomquirk/linkedin-api)
- [eilonmore/linkedin-private-api](https://github.com/eilonmore/linkedin-private-api)
- LinkedIn DataHub (this powers a lot of the backend)
- [Blog Post](https://engineering.linkedin.com/blog/2019/data-hub)
- [Github Repo](https://github.com/linkedin/datahub)
Expand Down Expand Up @@ -76,5 +78,32 @@ Here are some quick notes on Voyager responses and how data is grouped / nested:
- LI has limits on certain endpoints, and the amount of nested elements it will return
- See [PR #23](https://github.com/joshuatz/linkedin-to-jsonresume/pull/23) for an example of how this was implemented
### Voyager - Misc Notes
- Make sure you always include the `Host` header if making requests outside a web browser (browsers will automatically include this for you)
- Value should be: `www.linkedin.com`
- If you forget it, you will get 400 error (`invalid hostname`)
- For inline data, `<code></code>` with request payload usually ***follows*** `<img><code></code>` with *response* payload
- It appears as though whatever language the profile was ***first*** created with sticks as the "principal language", regardless if user changes language settings (more on this below).
- You can find this under the main profile object, where you would find `supportedLocales` - the default / initial locale is under - `defaultLocale`
### Voyager - Multilingual and Locales Support
> LI seems to be making changes related to this; this section might not be 100% up-to-date.
There are some really strange quirks around multi-locale profiles. When a multi-locale user is logged in and requesting *their own* profile, LI will *refuse* to let the `x-li-lang` header override the `defaultLocale` as specified by the profile (see [issue #35](https://github.com/joshuatz/linkedin-to-jsonresume/issues/35)). However, if *someone else* exports their profile, the same exact endpoints will respect the header and will return the correct data for the requested locale (assuming creator made a version of their profile with the requested locale).
Even stranger, this quirk only seems to apply to *certain* endpoints; e.g. `/me` respects the requested language, but `/profileView` does not (and *always* returns data corresponding with `defaultLocale`) 🙃
Furthermore, the `/dash` subset of endpoints does not ever (AFAIK) change the main key-value pairs based on `x-li-lang`; instead, it nests multi-locale data under `multiLocale` prefixed keys. For example:
```json
{
"firstName": "Алексе́й",
"multiLocaleFirstName": {
"ru_RU": "Алексе́й",
"en_US": "Alexey"
}
}
```
## LinkedIn TS Types
I've put some basics LI types in my `global.d.ts`. Eventually, it would be nice to re-write the core of this project as TS, as opposed to the current VSCode-powered typed JS approached.
I've put some basics LI types in my `global.d.ts`. Eventually, it would be nice to re-write the core of this project as TS, as opposed to the current VSCode-powered typed JS approached.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# LinkedIn Profile to JSON Resume Browser Tool ![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/joshuatz/linkedin-to-jsonresume)

> An extremely easy-to-use browser extension for exporting your full LinkedIn Profile to a JSON Resume file or string.
## Chrome Extension 📦 - [Webstore Link](https://chrome.google.com/webstore/detail/json-resume-exporter/caobgmmcpklomkcckaenhjlokpmfbdec)

## My LinkedIn Profile 👨‍💼 - [https://www.linkedin.com/in/joshuatzucker/](https://www.linkedin.com/in/joshuatzucker/)
## My LinkedIn Profile 👨‍💼 - [linkedin.com/in/joshuatzucker/](https://www.linkedin.com/in/joshuatzucker/)

![Demo GIF](demo-chrome_extension.gif "Demo Gif")

Expand Down
24 changes: 15 additions & 9 deletions browser-ext/popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ const STORAGE_KEYS = {
const SPEC_SELECT = /** @type {HTMLSelectElement} */ (document.getElementById('specSelect'));
/** @type {SchemaVersion[]} */
const SPEC_OPTIONS = ['beta', 'stable', 'latest'];
/** @type {HTMLSelectElement} */
const LANG_SELECT = document.querySelector('.langSelect');

/**
* Generate injectable code for capturing a value from the contentScript scope and passing back via message
Expand Down Expand Up @@ -41,13 +43,20 @@ const getLangStringsCode = `(async () => {
})();
`;

/**
* Get the currently selected lang locale in the selector
*/
const getSelectedLang = () => {
return LANG_SELECT.value;
};

/**
* Get JS string that can be eval'ed to get the program to run and show output
* Note: Be careful of strings versus vars, escaping, etc.
* @param {SchemaVersion} version
*/
const getRunAndShowCode = (version) => {
return `liToJrInstance.parseAndShowOutput('${version}');`;
return `liToJrInstance.preferLocale = '${getSelectedLang()}';liToJrInstance.parseAndShowOutput('${version}');`;
};

/**
Expand All @@ -66,14 +75,12 @@ const toggleEnabled = (isEnabled) => {
* @param {string[]} langs
*/
const loadLangs = (langs) => {
/** @type {HTMLSelectElement} */
const selectElem = document.querySelector('.langSelect');
selectElem.innerHTML = '';
LANG_SELECT.innerHTML = '';
langs.forEach((lang) => {
const option = document.createElement('option');
option.value = lang;
option.innerText = lang;
selectElem.appendChild(option);
LANG_SELECT.appendChild(option);
});
toggleEnabled(langs.length > 0);
};
Expand Down Expand Up @@ -171,13 +178,12 @@ document.getElementById('liToJsonButton').addEventListener('click', async () =>

document.getElementById('liToJsonDownloadButton').addEventListener('click', () => {
chrome.tabs.executeScript({
code: `liToJrInstance.parseAndDownload();`
code: `liToJrInstance.preferLocale = '${getSelectedLang()}';liToJrInstance.parseAndDownload();`
});
});

document.getElementById('langSelect').addEventListener('change', (evt) => {
const updatedLang = /** @type {HTMLSelectElement} */ (evt.target).value;
setLang(updatedLang);
LANG_SELECT.addEventListener('change', () => {
setLang(getSelectedLang());
});

document.getElementById('vcardExportButton').addEventListener('click', () => {
Expand Down
11 changes: 7 additions & 4 deletions global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ declare global {
interface LiEntity {
$type: string;
entityUrn: LiUrn;
objectUrn?: LiUrn;
[key: string]: any;
paging?: LiPaging;
}
Expand Down Expand Up @@ -82,9 +83,9 @@ declare global {
// Methods
getElementKeys: () => string[];
getElements: () => Array<LiEntity & {key: LIUrn}>;
getValueByKey: (key: string) => LiEntity;
getValuesByKey: (key: LiUrn, optTocValModifier?: TocValModifier) => LiEntity[];
getElementsByType: (typeStr: string) => LiEntity[];
getValueByKey: (key: string | string[]) => LiEntity;
getValuesByKey: (key: LiUrn | LiUrn[], optTocValModifier?: TocValModifier) => LiEntity[];
getElementsByType: (typeStr: string | string []) => LiEntity[];
getElementByUrn: (urn: string) => LiEntity | undefined;
/**
* Get multiple elements by URNs
Expand Down Expand Up @@ -116,7 +117,9 @@ declare global {
type CaptureResult = 'success' | 'fail' | 'incomplete' | 'empty';

interface ParseProfileSchemaResultSummary {
profileObj: LiResponse;
liResponse: LiResponse;
profileInfoObj?: LiEntity;
profileSrc: 'profileView' | 'dashFullProfileWithEntities';
pageUrl: string;
localeStr?: string;
parseSuccess: boolean;
Expand Down
Loading

0 comments on commit 0b25a9a

Please sign in to comment.