Skip to content
Open
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
68 changes: 68 additions & 0 deletions afapast/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Typescript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env
.env.bat
.env.mcm

# Transpiled JavaScript files from Typescript
/dist

# Cache used by TypeScript's incremental build
*.tsbuildinfo

.scannerwork
34 changes: 34 additions & 0 deletions afapast/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Check out https://hub.docker.com/_/node to select a new base image
FROM docker.io/library/node:20

# Install Python, make, g++, and other build tools required by node-gyp
RUN apt-get update && \
apt-get install -y python3 make g++ && \
ln -s /usr/bin/python3 /usr/bin/python

# Set to a non-root built-in user `node`
USER node

# Create app directory (with user `node`)
RUN mkdir -p /home/node/app

WORKDIR /home/node/app

# Install app dependencies

# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
COPY --chown=node package*.json ./

RUN npm install

# Bundle app source code
COPY --chown=node . .

RUN npm run build

# Bind to all network interfaces so that it can be mapped to the host OS
ENV HOST=0.0.0.0 PORT=3002

EXPOSE ${PORT}
CMD [ "node", "." ]
48 changes: 48 additions & 0 deletions afapast/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Afapast

A voucher distribution tool, reading mob subscription API and sending emails with vouchers.

Also an API in itself to manage tracked incentives and vouchers.

## Dependencies

Tested on node v20.18.0. Install deps with
```sh
npm install
```


## Run the application

Make sure to have the DB env variables set.

Migrate the db if it's the first time.
```sh
yarn migrate
```

```sh
yarn start
```

Open http://127.0.0.1:3002 in your browser.

## Env variables

| Variables | Description | Mandatory | Default value if unspecified |
| ------------------------ | ------------------------------------------------------------ | ----------- | ---------------------------- |
| HOST | Service host | No | '127.0.0.1' |
| PORT | Service port | No | 3002 |
| API_KEY | Api Key authorizing queries to this project API | No, but recommended | 'apikey' |
| EMAIL_FROM | email used as a sender, only used with NODE_ENV='production' | Only in production | |
| EMAIL_HOST | smtp host, only used with NODE_ENV='production' | Only in production | |
| EMAIL_PORT | smtp port, only used with NODE_ENV='production' | Only in production | |
| MOB_TOKEN_URL | oidc token url for mob | No | 'http://localhost:9000/auth/realms/mcm/protocol/openid-connect/token' |
| MOB_CLIENT_ID | oidc client id to fetch a token | No | 'simulation-maas-backend' |
| MOB_CLIENT_SECRET | oidc client secret to fetch a token | No | '4x1zfk4p4d7ZdLPAsaWBhd5mu86n5ZWN' |
| MOB_API_SUBSCRIPTION_URL | subscription url | No | 'http://localhost:3000/v1/subscriptions' |
| DB_HOST | database host | No | 'localhost' |
| DB_PORT | database port | No | 5432 |
| DB_SERVICE_USER | database user | No | 'afapast' |
| DB_SERVICE_PASSWORD | database password | No | 'afapast' |
| DB_DATABASE | database name | No | 'afapast_db' |
Binary file added afapast/data/db.sqlite
Binary file not shown.
3 changes: 3 additions & 0 deletions afapast/helper_scripts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Example scripts used to talk to the API.

This folder is unused during deployement.
22 changes: 22 additions & 0 deletions afapast/helper_scripts/add_vouchers_from_csv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import csv
import requests

CSV_FILE = "voucher_csv_exemple.csv"
API = "http://localhost:3002/vouchers"
API_KEY = "apikey"

headers = {
"X-API-Key": API_KEY
}
with open(CSV_FILE, 'r') as csvfile:
reader = csv.DictReader(csvfile, delimiter=',')
for line in reader:
json_params = {
"value": line['Code'],
"amount": line['Value']
}
res = requests.post(API, json=json_params, headers=headers)
if res.status_code < 300:
print(res.json())
else:
print("Error, " + res.text)
55 changes: 55 additions & 0 deletions afapast/helper_scripts/afapast_api_tips.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# coding: utf-8

# This file contains the main examples of API usage as a reminder
# An API explorer is also available at API_HOST/explorer for a complete documentation


import requests

API_HOST = "http://localhost:3002"
API_KEY = ""

headers = {
"X-API-Key": API_KEY
}

# Add a new incentive to track
url = API_HOST + "/tracked-incentives"
json_params = {
incentiveId: "67378718fce6b98d279a0f27", # Incentive id, required
ccContacts: "hello@test.com,hello2@test.com", # Comma separated list of emails, optional
}
res = requests.post(url, json=json_params, headers=headers)
print(res.json())


# Check status of a tracked incentive
url = API_HOST + "/tracked-incentives/67378718fce6b98d279a0f27"
res = requests.get(url, headers=headers)
print(res.json())

# Expected response:
# {
# "id": 1,
# "incentiveId": "67378718fce6b98d279a0f27",
# "lastReadTime": "2021-03-09T15:00:00.000Z", # Last time the subscriptions were checked
# "lastNbSubs": 0, # Total number of VALIDEE subscriptions during the last check
# "nbSubsHandled": 0, # Total number of subscriptions handled
# "ccContacts": "hello@test.com,hello2@test.com"
# }

# Check vouchers usage
url = API_HOST + "/vouchers"
res = requests.get(url, headers=headers)
print(res.json())

# Expected response:
# [{
# "id": 1,
# "value": "4A2NN3ES",
# "amount": "30.00",
# "status": "USED",
# "subscriptionId": "66e32c5974df3754b04faba0",
# "citizenId": "802ac8b1-cde8-42f2-b310-a1c0380018d8",
# "incentiveId": "67378718fce6b98d279a0f27" # Incentive id
# }]
69 changes: 69 additions & 0 deletions afapast/helper_scripts/create_formated_incentive_in_mob.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# coding: utf-8
import requests
import json

url = "http://localhost:3000/v1/incentives"
# There is no real way of getting this token programmatically
# We suggest logging in to the admin interface, and looking in dev tools for the token query to idp auth/realms/mcm/protocol/openid-connect/token
# The response should contain an access_token usable here
token = ""

# These are found in the admin interface
funderId = "318d18d5-0d02-457e-918e-8b272251d1b2"
territoryIds = ["66e328c074df3754b04fab9e"]

title = "DOMICILE- TRAVAIL - prise en charge à 92,5%"
minAmount = "92.5%"
allocatedAmount = "92.5%"

# title = "Forfait liberté - prise en charge à 75%"
# minAmount = "75%"
# allocatedAmount = "75%"

payload = json.dumps({
"title": title,
"description": "Aide Tiers Payant pour l'entreprise Communauté d’Agglomération de La Rochelle\n\nDemande de prise en charge directe de la part employeur, sans avance de frais.\nRèglement du reste à charge par l'employé directement sur la boutique en ligne : boutiqueenligne.fr ou sur l'application 'BOUTIQUE'",
"incentiveType": "AideEmployeur",
"funderId": funderId,
"minAmount": minAmount,
"transportList": [
"transportsCommun",
"velo"
],
"territoryIds": territoryIds,
"allocatedAmount": allocatedAmount,
"conditions": "- être employé de Communauté d’Agglomération de La Rochelle",
"paymentMethod": "- Validation par le gestionnaire \n- Application du droit en une fois directement sur la boutique en ligne\nou\n- Règlement en une fois sous la forme d'un coupon valable sur la boutique en ligne",
"contact": "",
"additionalInfos": "Pour plus d'information, consultez l [aide EN LIGNE](https://moncomptemobilite.fr/) ![image TEST](https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSqQXCfw2Ulfrfe1xG2NGkSe7FOnT0h9AEjcQ&s)",
"isMCMStaff": True,
"subscriptionCheckMode": "MANUEL",
"isCitizenNotificationsDisabled": False,
# "subscriptionLink": "",
"specificFields": [
{
"isRequired": True,
"title": "Type d'abonnement",
"inputFormat": "listeChoix",
"choiceList": {
"possibleChoicesNumber": 1,
"inputChoiceList": [
{
"inputChoice": "Abonnement Yélo seul"
},
{
"inputChoice": "Abonnement Yélo + Vélo Libre Service (VLS) à +5 €"
}
]
}
},
],
})
headers = {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'
}

response = requests.request("POST", url, headers=headers, data=payload)

print(response.json())
2 changes: 2 additions & 0 deletions afapast/helper_scripts/voucher_csv_exemple.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Code,Voucher Type,Value,Discount Type
4A2NN3ES,DISCOUNT_VOUCHER,30.00,AMOUNT
83 changes: 83 additions & 0 deletions afapast/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
{
"name": "afapast",
"version": "0.0.1",
"description": "afapast",
"keywords": [
"loopback-application",
"loopback"
],
"main": "dist/index.js",
"types": "dist/index.d.ts",
"engines": {
"node": "18 || 20 || 22"
},
"scripts": {
"build": "lb-tsc",
"postbuild": "npm run copy-png-files",
"build:watch": "lb-tsc --watch",
"lint": "npm run eslint && npm run prettier:check",
"lint:fix": "npm run eslint:fix && npm run prettier:fix",
"prettier:cli": "lb-prettier \"**/*.ts\" \"**/*.js\"",
"prettier:check": "npm run prettier:cli -- -l",
"prettier:fix": "npm run prettier:cli -- --write",
"eslint": "lb-eslint --report-unused-disable-directives .",
"eslint:fix": "npm run eslint -- --fix",
"pretest": "npm run rebuild",
"test": "lb-mocha --allow-console-logs \"dist/__tests__\"",
"posttest": "npm run lint",
"test:dev": "lb-mocha --allow-console-logs dist/__tests__/**/*.js && npm run posttest",
"docker:build": "docker build -t afapast .",
"docker:run": "docker run -p 3000:3000 -d afapast",
"premigrate": "npm run build",
"migrate": "node ./dist/migrate",
"preopenapi-spec": "npm run build",
"openapi-spec": "node ./dist/openapi-spec",
"prestart": "npm run rebuild",
"start": "node -r source-map-support/register .",
"clean": "lb-clean dist *.tsbuildinfo .eslintcache",
"rebuild": "npm run clean && npm run build",
"copy-png-files": "copyfiles -u 1 src/**/**.png dist/"
},
"repository": {
"type": "git",
"url": ""
},
"author": "TTalex",
"license": "",
"files": [
"README.md",
"dist",
"src",
"!*/__tests__"
],
"dependencies": {
"@loopback/authentication": "^11.0.6",
"@loopback/authentication-jwt": "^0.15.6",
"@loopback/boot": "^7.0.6",
"@loopback/core": "^6.1.3",
"@loopback/cron": "^0.12.6",
"@loopback/repository": "^7.0.6",
"@loopback/rest": "^14.0.6",
"@loopback/rest-explorer": "^7.0.6",
"@loopback/service-proxy": "^7.0.6",
"copyfiles": "^2.4.1",
"ejs": "^3.1.10",
"loopback-connector-postgresql": "^7.1.8",
"nodemailer": "^6.9.15",
"pdfkit": "^0.15.0",
"sqlite3": "^5.1.7",
"tslib": "^2.0.0"
},
"devDependencies": {
"@loopback/build": "^11.0.6",
"@loopback/eslint-config": "^15.0.4",
"@loopback/testlab": "^7.0.6",
"@types/ejs": "^3.1.5",
"@types/node": "^16.18.101",
"@types/nodemailer": "^6.4.16",
"@types/pdfkit": "^0.13.5",
"eslint": "^8.57.0",
"source-map-support": "^0.5.21",
"typescript": "~5.2.2"
}
}
Loading