Skip to content
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

Prince/ added country utility #84

Merged
merged 7 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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";
niloofar-deriv marked this conversation as resolved.
Show resolved Hide resolved
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> => {
niloofar-deriv marked this conversation as resolved.
Show resolved Hide resolved
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));
```