Skip to content

Commit

Permalink
Merge pull request #84 from deriv-com/added-country-utils
Browse files Browse the repository at this point in the history
Prince/ added country utility
  • Loading branch information
niloofar-deriv authored Nov 6, 2024
2 parents dead23a + d037bb7 commit 4ffc491
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ This library is divided into two main namespaces:
- `PromiseUtils`: Includes utilities for handling promises, such as timeout wrappers and promise chaining helpers.
- `URLUtils`: Contains functions for manipulating URLs, including parameter extraction, URL construction, and query string manipulation.
- `WebSocketUtils`: Encapsulates utilities specific to the Deriv WebSocket, addressing environment detection, login ID retrieval, and app ID management.
- `CountryUtils`: A utility for retrieving and managing the client's country and related data.

## Getting Started

Expand Down
19 changes: 19 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"@semantic-release/github": "^10.0.2",
"@semantic-release/npm": "^12.0.0",
"@semantic-release/release-notes-generator": "^13.0.0",
"@types/js-cookie": "^3.0.6",
"@types/node": "^20.11.18",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@vitest/coverage-istanbul": "^1.2.2",
Expand All @@ -63,6 +64,7 @@
"eslint-plugin-promise": "^6.1.1",
"happy-dom": "^13.3.8",
"husky": "^9.0.11",
"js-cookie": "^3.0.5",
"prettier": "3.2.5",
"typescript": "^5.3.3",
"vite": "^5.1.2",
Expand Down
2 changes: 1 addition & 1 deletion src/constants/app-id.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const domainAppId = {
"api.deriv.com": "36544",
"staging-api.deriv.com": "36545",
"smarttrader.deriv.com": "22168",
"staging-smarttrader.deriv.com": "22169"
"staging-smarttrader.deriv.com": "22169",
} as const;

export const appBrand = "deriv";
2 changes: 2 additions & 0 deletions src/constants/url.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,6 @@ export const queryParameters = {
action: "action",
} as const;

export const cloudflareTrace = "https://www.cloudflare.com/cdn-cgi/trace";

export type QueryParameters = keyof typeof queryParameters;
38 changes: 38 additions & 0 deletions src/utils/__test__/country.utils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { describe, beforeEach, afterEach, it, expect, vi } from "vitest";
import { getCountry } from "../country.utils";

describe("getCountry", () => {
beforeEach(() => {
global.fetch = vi.fn();
});

afterEach(() => {
vi.resetAllMocks();
});

it("should return the country code in lowercase when available", async () => {
// Mock fetch response
(global.fetch as vi.Mock).mockResolvedValue({
text: async () => "loc=US\nother=info\n",
});

const country = await getCountry();
expect(country).toBe("us");
});

it("should return an empty string if the loc field is not present", async () => {
(global.fetch as vi.Mock).mockResolvedValue({
text: async () => "other=info\n",
});

const country = await getCountry();
expect(country).toBe("");
});

it("should return an empty string if the fetch fails", async () => {
(global.fetch as vi.Mock).mockRejectedValue(new Error("Fetch failed"));

const country = await getCountry();
expect(country).toBe("");
});
});
32 changes: 32 additions & 0 deletions src/utils/country.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import Cookies from "js-cookie";
import { cloudflareTrace } from "../constants/url.constants";

type TraceData = {
loc?: string;
};

/**
* Fetches the country information based on Cloudflare's trace data or a fallback from cookies.
* This function attempts to retrieve the country location by first fetching trace data from Cloudflare
* and then falling back to the location stored in the cookies if the fetch fails.
*
* @returns {Promise<string>} A Promise that resolves to a string representing the country code in lowercase.
* Returns an empty string if no country data is available or if an error occurs.
*
* @example
* // Returns the country code in lowercase based on Cloudflare's trace data or cookies.
* getCountry().then(country => console.log(country));
*/

export const getCountry = async (): Promise<string> => {
try {
const response = await fetch(cloudflareTrace).catch(() => null);
if (!response) return "";
const text = await response.text().catch(() => "");
const entries = text ? text.split("\n").map((v) => v.split("=", 2)) : [];
const data: TraceData = entries.length ? Object.fromEntries(entries) : {};
return data.loc?.toLowerCase() || JSON.parse(Cookies.get("website_status") || "{}")?.loc || "";
} catch (error) {
return "";
}
};
2 changes: 2 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as URLUtils from "./url.utils";
import * as WebSocketUtils from "./websocket.utils";
import * as BrandUtils from "./brand.utils";
import * as OSDetectionUtils from "./os-detect.utils";
import * as CountryUtils from "./country.utils";

export {
ImageUtils,
Expand All @@ -18,4 +19,5 @@ export {
WebSocketUtils,
BrandUtils,
OSDetectionUtils,
CountryUtils,
};
19 changes: 19 additions & 0 deletions utils-docs/docs/Utils/country.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
sidebar_position: 2
---

# CountryUtils

### getCountry

Fetches the country code based on Cloudflare's trace data or a fallback from cookies.
This function attempts to retrieve the country location by first fetching trace data from Cloudflare
and then falling back to the location stored in the cookies if the fetch fails.

It returns a string representing the country code in lowercase.

#### Example

```js
getCountry().then((country) => console.log(country));
```

0 comments on commit 4ffc491

Please sign in to comment.