├── __init__.py ├── media ├── logo.png └── architecture.png ├── .gitmodules ├── utils.py ├── dashboard_setup.py ├── CODE_OF_CONDUCT.md ├── handler_setup.py ├── gateway_setup.py ├── core_setup.py ├── .gitignore ├── notify_setup.py ├── CONTRIBUTING.md ├── README.md ├── LICENSE └── api_doc.json /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /media/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tata1mg/notifyone/HEAD/media/logo.png -------------------------------------------------------------------------------- /media/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tata1mg/notifyone/HEAD/media/architecture.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "notifyone-gateway"] 2 | path = notifyone-gateway 3 | url = https://github.com/tata1mg/notifyone-gateway 4 | [submodule "notifyone-core"] 5 | path = notifyone-core 6 | url = https://github.com/tata1mg/notifyone-core 7 | [submodule "notifyone-dashboard"] 8 | path = notifyone-dashboard 9 | url = https://github.com/tata1mg/notifyone-dashboard.git 10 | [submodule "notifyone-handler"] 11 | path = notifyone-handler 12 | url = https://github.com/tata1mg/notifyone-handler.git 13 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | def extract_values(obj, key): 2 | """Pull all values of specified key from nested JSON.""" 3 | arr = [] 4 | 5 | def extract(obj, arr, key): 6 | """Recursively search for values of key in JSON tree.""" 7 | if isinstance(obj, dict): 8 | for k, v in obj.items(): 9 | if isinstance(v, (dict, list)): 10 | extract(v, arr, key) 11 | elif k == key and v: 12 | arr.append(v) 13 | elif isinstance(obj, list): 14 | for item in obj: 15 | extract(item, arr, key) 16 | return arr 17 | 18 | results = extract(obj, arr, key) 19 | return results -------------------------------------------------------------------------------- /dashboard_setup.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import json 3 | import os 4 | from sys import platform 5 | 6 | 7 | def setup_dashboard(sys_platform, cms_api_endpoint): 8 | print("\nSetting up notifyone-dashboard.....\n") 9 | 10 | os.chdir('notifyone-dashboard') 11 | 12 | _res = subprocess.run(['cp src/config.template.json src/config.json'], shell=True, capture_output=True) 13 | if _res.returncode != 0: 14 | print(str(_res.stderr.decode('utf-8'))) 15 | exit(1) 16 | 17 | with open('./src/config.json', 'r') as c: 18 | _dashboard_config = json.load(c) 19 | _dashboard_port = str(_dashboard_config.get("communicationAppUrlPort") or 8000) 20 | _dashboard_config["serverDomain"] = cms_api_endpoint 21 | # if platform.lower() == 'darwin': 22 | # _dashboard_config["communication"]["serverDomain"] = "http://host.docker.internal:9402" 23 | # _dashboard_config["server"]["base_notification_url"] = "http://host.docker.internal:9402" 24 | with open('./src/config.json', 'w') as f: 25 | json.dump(_dashboard_config, f) 26 | 27 | _stat = subprocess.run(['docker rm --force notifyone-dashboard'], shell=True) 28 | _stat = subprocess.run(["docker image rm notifyone-dashboard"], shell=True) 29 | 30 | _res = subprocess.run(['docker build . --tag notifyone-dashboard --build-arg SERVICE_NAME=notifyone_dashboard --build-arg SYS_PLATFORM={}'.format(sys_platform)], 31 | shell=True, capture_output=True) 32 | if _res.returncode != 0: 33 | print(str(_res.stderr.decode('utf-8'))) 34 | exit(1) 35 | 36 | _res = subprocess.run([ 37 | 'docker run -p ' + _dashboard_port + ':' + _dashboard_port + ' --detach --name notifyone-dashboard notifyone-dashboard'], 38 | shell=True, capture_output=True) 39 | if _res.returncode != 0: 40 | print(str(_res.stderr.decode('utf-8'))) 41 | exit(1) 42 | 43 | print("\nnotifyone-dashboard setup completed\n") 44 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. -------------------------------------------------------------------------------- /handler_setup.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import json 3 | import os 4 | from sys import platform 5 | from utils import extract_values 6 | 7 | def setup_handler(sys_platform): 8 | 9 | print("\nSetting up notifyone-handler.....\n") 10 | 11 | os.chdir('notifyone-handler') 12 | 13 | _res = subprocess.run(['cp config_template.json config.json'], shell=True, capture_output=True) 14 | if _res.returncode != 0: 15 | print(str(_res.stderr.decode('utf-8'))) 16 | exit(1) 17 | 18 | with open('config.json') as c: 19 | _handler_config = json.load(c) 20 | _handler_queue_names = extract_values(_handler_config, "QUEUE_NAME") 21 | _port = str(_handler_config.get("PORT") or 9403) 22 | if platform.lower() == 'darwin': 23 | _handler_config["NOTIFYONE_CORE"]["HOST"] = 'http://host.docker.internal:9402' 24 | _handler_config['SQS_AUTH']['SQS_ENDPOINT_URL'] = 'http://host.docker.internal:4566' 25 | with open('config.json', 'w') as f: 26 | json.dump(_handler_config, f) 27 | elif platform.lower() == "linux": 28 | _handler_config["NOTIFYONE_CORE"]["HOST"] = 'http://notifyone-core:9402' 29 | _handler_config['SQS_AUTH']['SQS_ENDPOINT_URL'] = 'http://localstack-main:4566' 30 | with open('config.json', 'w') as f: 31 | json.dump(_handler_config, f) 32 | 33 | # create local stack queues 34 | for _q in _handler_queue_names: 35 | _res = subprocess.run(["docker exec -i $(docker ps | grep localstack | awk '{print $1}') awslocal sqs create-queue --queue-name " + _q], 36 | shell=True, capture_output=True) 37 | if _res.returncode != 0: 38 | print("\nError in creating queue {}\n".format(_q)) 39 | print(_res.stderr.decode('utf-8')) 40 | else: 41 | pass 42 | 43 | _stat = subprocess.run(['docker rm --force notifyone-handler'], shell=True) 44 | _stat = subprocess.run(["docker image rm notifyone-handler"], shell=True) 45 | 46 | _res = subprocess.run(['docker build . --tag notifyone-handler --build-arg SERVICE_NAME=notifyone_handler --build-arg SYS_PLATFORM={}'.format(sys_platform)], 47 | shell=True, capture_output=True) 48 | if _res.returncode != 0: 49 | print(str(_res.stderr.decode('utf-8'))) 50 | exit(1) 51 | 52 | _res = subprocess.run(['docker run -p ' + _port + ':' + _port + ' --detach --name notifyone-handler notifyone-handler'], 53 | shell=True, capture_output=True) 54 | if _res.returncode != 0: 55 | print(str(_res.stderr.decode('utf-8'))) 56 | exit(1) 57 | 58 | print("\nnotifyone-core setup completed\n") -------------------------------------------------------------------------------- /gateway_setup.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import json 3 | import os 4 | from utils import extract_values 5 | from sys import platform 6 | 7 | 8 | def setup_gateway(sys_platform): 9 | print("\nSetting up notifyone-gateway.....\n") 10 | 11 | os.chdir('notifyone-gateway') 12 | 13 | _res = subprocess.run(['cp config_template.json config.json'], shell=True, capture_output=True) 14 | if _res.returncode != 0: 15 | print(str(_res.stderr.decode('utf-8'))) 16 | exit(1) 17 | 18 | with open('config.json', 'r') as c: 19 | _gateway_config = json.load(c) 20 | _gateway_port = str(_gateway_config.get("PORT") or 9401) 21 | _gateway_queue_names = extract_values(_gateway_config, "QUEUE_NAME") 22 | if platform.lower() == 'darwin': 23 | _gateway_config['NOTIFICATION_SERVICE']['HOST'] = 'http://host.docker.internal:9402' 24 | _gateway_config['TRIGGER_NOTIFICATIONS']['SQS']['SQS_ENDPOINT_URL'] = 'http://host.docker.internal:4566' 25 | with open('config.json', 'w') as f: 26 | json.dump(_gateway_config, f) 27 | elif platform.lower() == "linux": 28 | _gateway_config['NOTIFICATION_SERVICE']['HOST'] = 'http://notifyone-core:9402' 29 | _gateway_config['TRIGGER_NOTIFICATIONS']['SQS']['SQS_ENDPOINT_URL'] = 'http://localstack-main:4566' 30 | with open('config.json', 'w') as f: 31 | json.dump(_gateway_config, f) 32 | 33 | # create local stack queues 34 | for _q in _gateway_queue_names: 35 | _res = subprocess.run(["docker exec -i $(docker ps | grep localstack | awk '{print $1}') awslocal sqs create-queue --queue-name " + _q], 36 | shell=True, capture_output=True) 37 | if _res.returncode != 0: 38 | print("\nError in creating queue {}\n".format(_q)) 39 | print(_res.stderr.decode('utf-8')) 40 | else: 41 | pass 42 | 43 | _stat = subprocess.run(['docker rm --force notifyone-gateway'], shell=True) 44 | _stat = subprocess.run(["docker image rm notifyone-gateway"], shell=True) 45 | 46 | _res = subprocess.run(['docker build . --tag notifyone-gateway --build-arg SERVICE_NAME=notifyone_gateway --build-arg SYS_PLATFORM={}'.format(sys_platform)], 47 | shell=True, capture_output=True) 48 | if _res.returncode != 0: 49 | print(str(_res.stderr.decode('utf-8'))) 50 | exit(1) 51 | 52 | _res = subprocess.run([ 53 | 'docker run -p ' + _gateway_port + ':' + _gateway_port + ' --detach --name notifyone-gateway notifyone-gateway'], 54 | shell=True, capture_output=True) 55 | if _res.returncode != 0: 56 | print(str(_res.stderr.decode('utf-8'))) 57 | exit(1) 58 | 59 | print("\nnotifyone-gateway setup completed\n") -------------------------------------------------------------------------------- /core_setup.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import json 3 | import os 4 | from utils import extract_values 5 | from sys import platform 6 | 7 | def setup_core(sys_platform): 8 | print("\nSetting up notifyone-core....\n") 9 | 10 | os.chdir('notifyone-core') 11 | 12 | _res = subprocess.run(['cp config_template.json config.json'], shell=True, capture_output=True) 13 | if _res.returncode != 0: 14 | print(str(_res.stderr.decode('utf-8'))) 15 | exit(1) 16 | 17 | with open('config.json') as c: 18 | _core_config = json.load(c) 19 | _core_queue_names = extract_values(_core_config, "QUEUE_NAME") 20 | _port = str(_core_config.get("PORT") or 9402) 21 | if platform.lower() == 'darwin': 22 | _core_config['DB_CONNECTIONS']['connections']['default']['credentials']['host'] = 'host.docker.internal' 23 | _core_config['SUBSCRIBE_NOTIFICATION_STATUS_UPDATES']['SQS']['SQS_ENDPOINT_URL'] = 'http://host.docker.internal:4566' 24 | _core_config['DISPATCH_NOTIFICATION_REQUEST']['SQS']['SQS_ENDPOINT_URL'] = 'http://host.docker.internal:4566' 25 | _core_config['NOTIFICATION_REQUEST']['SQS']['SQS_ENDPOINT_URL'] = 'http://host.docker.internal:4566' 26 | _core_config['REDIS_CACHE_HOSTS']['default']['REDIS_HOST'] = 'host.docker.internal' 27 | with open('config.json', 'w') as f: 28 | json.dump(_core_config, f) 29 | elif platform.lower() == "linux": 30 | _core_config['DB_CONNECTIONS']['connections']['default']['credentials']['host'] = 'postgres_notify' 31 | _core_config['SUBSCRIBE_NOTIFICATION_STATUS_UPDATES']['SQS']['SQS_ENDPOINT_URL'] = 'http://localstack-main:4566' 32 | _core_config['DISPATCH_NOTIFICATION_REQUEST']['SQS']['SQS_ENDPOINT_URL'] = 'http://localstack-main:4566' 33 | _core_config['NOTIFICATION_REQUEST']['SQS']['SQS_ENDPOINT_URL'] = 'http://localstack-main:4566' 34 | _core_config['REDIS_CACHE_HOSTS']['default']['REDIS_HOST'] = 'redis_notify' 35 | with open('config.json', 'w') as f: 36 | json.dump(_core_config, f) 37 | 38 | # create local stack queues 39 | for _q in _core_queue_names: 40 | _res = subprocess.run(["docker exec -i $(docker ps | grep localstack | awk '{print $1}') awslocal sqs create-queue --region eu-west-2 --queue-name " + _q], 41 | shell=True, capture_output=True) 42 | if _res.returncode != 0: 43 | print("\nError in creating queue {}\n".format(_q)) 44 | print(_res.stderr.decode('utf-8')) 45 | else: 46 | pass 47 | 48 | _stat = subprocess.run(['docker rm --force notifyone-core'], shell=True) 49 | _stat = subprocess.run(["docker image rm notifyone-core"], shell=True) 50 | 51 | _res = subprocess.run(['docker build . --tag notifyone-core --build-arg SERVICE_NAME=notifyone_core --build-arg SYS_PLATFORM={}'.format(sys_platform)], 52 | shell=True, capture_output=True) 53 | if _res.returncode != 0: 54 | print(str(_res.stderr.decode('utf-8'))) 55 | exit(1) 56 | 57 | _res = subprocess.run(['docker run -p ' + _port + ':' + _port + ' --detach --name notifyone-core notifyone-core'], 58 | shell=True, capture_output=True) 59 | if _res.returncode != 0: 60 | print(str(_res.stderr.decode('utf-8'))) 61 | exit(1) 62 | 63 | _res = subprocess.run(["docker exec -i $(docker ps | grep notifyone-core | awk '{print $1}') python3 database.py upgrade "], 64 | shell=True, capture_output=True) 65 | if _res.returncode != 0: 66 | print("\nError in DB upgrade\n") 67 | print(_res.stderr.decode('utf-8')) 68 | else: 69 | pass 70 | 71 | print("\nnotifyone-core setup completed\n") -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ 161 | -------------------------------------------------------------------------------- /notify_setup.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import os 3 | import sys 4 | from sys import platform 5 | from gateway_setup import setup_gateway 6 | from core_setup import setup_core 7 | from handler_setup import setup_handler 8 | from dashboard_setup import setup_dashboard 9 | 10 | sys_platform = sys.argv[1] if len(sys.argv) > 1 else "linux/amd64" 11 | cms_api_endpoint = sys.argv[2] if len(sys.argv) > 2 else "" 12 | 13 | print("Building for system platform - {}".format(sys_platform)) 14 | 15 | _docker_check_res = subprocess.run(['docker info'], shell=True, capture_output=True) 16 | if _docker_check_res.returncode != 0: 17 | print("Either Docker is not installed or not running\n"+(str(_docker_check_res.stderr.decode('utf-8')))) 18 | exit(1) 19 | 20 | 21 | print("\n Setting up Postgres on Docker\n") 22 | _res = subprocess.run(['docker pull postgres:14.5'], shell=True, capture_output=True) 23 | if _res.returncode != 0: 24 | print(str(_res.stderr.decode('utf-8'))) 25 | exit(1) 26 | 27 | _stat = subprocess.run(['docker rm --force postgres_notify'], shell=True) 28 | _res = subprocess.run(["docker run --detach -p5432:5432 -e POSTGRES_PASSWORD='postgres' -e POSTGRES_USER=postgres --name postgres_notify postgres:14.5"], shell=True, capture_output=True) 29 | if _res.returncode != 0: 30 | print(str(_res.stderr.decode('utf-8'))) 31 | exit(1) 32 | 33 | print("\n Setting up Redis on Docker\n") 34 | _stat = subprocess.run(['docker rm --force redis_notify'], shell=True) 35 | _res = subprocess.run(["docker run --detach --name redis_notify -p 6379:6379 -d redis"], shell=True, capture_output=True) 36 | if _res.returncode != 0: 37 | print(str(_res.stderr.decode('utf-8'))) 38 | exit(1) 39 | 40 | 41 | print("\n Setting up localstack\n") 42 | # install localstack 43 | _res = subprocess.run(['localstack --version'], shell=True, capture_output=True) 44 | if _res.returncode != 0: 45 | print("Localstack not installed \n"+(str(_docker_check_res.stderr.decode('utf-8')))) 46 | print("\n installing localstack \n") 47 | _localstack_res = subprocess.run(['python3 -m pip install --force-reinstall localstack'], shell=True, capture_output=True) 48 | if _localstack_res.returncode != 0: 49 | print(str(_localstack_res.stderr.decode('utf-8'))) 50 | exit(1) 51 | else: 52 | pass 53 | else: 54 | pass 55 | 56 | 57 | 58 | _localstack_start_res = subprocess.run(['localstack start -d'], shell=True, capture_output=True) 59 | if _localstack_start_res.returncode != 0: 60 | print(str(_localstack_start_res.stderr.decode('utf-8'))) 61 | exit(1) 62 | else: 63 | pass 64 | 65 | # print("setup aws default region to eu-west-2") 66 | # _res = subprocess.run(["rm /tmp/config"], 67 | # shell=True, capture_output=True) 68 | # _res = subprocess.run(['touch /tmp/config | echo "[default]" >> /tmp/config | echo "region=eu-west-2" >> /tmp/config'], 69 | # shell=True, capture_output=True) 70 | # if _res.returncode != 0: 71 | # print("\nError in creating localstck aws config file\n") 72 | # print(_res.stderr.decode('utf-8')) 73 | # else: 74 | # pass 75 | # _res = subprocess.run( 76 | # ["docker exec -i $(docker ps | grep localstack | awk '{print $1}') mkdir /root/.aws"], 77 | # shell=True, capture_output=True) 78 | # _res = subprocess.run( 79 | # ["docker cp /tmp/config $(docker ps | grep localstack | awk '{print $1}'):/root/.aws/config"], 80 | # shell=True, capture_output=True) 81 | # if _res.returncode != 0: 82 | # print("\nError in copying localstck aws config file to localstack container\n") 83 | # print(_res.stderr.decode('utf-8')) 84 | # else: 85 | # pass 86 | 87 | print("### Init component submodules........") 88 | subprocess.run('git submodule init', shell=True, capture_output=True) 89 | print("### Update component submodules........") 90 | subprocess.run('git submodule update', shell=True, capture_output=True) 91 | 92 | # Pull required python slim image 93 | print("Pull required python slim image - python:3.9.10-slim") 94 | _res = subprocess.run('docker pull --platform="{}" python:3.9.10-slim'.format(sys_platform), shell=True, capture_output=True) 95 | if _res.returncode != 0: 96 | print(str(_res.stderr.decode('utf-8'))) 97 | exit(1) 98 | 99 | # Create a docker network and connect components to the network 100 | if platform == "linux": 101 | subprocess.run('docker network create notifyone-network', shell=True, capture_output=True) 102 | subprocess.run('docker network connect notifyone-network postgres_notify', shell=True, capture_output=True) 103 | subprocess.run('docker network connect notifyone-network redis_notify', shell=True, capture_output=True) 104 | subprocess.run('docker network connect notifyone-network localstack-main', shell=True, capture_output=True) 105 | 106 | setup_gateway(sys_platform) 107 | 108 | os.chdir('../') 109 | 110 | setup_core(sys_platform) 111 | 112 | os.chdir('../') 113 | 114 | setup_handler(sys_platform) 115 | 116 | os.chdir('../') 117 | 118 | setup_dashboard(sys_platform, cms_api_endpoint) 119 | 120 | os.chdir('../') 121 | 122 | if platform == "linux": 123 | subprocess.run('docker network connect notifyone-network notifyone-gateway', shell=True, capture_output=True) 124 | subprocess.run('docker network connect notifyone-network notifyone-core', shell=True, capture_output=True) 125 | subprocess.run('docker network connect notifyone-network notifyone-handler', shell=True, capture_output=True) 126 | subprocess.run('docker network connect notifyone-network notifyone-dashboard', shell=True, capture_output=True) 127 | 128 | _res = subprocess.run(["docker exec -i $(docker ps | grep notifyone-core | awk '{print $1}') python3 database.py upgrade "], 129 | shell=True, capture_output=True) 130 | if _res.returncode != 0: 131 | print("\nError in DB upgrade\n") 132 | print(_res.stderr.decode('utf-8')) 133 | else: 134 | print("\nDB upgrade Successful\n") 135 | 136 | print('##### Congratulations! NotifyOne system setup Completed #####') 137 | print('Service Hosts - \n\t notifyone-dashboard : http://localhost:8001 \n\t notifyone-gateway : http://localhost:9401 \n\t notifyone-core : http://localhost:9402 \n\t notifyone-handler : http://localhost:9403') 138 | print('Create App API documentation - \n\t http://localhost:9402/swagger/#/Apps/post_apps') 139 | print('Create Event API documentation - \n\t http://localhost:9402/swagger/#/Events/post_event_create') 140 | print('Send-Notification API documentation - \n\t http://localhost:9401/swagger/#/event_notification/post_send_notification') 141 | print('Get-Notification-Detail API documentation - \n\t http://localhost:9401/swagger/#/event_notification/get_get_notification') 142 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | *Pull requests, bug reports, and all other forms of contribution are welcomed and highly encouraged!* :octocat: 4 | 5 | ### Contents 6 | 7 | - [Code of Conduct](#code-of-conduct) 8 | - [Asking Questions, Discussing ideas](#asking-questions) 9 | - [Opening an Issue](#opening-an-issue) 10 | - [Feature Requests](#feature-requests) 11 | - [Submitting Pull Requests](#submitting-pull-requests) 12 | - [Writing Commit Messages](#writing-commit-messages) 13 | - [Code Review](#code-review) 14 | - [Coding Style](#coding-style) 15 | 16 | > **This guide serves to set clear expectations for everyone involved with the project so that we can improve it together while also creating a welcoming space for everyone to participate. Following these guidelines will help ensure a positive experience for contributors and maintainers.** 17 | 18 | ## Code of Conduct 19 | 20 | Please review our [Code of Conduct](https://github.com/tata1mg/notifyone/blob/master/CODE_OF_CONDUCT.md). It is in effect at all times. We expect it to be honored by everyone who contributes to this project. 21 | 22 | ## Asking Questions, Discussing ideas 23 | 24 | Please use [GitHub Discussion](https://github.com/tata1mg/notifyone/discussions) to discuss the ideas and ask questions. 25 | 26 | ## Opening an Issue 27 | Please use [GitHub Issues](https://github.com/tata1mg/notifyone/issues) to report issues or request a feature. 28 | 29 | Before [creating an issue](https://help.github.com/en/github/managing-your-work-on-github/creating-an-issue), check if you are using the latest version of the project. If you are not up-to-date, see if updating fixes your issue first. 30 | 31 | ### Bug Reports and Other Issues 32 | 33 | A great way to contribute to the project is to send a detailed issue when you encounter a problem. We always appreciate a well-written, thorough bug report. 34 | 35 | In short, since you are most likely a developer, **provide a ticket that you would like to receive**. 36 | 37 | - **Do not open a duplicate issue!** Search through existing issues to see if your issue has previously been reported. If your issue exists, comment with any additional information you have. You may simply note "I have this problem too", which helps prioritize the most common problems and requests. 38 | 39 | ### Feature Requests 40 | 41 | Feature requests are welcome! While we will consider all requests, we cannot guarantee your request will be accepted. We want to avoid [feature creep](https://en.wikipedia.org/wiki/Feature_creep). Your idea may be great, but also out-of-scope for the project. If accepted, we cannot make any commitments regarding the timeline for implementation and release. However, you are welcome to submit a pull request to help! 42 | 43 | - **Do not open a duplicate feature request.** Search for existing feature requests first. If you find your feature (or one very similar) previously requested, comment on that issue. 44 | - Be precise about the proposed outcome of the feature and how it relates to existing features. Include implementation details if possible. 45 | 46 | ## Submitting Pull Requests 47 | 48 | We **love** pull requests! Before [forking the repo](https://help.github.com/en/github/getting-started-with-github/fork-a-repo) and [creating a pull request](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/proposing-changes-to-your-work-with-pull-requests) for non-trivial changes, it is usually best to first open an issue to discuss the changes, or discuss your intended approach for solving the problem in the comments for an existing issue. 49 | 50 | *Note: All contributions will be licensed under the project's license.* 51 | 52 | - **Smaller is better.** Submit **one** pull request per bug fix or feature. A pull request should contain isolated changes pertaining to a single bug fix or feature implementation. **Do not** refactor or reformat code that is unrelated to your change. It is better to **submit many small pull requests** rather than a single large one. Enormous pull requests will take enormous amounts of time to review, or may be rejected altogether. 53 | 54 | - **Coordinate bigger changes.** For large and non-trivial changes, open an issue to discuss a strategy with the maintainers. Otherwise, you risk doing a lot of work for nothing! 55 | 56 | - **Prioritize understanding over cleverness.** Write code clearly and concisely. Remember that source code usually gets written once and read often. Ensure the code is clear to the reader. The purpose and logic should be obvious to a reasonably skilled developer, otherwise you should add a comment that explains it. 57 | 58 | - **Follow existing coding style and conventions.** Keep your code consistent with the style, formatting, and conventions in the rest of the code base. 59 | 60 | - **Update the CHANGELOG** for all enhancements and bug fixes. Include the corresponding issue number if one exists, and your GitHub username. (example: "- Fixed crash in profile view. #123 @jessesquires") 61 | 62 | - **Use the repo's default branch.** Branch from and [submit your pull request](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork) to the repo's default branch. 63 | 64 | - **[Resolve any merge conflicts](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/resolving-a-merge-conflict-on-github)** that occur. 65 | 66 | - **Promptly address any CI failures**. If your pull request fails to build or pass tests, please push another commit to fix it. 67 | 68 | - When writing comments, use properly constructed sentences, including punctuation. 69 | 70 | ## Writing Commit Messages 71 | 72 | Please write a great commit message. 73 | 74 | 1. Limit the subject line to 50 characters 75 | 1. Capitalize the subject line 76 | 1. Do not end the subject line with a period 77 | 1. Use the imperative mood in the subject line (example: "Fix networking issue") 78 | 1. Use the body to explain **why**, *not what and how* (the code shows that!) 79 | 80 | ## Code Review 81 | 82 | - **Review the code, not the author.** Look for and suggest improvements without disparaging or insulting the author. Provide actionable feedback and explain your reasoning. 83 | 84 | - **You are not your code.** When your code is critiqued, questioned, or constructively criticized, remember that you are not your code. Do not take code review personally. 85 | 86 | - **Always do your best.** No one writes bugs on purpose. Do your best, and learn from your mistakes. 87 | 88 | - Kindly note any violations to the guidelines specified in this document. 89 | 90 | ## Coding Style 91 | 92 | Consistency is the most important. Following the existing style, formatting, and naming conventions of the file you are modifying and of the overall project. Failure to do so will result in a prolonged review process that has to focus on updating the superficial aspects of your code, rather than improving its functionality and performance. 93 | 94 | When possible, style and format will be enforced with a linter. 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | Logo 5 | 6 |
7 |

An open-source, event-based notification system for everyone

8 |
9 | The ultimate solution for a scalable, multi-channel notification system 10 |
11 | 12 | ## ⭐️ Why NotifyOne? 13 | 14 | NotifyOne provides an event driven notification system that exposes a unified API to trigger multi channel notifications including Email, Sms, Push and Whatsapp. 15 | 16 | It lets you define an event in the system and configure different channels of notifications for that event. 17 | 18 | ## ✨ Features 19 | - Event-based notification system. 20 | - Low, medium, high, and critical priorities for the events to make sure important notifications are prioritized in the system. 21 | - Built upon open-source tech stack. 22 | - Supports all the important channels for notifications - email, sms, push, WhatsApp and VoIP (**VoIP to be supported in future releases**). 23 | - Highly scalable. Decoupled components for different tasks (gateway, rendering, and operator integration are decoupled which enables it to scale different components differently based on need). 24 | - Easy to set up and integrate - ideal for both stand-alone and container-based deployments 25 | - Equipped with CMS for creating advanced templates (Uses Jinja2 as templating engine) and reporting. (**WIP - to be released in future releases**) 26 | - Highly configurable - Add/Remove operators(service providers) for different channels with ease, change priority between operators, define custom priority logic to handle rare business use cases, all this with just a few clicks. 27 | - Fault-tolerant - usages queuing as and when needed for a fault-tolerant architecture. Provides automatic switching between different operators for a channel based on their performance. 28 | 29 | ## :ballot_box_with_check: Providers Supported 30 | Currently, we support below list of providers for different channels. More providers will be added with the future releases. 31 | 32 | If you wish to have a provider for a channel that is not in the list, feel free to integrate it and raise a PR. 33 | #### :envelope: EMAIL 34 | - [x] SparkPost 35 | - [x] AWS SES 36 | 37 | #### :memo: SMS 38 | - [x] SMS Country 39 | - [x] Plivo 40 | - [x] AWS SNS 41 | 42 | #### :iphone: PUSH 43 | - [x] FCM 44 | 45 | #### :pager: WhatsApp 46 | - [x] Interakt 47 | 48 | ## The Architecture 49 | NotifyOne uses a highly scalable, fault-tolerant architecture. It runs as a group of services that, combined together, work for the best performance. 50 | 51 | NotifyOne has been architected keeping in mind the actual production load and deployment strategies. The components have been decoupled keeping in mind the tasks they perform and their scaling needs. 52 | 53 | The NotifyOne system has been seen as an integration of it's four core components - 54 | 55 | 1. [Gateway](https://github.com/tata1mg/notifyone-gateway) - This is a light-weight components that exposes APIs for sending notifications and retriving notification statuses. It works as a single point of contact to the NotifyOne system for the outside world. 56 | 2. [Core](https://github.com/tata1mg/notifyone-core) - This is where the actual magic happens. Core component is responsible for `app` and `event` management, `template` creation and editing, `template` rendering, routing notification request for a channel to the designated handler, logging notification request etc. It also servers as the backend service to the NotifyOne CMS (Admin Panel) 57 | 3. [Handlers](https://github.com/tata1mg/notifyone-handler) - This component provides integrations with the actual operator gateways like Plivo and SmsCountry for sms, SparkPost and SES for email etc. This component supports multi channel deployment which means handle for each channel can also be deployed as a seprate service. Please refer to the service documentation for more detils. 58 | 4. CMS - (**WIP**) This is admin panel of our NotifyOne system. It provides event and app management, template management, operator and priority logic configurations, analytics and reporting. This component is currently under development and will be released soon in futre releases. 59 | 60 | #### # architecture diagram 61 | 62 |
63 | 64 | 65 | Logo 66 | 67 |
68 | 69 | - The architecture makes it easy to introduce a new channel or integrate with new providers. 70 | - Gateway, Core and Handlers can be scaled independently based on needs. 71 | 72 | ## 🚀 Getting Started 73 | #### Tools & Technologies you need before getting started 74 | - Postgres DB 75 | - Redis 76 | - AWS SQS 77 | - AWS S3 78 | 79 | The NotifyOne repository itself does not contain any code and implementations for all the components can be found in their respective repositories. 80 | 81 | ### Quick try-out ? 82 | We have created a script [notify_setup.py]() that can be used to setup the services quickly on your local system. The script sets up the `notifyone-gateway`, `notifyone-core` and `notifyone-handler` services quickly on your machine (it automatically installs dependencies and resolves connectivity between services). 83 | 84 | #### Prerequisites 85 | - Docker : your machine must have docker installed and running. 86 | - Python - your machine must have python version >= 3.7 installed on it. 87 | 88 | #### How to? 89 | - Clone the [NotifyOne](https://github.com/tata1mg/notifyone) project 90 | - git clone https://github.com/tata1mg/notifyone.git 91 | - `cd` to notifyone directory 92 | - cd notifyone 93 | - Run command - 94 | - python3 notify_setup.py 95 | 96 | ### Production like deployment? 97 | - Clone the [NotifyOne](https://github.com/tata1mg/notifyone) project 98 | - `git clone https://github.com/tata1mg/notifyone.git` 99 | - `cd` to notifyone directory 100 | - `cd notifyone` 101 | - Components are available as git submodule in the notifyone repo. So we need to `init` and `update` the submodules- 102 | - `git submodule init` 103 | - `git submodule update` 104 | - Deploy the Gateway, Core and Handlers components as per your preference (help on how to deploy can be found in respective repositories README.md) 105 | - Create test App and Event - Use APIs exposed in Core service to create a test App and Event 106 | - Trigger notification - use the Gateway's "send-notification" API to trigger notifications for the test Event created in step 2. 107 | 108 | ## Contribution guidelines 109 | Please refer to our [Contribution Guidlines](https://github.com/tata1mg/notifyone/blob/master/CONTRIBUTING.md) before for more details. 110 | 111 | ## License 112 | This project is licensed under the 113 | [Apache-2.0](https://github.com/tata1mg/notifyone/blob/master/LICENSE) License. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2020 long2ice 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /api_doc.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "3.0.0", 3 | "info": { 4 | "title": "API", 5 | "version": "1.0.0", 6 | "contact": {} 7 | }, 8 | "paths": { 9 | "/apps": { 10 | "post": { 11 | "tags": [ 12 | ], 13 | "security": [], 14 | "parameters": [], 15 | "responses": { 16 | "200": { 17 | "content": { 18 | "application/json": { 19 | "schema": { 20 | "type": "object", 21 | "properties": { 22 | "id": { 23 | "type": "integer", 24 | "format": "int32", 25 | "description": "ID of the newly created app", 26 | "example": 111 27 | }, 28 | "name": { 29 | "type": "string", 30 | "description": "App name provided in the request body", 31 | "example": "test_app" 32 | }, 33 | "message": { 34 | "type": "string", 35 | "description": "Message string", 36 | "example": "App created successfully" 37 | } 38 | } 39 | } 40 | } 41 | }, 42 | "status": 200, 43 | "description": "Default Response" 44 | } 45 | }, 46 | "summary": "Create App API", 47 | "description": "The API can be used to create new apps in the system", 48 | "requestBody": { 49 | "content": { 50 | "application/json": { 51 | "schema": { 52 | "type": "object", 53 | "properties": { 54 | "name": { 55 | "type": "string", 56 | "description": "App name provided in the request body", 57 | "example": "test_app" 58 | }, 59 | "callback_url": { 60 | "type": "string", 61 | "description": "Webhook URL to receive status updates", 62 | "example": "https://example.com/webhook" 63 | }, 64 | "callback_events": { 65 | "type": "object", 66 | "properties": { 67 | "email": { 68 | "type": "array", 69 | "items": { 70 | "type": "string", 71 | "default": "DELIVERED" 72 | }, 73 | "description": "List of events to subscribe for emails" 74 | }, 75 | "sms": { 76 | "type": "array", 77 | "items": { 78 | "type": "string", 79 | "default": "DELIVERED" 80 | }, 81 | "description": "List of events to subscribe for sms notifications" 82 | }, 83 | "push": { 84 | "type": "array", 85 | "items": { 86 | "type": "string", 87 | "default": "" 88 | }, 89 | "description": "List of events to subscribe for push notifications" 90 | }, 91 | "whatsapp": { 92 | "type": "array", 93 | "items": { 94 | "type": "string", 95 | "default": "DELIVERED" 96 | }, 97 | "description": "List of events to subscribe for whatsapp notifications" 98 | } 99 | } 100 | }, 101 | "metadata": { 102 | "type": "object", 103 | "properties": { 104 | "sender_details": { 105 | "type": "object", 106 | "properties": { 107 | "email": { 108 | "type": "object", 109 | "properties": { 110 | "name": { 111 | "type": "string", 112 | "description": "Sender name to be used in sending email", 113 | "example": "Tata 1mg", 114 | "required": true 115 | }, 116 | "address": { 117 | "type": "string", 118 | "description": "Sender email address to be used in sending email", 119 | "example": "sender@xyzmail.com", 120 | "required": true 121 | }, 122 | "reply_to": { 123 | "type": "string", 124 | "description": "Reply to email address for sending email", 125 | "example": "reply@xyzmail.com", 126 | "required": true 127 | } 128 | } 129 | } 130 | } 131 | } 132 | } 133 | } 134 | } 135 | } 136 | } 137 | }, 138 | "required": null, 139 | "description": null 140 | } 141 | } 142 | }, 143 | "\/event/create": { 144 | "post": { 145 | "tags": [ 146 | ], 147 | "security": [], 148 | "parameters": [], 149 | "responses": { 150 | "200": { 151 | "content": { 152 | "application/json": { 153 | "schema": { 154 | "type": "object", 155 | "properties": { 156 | "event_id": { 157 | "type": "string", 158 | "description": "Event ID generated for this event", 159 | "example": 111, 160 | "required": true 161 | }, 162 | "event_name": { 163 | "type": "string", 164 | "description": "Event name", 165 | "example": "test_event", 166 | "required": true 167 | }, 168 | "app_name": { 169 | "type": "string", 170 | "description": "App name", 171 | "example": "test_app", 172 | "required": true 173 | }, 174 | "action": { 175 | "type": "object", 176 | "properties": { 177 | "email": { 178 | "type": "integer", 179 | "format": "int32", 180 | "example": 1 181 | }, 182 | "sms": { 183 | "type": "integer", 184 | "format": "int32", 185 | "example": 1 186 | }, 187 | "push": { 188 | "type": "integer", 189 | "format": "int32", 190 | "example": 1 191 | }, 192 | "whatsapp": { 193 | "type": "integer", 194 | "format": "int32", 195 | "example": 1 196 | } 197 | } 198 | }, 199 | "trigger_limit": { 200 | "type": "object", 201 | "properties": { 202 | "name": { 203 | "type": "string", 204 | "description": "Whatsapp template name for Interkt", 205 | "example": "order_delivered", 206 | "required": true 207 | } 208 | } 209 | }, 210 | "created_by": { 211 | "type": "string", 212 | "description": "Email ID of the user requesting to create the event", 213 | "example": "test@test.com" 214 | } 215 | } 216 | } 217 | } 218 | }, 219 | "status": 200, 220 | "description": "Default Response" 221 | } 222 | }, 223 | "summary": "Create Event API", 224 | "description": "The API can be used to create new events in the system", 225 | "requestBody": { 226 | "content": { 227 | "application/json": { 228 | "schema": { 229 | "type": "object", 230 | "properties": { 231 | "event_name": { 232 | "type": "string", 233 | "description": "Event name", 234 | "example": "test_event", 235 | "required": true 236 | }, 237 | "app_name": { 238 | "type": "string", 239 | "description": "App name", 240 | "example": "test_app", 241 | "required": true 242 | }, 243 | "email": { 244 | "type": "object", 245 | "properties": { 246 | "description": { 247 | "type": "string", 248 | "description": "Description of your event", 249 | "example": "This is test event", 250 | "required": true 251 | }, 252 | "subject": { 253 | "type": "string", 254 | "description": "Email subject", 255 | "example": "Regarding your Order {{order.order_id}}", 256 | "required": true 257 | }, 258 | "content": { 259 | "type": "string", 260 | "description": "Email body", 261 | "example": "Your order {{order.order_id}} has been processed", 262 | "required": true 263 | } 264 | } 265 | }, 266 | "sms": { 267 | "type": "object", 268 | "properties": { 269 | "content": { 270 | "type": "string", 271 | "description": "Email body", 272 | "example": "Your order {{order.order_id}} has been processed", 273 | "required": true 274 | } 275 | } 276 | }, 277 | "push": { 278 | "type": "object", 279 | "properties": { 280 | "title": { 281 | "type": "string", 282 | "description": "Push message title", 283 | "example": "Order update", 284 | "required": true 285 | }, 286 | "body": { 287 | "type": "string", 288 | "description": "Push message body text", 289 | "example": "Order {{order.order_id}} has been delivered", 290 | "required": true 291 | } 292 | } 293 | }, 294 | "whatsapp": { 295 | "type": "object", 296 | "properties": { 297 | "name": { 298 | "type": "string", 299 | "description": "Whatsapp template name for Interkt", 300 | "example": "order_delivered", 301 | "required": true 302 | } 303 | } 304 | }, 305 | "priority": { 306 | "type": "string", 307 | "description": "Priority for this event. Priority can be one of these values - critical/high/medium/low", 308 | "example": "high", 309 | "required": true 310 | }, 311 | "event_type": { 312 | "type": "string", 313 | "description": "Type for this event. Type can be one of these values - promotional/transactional/other", 314 | "example": "transactional", 315 | "required": true 316 | }, 317 | "user_email": { 318 | "type": "string", 319 | "description": "Email ID of the user requesting to create the event", 320 | "example": "test@test.com", 321 | "required": true 322 | } 323 | } 324 | } 325 | } 326 | }, 327 | "required": null, 328 | "description": null 329 | } 330 | } 331 | }, 332 | "\/send-notification": { 333 | "post": { 334 | "tags": [ 335 | ], 336 | "security": [], 337 | "parameters": [], 338 | "responses": { 339 | "200": { 340 | "content": { 341 | "application\/json": { 342 | "schema": { 343 | "type": "object", 344 | "properties": { 345 | "data": { 346 | "type": "object", 347 | "properties": { 348 | "request_id": { 349 | "type": "string", 350 | "description": "Unique request ID generated by the Notification System for this request", 351 | "example": "3e1ba047-71b1-4bd1-b510-613a50030b69" 352 | }, 353 | "message": { 354 | "type": "string", 355 | "description": "a message string", 356 | "example": "success" 357 | }, 358 | "processing_type": { 359 | "type": "string", 360 | "description": "Processing type used for this request. It can SYNC or ASYNC", 361 | "example": "ASYNC" 362 | } 363 | } 364 | }, 365 | "is_success": { 366 | "type": "boolean", 367 | "description": "true\/false to denote if the request was successful or not", 368 | "example": true 369 | }, 370 | "status_code": { 371 | "type": "integer", 372 | "format": "int32", 373 | "description": "Http response status code", 374 | "example": 200 375 | } 376 | } 377 | } 378 | } 379 | }, 380 | "status": 200, 381 | "description": "Default Response" 382 | } 383 | }, 384 | "summary": "Send Notification API", 385 | "description": "This API can be used to trigger notifications for an already created event.The API triggers notifications for all the active and eligible channels in the event. The processing can either be ASYNC or SYNC depending upon the event priority", 386 | "requestBody": { 387 | "content": { 388 | "application\/json": { 389 | "schema": { 390 | "type": "object", 391 | "properties": { 392 | "event_id": { 393 | "type": "integer", 394 | "format": "int32", 395 | "example": 25, 396 | "description": "Event ID for this notification" 397 | }, 398 | "source_identifier": { 399 | "type": "string", 400 | "example": "ID1212122", 401 | "description": "Source identifier sent with the request" 402 | }, 403 | "to": { 404 | "type": "object", 405 | "properties": {} 406 | }, 407 | "channels": { 408 | "type": "object", 409 | "properties": { 410 | "email": { 411 | "type": "object", 412 | "properties": { 413 | "sender": { 414 | "type": "object", 415 | "properties": { 416 | "name": { 417 | "type": "string", 418 | "description": "Send name", 419 | "example": "Tata 1mg", 420 | "required": false 421 | }, 422 | "address": { 423 | "type": "string", 424 | "description": "Send email address", 425 | "example": "xyz@1mg.com", 426 | "required": false 427 | } 428 | } 429 | }, 430 | "reply_to": { 431 | "type": "string", 432 | "description": "Reply-to email ID", 433 | "required": false 434 | } 435 | } 436 | } 437 | } 438 | }, 439 | "attachments": { 440 | "type": "array", 441 | "items": { 442 | "type": "object", 443 | "properties": { 444 | "url": { 445 | "type": "string", 446 | "example": "https:\/\/1mg-odin-production.s3.ap-south-1.amazonaws.com\/upload\/sales_orders\/42550349\/6f55151e-adb5-4171-8fe2-5eb6599eafb7.pdf", 447 | "required": true 448 | }, 449 | "filename": { 450 | "type": "string", 451 | "example": "report.pdf", 452 | "required": true 453 | } 454 | } 455 | }, 456 | "required": false 457 | }, 458 | "body": { 459 | "type": "object", 460 | "properties": {} 461 | } 462 | } 463 | } 464 | } 465 | }, 466 | "required": null, 467 | "description": null 468 | } 469 | } 470 | }, 471 | "\/get-notification": { 472 | "get": { 473 | "tags": [ 474 | ], 475 | "security": [], 476 | "parameters": [ 477 | { 478 | "name": "notification_request_id", 479 | "schema": { 480 | "type": "string" 481 | }, 482 | "description": "Unique ID generated by the Notification System", 483 | "in": "query" 484 | }, 485 | { 486 | "name": "source_identifier", 487 | "schema": { 488 | "type": "string" 489 | }, 490 | "description": "Source identifier sent with the request", 491 | "in": "query" 492 | } 493 | ], 494 | "responses": { 495 | "200": { 496 | "content": { 497 | "application\/json": { 498 | "schema": { 499 | "type": "object", 500 | "properties": { 501 | "data": { 502 | "type": "object", 503 | "properties": { 504 | "data": { 505 | "type": "object", 506 | "properties": { 507 | "notifications": { 508 | "type": "array", 509 | "items": { 510 | "type": "object", 511 | "properties": { 512 | "id": { 513 | "type": "integer", 514 | "format": "int32", 515 | "example": 3250, 516 | "description": "Notification Identifier" 517 | }, 518 | "event_id": { 519 | "type": "integer", 520 | "format": "int32", 521 | "example": 25, 522 | "description": "Event ID for this notification" 523 | }, 524 | "notification_request_id": { 525 | "type": "string", 526 | "example": "3e1ba047-71b1-4bd1-b510-613a50030b69", 527 | "description": "Unique ID generated by the Notification System" 528 | }, 529 | "channel": { 530 | "type": "string", 531 | "example": "push", 532 | "description": "Channel type of notification" 533 | }, 534 | "status": { 535 | "type": "string", 536 | "example": "SUCCESS", 537 | "description": "Current status. Possible values are - NEW\/INITIATED\/FAILED\/SUCCESS\/NOT_ELIGIBLE" 538 | }, 539 | "sent_to": { 540 | "type": "string", 541 | "example": "DEVICE1211212", 542 | "description": "Recipient address" 543 | }, 544 | "source_identifier": { 545 | "type": "string", 546 | "example": "ID1212122", 547 | "description": "Source identifier sent with the request" 548 | }, 549 | "operator": { 550 | "type": "string", 551 | "example": "FCM", 552 | "description": "Operator used to send out the notification" 553 | }, 554 | "operator_event_id": { 555 | "type": "string", 556 | "example": "A112123232323", 557 | "description": "Operator side ID for this notification" 558 | }, 559 | "message": { 560 | "type": "string", 561 | "example": "Success", 562 | "description": "Optional message" 563 | }, 564 | "created": { 565 | "type": "integer", 566 | "format": "int32", 567 | "example": 1679969481, 568 | "description": "Epoch time when the notification was created" 569 | }, 570 | "updated": { 571 | "type": "integer", 572 | "format": "int32", 573 | "example": 1679969481, 574 | "description": "Epoch time when the notification was last updated" 575 | } 576 | } 577 | } 578 | } 579 | } 580 | } 581 | } 582 | }, 583 | "is_success": { 584 | "type": "boolean", 585 | "description": "true\/false to denote if the request was successful or not", 586 | "example": true 587 | }, 588 | "status_code": { 589 | "type": "integer", 590 | "format": "int32", 591 | "description": "Http response status code", 592 | "example": 200 593 | } 594 | } 595 | } 596 | } 597 | }, 598 | "status": 200, 599 | "description": "Default Response" 600 | } 601 | }, 602 | "summary": "Get Notifications API", 603 | "description": "This API can be used to fetch the status of a notification request.It returns list of notifications triggered for the notification request" 604 | } 605 | } 606 | }, 607 | "tags": [ 608 | ], 609 | "servers": [], 610 | "components": null, 611 | "externalDocs": null 612 | } 613 | --------------------------------------------------------------------------------