Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
107 commits
Select commit Hold shift + click to select a range
79f6f83
fix: workflow build-and-push
NatalieShaked Jan 5, 2025
1c79634
feat: feedback-api sends response when no result was chosen
NatalieShaked Jan 9, 2025
2c62d42
fix: test worker was still running when test ended
NatalieShaked Jan 9, 2025
68717a9
chore: put the redis subscriber in its own file for later testing
NatalieShaked Jan 9, 2025
97a31f9
feat: saved geocoding response in seperate redis index
NatalieShaked Jan 12, 2025
66ddcab
test: added integrations tests
NatalieShaked Jan 15, 2025
0cd9021
fix: devided redis into two clients and fixed tests
NatalieShaked Jan 16, 2025
4aab75d
chore: removed unused imports and unnecessary comments
NatalieShaked Jan 16, 2025
bb3c90d
chore: removed unnecessary comment
NatalieShaked Jan 16, 2025
8aeccf6
test: added test timeout in test config
NatalieShaked Jan 16, 2025
2aa02bf
fix: PR tests
NatalieShaked Jan 16, 2025
5fa1dcd
fix: PR tests
NatalieShaked Jan 16, 2025
d3327c5
fix: PR tests
NatalieShaked Jan 16, 2025
2ee8bcb
fix: PR tests
NatalieShaked Jan 16, 2025
f9a7514
fix: PR tests
NatalieShaked Jan 16, 2025
fb5ebd8
fix: PR tests
NatalieShaked Jan 16, 2025
f1d8a5d
fix: PR tests
NatalieShaked Jan 16, 2025
6f2d186
fix: PR tests
NatalieShaked Jan 19, 2025
dea432c
fix: PR tests
NatalieShaked Jan 19, 2025
512858a
fix: PR tests
NatalieShaked Jan 19, 2025
473f4fe
fix: PR tests
NatalieShaked Jan 19, 2025
a5dfc29
fix: PR tests
NatalieShaked Jan 19, 2025
e8ee169
fix: PR tests
NatalieShaked Jan 19, 2025
32f7267
fix: PR tests
NatalieShaked Jan 19, 2025
a0774e7
fix: PR tests
NatalieShaked Jan 19, 2025
766659a
fix: PR tests
NatalieShaked Jan 19, 2025
c9a4f59
fix: PR tests
NatalieShaked Jan 19, 2025
3bbe07d
fix: PR tests
NatalieShaked Jan 19, 2025
a7b098c
fix: PR tests
NatalieShaked Jan 19, 2025
58b56e9
fix: PR tests
NatalieShaked Jan 19, 2025
c354faf
fix: PR tests
NatalieShaked Jan 19, 2025
2deda63
fix: PR tests
NatalieShaked Jan 19, 2025
60f13ca
fix: PR tests
NatalieShaked Jan 19, 2025
0313394
fix: PR tests
NatalieShaked Jan 19, 2025
dbeba6b
fix: PR tests
NatalieShaked Jan 19, 2025
0c6ada9
fix: PR tests
NatalieShaked Jan 19, 2025
3d73b61
fix: PR tests
NatalieShaked Jan 19, 2025
458be8e
fix: PR tests
NatalieShaked Jan 19, 2025
01438b0
fix: PR tests
NatalieShaked Jan 19, 2025
f887eee
fix: PR tests
NatalieShaked Jan 19, 2025
67f14d1
fix: PR tests
NatalieShaked Jan 19, 2025
1506ae2
fix: PR tests
NatalieShaked Jan 19, 2025
15bf274
fix: PR tests
NatalieShaked Jan 19, 2025
ec08e86
ci: fixed pull_request
NatalieShaked Jan 19, 2025
191574b
ci: fixed pull_request
NatalieShaked Jan 19, 2025
7fefa45
ci: fixed pull_request
NatalieShaked Jan 19, 2025
91f19cf
ci: fixed pull_request
NatalieShaked Jan 19, 2025
843093c
ci: fixed pull_request
NatalieShaked Jan 19, 2025
95c6576
ci: fixed pull_request
NatalieShaked Jan 21, 2025
af45c02
ci: fixed pull_request
NatalieShaked Jan 21, 2025
f6a4c12
ci: fixed pull_request
NatalieShaked Jan 21, 2025
de1a3a2
ci: fixed pull_request
NatalieShaked Jan 21, 2025
5867b63
ci: fixed pull_request
NatalieShaked Jan 21, 2025
a509ce3
ci: fixed pull_request
NatalieShaked Jan 21, 2025
4d350da
ci: fixed pull_request
NatalieShaked Jan 21, 2025
b8e2e59
ci: fixed pull_request
NatalieShaked Jan 21, 2025
3e07dbd
ci: fixed pull_request
NatalieShaked Jan 21, 2025
e322797
ci: fixed pull_request
NatalieShaked Jan 21, 2025
23ba4e2
ci: fixed pull_request
NatalieShaked Jan 21, 2025
0fe58eb
ci: fixed pull_request
NatalieShaked Jan 21, 2025
dd4a544
ci: fixed pull_request
NatalieShaked Jan 21, 2025
68f894f
fix: updated healthcheck
NatalieShaked Jan 21, 2025
200f52e
ci: fixed pull_request
NatalieShaked Jan 21, 2025
e0479e1
ci: fixed pull_request
NatalieShaked Jan 21, 2025
cbb4b7a
ci: fixed pull_request
NatalieShaked Jan 21, 2025
79c1bf0
ci: fixed pull_request
NatalieShaked Jan 21, 2025
d7069a1
ci: fixed pull_request
NatalieShaked Jan 21, 2025
af5cf92
ci: fixed pull_request
NatalieShaked Jan 21, 2025
42bcf02
ci: fixed pull_request
NatalieShaked Jan 21, 2025
cc6255c
ci: fixed pull_request
NatalieShaked Jan 21, 2025
da8887b
ci: fixed pull_request
NatalieShaked Jan 21, 2025
4a4f49a
ci: fixed pull_request
NatalieShaked Jan 21, 2025
5c23e9a
ci: fixed pull_request
NatalieShaked Jan 21, 2025
3745ea3
ci: fixed pull_request
NatalieShaked Jan 21, 2025
71de698
ci: fixed pull_request
NatalieShaked Jan 21, 2025
b736a56
fix: removed unused dependency
NatalieShaked Jan 21, 2025
34e7a77
ci: fixed pull_request
NatalieShaked Jan 21, 2025
a4940f8
ci: fixed pull_request
NatalieShaked Jan 21, 2025
5c3f086
ci: fixed pull_request
NatalieShaked Jan 21, 2025
e7e5749
ci: fixed pull_request
NatalieShaked Jan 21, 2025
08f74b8
ci: fixed pull_request
NatalieShaked Jan 21, 2025
1d3688c
ci: fixed pull_request
NatalieShaked Jan 21, 2025
a716af7
ci: fixed pull_request
NatalieShaked Jan 21, 2025
963a2ef
ci: fixed pull_request
NatalieShaked Jan 21, 2025
c78336d
ci: fixed pull_request
NatalieShaked Jan 21, 2025
d68f5ab
ci: fixed pull_request
NatalieShaked Jan 21, 2025
1b6b547
ci: fixed pull_request
NatalieShaked Jan 21, 2025
221d70d
ci: fixed pull_request
NatalieShaked Jan 21, 2025
6f0f75d
ci: fixed pull_request
NatalieShaked Jan 21, 2025
711224f
ci: fixed pull_request
NatalieShaked Jan 21, 2025
ba85ac3
ci: fixed pull_request
NatalieShaked Jan 21, 2025
e18b2f6
ci: fixed pull_request
NatalieShaked Jan 21, 2025
2bc15d9
ci: fixed pull_request
NatalieShaked Jan 21, 2025
f9cacc9
ci: fixed pull_request
NatalieShaked Jan 21, 2025
d33aace
ci: fixed pull_request
NatalieShaked Jan 21, 2025
d568270
ci: fixed pull_request
NatalieShaked Jan 21, 2025
eeb8e73
ci: fixed pull_request
NatalieShaked Jan 21, 2025
2f6cee4
ci: fixed pull_request
NatalieShaked Jan 21, 2025
a1f0db0
ci: fixed pull_request
NatalieShaked Jan 21, 2025
b1da996
ci: PR comments
NatalieShaked Jan 28, 2025
b58e581
fix: PR comments
NatalieShaked Jan 28, 2025
0f43e1d
fix: PR comments - united redis injections
NatalieShaked Feb 2, 2025
93f330b
test: PR comment removed the use of type any
NatalieShaked Feb 3, 2025
8258b86
docs: added documentation to README.md
NatalieShaked Feb 3, 2025
78ab668
docs: created a public link for the worflow image and removed the ima…
NatalieShaked Feb 3, 2025
50e2027
chore: PR comment removed unnecessary return boolean
NatalieShaked Feb 3, 2025
2e6395c
fix: build-and-push workflow
NatalieShaked Feb 9, 2025
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
41 changes: 16 additions & 25 deletions .github/workflows/build-and-push.yaml
Original file line number Diff line number Diff line change
@@ -1,32 +1,23 @@
name: Publish Release
name: Build and push artifacts

on:
push:
tags:
- 'v*'
- "v*"

jobs:

build_and_test:
runs-on: ubuntu-latest
workflow_dispatch:
inputs:
version:
required: true
type: string
env:
HELM_EXPERIMENTAL_OCI: 1

steps:
- name: Azure Pipelines Action
uses: Azure/pipelines@v1
with:
azure-devops-project-url: https://dev.azure.com/Libot-Mipui-Org/osm-sync-tracker
azure-pipeline-name: 'build-and-push-to-ACR'
azure-devops-token: ${{ secrets.AZURE_DEVOPS_TOKEN }}
permissions:
contents: write
pull-requests: write

publish_release:
runs-on: ubuntu-latest
steps:
- name: Checkout code for CHANGELOG.md
uses: actions/checkout@v2

- name: Publish Release to Github
uses: softprops/action-gh-release@v1
with:
body_path: CHANGELOG.md
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
jobs:
build_and_push_docker:
uses: MapColonies/shared-workflows/.github/workflows/build-and-push-docker.yaml@v2
secrets: inherit
54 changes: 6 additions & 48 deletions .github/workflows/pull_request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ jobs:
uses: wearerequired/lint-action@v1
with:
github_token: ${{ secrets.github_token }}
# Enable linters
eslint: true
prettier: true
eslint_extensions: ts
Expand All @@ -38,46 +37,17 @@ jobs:
tests:
name: Run Tests
runs-on: ubuntu-latest
container: node:16

services:
# Label used to access the service container
redis:
# Docker Hub image
image: bitnami/redis:6.2.7
# Provide the password for postgres
image: redis:7.2.3
env:
REDIS_ENABLE_SSL_AUTH: false
REDIS_DATABASE: 1
ALLOW_EMPTY_PASSWORD: yes
ports:
- 6379:6379

# Set health checks to wait until postgres has started
options: >-
--health-cmd "redis-cli ping || exit 1"
--health-interval 10s
--health-timeout 5s
--health-retries 5

zookeeper:
image: bitnami/zookeeper:3.8.0
ports:
- 2181:2181
env:
ALLOW_ANONYMOUS_LOGIN: yes

kafka:
image: bitnami/kafka:3.8.0
ports:
- 9092:9092
env:
KAFKA_BROKER_ID: 1
KAFKA_CFG_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_CFG_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092
ALLOW_PLAINTEXT_LISTENER: yes
options: >-
--health-cmd "kafka-topics.sh --bootstrap-server localhost:9092 --list || exit 1"
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
Expand All @@ -98,22 +68,10 @@ jobs:
- name: Install Node.js dependencies
run: npm ci

- name: Install Docker
run: |
apt-get update
apt-get install -y docker.io

- name: Wait for Kafka
run: |
until docker exec $(docker ps -qf "name=kafka") kafka-topics.sh --list --bootstrap-server localhost:9092; do
echo "Waiting for Kafka to be ready...";
sleep 5;
done

- name: Create Kafka testTopic
- name: Create Redis PubSub
run: |
docker exec $(docker ps -qf "name=kafka") \
kafka-topics.sh --create --topic testTopic --bootstrap-server localhost:9092
docker exec $(docker ps -qf "name=redis") \
redis-cli config set notify-keyspace-events KEA

- name: Run tests
run: npm run test
Expand Down Expand Up @@ -142,4 +100,4 @@ jobs:
uses: actions/checkout@v2

- name: build Docker image
run: docker build -t test-build:latest .
run: docker build -t test-build:latest .
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
# Feedback-Api
This is an API that connects to MapColonies geocoding service, and get's feedback from the users.
Geocoding's feedback API collects usage data from the [Geocoding API](https://github.com/MapColonies/Geocoding) user's response and stores it for BI purposes. We use this data to better understand and measure the relevance of our responses and adjust the data and algorithm accordingly.

## API
Checkout the OpenAPI spec [here](/openapi3.yaml)

## Workflow
![workflow img](https://github.com/user-attachments/assets/7ed767ad-02dd-46f3-9de7-d2be72205e2c)

## How does it work?
Once a Geocoding user searches for something using Geocoding's api, it enters Redis (in the geocoding DB index), and an event is triggered. This event adds the `requestId` (from geocoding) to a different Redis DB with a ttl of 5 minutes (TTL DB index).<br/><br/>
When the Geocoding user chooses a result, the requesting system will then send the `request_id`, the `chosen_response_id`, and the `user_id` back to us using the [Feedback api](/openapi3.yaml).<br/>
Once we get the chosen response from the api, we validate it and make sure it exists in the Redis geocoding DB, and once it is validated we add a `wasUsed = true` parameter to the geocoding response (in Redis) for later use. We then send the response to Kafka (where it will later be enriched and added to elastic -> see [Geocoding Enrichment](https://github.com/MapColonies/geocoding-enrichment) for more details).<br/>
Once the TTL of the `requestId` expires an even is triggered, and there are two options:<br/>
1. One or more responses were chosen.
2. No response was chosen (only searched for).

We now go back to the `wasUsed` parameter from earlier, and check to see its value (or even if it exists).<br/>
- If `wasUsed = true` we know from earlier that this response was already chosen and sent to Kafka previously, therefore we can remove it from the Redis geocoding DB index.
- If `wasUsed` equals to one of the following: `false`, `null` or `undefined` then we create a new feedback response and make the `chosenResultId` `null`, and then send this response to Kafka, and delete the request from the Redis geocoding DB index.

## Installation
Install deps with npm

Expand Down
8 changes: 6 additions & 2 deletions config/custom-environment-variables.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,12 @@
"key": "REDIS_KEY_PATH",
"cert": "REDIS_CERT_PATH"
},
"database": {
"__name": "REDIS_DATABASE",
"databases": {
"geocodingIndex": "REDIS_GEOCODING_INDEX",
"ttlIndex": "REDIS_TTL_INDEX"
},
"ttl": {
"__name": "REDIS_TTL",
"__format": "number"
}
},
Expand Down
6 changes: 5 additions & 1 deletion config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@
"key": "",
"cert": ""
},
"database": 0
"databases": {
"geocodingIndex": 0,
"ttlIndex": 1
},
"ttl": 300
},
"kafka": {
"brokers": "KAFKA_URL:9092",
Expand Down
8 changes: 6 additions & 2 deletions config/test.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"redis": {
"host": "redis",
"host": "localhost",
"port": 6379,
"username": "",
"password": "",
Expand All @@ -10,7 +10,11 @@
"key": "",
"cert": ""
},
"database": 1
"databases": {
"geocodingIndex": 2,
"ttlIndex": 3
},
"ttl": 2
},
"kafka": {
"brokers": "kafka:9092"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"main": "./src/index.ts",
"scripts": {
"test:unit": "jest --config=./tests/configurations/unit/jest.config.js",
"test:integration": "jest --config=./tests/configurations/integration/jest.config.js --forceExit",
"test:integration": "jest --config=./tests/configurations/integration/jest.config.js",
"format": "prettier --check .",
"format:fix": "prettier --write .",
"prelint:fix": "npm run format:fix",
Expand Down
9 changes: 6 additions & 3 deletions src/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@ export const IGNORED_OUTGOING_TRACE_ROUTES = [/^.*\/v1\/metrics.*$/];
export const IGNORED_INCOMING_TRACE_ROUTES = [/^.*\/docs.*$/];

/* eslint-disable @typescript-eslint/naming-convention */
export const SERVICES: Record<string, symbol> = {
export const SERVICES = {
LOGGER: Symbol('Logger'),
CONFIG: Symbol('Config'),
TRACER: Symbol('Tracer'),
METER: Symbol('Meter'),
REDIS: Symbol('Redis'),
GEOCODING_REDIS: Symbol('GeocodingRedis'),
TTL_REDIS: Symbol('TTLRedis'),
KAFKA: Symbol('Kafka'),
};
} satisfies Record<string, symbol>;
/* eslint-enable @typescript-eslint/naming-convention */

export const ON_SIGNAL = Symbol('onSignal');
export const HEALTHCHECK = Symbol('healthcheck');
export const CLEANUP_REGISTRY = Symbol('cleanupRegistry');
export const REDIS_SUB = Symbol('redisSubClient');
export const REDIS_CLIENT_FACTORY = Symbol('redisClientFactory');
4 changes: 4 additions & 0 deletions src/common/dependencyRegistration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface InjectionObject<T> {
provider: Providers<T>;
options?: RegistrationOptions;
postInjectionHook?: (container: DependencyContainer) => Promise<void>;
afterAllInjectionHook?: (container: DependencyContainer) => void | Promise<void>;
}

export const registerDependencies = async (
Expand All @@ -23,5 +24,8 @@ export const registerDependencies = async (
await injectionObj.postInjectionHook?.(container);
}

for (const dep of dependencies) {
await dep.afterAllInjectionHook?.(container);
}
return container;
};
6 changes: 4 additions & 2 deletions src/common/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export type RedisConfig = {
port: number;
enableSslAuth: boolean;
sslPaths: { ca: string; cert: string; key: string };
databases: { geocodingIndex: number; ttlIndex: number };
} & RedisClientOptions;

export type KafkaOptions = {
Expand All @@ -28,16 +29,17 @@ export type KafkaOptions = {

export interface FeedbackResponse {
requestId: string;
chosenResultId: number;
chosenResultId: number | null;
userId: string;
responseTime: Date; // from FeedbackApi
geocodingResponse: GeocodingResponse;
}

export interface GeocodingResponse {
userId: string;
userId?: string;
apiKey: string;
site: string;
response: JSON;
respondedAt: Date; // from Geocoding
wasUsed?: boolean;
}
42 changes: 31 additions & 11 deletions src/common/utils.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,34 @@
import { TimeoutError } from './errors';
import { DependencyContainer, FactoryFunction } from 'tsyringe';
import { Logger } from '@map-colonies/js-logger';
import { RedisClient } from '../redis/index';
import { SERVICES } from './constants';

export const promiseTimeout = async <T>(ms: number, promise: Promise<T>): Promise<T> => {
// create a promise that rejects in <ms> milliseconds
const timeout = new Promise<T>((_, reject) => {
const id = setTimeout(() => {
clearTimeout(id);
reject(new TimeoutError(`Timed out in + ${ms} + ms.`));
}, ms);
});
export const healthCheckFactory: FactoryFunction<void> = (container: DependencyContainer): void => {
const logger = container.resolve<Logger>(SERVICES.LOGGER);
const geocodingRedis = container.resolve<RedisClient>(SERVICES.GEOCODING_REDIS);
const ttlRedis = container.resolve<RedisClient>(SERVICES.TTL_REDIS);

// returns a race between our timeout and the passed in promise
return Promise.race([promise, timeout]);
geocodingRedis
.ping()
.then(() => {
return;
})
.catch((error: Error) => {
logger.error({
message: `Healthcheck failed for GeocodingRedis.`,
error,
});
});

ttlRedis
.ping()
.then(() => {
return;
})
.catch((error: Error) => {
logger.error({
message: `Healthcheck failed for GeocodingRedis.`,
error,
});
});
};
Loading