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

Release TW Extension 1.26.0 #172

Merged
merged 4 commits into from
Sep 9, 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 .env.dist
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ THREE_ROUTE_API_AUTH_TOKEN=
REDIS_URL=
ADMIN_USERNAME=
ADMIN_PASSWORD=
COVALENT_API_KEY=
2 changes: 1 addition & 1 deletion .github/workflows/code-quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: macos-latest
steps:
- name: Clone repository
uses: actions/checkout@v2
uses: actions/checkout@v4

- name: Repository setup
uses: ./.github/workflows/repository-setup
Expand Down
13 changes: 5 additions & 8 deletions .github/workflows/repository-setup/action.yml
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
name: Repository setup

description: Setups Node 16.x & installs dependencies
description: Setups Node.js & installs dependencies

runs:
using: 'composite'
steps:
- name: Setup Node 16.x
uses: actions/setup-node@v2
- name: Setup Node 20.x
uses: actions/setup-node@v4
with:
node-version: '16.x'

- name: Get branch name
uses: EthanSK/git-branch-name-action@v1
node-version: '20.x'

- name: Get yarn cache directory path
id: yarn-cache-dir-path
shell: bash
run: echo "::set-output name=dir::$(yarn cache dir)"

- uses: actions/cache@v3
- uses: actions/cache@v4
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ dist
# DynamoDB Local files
.dynamodb/

# Redis
*.rdb

# TernJS port file
.tern-port

Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:14
FROM node:20-alpine3.18
WORKDIR /usr/src/app
COPY package.json yarn.lock ./
RUN yarn
Expand Down
5 changes: 5 additions & 0 deletions src/advertising/external-ads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ const PERMANENT_AD_PLACES_RULES_KEY = 'permanent_ad_places_rules';
const PERMANENT_NATIVE_AD_PLACES_RULES_KEY = 'permanent_native_ad_places_rules';
const REPLACE_ADS_URLS_BLACKLIST_KEY = 'replace_ads_urls_blacklist';
const ELEMENTS_TO_HIDE_OR_REMOVE_KEY = 'elements_to_hide_or_remove';
const AD_PROVIDERS_CATEGORIES_KEY = 'ad_providers_categories';

export const adPlacesRulesMethods = objectStorageMethodsFactory<AdPlacesRule[]>(AD_PLACES_RULES_KEY, []);

Expand Down Expand Up @@ -205,6 +206,10 @@ export const elementsToHideOrRemoveMethods = objectStorageMethodsFactory<Element
[]
);

export const adProvidersCategoriesMethods = objectStorageMethodsFactory<string[]>(AD_PROVIDERS_CATEGORIES_KEY, [
'crypto'
]);

export const getAdProvidersForAllSites = async () => redisClient.smembers(AD_PROVIDERS_ALL_SITES_KEY);

export const addAdProvidersForAllSites = async (providers: string[]) =>
Expand Down
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@
import { getAliceBobOrderInfo } from './utils/alice-bob/get-alice-bob-order-info';
import { getAliceBobPairInfo } from './utils/alice-bob/get-alice-bob-pair-info';
import { getAliceBobPairsInfo } from './utils/alice-bob/get-alice-bob-pairs-info';
import { btcExchangeRateProvider, tezExchangeRateProvider } from './utils/coingecko';
import { CodedError } from './utils/errors';
import { coinGeckoTokens } from './utils/gecko-tokens';
import { getExternalApiErrorPayload, isDefined, isNonEmptyString } from './utils/helpers';
import logger from './utils/logger';
import { getSignedMoonPayUrl } from './utils/moonpay/get-signed-moonpay-url';
import { getSigningNonce } from './utils/signing-nonce';
import SingleQueryDataProvider from './utils/SingleQueryDataProvider';
import { tezExchangeRateProvider } from './utils/tezos';
import { getExchangeRates } from './utils/tokens';

const PINO_LOGGER = {
Expand Down Expand Up @@ -156,7 +156,7 @@
await redisClient.lpush('notifications', JSON.stringify(newNotification));

res.status(200).send({ message: 'Notification added successfully', notification: newNotification });
} catch (error: any) {

Check warning on line 159 in src/index.ts

View workflow job for this annotation

GitHub Actions / Checks if ts and lint works

Unexpected any. Specify a different type
res.status(500).send({ error: error.message });
}
});
Expand All @@ -175,6 +175,7 @@
});

app.get('/api/exchange-rates/tez', makeProviderDataRequestHandler(tezExchangeRateProvider));
app.get('/api/exchange-rates/btc', makeProviderDataRequestHandler(btcExchangeRateProvider));

app.get('/api/exchange-rates', async (_req, res) => {
const tokensExchangeRates = await getExchangeRates();
Expand Down Expand Up @@ -342,7 +343,7 @@
await startMagicSquareQuest(req.body);

res.status(200).send({ message: 'Quest successfully started' });
} catch (error: any) {

Check warning on line 346 in src/index.ts

View workflow job for this annotation

GitHub Actions / Checks if ts and lint works

Unexpected any. Specify a different type
console.error(error);

if (error instanceof CodedError) {
Expand All @@ -356,7 +357,7 @@
app.get('/api/magic-square-quest/participants', basicAuth, async (req, res) => {
try {
res.status(200).send(await getMagicSquareQuestParticipants());
} catch (error: any) {

Check warning on line 360 in src/index.ts

View workflow job for this annotation

GitHub Actions / Checks if ts and lint works

Unexpected any. Specify a different type
console.error(error);

if (error instanceof CodedError) {
Expand All @@ -370,10 +371,10 @@
app.get('/api/signing-nonce', (req, res) => {
try {
const pkh = req.query.pkh;
if (!pkh || typeof pkh !== 'string') throw new Error('PKH is not a string');

Check warning on line 374 in src/index.ts

View workflow job for this annotation

GitHub Actions / Checks if ts and lint works

Unexpected value in conditional. A boolean expression is required

res.status(200).send(getSigningNonce(pkh));
} catch (error: any) {

Check warning on line 377 in src/index.ts

View workflow job for this annotation

GitHub Actions / Checks if ts and lint works

Unexpected any. Specify a different type
console.error(error);

if (error instanceof CodedError) {
Expand Down
84 changes: 82 additions & 2 deletions src/routers/slise-ad-rules/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
adProvidersByDomainRulesMethods,
AdProviderSelectorsRule,
filterRules,
AdProvidersByDomainRule
AdProvidersByDomainRule,
adProvidersCategoriesMethods
} from '../../advertising/external-ads';
import { basicAuth } from '../../middlewares/basic-auth.middleware';
import { addObjectStorageMethodsToRouter, withBodyValidation, withExceptionHandler } from '../../utils/express-helpers';
Expand All @@ -18,7 +19,8 @@ import {
nonEmptyStringsListSchema,
hostnamesListSchema,
adProvidersByDomainsRulesDictionarySchema,
adProvidersDictionarySchema
adProvidersDictionarySchema,
adProvidersCategoriesDictionarySchema
} from '../../utils/schemas';

/**
Expand Down Expand Up @@ -116,6 +118,12 @@ import {
* type: array
* items:
* $ref: '#/components/schemas/AdProvidersInputValue'
* AdProvidersCategoriesDictionary:
* type: object
* additionalProperties:
* type: array
* items:
* type: string
*/

export const adProvidersRouter = Router();
Expand Down Expand Up @@ -328,6 +336,78 @@ addObjectStorageMethodsToRouter<AdProvidersByDomainRule[]>(adProvidersRouter, {
objectTransformFn: identity
});

/**
* @swagger
* /api/slise-ad-rules/providers/categories:
* get:
* summary: Get categories for providers
* tags:
* - Known ads providers
* responses:
* '200':
* description: Provider - categories dictionary
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/AdProvidersCategoriesDictionary'
* '500':
* $ref: '#/components/responses/ErrorResponse'
* post:
* summary: Upsert categories for providers
* tags:
* - Known ads providers
* security:
* - basicAuth: []
* requestBody:
* description: Provider - categories dictionary
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/AdProvidersCategoriesDictionary'
* responses:
* '200':
* $ref: '#/components/responses/SuccessResponse'
* '400':
* $ref: '#/components/responses/ErrorResponse'
* '401':
* $ref: '#/components/responses/UnauthorizedError'
* '500':
* $ref: '#/components/responses/ErrorResponse'
* delete:
* summary: Delete categories for providers
* tags:
* - Known ads providers
* security:
* - basicAuth: []
* requestBody:
* description: List of provider IDs for which categories should be deleted
* content:
* application/json:
* schema:
* type: array
* items:
* type: string
* responses:
* '200':
* $ref: '#/components/responses/SuccessResponse'
* '400':
* $ref: '#/components/responses/ErrorResponse'
* '401':
* $ref: '#/components/responses/UnauthorizedError'
* '500':
* $ref: '#/components/responses/ErrorResponse'
*/
addObjectStorageMethodsToRouter<string[]>(adProvidersRouter, {
path: '/categories',
methods: adProvidersCategoriesMethods,
keyName: 'providerId',
objectValidationSchema: adProvidersCategoriesDictionarySchema,
keysArrayValidationSchema: nonEmptyStringsListSchema,
successfulRemovalMessage: entriesCount => `${entriesCount} entries have been removed`,
valueTransformFn: identity,
objectTransformFn: identity
});

/**
* @swagger
* /api/slise-ad-rules/providers/negative-selectors:
Expand Down
31 changes: 30 additions & 1 deletion src/utils/coingecko.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { range } from './helpers';
import { AxiosError } from 'axios';

import { isDefined, range } from './helpers';
import logger from './logger';
import { makeBuildQueryFn } from './makeBuildQueryFn';
import SingleQueryDataProvider from './SingleQueryDataProvider';

Expand Down Expand Up @@ -89,3 +92,29 @@ export const getMarketsBySymbols = async (symbols: string[]) => {

return chunks.flat();
};

const createCoingeckoExchangeRateProvider = (tokenSymbol: string) =>
new SingleQueryDataProvider(60000, async () => {
try {
const [market] = await getMarketsBySymbols([tokenSymbol]);

return market.current_price;
} catch (e) {
if (!(e instanceof AxiosError)) {
logger.error(`Request for ${tokenSymbol} exchange rate failed with unknown error`);
} else if (isDefined(e.response) && isDefined(e.response.data)) {
logger.error(
`Request for ${tokenSymbol} exchange rate failed with status ${e.response.status} and message ${e.response.data}`
);
} else if (isDefined(e.response) && isDefined(e.response.status)) {
logger.error(`Request for ${tokenSymbol} exchange rate failed with status ${e.response.status}`);
} else {
logger.error(`Request for ${tokenSymbol} exchange rate failed without response`);
}

throw e;
}
});

export const tezExchangeRateProvider = createCoingeckoExchangeRateProvider('xtz');
export const btcExchangeRateProvider = createCoingeckoExchangeRateProvider('btc');
2 changes: 1 addition & 1 deletion src/utils/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
type StatusCodeNumber = (typeof StatusCodes)[keyof typeof StatusCodes];

export class CodedError extends Error {
constructor(public code: StatusCodeNumber | number, message: string, public errorCode?: string) {
constructor(public code: StatusCodeNumber, message: string, public errorCode?: string) {
super(message);
}

buildResponse() {
const res: CodedErrorForResponse = { message: this.message };
if (this.errorCode) res.code = this.errorCode;

Check warning on line 17 in src/utils/errors.ts

View workflow job for this annotation

GitHub Actions / Checks if ts and lint works

Unexpected nullable string value in conditional. Please handle the nullish/empty cases explicitly

return res;
}
Expand Down
5 changes: 5 additions & 0 deletions src/utils/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,11 @@
elementsToMeasureSelectors: objectSchema()
.shape({ width: cssSelectorSchema.clone(), height: cssSelectorSchema.clone() })
.test('all-fields-present', function (value: unknown) {
if (!value || typeof value !== 'object') {

Check warning on line 180 in src/utils/schemas.ts

View workflow job for this annotation

GitHub Actions / Checks if ts and lint works

Unexpected any value in conditional. An explicit comparison or type cast is required
return true;
}

if (typeof (value as any).width === 'string' && typeof (value as any).height === 'string') {

Check warning on line 184 in src/utils/schemas.ts

View workflow job for this annotation

GitHub Actions / Checks if ts and lint works

Unexpected any. Specify a different type
return true;
}

Expand Down Expand Up @@ -278,3 +278,8 @@
hostnameSchema.clone().required(),
arraySchema().of(elementsToHideOrRemoveEntrySchema.clone().required()).required()
).required();

export const adProvidersCategoriesDictionarySchema = makeDictionarySchema<string[]>(
nonEmptyStringSchema.clone().required(),
arraySchema().of(nonEmptyStringSchema.clone().required()).required()
).required();
29 changes: 0 additions & 29 deletions src/utils/tezos.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
import { compose, MichelCodecPacker, Signer, TezosToolkit } from '@taquito/taquito';
import { tzip12 } from '@taquito/tzip12';
import { tzip16 } from '@taquito/tzip16';
import { AxiosError } from 'axios';
import memoizee from 'memoizee';

import { getMarketsBySymbols } from './coingecko';
import { isDefined } from './helpers';
import logger from './logger';
import SingleQueryDataProvider from './SingleQueryDataProvider';
import { BcdTokenData } from './tzkt';

const RPC_URL = process.env.RPC_URL ?? 'https://mainnet-node.madfish.solutions';
Expand Down Expand Up @@ -54,30 +49,6 @@ export const getStorage = memoizee(
{ promise: true, maxAge: 30000 }
);

const getTezExchangeRate = async () => {
try {
const [xtzMarket] = await getMarketsBySymbols(['xtz']);

return xtzMarket.current_price;
} catch (e) {
if (!(e instanceof AxiosError)) {
logger.error('Request for TEZ exchange rate failed with unknown error');
} else if (isDefined(e.response) && isDefined(e.response.data)) {
logger.error(
`Request for TEZ exchange rate failed with status ${e.response.status} and message ${e.response.data}`
);
} else if (isDefined(e.response) && isDefined(e.response.status)) {
logger.error(`Request for TEZ exchange rate failed with status ${e.response.status}`);
} else {
logger.error('Request for TEZ exchange rate failed without response');
}

throw e;
}
};

export const tezExchangeRateProvider = new SingleQueryDataProvider(60000, getTezExchangeRate);

export class MetadataParseError extends Error {}

export const getTokenMetadata = memoizee(
Expand Down
2 changes: 1 addition & 1 deletion src/utils/tokens.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { BigNumber } from 'bignumber.js';

import { redisClient } from '../redis';
import { tezExchangeRateProvider } from './coingecko';
import { isDefined } from './helpers';
import logger from './logger';
import SingleQueryDataProvider, { SingleQueryDataProviderState } from './SingleQueryDataProvider';
import { tezExchangeRateProvider } from './tezos';
import {
getThreeRouteExchangeRates,
getThreeRouteTokens,
Expand Down
Loading