Compare commits

..

1 Commits

Author SHA1 Message Date
standardci 1322f99808 chore(release): publish new version
- @standardnotes/analytics@2.9.7
 - @standardnotes/api-gateway@1.38.2
 - @standardnotes/auth-server@1.59.8
 - @standardnotes/common@1.44.2
 - @standardnotes/domain-events-infra@1.9.24
 - @standardnotes/domain-events@2.86.1
 - @standardnotes/event-store@1.6.19
 - @standardnotes/files-server@1.8.19
 - @standardnotes/predicates@1.5.5
 - @standardnotes/scheduler-server@1.13.20
 - @standardnotes/security@1.6.2
 - @standardnotes/settings@1.18.1
 - @standardnotes/sncrypto-node@1.12.1
 - @standardnotes/syncing-server@1.13.3
 - @standardnotes/time@1.13.1
 - @standardnotes/websockets-server@1.4.21
 - @standardnotes/workspace-server@1.17.19
2022-11-14 09:08:49 +00:00
617 changed files with 7225 additions and 15896 deletions
@@ -187,12 +187,12 @@ jobs:
tags: standardnotes/${{ inputs.service_name }}:${{ github.sha }} tags: standardnotes/${{ inputs.service_name }}:${{ github.sha }}
- name: Run E2E test suite - name: Run E2E test suite
uses: convictional/trigger-workflow-and-wait@master uses: convictional/trigger-workflow-and-wait@v1.6.3
with: with:
owner: standardnotes owner: standardnotes
repo: self-hosted repo: e2e
github_token: ${{ secrets.CI_PAT_TOKEN }} github_token: ${{ secrets.CI_PAT_TOKEN }}
workflow_file_name: testing-with-updating-client-and-server.yml workflow_file_name: testing-with-stable-client.yml
wait_interval: 30 wait_interval: 30
client_payload: '{"${{ inputs.e2e_tag_parameter_name }}": "${{ github.sha }}"}' client_payload: '{"${{ inputs.e2e_tag_parameter_name }}": "${{ github.sha }}"}'
propagate_failure: true propagate_failure: true
-46
View File
@@ -1,46 +0,0 @@
name: Revisions Server
concurrency:
group: revisions_server
cancel-in-progress: true
on:
push:
tags:
- '*standardnotes/revisions-server*'
workflow_dispatch:
jobs:
call_server_application_workflow:
name: Server Application
uses: standardnotes/server/.github/workflows/common-server-application.yml@main
with:
service_name: revisions
workspace_name: "@standardnotes/revisions-server"
e2e_tag_parameter_name: revisions_image_tag
package_path: packages/revisions
secrets: inherit
newrelic:
needs: call_server_application_workflow
runs-on: ubuntu-latest
steps:
- name: Create New Relic deployment marker for Web
uses: newrelic/deployment-marker-action@v1
with:
accountId: ${{ secrets.NEW_RELIC_ACCOUNT_ID }}
apiKey: ${{ secrets.NEW_RELIC_API_KEY }}
applicationId: ${{ secrets.NEW_RELIC_APPLICATION_ID_REVISIONS_WEB_PROD }}
revision: "${{ github.sha }}"
description: "Automated Deployment via Github Actions"
user: "${{ github.actor }}"
- name: Create New Relic deployment marker for Worker
uses: newrelic/deployment-marker-action@v1
with:
accountId: ${{ secrets.NEW_RELIC_ACCOUNT_ID }}
apiKey: ${{ secrets.NEW_RELIC_API_KEY }}
applicationId: ${{ secrets.NEW_RELIC_APPLICATION_ID_REVISIONS_WORKER_PROD }}
revision: "${{ github.sha }}"
description: "Automated Deployment via Github Actions"
user: "${{ github.actor }}"
+1 -1
View File
@@ -1 +1 @@
18.12.1 16.15.1
Generated
+125 -532
View File
File diff suppressed because it is too large Load Diff
-6
View File
@@ -1,6 +0,0 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 120,
"semi": false
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -326,8 +326,8 @@ ifeq ($(strip $(foreach prefix,$(NO_LOAD),\
endif endif
quiet_cmd_regen_makefile = ACTION Regenerating $@ quiet_cmd_regen_makefile = ACTION Regenerating $@
cmd_regen_makefile = cd $(srcdir); /Users/karolsojko/workspace/server/.yarn/unplugged/node-gyp-npm-9.0.0-0eccfca4d1/node_modules/node-gyp/gyp/gyp_main.py -fmake --ignore-environment "-Dlibrary=shared_library" "-Dvisibility=default" "-Dnode_root_dir=/Users/karolsojko/Library/Caches/node-gyp/18.12.1" "-Dnode_gyp_dir=/Users/karolsojko/workspace/server/.yarn/unplugged/node-gyp-npm-9.0.0-0eccfca4d1/node_modules/node-gyp" "-Dnode_lib_file=/Users/karolsojko/Library/Caches/node-gyp/18.12.1/<(target_arch)/node.lib" "-Dmodule_root_dir=/Users/karolsojko/workspace/server/.yarn/unplugged/@newrelic-native-metrics-npm-9.0.0-590d2e713a/node_modules/@newrelic/native-metrics" "-Dnode_engine=v8" "--depth=." "-Goutput_dir=." "--generator-output=build" -I/Users/karolsojko/workspace/server/.yarn/unplugged/@newrelic-native-metrics-npm-9.0.0-590d2e713a/node_modules/@newrelic/native-metrics/build/config.gypi -I/Users/karolsojko/workspace/server/.yarn/unplugged/node-gyp-npm-9.0.0-0eccfca4d1/node_modules/node-gyp/addon.gypi -I/Users/karolsojko/Library/Caches/node-gyp/18.12.1/include/node/common.gypi "--toplevel-dir=." binding.gyp cmd_regen_makefile = cd $(srcdir); /Users/karolsojko/workspace/server/.yarn/unplugged/node-gyp-npm-9.0.0-0eccfca4d1/node_modules/node-gyp/gyp/gyp_main.py -fmake --ignore-environment "-Dlibrary=shared_library" "-Dvisibility=default" "-Dnode_root_dir=/Users/karolsojko/Library/Caches/node-gyp/16.15.1" "-Dnode_gyp_dir=/Users/karolsojko/workspace/server/.yarn/unplugged/node-gyp-npm-9.0.0-0eccfca4d1/node_modules/node-gyp" "-Dnode_lib_file=/Users/karolsojko/Library/Caches/node-gyp/16.15.1/<(target_arch)/node.lib" "-Dmodule_root_dir=/Users/karolsojko/workspace/server/.yarn/unplugged/@newrelic-native-metrics-npm-9.0.0-590d2e713a/node_modules/@newrelic/native-metrics" "-Dnode_engine=v8" "--depth=." "-Goutput_dir=." "--generator-output=build" -I/Users/karolsojko/workspace/server/.yarn/unplugged/@newrelic-native-metrics-npm-9.0.0-590d2e713a/node_modules/@newrelic/native-metrics/build/config.gypi -I/Users/karolsojko/workspace/server/.yarn/unplugged/node-gyp-npm-9.0.0-0eccfca4d1/node_modules/node-gyp/addon.gypi -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/common.gypi "--toplevel-dir=." binding.gyp
Makefile: $(srcdir)/build/config.gypi $(srcdir)/../../../../node-gyp-npm-9.0.0-0eccfca4d1/node_modules/node-gyp/addon.gypi $(srcdir)/../../../../../../../../Library/Caches/node-gyp/18.12.1/include/node/common.gypi $(srcdir)/binding.gyp Makefile: $(srcdir)/binding.gyp $(srcdir)/../../../../node-gyp-npm-9.0.0-0eccfca4d1/node_modules/node-gyp/addon.gypi $(srcdir)/../../../../../../../../Library/Caches/node-gyp/16.15.1/include/node/common.gypi $(srcdir)/build/config.gypi
$(call do_cmd,regen_makefile) $(call do_cmd,regen_makefile)
# "all" is a concatenation of the "all" targets from all the included # "all" is a concatenation of the "all" targets from all the included
@@ -0,0 +1 @@
cmd_Release/native_metrics.node := c++ -bundle -undefined dynamic_lookup -Wl,-search_paths_first -mmacosx-version-min=10.13 -arch x86_64 -L./Release -stdlib=libc++ -o Release/native_metrics.node Release/obj.target/native_metrics/src/native_metrics.o Release/obj.target/native_metrics/src/GCBinder.o Release/obj.target/native_metrics/src/LoopChecker.o
@@ -0,0 +1,69 @@
cmd_Release/obj.target/native_metrics/src/GCBinder.o := c++ -o Release/obj.target/native_metrics/src/GCBinder.o ../src/GCBinder.cpp '-DNODE_GYP_MODULE_NAME=native_metrics' '-DUSING_UV_SHARED=1' '-DUSING_V8_SHARED=1' '-DV8_DEPRECATION_WARNINGS=1' '-DV8_DEPRECATION_WARNINGS' '-DV8_IMMINENT_DEPRECATION_WARNINGS' '-D_GLIBCXX_USE_CXX11_ABI=1' '-D_DARWIN_USE_64_BIT_INODE=1' '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-DOPENSSL_NO_PINSHARED' '-DOPENSSL_THREADS' '-DNOMINMAX' '-DBUILDING_NODE_EXTENSION' -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/src -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/deps/openssl/config -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/deps/openssl/openssl/include -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/deps/uv/include -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/deps/zlib -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/deps/v8/include -I../src -I../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan -O3 -gdwarf-2 -mmacosx-version-min=10.13 -arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -std=gnu++14 -stdlib=libc++ -fno-rtti -fno-exceptions -fno-strict-aliasing -MMD -MF ./Release/.deps/Release/obj.target/native_metrics/src/GCBinder.o.d.raw -c
Release/obj.target/native_metrics/src/GCBinder.o: ../src/GCBinder.cpp \
../src/GCBinder.hpp \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/node_version.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/errno.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/version.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/unix.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/threadpool.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/darwin.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/node.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/cppgc/common.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8config.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8-internal.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8-version.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8-platform.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/node_buffer.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/node_object_wrap.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_callbacks.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_callbacks_12_inl.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_maybe_43_inl.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_converters.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_converters_43_inl.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_new.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_implementation_12_inl.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_persistent_12_inl.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_weak.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_object_wrap.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_private.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_typedarray_contents.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_json.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_scriptorigin.h \
../src/Metric.hpp
../src/GCBinder.cpp:
../src/GCBinder.hpp:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/node_version.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/errno.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/version.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/unix.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/threadpool.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/darwin.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/node.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/cppgc/common.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8config.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8-internal.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8-version.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8-platform.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/node_buffer.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/node_object_wrap.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_callbacks.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_callbacks_12_inl.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_maybe_43_inl.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_converters.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_converters_43_inl.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_new.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_implementation_12_inl.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_persistent_12_inl.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_weak.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_object_wrap.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_private.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_typedarray_contents.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_json.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_scriptorigin.h:
../src/Metric.hpp:
@@ -0,0 +1,70 @@
cmd_Release/obj.target/native_metrics/src/LoopChecker.o := c++ -o Release/obj.target/native_metrics/src/LoopChecker.o ../src/LoopChecker.cpp '-DNODE_GYP_MODULE_NAME=native_metrics' '-DUSING_UV_SHARED=1' '-DUSING_V8_SHARED=1' '-DV8_DEPRECATION_WARNINGS=1' '-DV8_DEPRECATION_WARNINGS' '-DV8_IMMINENT_DEPRECATION_WARNINGS' '-D_GLIBCXX_USE_CXX11_ABI=1' '-D_DARWIN_USE_64_BIT_INODE=1' '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-DOPENSSL_NO_PINSHARED' '-DOPENSSL_THREADS' '-DNOMINMAX' '-DBUILDING_NODE_EXTENSION' -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/src -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/deps/openssl/config -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/deps/openssl/openssl/include -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/deps/uv/include -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/deps/zlib -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/deps/v8/include -I../src -I../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan -O3 -gdwarf-2 -mmacosx-version-min=10.13 -arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -std=gnu++14 -stdlib=libc++ -fno-rtti -fno-exceptions -fno-strict-aliasing -MMD -MF ./Release/.deps/Release/obj.target/native_metrics/src/LoopChecker.o.d.raw -c
Release/obj.target/native_metrics/src/LoopChecker.o: \
../src/LoopChecker.cpp \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/errno.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/version.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/unix.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/threadpool.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/darwin.h \
../src/LoopChecker.hpp \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/node_version.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/node.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/cppgc/common.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8config.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8-internal.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8-version.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8-platform.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/node_buffer.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/node_object_wrap.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_callbacks.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_callbacks_12_inl.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_maybe_43_inl.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_converters.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_converters_43_inl.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_new.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_implementation_12_inl.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_persistent_12_inl.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_weak.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_object_wrap.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_private.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_typedarray_contents.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_json.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_scriptorigin.h \
../src/Metric.hpp
../src/LoopChecker.cpp:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/errno.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/version.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/unix.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/threadpool.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/darwin.h:
../src/LoopChecker.hpp:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/node_version.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/node.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/cppgc/common.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8config.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8-internal.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8-version.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8-platform.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/node_buffer.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/node_object_wrap.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_callbacks.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_callbacks_12_inl.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_maybe_43_inl.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_converters.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_converters_43_inl.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_new.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_implementation_12_inl.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_persistent_12_inl.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_weak.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_object_wrap.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_private.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_typedarray_contents.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_json.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_scriptorigin.h:
../src/Metric.hpp:
@@ -0,0 +1,70 @@
cmd_Release/obj.target/native_metrics/src/native_metrics.o := c++ -o Release/obj.target/native_metrics/src/native_metrics.o ../src/native_metrics.cpp '-DNODE_GYP_MODULE_NAME=native_metrics' '-DUSING_UV_SHARED=1' '-DUSING_V8_SHARED=1' '-DV8_DEPRECATION_WARNINGS=1' '-DV8_DEPRECATION_WARNINGS' '-DV8_IMMINENT_DEPRECATION_WARNINGS' '-D_GLIBCXX_USE_CXX11_ABI=1' '-D_DARWIN_USE_64_BIT_INODE=1' '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-DOPENSSL_NO_PINSHARED' '-DOPENSSL_THREADS' '-DNOMINMAX' '-DBUILDING_NODE_EXTENSION' -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/src -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/deps/openssl/config -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/deps/openssl/openssl/include -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/deps/uv/include -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/deps/zlib -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/deps/v8/include -I../src -I../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan -O3 -gdwarf-2 -mmacosx-version-min=10.13 -arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -std=gnu++14 -stdlib=libc++ -fno-rtti -fno-exceptions -fno-strict-aliasing -MMD -MF ./Release/.deps/Release/obj.target/native_metrics/src/native_metrics.o.d.raw -c
Release/obj.target/native_metrics/src/native_metrics.o: \
../src/native_metrics.cpp \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/node_version.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/errno.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/version.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/unix.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/threadpool.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/darwin.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/node.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/cppgc/common.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8config.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8-internal.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8-version.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8-platform.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/node_buffer.h \
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/node_object_wrap.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_callbacks.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_callbacks_12_inl.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_maybe_43_inl.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_converters.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_converters_43_inl.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_new.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_implementation_12_inl.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_persistent_12_inl.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_weak.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_object_wrap.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_private.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_typedarray_contents.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_json.h \
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_scriptorigin.h \
../src/GCBinder.hpp ../src/Metric.hpp ../src/LoopChecker.hpp
../src/native_metrics.cpp:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/node_version.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/errno.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/version.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/unix.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/threadpool.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/uv/darwin.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/node.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/cppgc/common.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8config.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8-internal.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8-version.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/v8-platform.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/node_buffer.h:
/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node/node_object_wrap.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_callbacks.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_callbacks_12_inl.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_maybe_43_inl.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_converters.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_converters_43_inl.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_new.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_implementation_12_inl.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_persistent_12_inl.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_weak.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_object_wrap.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_private.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_typedarray_contents.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_json.h:
../../../../../nan-npm-2.16.0-cac314a230/node_modules/nan/nan_scriptorigin.h:
../src/GCBinder.hpp:
../src/Metric.hpp:
../src/LoopChecker.hpp:
@@ -19,316 +19,293 @@
"error_on_warn": "false", "error_on_warn": "false",
"force_dynamic_crt": 0, "force_dynamic_crt": 0,
"host_arch": "x64", "host_arch": "x64",
"icu_data_in": "../../deps/icu-tmp/icudt71l.dat", "icu_data_in": "../../deps/icu-tmp/icudt70l.dat",
"icu_endianness": "l", "icu_endianness": "l",
"icu_gyp_path": "tools/icu/icu-generic.gyp", "icu_gyp_path": "tools/icu/icu-generic.gyp",
"icu_path": "deps/icu-small", "icu_path": "deps/icu-small",
"icu_small": "false", "icu_small": "false",
"icu_ver_major": "71", "icu_ver_major": "70",
"is_debug": 0, "is_debug": 0,
"libdir": "lib",
"llvm_version": "11.0", "llvm_version": "11.0",
"napi_build_version": "8", "napi_build_version": "8",
"node_byteorder": "little", "node_byteorder": "little",
"node_debug_lib": "false", "node_debug_lib": "false",
"node_enable_d8": "false", "node_enable_d8": "false",
"node_fipsinstall": "false",
"node_install_corepack": "true", "node_install_corepack": "true",
"node_install_npm": "true", "node_install_npm": "true",
"node_library_files": [ "node_library_files": [
"lib/_http_agent.js",
"lib/_http_client.js",
"lib/_http_common.js",
"lib/_http_incoming.js",
"lib/_http_outgoing.js",
"lib/_http_server.js",
"lib/_stream_duplex.js",
"lib/_stream_passthrough.js",
"lib/_stream_readable.js",
"lib/_stream_transform.js",
"lib/_stream_wrap.js",
"lib/_stream_writable.js",
"lib/_tls_common.js",
"lib/_tls_wrap.js",
"lib/assert.js",
"lib/assert/strict.js",
"lib/async_hooks.js",
"lib/buffer.js",
"lib/child_process.js",
"lib/cluster.js",
"lib/console.js",
"lib/constants.js", "lib/constants.js",
"lib/crypto.js",
"lib/dgram.js",
"lib/diagnostics_channel.js",
"lib/dns.js",
"lib/dns/promises.js",
"lib/domain.js",
"lib/events.js",
"lib/fs.js",
"lib/fs/promises.js",
"lib/http.js",
"lib/http2.js",
"lib/https.js",
"lib/inspector.js",
"lib/internal/abort_controller.js",
"lib/internal/assert.js",
"lib/internal/assert/assertion_error.js",
"lib/internal/assert/calltracker.js",
"lib/internal/assert/snapshot.js",
"lib/internal/async_hooks.js",
"lib/internal/blob.js",
"lib/internal/blocklist.js",
"lib/internal/bootstrap/browser.js",
"lib/internal/bootstrap/loaders.js",
"lib/internal/bootstrap/node.js",
"lib/internal/bootstrap/switches/does_not_own_process_state.js",
"lib/internal/bootstrap/switches/does_own_process_state.js",
"lib/internal/bootstrap/switches/is_main_thread.js",
"lib/internal/bootstrap/switches/is_not_main_thread.js",
"lib/internal/buffer.js",
"lib/internal/child_process.js",
"lib/internal/child_process/serialization.js",
"lib/internal/cli_table.js",
"lib/internal/cluster/child.js",
"lib/internal/cluster/primary.js",
"lib/internal/cluster/round_robin_handle.js",
"lib/internal/cluster/shared_handle.js",
"lib/internal/cluster/utils.js",
"lib/internal/cluster/worker.js",
"lib/internal/console/constructor.js",
"lib/internal/console/global.js",
"lib/internal/constants.js",
"lib/internal/crypto/aes.js",
"lib/internal/crypto/certificate.js",
"lib/internal/crypto/cfrg.js",
"lib/internal/crypto/cipher.js",
"lib/internal/crypto/diffiehellman.js",
"lib/internal/crypto/ec.js",
"lib/internal/crypto/hash.js",
"lib/internal/crypto/hashnames.js",
"lib/internal/crypto/hkdf.js",
"lib/internal/crypto/keygen.js",
"lib/internal/crypto/keys.js",
"lib/internal/crypto/mac.js",
"lib/internal/crypto/pbkdf2.js",
"lib/internal/crypto/random.js",
"lib/internal/crypto/rsa.js",
"lib/internal/crypto/scrypt.js",
"lib/internal/crypto/sig.js",
"lib/internal/crypto/util.js",
"lib/internal/crypto/webcrypto.js",
"lib/internal/crypto/x509.js",
"lib/internal/debugger/inspect.js",
"lib/internal/debugger/inspect_client.js",
"lib/internal/debugger/inspect_repl.js",
"lib/internal/dgram.js",
"lib/internal/dns/callback_resolver.js",
"lib/internal/dns/promises.js",
"lib/internal/dns/utils.js",
"lib/internal/dtrace.js",
"lib/internal/encoding.js",
"lib/internal/error_serdes.js",
"lib/internal/errors.js",
"lib/internal/event_target.js",
"lib/internal/fixed_queue.js",
"lib/internal/freelist.js",
"lib/internal/freeze_intrinsics.js",
"lib/internal/fs/cp/cp-sync.js",
"lib/internal/fs/cp/cp.js",
"lib/internal/fs/dir.js",
"lib/internal/fs/promises.js",
"lib/internal/fs/read_file_context.js",
"lib/internal/fs/rimraf.js",
"lib/internal/fs/streams.js",
"lib/internal/fs/sync_write_stream.js",
"lib/internal/fs/utils.js",
"lib/internal/fs/watchers.js",
"lib/internal/heap_utils.js",
"lib/internal/histogram.js",
"lib/internal/http.js",
"lib/internal/http2/compat.js",
"lib/internal/http2/core.js",
"lib/internal/http2/util.js",
"lib/internal/idna.js",
"lib/internal/inspector_async_hook.js",
"lib/internal/js_stream_socket.js",
"lib/internal/legacy/processbinding.js",
"lib/internal/linkedlist.js",
"lib/internal/main/check_syntax.js",
"lib/internal/main/environment.js",
"lib/internal/main/eval_stdin.js",
"lib/internal/main/eval_string.js",
"lib/internal/main/inspect.js",
"lib/internal/main/mksnapshot.js",
"lib/internal/main/print_help.js",
"lib/internal/main/prof_process.js",
"lib/internal/main/repl.js",
"lib/internal/main/run_main_module.js",
"lib/internal/main/test_runner.js",
"lib/internal/main/watch_mode.js",
"lib/internal/main/worker_thread.js",
"lib/internal/modules/cjs/helpers.js",
"lib/internal/modules/cjs/loader.js",
"lib/internal/modules/esm/assert.js",
"lib/internal/modules/esm/create_dynamic_module.js",
"lib/internal/modules/esm/fetch_module.js",
"lib/internal/modules/esm/formats.js",
"lib/internal/modules/esm/get_format.js",
"lib/internal/modules/esm/handle_process_exit.js",
"lib/internal/modules/esm/initialize_import_meta.js",
"lib/internal/modules/esm/load.js",
"lib/internal/modules/esm/loader.js",
"lib/internal/modules/esm/module_job.js",
"lib/internal/modules/esm/module_map.js",
"lib/internal/modules/esm/package_config.js",
"lib/internal/modules/esm/resolve.js",
"lib/internal/modules/esm/translators.js",
"lib/internal/modules/package_json_reader.js",
"lib/internal/modules/run_main.js",
"lib/internal/net.js",
"lib/internal/options.js",
"lib/internal/per_context/domexception.js",
"lib/internal/per_context/messageport.js",
"lib/internal/per_context/primordials.js",
"lib/internal/perf/event_loop_delay.js",
"lib/internal/perf/event_loop_utilization.js",
"lib/internal/perf/nodetiming.js",
"lib/internal/perf/observe.js",
"lib/internal/perf/performance.js",
"lib/internal/perf/performance_entry.js",
"lib/internal/perf/resource_timing.js",
"lib/internal/perf/timerify.js",
"lib/internal/perf/usertiming.js",
"lib/internal/perf/utils.js",
"lib/internal/policy/manifest.js",
"lib/internal/policy/sri.js",
"lib/internal/priority_queue.js",
"lib/internal/process/esm_loader.js",
"lib/internal/process/execution.js",
"lib/internal/process/per_thread.js",
"lib/internal/process/policy.js",
"lib/internal/process/pre_execution.js",
"lib/internal/process/promises.js",
"lib/internal/process/report.js",
"lib/internal/process/signal.js",
"lib/internal/process/task_queues.js",
"lib/internal/process/warning.js",
"lib/internal/process/worker_thread_only.js",
"lib/internal/promise_hooks.js",
"lib/internal/querystring.js",
"lib/internal/readline/callbacks.js",
"lib/internal/readline/emitKeypressEvents.js",
"lib/internal/readline/interface.js",
"lib/internal/readline/promises.js",
"lib/internal/readline/utils.js",
"lib/internal/repl.js",
"lib/internal/repl/await.js",
"lib/internal/repl/history.js",
"lib/internal/repl/utils.js",
"lib/internal/socket_list.js",
"lib/internal/socketaddress.js",
"lib/internal/source_map/prepare_stack_trace.js",
"lib/internal/source_map/source_map.js",
"lib/internal/source_map/source_map_cache.js",
"lib/internal/stream_base_commons.js",
"lib/internal/streams/add-abort-signal.js",
"lib/internal/streams/buffer_list.js",
"lib/internal/streams/compose.js",
"lib/internal/streams/destroy.js",
"lib/internal/streams/duplex.js",
"lib/internal/streams/duplexify.js",
"lib/internal/streams/end-of-stream.js",
"lib/internal/streams/from.js",
"lib/internal/streams/lazy_transform.js",
"lib/internal/streams/legacy.js",
"lib/internal/streams/operators.js",
"lib/internal/streams/passthrough.js",
"lib/internal/streams/pipeline.js",
"lib/internal/streams/readable.js",
"lib/internal/streams/state.js",
"lib/internal/streams/transform.js",
"lib/internal/streams/utils.js",
"lib/internal/streams/writable.js",
"lib/internal/structured_clone.js",
"lib/internal/test/binding.js",
"lib/internal/test/transfer.js",
"lib/internal/test_runner/harness.js",
"lib/internal/test_runner/runner.js",
"lib/internal/test_runner/tap_stream.js",
"lib/internal/test_runner/test.js",
"lib/internal/test_runner/utils.js",
"lib/internal/timers.js",
"lib/internal/tls/secure-context.js",
"lib/internal/tls/secure-pair.js",
"lib/internal/trace_events_async_hooks.js",
"lib/internal/tty.js",
"lib/internal/url.js",
"lib/internal/util.js",
"lib/internal/util/colors.js",
"lib/internal/util/comparisons.js",
"lib/internal/util/debuglog.js",
"lib/internal/util/inspect.js",
"lib/internal/util/inspector.js",
"lib/internal/util/iterable_weak_map.js",
"lib/internal/util/parse_args/parse_args.js",
"lib/internal/util/parse_args/utils.js",
"lib/internal/util/types.js",
"lib/internal/v8/startup_snapshot.js",
"lib/internal/v8_prof_polyfill.js",
"lib/internal/v8_prof_processor.js",
"lib/internal/validators.js",
"lib/internal/vm/module.js",
"lib/internal/wasm_web_api.js",
"lib/internal/watch_mode/files_watcher.js",
"lib/internal/watchdog.js",
"lib/internal/webstreams/adapters.js",
"lib/internal/webstreams/compression.js",
"lib/internal/webstreams/encoding.js",
"lib/internal/webstreams/queuingstrategies.js",
"lib/internal/webstreams/readablestream.js",
"lib/internal/webstreams/transfer.js",
"lib/internal/webstreams/transformstream.js",
"lib/internal/webstreams/util.js",
"lib/internal/webstreams/writablestream.js",
"lib/internal/worker.js",
"lib/internal/worker/io.js",
"lib/internal/worker/js_transferable.js",
"lib/module.js",
"lib/net.js", "lib/net.js",
"lib/os.js", "lib/trace_events.js",
"lib/path.js", "lib/events.js",
"lib/path/posix.js",
"lib/path/win32.js",
"lib/perf_hooks.js",
"lib/process.js",
"lib/punycode.js",
"lib/querystring.js",
"lib/readline.js",
"lib/readline/promises.js",
"lib/repl.js", "lib/repl.js",
"lib/util.js",
"lib/dgram.js",
"lib/vm.js",
"lib/stream.js", "lib/stream.js",
"lib/child_process.js",
"lib/assert.js",
"lib/_tls_wrap.js",
"lib/http2.js",
"lib/inspector.js",
"lib/os.js",
"lib/_http_server.js",
"lib/console.js",
"lib/perf_hooks.js",
"lib/readline.js",
"lib/punycode.js",
"lib/_http_incoming.js",
"lib/https.js",
"lib/_stream_wrap.js",
"lib/domain.js",
"lib/dns.js",
"lib/_http_client.js",
"lib/diagnostics_channel.js",
"lib/tty.js",
"lib/_http_agent.js",
"lib/timers.js",
"lib/_http_outgoing.js",
"lib/querystring.js",
"lib/_tls_common.js",
"lib/module.js",
"lib/_stream_passthrough.js",
"lib/_stream_transform.js",
"lib/worker_threads.js",
"lib/sys.js",
"lib/_stream_duplex.js",
"lib/path.js",
"lib/_http_common.js",
"lib/string_decoder.js",
"lib/cluster.js",
"lib/v8.js",
"lib/crypto.js",
"lib/wasi.js",
"lib/_stream_readable.js",
"lib/zlib.js",
"lib/url.js",
"lib/tls.js",
"lib/_stream_writable.js",
"lib/async_hooks.js",
"lib/process.js",
"lib/http.js",
"lib/buffer.js",
"lib/fs.js",
"lib/util/types.js",
"lib/timers/promises.js",
"lib/path/win32.js",
"lib/path/posix.js",
"lib/stream/consumers.js", "lib/stream/consumers.js",
"lib/stream/promises.js", "lib/stream/promises.js",
"lib/stream/web.js", "lib/stream/web.js",
"lib/string_decoder.js", "lib/internal/constants.js",
"lib/sys.js", "lib/internal/abort_controller.js",
"lib/test.js", "lib/internal/net.js",
"lib/timers.js", "lib/internal/v8_prof_processor.js",
"lib/timers/promises.js", "lib/internal/event_target.js",
"lib/tls.js", "lib/internal/inspector_async_hook.js",
"lib/trace_events.js", "lib/internal/validators.js",
"lib/tty.js", "lib/internal/linkedlist.js",
"lib/url.js", "lib/internal/cli_table.js",
"lib/util.js", "lib/internal/repl.js",
"lib/util/types.js", "lib/internal/util.js",
"lib/v8.js", "lib/internal/histogram.js",
"lib/vm.js", "lib/internal/error_serdes.js",
"lib/wasi.js", "lib/internal/dgram.js",
"lib/worker_threads.js", "lib/internal/child_process.js",
"lib/zlib.js" "lib/internal/assert.js",
"lib/internal/fixed_queue.js",
"lib/internal/blocklist.js",
"lib/internal/v8_prof_polyfill.js",
"lib/internal/options.js",
"lib/internal/worker.js",
"lib/internal/dtrace.js",
"lib/internal/idna.js",
"lib/internal/watchdog.js",
"lib/internal/encoding.js",
"lib/internal/tty.js",
"lib/internal/freeze_intrinsics.js",
"lib/internal/timers.js",
"lib/internal/heap_utils.js",
"lib/internal/querystring.js",
"lib/internal/js_stream_socket.js",
"lib/internal/errors.js",
"lib/internal/priority_queue.js",
"lib/internal/freelist.js",
"lib/internal/blob.js",
"lib/internal/socket_list.js",
"lib/internal/socketaddress.js",
"lib/internal/promise_hooks.js",
"lib/internal/stream_base_commons.js",
"lib/internal/url.js",
"lib/internal/async_hooks.js",
"lib/internal/http.js",
"lib/internal/buffer.js",
"lib/internal/trace_events_async_hooks.js",
"lib/internal/crypto/sig.js",
"lib/internal/crypto/rsa.js",
"lib/internal/crypto/aes.js",
"lib/internal/crypto/util.js",
"lib/internal/crypto/scrypt.js",
"lib/internal/crypto/random.js",
"lib/internal/crypto/keys.js",
"lib/internal/crypto/x509.js",
"lib/internal/crypto/certificate.js",
"lib/internal/crypto/ec.js",
"lib/internal/crypto/keygen.js",
"lib/internal/crypto/mac.js",
"lib/internal/crypto/diffiehellman.js",
"lib/internal/crypto/hkdf.js",
"lib/internal/crypto/cipher.js",
"lib/internal/crypto/hash.js",
"lib/internal/crypto/pbkdf2.js",
"lib/internal/crypto/webcrypto.js",
"lib/internal/crypto/dsa.js",
"lib/internal/crypto/hashnames.js",
"lib/internal/cluster/shared_handle.js",
"lib/internal/cluster/round_robin_handle.js",
"lib/internal/cluster/worker.js",
"lib/internal/cluster/primary.js",
"lib/internal/cluster/utils.js",
"lib/internal/cluster/child.js",
"lib/internal/webstreams/compression.js",
"lib/internal/webstreams/util.js",
"lib/internal/webstreams/writablestream.js",
"lib/internal/webstreams/readablestream.js",
"lib/internal/webstreams/queuingstrategies.js",
"lib/internal/webstreams/encoding.js",
"lib/internal/webstreams/transformstream.js",
"lib/internal/webstreams/adapters.js",
"lib/internal/webstreams/transfer.js",
"lib/internal/bootstrap/loaders.js",
"lib/internal/bootstrap/pre_execution.js",
"lib/internal/bootstrap/node.js",
"lib/internal/bootstrap/environment.js",
"lib/internal/bootstrap/switches/does_not_own_process_state.js",
"lib/internal/bootstrap/switches/is_not_main_thread.js",
"lib/internal/bootstrap/switches/does_own_process_state.js",
"lib/internal/bootstrap/switches/is_main_thread.js",
"lib/internal/test/binding.js",
"lib/internal/test/transfer.js",
"lib/internal/util/types.js",
"lib/internal/util/inspector.js",
"lib/internal/util/comparisons.js",
"lib/internal/util/debuglog.js",
"lib/internal/util/inspect.js",
"lib/internal/util/iterable_weak_map.js",
"lib/internal/streams/add-abort-signal.js",
"lib/internal/streams/compose.js",
"lib/internal/streams/duplexify.js",
"lib/internal/streams/destroy.js",
"lib/internal/streams/legacy.js",
"lib/internal/streams/passthrough.js",
"lib/internal/streams/operators.js",
"lib/internal/streams/readable.js",
"lib/internal/streams/from.js",
"lib/internal/streams/writable.js",
"lib/internal/streams/state.js",
"lib/internal/streams/buffer_list.js",
"lib/internal/streams/end-of-stream.js",
"lib/internal/streams/utils.js",
"lib/internal/streams/transform.js",
"lib/internal/streams/lazy_transform.js",
"lib/internal/streams/duplex.js",
"lib/internal/streams/pipeline.js",
"lib/internal/readline/interface.js",
"lib/internal/readline/utils.js",
"lib/internal/readline/emitKeypressEvents.js",
"lib/internal/readline/callbacks.js",
"lib/internal/repl/history.js",
"lib/internal/repl/utils.js",
"lib/internal/repl/await.js",
"lib/internal/legacy/processbinding.js",
"lib/internal/assert/calltracker.js",
"lib/internal/assert/assertion_error.js",
"lib/internal/http2/util.js",
"lib/internal/http2/core.js",
"lib/internal/http2/compat.js",
"lib/internal/per_context/messageport.js",
"lib/internal/per_context/primordials.js",
"lib/internal/per_context/domexception.js",
"lib/internal/vm/module.js",
"lib/internal/tls/secure-pair.js",
"lib/internal/tls/parse-cert-string.js",
"lib/internal/tls/secure-context.js",
"lib/internal/child_process/serialization.js",
"lib/internal/debugger/inspect_repl.js",
"lib/internal/debugger/inspect_client.js",
"lib/internal/debugger/inspect.js",
"lib/internal/worker/io.js",
"lib/internal/worker/js_transferable.js",
"lib/internal/main/repl.js",
"lib/internal/main/print_help.js",
"lib/internal/main/eval_string.js",
"lib/internal/main/check_syntax.js",
"lib/internal/main/prof_process.js",
"lib/internal/main/worker_thread.js",
"lib/internal/main/inspect.js",
"lib/internal/main/eval_stdin.js",
"lib/internal/main/run_main_module.js",
"lib/internal/modules/run_main.js",
"lib/internal/modules/package_json_reader.js",
"lib/internal/modules/esm/module_job.js",
"lib/internal/modules/esm/assert.js",
"lib/internal/modules/esm/fetch_module.js",
"lib/internal/modules/esm/get_source.js",
"lib/internal/modules/esm/translators.js",
"lib/internal/modules/esm/resolve.js",
"lib/internal/modules/esm/create_dynamic_module.js",
"lib/internal/modules/esm/load.js",
"lib/internal/modules/esm/handle_process_exit.js",
"lib/internal/modules/esm/initialize_import_meta.js",
"lib/internal/modules/esm/module_map.js",
"lib/internal/modules/esm/get_format.js",
"lib/internal/modules/esm/formats.js",
"lib/internal/modules/esm/loader.js",
"lib/internal/modules/cjs/helpers.js",
"lib/internal/modules/cjs/loader.js",
"lib/internal/source_map/source_map.js",
"lib/internal/source_map/prepare_stack_trace.js",
"lib/internal/source_map/source_map_cache.js",
"lib/internal/dns/promises.js",
"lib/internal/dns/utils.js",
"lib/internal/fs/watchers.js",
"lib/internal/fs/promises.js",
"lib/internal/fs/read_file_context.js",
"lib/internal/fs/rimraf.js",
"lib/internal/fs/sync_write_stream.js",
"lib/internal/fs/dir.js",
"lib/internal/fs/streams.js",
"lib/internal/fs/utils.js",
"lib/internal/fs/cp/cp.js",
"lib/internal/fs/cp/cp-sync.js",
"lib/internal/perf/nodetiming.js",
"lib/internal/perf/usertiming.js",
"lib/internal/perf/performance_entry.js",
"lib/internal/perf/performance.js",
"lib/internal/perf/timerify.js",
"lib/internal/perf/utils.js",
"lib/internal/perf/observe.js",
"lib/internal/perf/event_loop_delay.js",
"lib/internal/perf/event_loop_utilization.js",
"lib/internal/policy/manifest.js",
"lib/internal/policy/sri.js",
"lib/internal/process/task_queues.js",
"lib/internal/process/per_thread.js",
"lib/internal/process/warning.js",
"lib/internal/process/policy.js",
"lib/internal/process/promises.js",
"lib/internal/process/signal.js",
"lib/internal/process/execution.js",
"lib/internal/process/esm_loader.js",
"lib/internal/process/report.js",
"lib/internal/process/worker_thread_only.js",
"lib/internal/console/constructor.js",
"lib/internal/console/global.js",
"lib/assert/strict.js",
"lib/dns/promises.js",
"lib/fs/promises.js"
], ],
"node_module_version": 108, "node_module_version": 93,
"node_no_browser_globals": "false", "node_no_browser_globals": "false",
"node_prefix": "/", "node_prefix": "/",
"node_release_urlbase": "https://nodejs.org/download/release/", "node_release_urlbase": "https://nodejs.org/download/release/",
@@ -353,22 +330,20 @@
"node_use_v8_platform": "true", "node_use_v8_platform": "true",
"node_with_ltcg": "false", "node_with_ltcg": "false",
"node_without_node_options": "false", "node_without_node_options": "false",
"openssl_fips": "",
"openssl_is_fips": "false", "openssl_is_fips": "false",
"openssl_quic": "true", "openssl_quic": "true",
"ossfuzz": "false", "ossfuzz": "false",
"shlib_suffix": "108.dylib", "shlib_suffix": "93.dylib",
"target_arch": "x64", "target_arch": "x64",
"v8_enable_31bit_smis_on_64bit_arch": 0, "v8_enable_31bit_smis_on_64bit_arch": 0,
"v8_enable_gdbjit": 0, "v8_enable_gdbjit": 0,
"v8_enable_hugepage": 0, "v8_enable_hugepage": 0,
"v8_enable_i18n_support": 1, "v8_enable_i18n_support": 1,
"v8_enable_inspector": 1, "v8_enable_inspector": 1,
"v8_enable_javascript_promise_hooks": 1,
"v8_enable_lite_mode": 0, "v8_enable_lite_mode": 0,
"v8_enable_object_print": 1, "v8_enable_object_print": 1,
"v8_enable_pointer_compression": 0, "v8_enable_pointer_compression": 0,
"v8_enable_shared_ro_heap": 1,
"v8_enable_short_builtin_calls": 1,
"v8_enable_webassembly": 1, "v8_enable_webassembly": 1,
"v8_no_strict_aliasing": 1, "v8_no_strict_aliasing": 1,
"v8_optimized_debug": 1, "v8_optimized_debug": 1,
@@ -378,8 +353,8 @@
"v8_use_siphash": 1, "v8_use_siphash": 1,
"want_separate_host_toolset": 0, "want_separate_host_toolset": 0,
"xcode_version": "11.0", "xcode_version": "11.0",
"nodedir": "/Users/karolsojko/Library/Caches/node-gyp/18.12.1", "nodedir": "/Users/karolsojko/Library/Caches/node-gyp/16.15.1",
"standalone_static_library": 1, "standalone_static_library": 1,
"user_agent": "yarn/4.0.0-rc.25 npm/? node/v18.12.1 darwin x64" "user_agent": "yarn/3.2.1 npm/? node/v16.15.1 darwin x64"
} }
} }
@@ -25,7 +25,7 @@ DEFS_Debug := \
CFLAGS_Debug := \ CFLAGS_Debug := \
-O0 \ -O0 \
-gdwarf-2 \ -gdwarf-2 \
-mmacosx-version-min=10.15 \ -mmacosx-version-min=10.13 \
-arch x86_64 \ -arch x86_64 \
-Wall \ -Wall \
-Wendif-labels \ -Wendif-labels \
@@ -38,7 +38,7 @@ CFLAGS_C_Debug := \
# Flags passed to only C++ files. # Flags passed to only C++ files.
CFLAGS_CC_Debug := \ CFLAGS_CC_Debug := \
-std=gnu++17 \ -std=gnu++14 \
-stdlib=libc++ \ -stdlib=libc++ \
-fno-rtti \ -fno-rtti \
-fno-exceptions \ -fno-exceptions \
@@ -51,13 +51,13 @@ CFLAGS_OBJC_Debug :=
CFLAGS_OBJCC_Debug := CFLAGS_OBJCC_Debug :=
INCS_Debug := \ INCS_Debug := \
-I/Users/karolsojko/Library/Caches/node-gyp/18.12.1/include/node \ -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node \
-I/Users/karolsojko/Library/Caches/node-gyp/18.12.1/src \ -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/src \
-I/Users/karolsojko/Library/Caches/node-gyp/18.12.1/deps/openssl/config \ -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/deps/openssl/config \
-I/Users/karolsojko/Library/Caches/node-gyp/18.12.1/deps/openssl/openssl/include \ -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/deps/openssl/openssl/include \
-I/Users/karolsojko/Library/Caches/node-gyp/18.12.1/deps/uv/include \ -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/deps/uv/include \
-I/Users/karolsojko/Library/Caches/node-gyp/18.12.1/deps/zlib \ -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/deps/zlib \
-I/Users/karolsojko/Library/Caches/node-gyp/18.12.1/deps/v8/include \ -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/deps/v8/include \
-I$(srcdir)/src \ -I$(srcdir)/src \
-I$(srcdir)/../../../../nan-npm-2.16.0-cac314a230/node_modules/nan -I$(srcdir)/../../../../nan-npm-2.16.0-cac314a230/node_modules/nan
@@ -81,7 +81,7 @@ DEFS_Release := \
CFLAGS_Release := \ CFLAGS_Release := \
-O3 \ -O3 \
-gdwarf-2 \ -gdwarf-2 \
-mmacosx-version-min=10.15 \ -mmacosx-version-min=10.13 \
-arch x86_64 \ -arch x86_64 \
-Wall \ -Wall \
-Wendif-labels \ -Wendif-labels \
@@ -94,7 +94,7 @@ CFLAGS_C_Release := \
# Flags passed to only C++ files. # Flags passed to only C++ files.
CFLAGS_CC_Release := \ CFLAGS_CC_Release := \
-std=gnu++17 \ -std=gnu++14 \
-stdlib=libc++ \ -stdlib=libc++ \
-fno-rtti \ -fno-rtti \
-fno-exceptions \ -fno-exceptions \
@@ -107,13 +107,13 @@ CFLAGS_OBJC_Release :=
CFLAGS_OBJCC_Release := CFLAGS_OBJCC_Release :=
INCS_Release := \ INCS_Release := \
-I/Users/karolsojko/Library/Caches/node-gyp/18.12.1/include/node \ -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/include/node \
-I/Users/karolsojko/Library/Caches/node-gyp/18.12.1/src \ -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/src \
-I/Users/karolsojko/Library/Caches/node-gyp/18.12.1/deps/openssl/config \ -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/deps/openssl/config \
-I/Users/karolsojko/Library/Caches/node-gyp/18.12.1/deps/openssl/openssl/include \ -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/deps/openssl/openssl/include \
-I/Users/karolsojko/Library/Caches/node-gyp/18.12.1/deps/uv/include \ -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/deps/uv/include \
-I/Users/karolsojko/Library/Caches/node-gyp/18.12.1/deps/zlib \ -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/deps/zlib \
-I/Users/karolsojko/Library/Caches/node-gyp/18.12.1/deps/v8/include \ -I/Users/karolsojko/Library/Caches/node-gyp/16.15.1/deps/v8/include \
-I$(srcdir)/src \ -I$(srcdir)/src \
-I$(srcdir)/../../../../nan-npm-2.16.0-cac314a230/node_modules/nan -I$(srcdir)/../../../../nan-npm-2.16.0-cac314a230/node_modules/nan
@@ -151,7 +151,7 @@ $(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cpp FORCE_DO_CMD
LDFLAGS_Debug := \ LDFLAGS_Debug := \
-undefined dynamic_lookup \ -undefined dynamic_lookup \
-Wl,-search_paths_first \ -Wl,-search_paths_first \
-mmacosx-version-min=10.15 \ -mmacosx-version-min=10.13 \
-arch x86_64 \ -arch x86_64 \
-L$(builddir) \ -L$(builddir) \
-stdlib=libc++ -stdlib=libc++
@@ -163,7 +163,7 @@ LIBTOOLFLAGS_Debug := \
LDFLAGS_Release := \ LDFLAGS_Release := \
-undefined dynamic_lookup \ -undefined dynamic_lookup \
-Wl,-search_paths_first \ -Wl,-search_paths_first \
-mmacosx-version-min=10.15 \ -mmacosx-version-min=10.13 \
-arch x86_64 \ -arch x86_64 \
-L$(builddir) \ -L$(builddir) \
-stdlib=libc++ -stdlib=libc++
+4 -5
View File
@@ -8,7 +8,7 @@
] ]
}, },
"engines": { "engines": {
"node": ">=18.0.0 <19.0.0" "node": ">=16.0.0 <17.0.0"
}, },
"scripts": { "scripts": {
"lint": "yarn workspaces foreach -p -j 10 --verbose run lint", "lint": "yarn workspaces foreach -p -j 10 --verbose run lint",
@@ -21,7 +21,6 @@
"lint:websockets": "yarn workspace @standardnotes/websockets-server lint", "lint:websockets": "yarn workspace @standardnotes/websockets-server lint",
"lint:workspace": "yarn workspace @standardnotes/workspace-server lint", "lint:workspace": "yarn workspace @standardnotes/workspace-server lint",
"lint:analytics": "yarn workspace @standardnotes/analytics lint", "lint:analytics": "yarn workspace @standardnotes/analytics lint",
"lint:revisions": "yarn workspace @standardnotes/revisions-server lint",
"clean": "yarn workspaces foreach -p --verbose run clean", "clean": "yarn workspaces foreach -p --verbose run clean",
"setup:env": "cp .env.sample .env && yarn workspaces foreach -p --verbose run setup:env", "setup:env": "cp .env.sample .env && yarn workspaces foreach -p --verbose run setup:env",
"start:auth": "yarn workspace @standardnotes/auth-server start", "start:auth": "yarn workspace @standardnotes/auth-server start",
@@ -35,7 +34,6 @@
"start:websockets": "yarn workspace @standardnotes/websockets-server start", "start:websockets": "yarn workspace @standardnotes/websockets-server start",
"start:workspace": "yarn workspace @standardnotes/workspace-server start", "start:workspace": "yarn workspace @standardnotes/workspace-server start",
"start:analytics": "yarn workspace @standardnotes/analytics worker", "start:analytics": "yarn workspace @standardnotes/analytics worker",
"start:revisions": "yarn workspace @standardnotes/revisions-server start",
"release": "lerna version --conventional-graduate --conventional-commits --yes -m \"chore(release): publish new version\"", "release": "lerna version --conventional-graduate --conventional-commits --yes -m \"chore(release): publish new version\"",
"publish": "lerna publish from-git --yes --no-verify-access --loglevel verbose", "publish": "lerna publish from-git --yes --no-verify-access --loglevel verbose",
"postversion": "./scripts/push-tags-one-by-one.sh", "postversion": "./scripts/push-tags-one-by-one.sh",
@@ -49,7 +47,7 @@
"@lerna-lite/run": "^1.5.1", "@lerna-lite/run": "^1.5.1",
"@types/jest": "^29.1.1", "@types/jest": "^29.1.1",
"@types/newrelic": "^7.0.4", "@types/newrelic": "^7.0.4",
"@types/node": "^18.11.9", "@types/node": "^18.0.0",
"@typescript-eslint/parser": "^5.40.1", "@typescript-eslint/parser": "^5.40.1",
"eslint": "^8.17.0", "eslint": "^8.17.0",
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^8.5.0",
@@ -61,7 +59,8 @@
}, },
"packageManager": "yarn@4.0.0-rc.25", "packageManager": "yarn@4.0.0-rc.25",
"dependencies": { "dependencies": {
"@sentry/node": "^7.28.1", "@newrelic/native-metrics": "^9.0.0",
"@sentry/node": "^7.3.0",
"newrelic": "^9.6.0" "newrelic": "^9.6.0"
} }
} }
-5
View File
@@ -10,8 +10,6 @@ DB_DATABASE=analytics
DB_DEBUG_LEVEL=all # "all" | "query" | "schema" | "error" | "warn" | "info" | "log" | "migration" DB_DEBUG_LEVEL=all # "all" | "query" | "schema" | "error" | "warn" | "info" | "log" | "migration"
DB_MIGRATIONS_PATH=dist/migrations/*.js DB_MIGRATIONS_PATH=dist/migrations/*.js
ADMIN_EMAILS=test@standardnotes.com
REDIS_URL=redis://cache REDIS_URL=redis://cache
REDIS_EVENTS_CHANNEL=events REDIS_EVENTS_CHANNEL=events
@@ -28,6 +26,3 @@ NEW_RELIC_NO_CONFIG_FILE=true
NEW_RELIC_DISTRIBUTED_TRACING_ENABLED=false NEW_RELIC_DISTRIBUTED_TRACING_ENABLED=false
NEW_RELIC_LOG_ENABLED=false NEW_RELIC_LOG_ENABLED=false
NEW_RELIC_LOG_LEVEL=info NEW_RELIC_LOG_LEVEL=info
# (Optional) Mixpanel
MIXPANEL_TOKEN=
+1 -334
View File
@@ -3,343 +3,10 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.19.3](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.19.2...@standardnotes/analytics@2.19.3) (2023-01-16) ## [2.9.7](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.9.6...@standardnotes/analytics@2.9.7) (2022-11-14)
**Note:** Version bump only for package @standardnotes/analytics **Note:** Version bump only for package @standardnotes/analytics
## [2.19.2](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.19.1...@standardnotes/analytics@2.19.2) (2023-01-13)
**Note:** Version bump only for package @standardnotes/analytics
## [2.19.1](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.19.0...@standardnotes/analytics@2.19.1) (2022-12-30)
### Bug Fixes
* **analytics:** remove unnecesary context from mixpanel events ([ba1e1ad](https://github.com/standardnotes/server/commit/ba1e1ad5ad82b052be4cc2d1cc2abdaf3b72cf4c))
# [2.19.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.18.0...@standardnotes/analytics@2.19.0) (2022-12-30)
### Features
* **analytics:** add mixpanel events tracking ([df6e3f0](https://github.com/standardnotes/server/commit/df6e3f06a6868e30e60dd98431122983724644b4))
# [2.18.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.17.8...@standardnotes/analytics@2.18.0) (2022-12-30)
### Features
* **analytics:** add mixpanel ([893d617](https://github.com/standardnotes/server/commit/893d6176c3b0b56c45e5188fe982232db2ceedc4))
## [2.17.8](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.17.7...@standardnotes/analytics@2.17.8) (2022-12-28)
**Note:** Version bump only for package @standardnotes/analytics
## [2.17.7](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.17.6...@standardnotes/analytics@2.17.7) (2022-12-20)
### Bug Fixes
* **analytics:** monthly numbers of active users ([b34bbca](https://github.com/standardnotes/server/commit/b34bbcac8b9604283b3a5959ab3218c468ce8a00))
## [2.17.6](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.17.5...@standardnotes/analytics@2.17.6) (2022-12-20)
### Bug Fixes
* **analytics:** filtered counts for user activity check ([17b2ea1](https://github.com/standardnotes/server/commit/17b2ea126c5ad2d7cf07657def63f9977f239a3c))
## [2.17.5](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.17.4...@standardnotes/analytics@2.17.5) (2022-12-20)
### Bug Fixes
* **analytics:** accessing analytics in report ([ef26dc8](https://github.com/standardnotes/server/commit/ef26dc8cbb967e088ae7387ff6dbec1e60dc3ee4))
## [2.17.4](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.17.3...@standardnotes/analytics@2.17.4) (2022-12-20)
**Note:** Version bump only for package @standardnotes/analytics
## [2.17.3](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.17.2...@standardnotes/analytics@2.17.3) (2022-12-20)
### Bug Fixes
* **analytics:** add debug logs for the report ([031fcd7](https://github.com/standardnotes/server/commit/031fcd75eecdcf4c2f17257754a0ba3f24ba6d6e))
## [2.17.2](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.17.1...@standardnotes/analytics@2.17.2) (2022-12-20)
### Bug Fixes
* **analytics:** calculating active users ([a304993](https://github.com/standardnotes/server/commit/a3049938a31e21a5867a314ac62bee6aa4990d57))
## [2.17.1](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.17.0...@standardnotes/analytics@2.17.1) (2022-12-20)
### Bug Fixes
* **analytics:** container binding ([04ffc69](https://github.com/standardnotes/server/commit/04ffc69e000803107d8834c286de97b3d213a842))
* **auth:** replace date object with number timestamp ([5b4bb6e](https://github.com/standardnotes/server/commit/5b4bb6e7a78a1b0f4e663bb990619f65f6a5c757))
# [2.17.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.16.0...@standardnotes/analytics@2.17.0) (2022-12-20)
### Features
* **analytics:** add users activit to the report email ([ed5a4eb](https://github.com/standardnotes/server/commit/ed5a4eb960a6c8fe9d0c77331f29dc3c7ffb9100))
# [2.16.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.15.1...@standardnotes/analytics@2.16.0) (2022-12-20)
### Features
* **analytics:** add active users stats to report ([6e16620](https://github.com/standardnotes/server/commit/6e1662038c3340fb60939464616789bab7639160))
## [2.15.1](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.15.0...@standardnotes/analytics@2.15.1) (2022-12-20)
### Bug Fixes
* **auth:** add persisting statistics for all subscription plans ([addedb3](https://github.com/standardnotes/server/commit/addedb3091ddae81618d56663e18f2ae76a43c4e))
# [2.15.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.14.0...@standardnotes/analytics@2.15.0) (2022-12-19)
### Features
* **auth:** add requesting persisting statistics ([a35271f](https://github.com/standardnotes/server/commit/a35271fbb399b68a3ac7021395d8063707fba222))
# [2.14.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.13.0...@standardnotes/analytics@2.14.0) (2022-12-19)
### Features
* **analytics:** add persisting statistics on demand ([0f84575](https://github.com/standardnotes/server/commit/0f8457534c1829c58f3c036749d262307ddeb779))
# [2.13.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.27...@standardnotes/analytics@2.13.0) (2022-12-19)
### Features
* **auth:** add session traces ([8bcb552](https://github.com/standardnotes/server/commit/8bcb552783b2d12f3296b3195752168482790bc8))
## [2.12.27](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.26...@standardnotes/analytics@2.12.27) (2022-12-15)
**Note:** Version bump only for package @standardnotes/analytics
## [2.12.26](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.25...@standardnotes/analytics@2.12.26) (2022-12-15)
**Note:** Version bump only for package @standardnotes/analytics
## [2.12.25](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.24...@standardnotes/analytics@2.12.25) (2022-12-12)
**Note:** Version bump only for package @standardnotes/analytics
## [2.12.24](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.23...@standardnotes/analytics@2.12.24) (2022-12-12)
### Bug Fixes
* **analytics:** daily analytics report template ([41906ec](https://github.com/standardnotes/server/commit/41906ec2f9fd4d605b1c002826173e14fb534e00))
## [2.12.23](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.22...@standardnotes/analytics@2.12.23) (2022-12-12)
**Note:** Version bump only for package @standardnotes/analytics
## [2.12.22](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.21...@standardnotes/analytics@2.12.22) (2022-12-12)
### Bug Fixes
* **analytics:** report event publishing ([a605660](https://github.com/standardnotes/server/commit/a6056600eb96bf175189ad6d62870c9d736f331b))
## [2.12.21](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.20...@standardnotes/analytics@2.12.21) (2022-12-12)
### Bug Fixes
* **analytics:** add debug logs for report ([48c0cb5](https://github.com/standardnotes/server/commit/48c0cb5e62dc8af930de191deaa1eb3ff6c5a29f))
## [2.12.20](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.19...@standardnotes/analytics@2.12.20) (2022-12-09)
**Note:** Version bump only for package @standardnotes/analytics
## [2.12.19](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.18...@standardnotes/analytics@2.12.19) (2022-12-09)
**Note:** Version bump only for package @standardnotes/analytics
## [2.12.18](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.17...@standardnotes/analytics@2.12.18) (2022-12-09)
**Note:** Version bump only for package @standardnotes/analytics
## [2.12.17](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.16...@standardnotes/analytics@2.12.17) (2022-12-09)
**Note:** Version bump only for package @standardnotes/analytics
## [2.12.16](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.15...@standardnotes/analytics@2.12.16) (2022-12-09)
**Note:** Version bump only for package @standardnotes/analytics
## [2.12.15](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.14...@standardnotes/analytics@2.12.15) (2022-12-09)
**Note:** Version bump only for package @standardnotes/analytics
## [2.12.14](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.13...@standardnotes/analytics@2.12.14) (2022-12-09)
**Note:** Version bump only for package @standardnotes/analytics
## [2.12.13](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.12...@standardnotes/analytics@2.12.13) (2022-12-09)
**Note:** Version bump only for package @standardnotes/analytics
## [2.12.12](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.11...@standardnotes/analytics@2.12.12) (2022-12-08)
**Note:** Version bump only for package @standardnotes/analytics
## [2.12.11](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.10...@standardnotes/analytics@2.12.11) (2022-12-08)
**Note:** Version bump only for package @standardnotes/analytics
## [2.12.10](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.9...@standardnotes/analytics@2.12.10) (2022-12-08)
**Note:** Version bump only for package @standardnotes/analytics
## [2.12.9](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.8...@standardnotes/analytics@2.12.9) (2022-12-08)
**Note:** Version bump only for package @standardnotes/analytics
## [2.12.8](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.7...@standardnotes/analytics@2.12.8) (2022-12-08)
**Note:** Version bump only for package @standardnotes/analytics
## [2.12.7](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.6...@standardnotes/analytics@2.12.7) (2022-12-07)
**Note:** Version bump only for package @standardnotes/analytics
## [2.12.6](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.5...@standardnotes/analytics@2.12.6) (2022-12-07)
**Note:** Version bump only for package @standardnotes/analytics
## [2.12.5](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.4...@standardnotes/analytics@2.12.5) (2022-12-07)
**Note:** Version bump only for package @standardnotes/analytics
## [2.12.4](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.3...@standardnotes/analytics@2.12.4) (2022-12-07)
**Note:** Version bump only for package @standardnotes/analytics
## [2.12.3](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.2...@standardnotes/analytics@2.12.3) (2022-12-06)
**Note:** Version bump only for package @standardnotes/analytics
## [2.12.2](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.1...@standardnotes/analytics@2.12.2) (2022-12-05)
**Note:** Version bump only for package @standardnotes/analytics
## [2.12.1](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.0...@standardnotes/analytics@2.12.1) (2022-12-05)
**Note:** Version bump only for package @standardnotes/analytics
# [2.12.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.11.17...@standardnotes/analytics@2.12.0) (2022-12-05)
### Features
* **domain-core:** distinguish between username and email ([06fd404](https://github.com/standardnotes/server/commit/06fd404d44b44a53733f889aabd4da63f21e2f36))
## [2.11.17](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.11.16...@standardnotes/analytics@2.11.17) (2022-12-02)
**Note:** Version bump only for package @standardnotes/analytics
## [2.11.16](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.11.15...@standardnotes/analytics@2.11.16) (2022-12-02)
**Note:** Version bump only for package @standardnotes/analytics
## [2.11.15](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.11.14...@standardnotes/analytics@2.11.15) (2022-11-30)
**Note:** Version bump only for package @standardnotes/analytics
## [2.11.14](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.11.13...@standardnotes/analytics@2.11.14) (2022-11-28)
**Note:** Version bump only for package @standardnotes/analytics
## [2.11.13](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.11.12...@standardnotes/analytics@2.11.13) (2022-11-25)
**Note:** Version bump only for package @standardnotes/analytics
## [2.11.12](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.11.11...@standardnotes/analytics@2.11.12) (2022-11-24)
**Note:** Version bump only for package @standardnotes/analytics
## [2.11.11](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.11.10...@standardnotes/analytics@2.11.11) (2022-11-24)
**Note:** Version bump only for package @standardnotes/analytics
## [2.11.10](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.11.9...@standardnotes/analytics@2.11.10) (2022-11-24)
**Note:** Version bump only for package @standardnotes/analytics
## [2.11.9](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.11.8...@standardnotes/analytics@2.11.9) (2022-11-24)
**Note:** Version bump only for package @standardnotes/analytics
## [2.11.8](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.11.7...@standardnotes/analytics@2.11.8) (2022-11-23)
**Note:** Version bump only for package @standardnotes/analytics
## [2.11.7](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.11.6...@standardnotes/analytics@2.11.7) (2022-11-23)
### Bug Fixes
* binding of sns and sqs with additional config ([74bc791](https://github.com/standardnotes/server/commit/74bc79116bc50d9a5af1a558db1b7108dcda6d0e))
## [2.11.6](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.11.5...@standardnotes/analytics@2.11.6) (2022-11-22)
**Note:** Version bump only for package @standardnotes/analytics
## [2.11.5](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.11.4...@standardnotes/analytics@2.11.5) (2022-11-21)
**Note:** Version bump only for package @standardnotes/analytics
## [2.11.4](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.11.3...@standardnotes/analytics@2.11.4) (2022-11-21)
**Note:** Version bump only for package @standardnotes/analytics
## [2.11.3](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.11.2...@standardnotes/analytics@2.11.3) (2022-11-18)
**Note:** Version bump only for package @standardnotes/analytics
## [2.11.2](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.11.1...@standardnotes/analytics@2.11.2) (2022-11-18)
### Bug Fixes
* **analytics:** specs ([507d43b](https://github.com/standardnotes/server/commit/507d43b3289d1e178644df6d3e15d1d55e56c7bb))
## [2.11.1](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.11.0...@standardnotes/analytics@2.11.1) (2022-11-18)
### Bug Fixes
* mapper interface imports ([1ec0723](https://github.com/standardnotes/server/commit/1ec072373d640c4e2f24b9bb12fec0c678b48032))
# [2.11.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.10.3...@standardnotes/analytics@2.11.0) (2022-11-16)
### Features
* **analytics:** add publishing churn calculation values in the report ([6c43a33](https://github.com/standardnotes/server/commit/6c43a331d09c2dcf1300742509da6a1d8ef2f5b7))
## [2.10.3](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.10.2...@standardnotes/analytics@2.10.3) (2022-11-16)
### Bug Fixes
* **analytics:** exclude five year plans from mrr stats ([fe1b2a0](https://github.com/standardnotes/server/commit/fe1b2a0e0744417e592f3f61f42610765b416ce6))
## [2.10.2](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.10.1...@standardnotes/analytics@2.10.2) (2022-11-14)
### Bug Fixes
* **analytics:** imports from domain-core ([15dfd6d](https://github.com/standardnotes/server/commit/15dfd6dcba75a772000eeb01b78a532067b01d5b))
## [2.10.1](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.10.0...@standardnotes/analytics@2.10.1) (2022-11-14)
### Bug Fixes
* **syncing-server:** retrieving revisions ([50f7ae3](https://github.com/standardnotes/server/commit/50f7ae338ad66d3465fa16c31e7c47c57b1e0c3c))
# [2.10.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.9.9...@standardnotes/analytics@2.10.0) (2022-11-14)
### Features
* **analytics:** extract domain core into a separate package ([0f94e2a](https://github.com/standardnotes/server/commit/0f94e2ad0c8927733eac31f130cbe649dce765f9))
## [2.9.9](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.9.6...@standardnotes/analytics@2.9.9) (2022-11-14)
### Bug Fixes
* **analytics:** bump version ([8715fe1](https://github.com/standardnotes/server/commit/8715fe182221f67f02b5f49e566366db3db581f4))
## [2.9.6](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.9.5...@standardnotes/analytics@2.9.6) (2022-11-14) ## [2.9.6](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.9.5...@standardnotes/analytics@2.9.6) (2022-11-14)
**Note:** Version bump only for package @standardnotes/analytics **Note:** Version bump only for package @standardnotes/analytics
+1 -1
View File
@@ -1,4 +1,4 @@
FROM node:18.12.1-alpine FROM node:16.15.1-alpine
RUN apk add --update \ RUN apk add --update \
curl \ curl \
+37 -72
View File
@@ -4,10 +4,10 @@ import 'newrelic'
import { Logger } from 'winston' import { Logger } from 'winston'
import { EmailLevel } from '@standardnotes/domain-core'
import { DomainEventPublisherInterface } from '@standardnotes/domain-events' import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
import { AnalyticsActivity } from '../src/Domain/Analytics/AnalyticsActivity' import { AnalyticsActivity } from '../src/Domain/Analytics/AnalyticsActivity'
import { Period } from '../src/Domain/Time/Period' import { Period } from '../src/Domain/Time/Period'
import { StatisticsMeasure } from '../src/Domain/Statistics/StatisticsMeasure'
import { AnalyticsStoreInterface } from '../src/Domain/Analytics/AnalyticsStoreInterface' import { AnalyticsStoreInterface } from '../src/Domain/Analytics/AnalyticsStoreInterface'
import { StatisticsStoreInterface } from '../src/Domain/Statistics/StatisticsStoreInterface' import { StatisticsStoreInterface } from '../src/Domain/Statistics/StatisticsStoreInterface'
import { PeriodKeyGeneratorInterface } from '../src/Domain/Time/PeriodKeyGeneratorInterface' import { PeriodKeyGeneratorInterface } from '../src/Domain/Time/PeriodKeyGeneratorInterface'
@@ -16,9 +16,6 @@ import TYPES from '../src/Bootstrap/Types'
import { Env } from '../src/Bootstrap/Env' import { Env } from '../src/Bootstrap/Env'
import { DomainEventFactoryInterface } from '../src/Domain/Event/DomainEventFactoryInterface' import { DomainEventFactoryInterface } from '../src/Domain/Event/DomainEventFactoryInterface'
import { CalculateMonthlyRecurringRevenue } from '../src/Domain/UseCase/CalculateMonthlyRecurringRevenue/CalculateMonthlyRecurringRevenue' import { CalculateMonthlyRecurringRevenue } from '../src/Domain/UseCase/CalculateMonthlyRecurringRevenue/CalculateMonthlyRecurringRevenue'
import { getBody, getSubject } from '../src/Domain/Email/DailyAnalyticsReport'
import { TimerInterface } from '@standardnotes/time'
import { StatisticMeasureName } from '../src/Domain/Statistics/StatisticMeasureName'
const requestReport = async ( const requestReport = async (
analyticsStore: AnalyticsStoreInterface, analyticsStore: AnalyticsStoreInterface,
@@ -27,8 +24,6 @@ const requestReport = async (
domainEventPublisher: DomainEventPublisherInterface, domainEventPublisher: DomainEventPublisherInterface,
periodKeyGenerator: PeriodKeyGeneratorInterface, periodKeyGenerator: PeriodKeyGeneratorInterface,
calculateMonthlyRecurringRevenue: CalculateMonthlyRecurringRevenue, calculateMonthlyRecurringRevenue: CalculateMonthlyRecurringRevenue,
timer: TimerInterface,
adminEmails: string[],
): Promise<void> => { ): Promise<void> => {
await calculateMonthlyRecurringRevenue.execute({}) await calculateMonthlyRecurringRevenue.execute({})
@@ -115,16 +110,12 @@ const requestReport = async (
}> = [] }> = []
const thirtyDaysStatisticsNames = [ const thirtyDaysStatisticsNames = [
StatisticMeasureName.NAMES.MRR, StatisticsMeasure.MRR,
StatisticMeasureName.NAMES.AnnualPlansMRR, StatisticsMeasure.AnnualPlansMRR,
StatisticMeasureName.NAMES.MonthlyPlansMRR, StatisticsMeasure.MonthlyPlansMRR,
StatisticMeasureName.NAMES.FiveYearPlansMRR, StatisticsMeasure.FiveYearPlansMRR,
StatisticMeasureName.NAMES.PlusPlansMRR, StatisticsMeasure.PlusPlansMRR,
StatisticMeasureName.NAMES.ProPlansMRR, StatisticsMeasure.ProPlansMRR,
StatisticMeasureName.NAMES.ActiveUsers,
StatisticMeasureName.NAMES.ActiveFreeUsers,
StatisticMeasureName.NAMES.ActivePlusUsers,
StatisticMeasureName.NAMES.ActiveProUsers,
] ]
for (const statisticName of thirtyDaysStatisticsNames) { for (const statisticName of thirtyDaysStatisticsNames) {
statisticsOverTime.push({ statisticsOverTime.push({
@@ -134,7 +125,7 @@ const requestReport = async (
}) })
} }
const monthlyStatisticsNames = [StatisticMeasureName.NAMES.MRR] const monthlyStatisticsNames = [StatisticsMeasure.MRR]
for (const statisticName of monthlyStatisticsNames) { for (const statisticName of monthlyStatisticsNames) {
statisticsOverTime.push({ statisticsOverTime.push({
name: statisticName, name: statisticName,
@@ -144,22 +135,22 @@ const requestReport = async (
} }
const statisticMeasureNames = [ const statisticMeasureNames = [
StatisticMeasureName.NAMES.Income, StatisticsMeasure.Income,
StatisticMeasureName.NAMES.PlusSubscriptionInitialAnnualPaymentsIncome, StatisticsMeasure.PlusSubscriptionInitialAnnualPaymentsIncome,
StatisticMeasureName.NAMES.PlusSubscriptionInitialMonthlyPaymentsIncome, StatisticsMeasure.PlusSubscriptionInitialMonthlyPaymentsIncome,
StatisticMeasureName.NAMES.PlusSubscriptionRenewingAnnualPaymentsIncome, StatisticsMeasure.PlusSubscriptionRenewingAnnualPaymentsIncome,
StatisticMeasureName.NAMES.PlusSubscriptionRenewingMonthlyPaymentsIncome, StatisticsMeasure.PlusSubscriptionRenewingMonthlyPaymentsIncome,
StatisticMeasureName.NAMES.ProSubscriptionInitialAnnualPaymentsIncome, StatisticsMeasure.ProSubscriptionInitialAnnualPaymentsIncome,
StatisticMeasureName.NAMES.ProSubscriptionInitialMonthlyPaymentsIncome, StatisticsMeasure.ProSubscriptionInitialMonthlyPaymentsIncome,
StatisticMeasureName.NAMES.ProSubscriptionRenewingAnnualPaymentsIncome, StatisticsMeasure.ProSubscriptionRenewingAnnualPaymentsIncome,
StatisticMeasureName.NAMES.ProSubscriptionRenewingMonthlyPaymentsIncome, StatisticsMeasure.ProSubscriptionRenewingMonthlyPaymentsIncome,
StatisticMeasureName.NAMES.Refunds, StatisticsMeasure.Refunds,
StatisticMeasureName.NAMES.RegistrationLength, StatisticsMeasure.RegistrationLength,
StatisticMeasureName.NAMES.SubscriptionLength, StatisticsMeasure.SubscriptionLength,
StatisticMeasureName.NAMES.RegistrationToSubscriptionTime, StatisticsMeasure.RegistrationToSubscriptionTime,
StatisticMeasureName.NAMES.RemainingSubscriptionTimePercentage, StatisticsMeasure.RemainingSubscriptionTimePercentage,
StatisticMeasureName.NAMES.NewCustomers, StatisticsMeasure.NewCustomers,
StatisticMeasureName.NAMES.TotalCustomers, StatisticsMeasure.TotalCustomers,
] ]
const statisticMeasures: Array<{ const statisticMeasures: Array<{
name: string name: string
@@ -184,9 +175,6 @@ const requestReport = async (
const churnRates: Array<{ const churnRates: Array<{
rate: number rate: number
periodKey: string periodKey: string
averageCustomersCount: number
existingCustomersChurn: number
newCustomersChurn: number
}> = [] }> = []
for (const monthPeriodKey of monthlyPeriodKeys) { for (const monthPeriodKey of monthlyPeriodKeys) {
const monthPeriod = periodKeyGenerator.convertPeriodKeyToPeriod(monthPeriodKey) const monthPeriod = periodKeyGenerator.convertPeriodKeyToPeriod(monthPeriodKey)
@@ -194,10 +182,7 @@ const requestReport = async (
const totalCustomerCounts: Array<number> = [] const totalCustomerCounts: Array<number> = []
for (const dailyPeriodKey of dailyPeriodKeys) { for (const dailyPeriodKey of dailyPeriodKeys) {
const customersCount = await statisticsStore.getMeasureTotal( const customersCount = await statisticsStore.getMeasureTotal(StatisticsMeasure.TotalCustomers, dailyPeriodKey)
StatisticMeasureName.NAMES.TotalCustomers,
dailyPeriodKey,
)
totalCustomerCounts.push(customersCount) totalCustomerCounts.push(customersCount)
} }
const filteredTotalCustomerCounts = totalCustomerCounts.filter((count) => !!count) const filteredTotalCustomerCounts = totalCustomerCounts.filter((count) => !!count)
@@ -219,35 +204,21 @@ const requestReport = async (
churnRates.push({ churnRates.push({
periodKey: monthPeriodKey, periodKey: monthPeriodKey,
rate: averageCustomersCount ? (totalChurn / averageCustomersCount) * 100 : 0, rate: averageCustomersCount ? (totalChurn / averageCustomersCount) * 100 : 0,
averageCustomersCount,
existingCustomersChurn,
newCustomersChurn,
}) })
} }
for (const adminEmail of adminEmails) { const event = domainEventFactory.createDailyAnalyticsReportGeneratedEvent({
await domainEventPublisher.publish( activityStatistics: yesterdayActivityStatistics,
domainEventFactory.createEmailRequestedEvent({ activityStatisticsOverTime: analyticsOverTime,
messageIdentifier: 'VERSION_ADOPTION_REPORT', statisticsOverTime,
subject: getSubject(), statisticMeasures,
body: getBody( churn: {
{ periodKeys: monthlyPeriodKeys,
activityStatistics: yesterdayActivityStatistics, values: churnRates,
activityStatisticsOverTime: analyticsOverTime, },
statisticsOverTime, })
statisticMeasures,
churn: { await domainEventPublisher.publish(event)
periodKeys: monthlyPeriodKeys,
values: churnRates,
},
},
timer,
),
level: EmailLevel.LEVELS.System,
userEmail: adminEmail,
}),
)
}
} }
const container = new ContainerConfigLoader() const container = new ContainerConfigLoader()
@@ -264,13 +235,9 @@ void container.load().then((container) => {
const domainEventFactory: DomainEventFactoryInterface = container.get(TYPES.DomainEventFactory) const domainEventFactory: DomainEventFactoryInterface = container.get(TYPES.DomainEventFactory)
const domainEventPublisher: DomainEventPublisherInterface = container.get(TYPES.DomainEventPublisher) const domainEventPublisher: DomainEventPublisherInterface = container.get(TYPES.DomainEventPublisher)
const periodKeyGenerator: PeriodKeyGeneratorInterface = container.get(TYPES.PeriodKeyGenerator) const periodKeyGenerator: PeriodKeyGeneratorInterface = container.get(TYPES.PeriodKeyGenerator)
const timer: TimerInterface = container.get(TYPES.Timer)
const calculateMonthlyRecurringRevenue: CalculateMonthlyRecurringRevenue = container.get( const calculateMonthlyRecurringRevenue: CalculateMonthlyRecurringRevenue = container.get(
TYPES.CalculateMonthlyRecurringRevenue, TYPES.CalculateMonthlyRecurringRevenue,
) )
const adminEmails = container.get(TYPES.ADMIN_EMAILS) as string[]
logger.info(`Sending report to following admins: ${adminEmails}`)
Promise.resolve( Promise.resolve(
requestReport( requestReport(
@@ -280,8 +247,6 @@ void container.load().then((container) => {
domainEventPublisher, domainEventPublisher,
periodKeyGenerator, periodKeyGenerator,
calculateMonthlyRecurringRevenue, calculateMonthlyRecurringRevenue,
timer,
adminEmails,
), ),
) )
.then(() => { .then(() => {
+3 -3
View File
@@ -5,17 +5,17 @@ COMMAND=$1 && shift 1
case "$COMMAND" in case "$COMMAND" in
'start-worker' ) 'start-worker' )
echo "[Docker] Starting Worker..." echo "Starting Worker..."
yarn workspace @standardnotes/analytics worker yarn workspace @standardnotes/analytics worker
;; ;;
'report' ) 'report' )
echo "[Docker] Starting Usage Report Generation..." echo "Starting Usage Report Generation..."
yarn workspace @standardnotes/analytics report yarn workspace @standardnotes/analytics report
;; ;;
* ) * )
echo "[Docker] Unknown command" echo "Unknown command"
;; ;;
esac esac
+1 -1
View File
@@ -7,5 +7,5 @@ module.exports = {
transform: { transform: {
...tsjPreset.transform, ...tsjPreset.transform,
}, },
coveragePathIgnorePatterns: ['/Infra/', '/Domain/Email/', '/Handler/'], coveragePathIgnorePatterns: ['/Infra/'],
} }
+12 -11
View File
@@ -1,8 +1,8 @@
{ {
"name": "@standardnotes/analytics", "name": "@standardnotes/analytics",
"version": "2.19.3", "version": "2.9.7",
"engines": { "engines": {
"node": ">=18.0.0 <19.0.0" "node": ">=14.0.0 <17.0.0"
}, },
"private": true, "private": true,
"description": "Analytics tools for Standard Notes projects", "description": "Analytics tools for Standard Notes projects",
@@ -25,11 +25,11 @@
"typeorm": "typeorm-ts-node-commonjs" "typeorm": "typeorm-ts-node-commonjs"
}, },
"devDependencies": { "devDependencies": {
"@types/ioredis": "^5.0.0", "@types/ioredis": "^4.28.10",
"@types/jest": "^29.1.1", "@types/jest": "^29.1.1",
"@types/mixpanel": "^2.14.4",
"@types/newrelic": "^7.0.4", "@types/newrelic": "^7.0.4",
"@types/node": "^18.11.9", "@types/node": "^18.0.0",
"@types/uuid": "^8.3.0",
"@typescript-eslint/eslint-plugin": "^5.30.0", "@typescript-eslint/eslint-plugin": "^5.30.0",
"eslint": "^8.14.0", "eslint": "^8.14.0",
"eslint-plugin-prettier": "^4.2.1", "eslint-plugin-prettier": "^4.2.1",
@@ -38,23 +38,24 @@
"typescript": "^4.8.4" "typescript": "^4.8.4"
}, },
"dependencies": { "dependencies": {
"@newrelic/native-metrics": "^9.0.0",
"@newrelic/winston-enricher": "^4.0.0", "@newrelic/winston-enricher": "^4.0.0",
"@sentry/node": "^7.28.1", "@sentry/node": "^7.3.0",
"@standardnotes/common": "workspace:*", "@standardnotes/common": "workspace:*",
"@standardnotes/domain-core": "workspace:^",
"@standardnotes/domain-events": "workspace:*", "@standardnotes/domain-events": "workspace:*",
"@standardnotes/domain-events-infra": "workspace:*", "@standardnotes/domain-events-infra": "workspace:*",
"@standardnotes/time": "workspace:*", "@standardnotes/time": "workspace:*",
"aws-sdk": "^2.1260.0", "aws-sdk": "^2.1158.0",
"dayjs": "^1.11.6", "dayjs": "^1.11.6",
"dotenv": "^16.0.1", "dotenv": "^16.0.1",
"inversify": "^6.0.1", "inversify": "^6.0.1",
"ioredis": "^5.2.4", "ioredis": "^5.2.3",
"mixpanel": "^0.17.0",
"mysql2": "^2.3.3", "mysql2": "^2.3.3",
"newrelic": "^9.6.0", "newrelic": "^9.6.0",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"typeorm": "^0.3.10", "shallow-equal-object": "^1.1.1",
"typeorm": "^0.3.6",
"uuid": "^9.0.0",
"winston": "^3.8.1" "winston": "^3.8.1"
} }
} }
+32 -70
View File
@@ -7,9 +7,6 @@ import {
DomainEventMessageHandlerInterface, DomainEventMessageHandlerInterface,
DomainEventSubscriberFactoryInterface, DomainEventSubscriberFactoryInterface,
} from '@standardnotes/domain-events' } from '@standardnotes/domain-events'
import { MapperInterface } from '@standardnotes/domain-core'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const Mixpanel = require('mixpanel')
import { Env } from './Env' import { Env } from './Env'
import TYPES from './Types' import TYPES from './Types'
@@ -50,13 +47,11 @@ import { RefundProcessedEventHandler } from '../Domain/Handler/RefundProcessedEv
import { RevenueModificationRepositoryInterface } from '../Domain/Revenue/RevenueModificationRepositoryInterface' import { RevenueModificationRepositoryInterface } from '../Domain/Revenue/RevenueModificationRepositoryInterface'
import { MySQLRevenueModificationRepository } from '../Infra/MySQL/MySQLRevenueModificationRepository' import { MySQLRevenueModificationRepository } from '../Infra/MySQL/MySQLRevenueModificationRepository'
import { TypeORMRevenueModification } from '../Infra/TypeORM/TypeORMRevenueModification' import { TypeORMRevenueModification } from '../Infra/TypeORM/TypeORMRevenueModification'
import { MapInterface } from '../Domain/Map/MapInterface'
import { RevenueModification } from '../Domain/Revenue/RevenueModification' import { RevenueModification } from '../Domain/Revenue/RevenueModification'
import { RevenueModificationMap } from '../Domain/Map/RevenueModificationMap' import { RevenueModificationMap } from '../Domain/Map/RevenueModificationMap'
import { SaveRevenueModification } from '../Domain/UseCase/SaveRevenueModification/SaveRevenueModification' import { SaveRevenueModification } from '../Domain/UseCase/SaveRevenueModification/SaveRevenueModification'
import { CalculateMonthlyRecurringRevenue } from '../Domain/UseCase/CalculateMonthlyRecurringRevenue/CalculateMonthlyRecurringRevenue' import { CalculateMonthlyRecurringRevenue } from '../Domain/UseCase/CalculateMonthlyRecurringRevenue/CalculateMonthlyRecurringRevenue'
import { PersistStatistic } from '../Domain/UseCase/PersistStatistic/PersistStatistic'
import { StatisticMeasureRepositoryInterface } from '../Domain/Statistics/StatisticMeasureRepositoryInterface'
import { StatisticPersistenceRequestedEventHandler } from '../Domain/Handler/StatisticPersistenceRequestedEventHandler'
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
const newrelicFormatter = require('@newrelic/winston-enricher') const newrelicFormatter = require('@newrelic/winston-enricher')
@@ -94,24 +89,13 @@ export class ContainerConfigLoader {
}) })
container.bind<winston.Logger>(TYPES.Logger).toConstantValue(logger) container.bind<winston.Logger>(TYPES.Logger).toConstantValue(logger)
if (env.get('SNS_TOPIC_ARN', true)) { if (env.get('SNS_AWS_REGION', true)) {
const snsConfig: AWS.SNS.Types.ClientConfiguration = { container.bind<AWS.SNS>(TYPES.SNS).toConstantValue(
apiVersion: 'latest', new AWS.SNS({
region: env.get('SNS_AWS_REGION', true), apiVersion: 'latest',
} region: env.get('SNS_AWS_REGION', true),
if (env.get('SNS_ENDPOINT', true)) { }),
snsConfig.endpoint = env.get('SNS_ENDPOINT', true) )
}
if (env.get('SNS_DISABLE_SSL', true) === 'true') {
snsConfig.sslEnabled = false
}
if (env.get('SNS_ACCESS_KEY_ID', true) && env.get('SNS_SECRET_ACCESS_KEY', true)) {
snsConfig.credentials = {
accessKeyId: env.get('SNS_ACCESS_KEY_ID', true),
secretAccessKey: env.get('SNS_SECRET_ACCESS_KEY', true),
}
}
container.bind<AWS.SNS>(TYPES.SNS).toConstantValue(new AWS.SNS(snsConfig))
} }
if (env.get('SQS_QUEUE_URL', true)) { if (env.get('SQS_QUEUE_URL', true)) {
@@ -135,34 +119,6 @@ export class ContainerConfigLoader {
container.bind(TYPES.SQS_QUEUE_URL).toConstantValue(env.get('SQS_QUEUE_URL', true)) container.bind(TYPES.SQS_QUEUE_URL).toConstantValue(env.get('SQS_QUEUE_URL', true))
container.bind(TYPES.REDIS_EVENTS_CHANNEL).toConstantValue(env.get('REDIS_EVENTS_CHANNEL')) container.bind(TYPES.REDIS_EVENTS_CHANNEL).toConstantValue(env.get('REDIS_EVENTS_CHANNEL'))
container.bind(TYPES.NEW_RELIC_ENABLED).toConstantValue(env.get('NEW_RELIC_ENABLED', true)) container.bind(TYPES.NEW_RELIC_ENABLED).toConstantValue(env.get('NEW_RELIC_ENABLED', true))
container.bind(TYPES.ADMIN_EMAILS).toConstantValue(env.get('ADMIN_EMAILS').split(','))
container.bind(TYPES.MIXPANEL_TOKEN).toConstantValue(env.get('MIXPANEL_TOKEN', true))
// Services
container.bind<DomainEventFactory>(TYPES.DomainEventFactory).to(DomainEventFactory)
container.bind<PeriodKeyGeneratorInterface>(TYPES.PeriodKeyGenerator).toConstantValue(new PeriodKeyGenerator())
container
.bind<AnalyticsStoreInterface>(TYPES.AnalyticsStore)
.toConstantValue(new RedisAnalyticsStore(container.get(TYPES.PeriodKeyGenerator), container.get(TYPES.Redis)))
container
.bind<StatisticsStoreInterface>(TYPES.StatisticsStore)
.toConstantValue(new RedisStatisticsStore(container.get(TYPES.PeriodKeyGenerator), container.get(TYPES.Redis)))
container.bind<TimerInterface>(TYPES.Timer).toConstantValue(new Timer())
if (env.get('SNS_TOPIC_ARN', true)) {
container
.bind<SNSDomainEventPublisher>(TYPES.DomainEventPublisher)
.toConstantValue(new SNSDomainEventPublisher(container.get(TYPES.SNS), container.get(TYPES.SNS_TOPIC_ARN)))
} else {
container
.bind<RedisDomainEventPublisher>(TYPES.DomainEventPublisher)
.toConstantValue(
new RedisDomainEventPublisher(container.get(TYPES.Redis), container.get(TYPES.REDIS_EVENTS_CHANNEL)),
)
}
if (env.get('MIXPANEL_TOKEN', true)) {
container.bind<Mixpanel>(TYPES.MixpanelClient).toConstantValue(Mixpanel.init(env.get('MIXPANEL_TOKEN', true)))
}
// Repositories // Repositories
container container
@@ -171,9 +127,6 @@ export class ContainerConfigLoader {
container container
.bind<RevenueModificationRepositoryInterface>(TYPES.RevenueModificationRepository) .bind<RevenueModificationRepositoryInterface>(TYPES.RevenueModificationRepository)
.to(MySQLRevenueModificationRepository) .to(MySQLRevenueModificationRepository)
container
.bind<StatisticMeasureRepositoryInterface>(TYPES.StatisticMeasureRepository)
.toConstantValue(new RedisStatisticsStore(container.get(TYPES.PeriodKeyGenerator), container.get(TYPES.Redis)))
// ORM // ORM
container container
@@ -189,9 +142,6 @@ export class ContainerConfigLoader {
container container
.bind<CalculateMonthlyRecurringRevenue>(TYPES.CalculateMonthlyRecurringRevenue) .bind<CalculateMonthlyRecurringRevenue>(TYPES.CalculateMonthlyRecurringRevenue)
.to(CalculateMonthlyRecurringRevenue) .to(CalculateMonthlyRecurringRevenue)
container
.bind<PersistStatistic>(TYPES.PersistStatistic)
.toConstantValue(new PersistStatistic(container.get(TYPES.StatisticMeasureRepository)))
// Hanlders // Hanlders
container.bind<UserRegisteredEventHandler>(TYPES.UserRegisteredEventHandler).to(UserRegisteredEventHandler) container.bind<UserRegisteredEventHandler>(TYPES.UserRegisteredEventHandler).to(UserRegisteredEventHandler)
@@ -219,22 +169,35 @@ export class ContainerConfigLoader {
.bind<SubscriptionReactivatedEventHandler>(TYPES.SubscriptionReactivatedEventHandler) .bind<SubscriptionReactivatedEventHandler>(TYPES.SubscriptionReactivatedEventHandler)
.to(SubscriptionReactivatedEventHandler) .to(SubscriptionReactivatedEventHandler)
container.bind<RefundProcessedEventHandler>(TYPES.RefundProcessedEventHandler).to(RefundProcessedEventHandler) container.bind<RefundProcessedEventHandler>(TYPES.RefundProcessedEventHandler).to(RefundProcessedEventHandler)
container
.bind<StatisticPersistenceRequestedEventHandler>(TYPES.StatisticPersistenceRequestedEventHandler)
.toConstantValue(
new StatisticPersistenceRequestedEventHandler(
container.get(TYPES.PersistStatistic),
container.get(TYPES.Timer),
container.get(TYPES.Logger),
env.get('MIXPANEL_TOKEN', true) ? container.get(TYPES.MixpanelClient) : null,
),
)
// Maps // Maps
container container
.bind<MapperInterface<RevenueModification, TypeORMRevenueModification>>(TYPES.RevenueModificationMap) .bind<MapInterface<RevenueModification, TypeORMRevenueModification>>(TYPES.RevenueModificationMap)
.to(RevenueModificationMap) .to(RevenueModificationMap)
// Services
container.bind<DomainEventFactory>(TYPES.DomainEventFactory).to(DomainEventFactory)
container.bind<PeriodKeyGeneratorInterface>(TYPES.PeriodKeyGenerator).toConstantValue(new PeriodKeyGenerator())
container
.bind<AnalyticsStoreInterface>(TYPES.AnalyticsStore)
.toConstantValue(new RedisAnalyticsStore(container.get(TYPES.PeriodKeyGenerator), container.get(TYPES.Redis)))
container
.bind<StatisticsStoreInterface>(TYPES.StatisticsStore)
.toConstantValue(new RedisStatisticsStore(container.get(TYPES.PeriodKeyGenerator), container.get(TYPES.Redis)))
container.bind<TimerInterface>(TYPES.Timer).toConstantValue(new Timer())
if (env.get('SNS_TOPIC_ARN', true)) {
container
.bind<SNSDomainEventPublisher>(TYPES.DomainEventPublisher)
.toConstantValue(new SNSDomainEventPublisher(container.get(TYPES.SNS), container.get(TYPES.SNS_TOPIC_ARN)))
} else {
container
.bind<RedisDomainEventPublisher>(TYPES.DomainEventPublisher)
.toConstantValue(
new RedisDomainEventPublisher(container.get(TYPES.Redis), container.get(TYPES.REDIS_EVENTS_CHANNEL)),
)
}
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([ const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
['USER_REGISTERED', container.get(TYPES.UserRegisteredEventHandler)], ['USER_REGISTERED', container.get(TYPES.UserRegisteredEventHandler)],
['ACCOUNT_DELETION_REQUESTED', container.get(TYPES.AccountDeletionRequestedEventHandler)], ['ACCOUNT_DELETION_REQUESTED', container.get(TYPES.AccountDeletionRequestedEventHandler)],
@@ -247,7 +210,6 @@ export class ContainerConfigLoader {
['SUBSCRIPTION_EXPIRED', container.get(TYPES.SubscriptionExpiredEventHandler)], ['SUBSCRIPTION_EXPIRED', container.get(TYPES.SubscriptionExpiredEventHandler)],
['SUBSCRIPTION_REACTIVATED', container.get(TYPES.SubscriptionReactivatedEventHandler)], ['SUBSCRIPTION_REACTIVATED', container.get(TYPES.SubscriptionReactivatedEventHandler)],
['REFUND_PROCESSED', container.get(TYPES.RefundProcessedEventHandler)], ['REFUND_PROCESSED', container.get(TYPES.RefundProcessedEventHandler)],
['STATISTIC_PERSISTENCE_REQUESTED', container.get(TYPES.StatisticPersistenceRequestedEventHandler)],
]) ])
if (env.get('SQS_QUEUE_URL', true)) { if (env.get('SQS_QUEUE_URL', true)) {
@@ -11,12 +11,9 @@ const TYPES = {
SQS_AWS_REGION: Symbol.for('SQS_AWS_REGION'), SQS_AWS_REGION: Symbol.for('SQS_AWS_REGION'),
REDIS_EVENTS_CHANNEL: Symbol.for('REDIS_EVENTS_CHANNEL'), REDIS_EVENTS_CHANNEL: Symbol.for('REDIS_EVENTS_CHANNEL'),
NEW_RELIC_ENABLED: Symbol.for('NEW_RELIC_ENABLED'), NEW_RELIC_ENABLED: Symbol.for('NEW_RELIC_ENABLED'),
ADMIN_EMAILS: Symbol.for('ADMIN_EMAILS'),
MIXPANEL_TOKEN: Symbol.for('MIXPANEL_TOKEN'),
// Repositories // Repositories
AnalyticsEntityRepository: Symbol.for('AnalyticsEntityRepository'), AnalyticsEntityRepository: Symbol.for('AnalyticsEntityRepository'),
RevenueModificationRepository: Symbol.for('RevenueModificationRepository'), RevenueModificationRepository: Symbol.for('RevenueModificationRepository'),
StatisticMeasureRepository: Symbol.for('StatisticMeasureRepository'),
// ORM // ORM
ORMAnalyticsEntityRepository: Symbol.for('ORMAnalyticsEntityRepository'), ORMAnalyticsEntityRepository: Symbol.for('ORMAnalyticsEntityRepository'),
ORMRevenueModificationRepository: Symbol.for('ORMRevenueModificationRepository'), ORMRevenueModificationRepository: Symbol.for('ORMRevenueModificationRepository'),
@@ -24,7 +21,6 @@ const TYPES = {
GetUserAnalyticsId: Symbol.for('GetUserAnalyticsId'), GetUserAnalyticsId: Symbol.for('GetUserAnalyticsId'),
SaveRevenueModification: Symbol.for('SaveRevenueModification'), SaveRevenueModification: Symbol.for('SaveRevenueModification'),
CalculateMonthlyRecurringRevenue: Symbol.for('CalculateMonthlyRecurringRevenue'), CalculateMonthlyRecurringRevenue: Symbol.for('CalculateMonthlyRecurringRevenue'),
PersistStatistic: Symbol.for('PersistStatistic'),
// Handlers // Handlers
UserRegisteredEventHandler: Symbol.for('UserRegisteredEventHandler'), UserRegisteredEventHandler: Symbol.for('UserRegisteredEventHandler'),
AccountDeletionRequestedEventHandler: Symbol.for('AccountDeletionRequestedEventHandler'), AccountDeletionRequestedEventHandler: Symbol.for('AccountDeletionRequestedEventHandler'),
@@ -37,7 +33,6 @@ const TYPES = {
SubscriptionExpiredEventHandler: Symbol.for('SubscriptionExpiredEventHandler'), SubscriptionExpiredEventHandler: Symbol.for('SubscriptionExpiredEventHandler'),
SubscriptionReactivatedEventHandler: Symbol.for('SubscriptionReactivatedEventHandler'), SubscriptionReactivatedEventHandler: Symbol.for('SubscriptionReactivatedEventHandler'),
RefundProcessedEventHandler: Symbol.for('RefundProcessedEventHandler'), RefundProcessedEventHandler: Symbol.for('RefundProcessedEventHandler'),
StatisticPersistenceRequestedEventHandler: Symbol.for('StatisticPersistenceRequestedEventHandler'),
// Maps // Maps
RevenueModificationMap: Symbol.for('RevenueModificationMap'), RevenueModificationMap: Symbol.for('RevenueModificationMap'),
// Services // Services
@@ -49,7 +44,6 @@ const TYPES = {
StatisticsStore: Symbol.for('StatisticsStore'), StatisticsStore: Symbol.for('StatisticsStore'),
Timer: Symbol.for('Timer'), Timer: Symbol.for('Timer'),
PeriodKeyGenerator: Symbol.for('PeriodKeyGenerator'), PeriodKeyGenerator: Symbol.for('PeriodKeyGenerator'),
MixpanelClient: Symbol.for('MixpanelClient'),
} }
export default TYPES export default TYPES
@@ -9,7 +9,7 @@ describe('Email', () => {
}) })
it('should not create an invalid value object', () => { it('should not create an invalid value object', () => {
const valueOrError = Email.create('foobar') const valueOrError = Email.create('')
expect(valueOrError.isFailed()).toBeTruthy() expect(valueOrError.isFailed()).toBeTruthy()
}) })
@@ -1,7 +1,6 @@
import { ValueObject } from '../Core/ValueObject' import { ValueObject } from '../Core/ValueObject'
import { Result } from '../Core/Result' import { Result } from '../Core/Result'
import { EmailProps } from './EmailProps' import { EmailProps } from './EmailProps'
import { Validator } from '../Core/Validator'
export class Email extends ValueObject<EmailProps> { export class Email extends ValueObject<EmailProps> {
get value(): string { get value(): string {
@@ -13,11 +12,10 @@ export class Email extends ValueObject<EmailProps> {
} }
static create(email: string): Result<Email> { static create(email: string): Result<Email> {
const emailValidation = Validator.isValidEmail(email) if (!!email === false || email.length === 0) {
if (emailValidation.isFailed()) { return Result.fail<Email>('Email cannot be empty')
return Result.fail<Email>(emailValidation.getError()) } else {
return Result.ok<Email>(new Email({ value: email }))
} }
return Result.ok<Email>(new Email({ value: email }))
} }
} }
@@ -2,14 +2,14 @@ import { Uuid } from './Uuid'
describe('Uuid', () => { describe('Uuid', () => {
it('should create a value object', () => { it('should create a value object', () => {
const valueOrError = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d') const valueOrError = Uuid.create('1-2-3')
expect(valueOrError.isFailed()).toBeFalsy() expect(valueOrError.isFailed()).toBeFalsy()
expect(valueOrError.getValue().value).toEqual('84c0f8e8-544a-4c7e-9adf-26209303bc1d') expect(valueOrError.getValue().value).toEqual('1-2-3')
}) })
it('should not create an invalid value object', () => { it('should not create an invalid value object', () => {
const valueOrError = Uuid.create('1-2-3') const valueOrError = Uuid.create('')
expect(valueOrError.isFailed()).toBeTruthy() expect(valueOrError.isFailed()).toBeTruthy()
}) })
@@ -1,7 +1,6 @@
import { ValueObject } from '../Core/ValueObject' import { ValueObject } from '../Core/ValueObject'
import { Result } from '../Core/Result' import { Result } from '../Core/Result'
import { UuidProps } from './UuidProps' import { UuidProps } from './UuidProps'
import { Validator } from '../Core/Validator'
export class Uuid extends ValueObject<UuidProps> { export class Uuid extends ValueObject<UuidProps> {
get value(): string { get value(): string {
@@ -13,9 +12,8 @@ export class Uuid extends ValueObject<UuidProps> {
} }
static create(uuid: string): Result<Uuid> { static create(uuid: string): Result<Uuid> {
const validUuidOrError = Validator.isValidUuid(uuid) if (!!uuid === false || uuid.length === 0) {
if (validUuidOrError.isFailed()) { return Result.fail<Uuid>('Uuid cannot be empty')
return Result.fail<Uuid>(validUuidOrError.getError())
} else { } else {
return Result.ok<Uuid>(new Uuid({ value: uuid })) return Result.ok<Uuid>(new Uuid({ value: uuid }))
} }
@@ -1,11 +0,0 @@
import { TimerInterface } from '@standardnotes/time'
import { html } from './daily-analytics-report.html'
export function getSubject(): string {
return `Daily analytics report ${new Date().toLocaleDateString('en-US')}`
}
export function getBody(data: unknown, timer: TimerInterface): string {
return html(data, timer)
}
@@ -1,979 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { TimerInterface } from '@standardnotes/time'
import { AnalyticsActivity } from '../Analytics/AnalyticsActivity'
import { StatisticMeasureName } from '../Statistics/StatisticMeasureName'
import { Period } from '../Time/Period'
const countActiveUsers = (measureName: string, data: any): { yesterday: number; last30Days: number } => {
const totalActiveUsersLast30DaysIncludingToday = data.statisticsOverTime.find(
(a: { name: string; period: number }) => a.name === measureName && a.period === 27,
)
const totalActiveUsersYesterday =
totalActiveUsersLast30DaysIncludingToday.counts[totalActiveUsersLast30DaysIncludingToday.counts.length - 2]
.totalCount
const filteredCounts = totalActiveUsersLast30DaysIncludingToday.counts.filter(
(count: { totalCount: number }) => count.totalCount !== 0,
)
if (filteredCounts.length === 0) {
return {
yesterday: 0,
last30Days: 0,
}
}
const last30DaysNumbers = filteredCounts.map((count: { totalCount: number }) => count.totalCount)
const last30DaysCount = last30DaysNumbers.reduce((previousValue: number, currentValue: number) => {
return previousValue + currentValue
})
const averageActiveUsersLast30Days = Math.floor(last30DaysCount / last30DaysNumbers.length)
return {
yesterday: totalActiveUsersYesterday,
last30Days: averageActiveUsersLast30Days,
}
}
const getChartUrls = (
data: any,
): {
subscriptions: string
users: string
quarterlyPerformance: string
churn: string
mrrMonthly: string
} => {
const subscriptionPurchasingOverTime = data.activityStatisticsOverTime.find(
(a: { name: AnalyticsActivity; period: Period }) =>
a.name === AnalyticsActivity.SubscriptionPurchased && a.period === Period.Last30Days,
)
const subscriptionRenewingOverTime = data.activityStatisticsOverTime.find(
(a: { name: AnalyticsActivity; period: Period }) =>
a.name === AnalyticsActivity.SubscriptionRenewed && a.period === Period.Last30Days,
)
const subscriptionRefundingOverTime = data.activityStatisticsOverTime.find(
(a: { name: AnalyticsActivity; period: Period }) =>
a.name === AnalyticsActivity.SubscriptionRefunded && a.period === Period.Last30Days,
)
const subscriptionCancelledOverTime = data.activityStatisticsOverTime.find(
(a: { name: AnalyticsActivity; period: Period }) =>
a.name === AnalyticsActivity.SubscriptionCancelled && a.period === Period.Last30Days,
)
const subscriptionReactivatedOverTime = data.activityStatisticsOverTime.find(
(a: { name: AnalyticsActivity; period: Period }) =>
a.name === AnalyticsActivity.SubscriptionReactivated && a.period === Period.Last30Days,
)
const subscriptionsLinerOverTimeConfig = {
type: 'line',
data: {
labels: subscriptionPurchasingOverTime?.counts.map((count: { periodKey: any }) => count.periodKey),
datasets: [
{
label: 'Subscription Purchases',
backgroundColor: 'rgb(25, 255, 140)',
borderColor: 'rgb(25, 255, 140)',
data: subscriptionPurchasingOverTime?.counts.map((count: { totalCount: any }) => count.totalCount),
fill: false,
pointRadius: 2,
},
{
label: 'Subscription Renewals',
backgroundColor: 'rgb(54, 162, 235)',
borderColor: 'rgb(54, 162, 235)',
data: subscriptionRenewingOverTime?.counts.map((count: { totalCount: any }) => count.totalCount),
fill: false,
pointRadius: 2,
},
{
label: 'Subscription Refunds',
backgroundColor: 'rgb(255, 221, 51)',
borderColor: 'rgb(255, 221, 51)',
data: subscriptionRefundingOverTime?.counts.map((count: { totalCount: any }) => count.totalCount),
fill: false,
pointRadius: 2,
},
{
label: 'Subscription Cancels',
backgroundColor: 'rgb(255, 99, 132)',
borderColor: 'rgb(255, 99, 132)',
data: subscriptionCancelledOverTime?.counts.map((count: { totalCount: any }) => count.totalCount),
fill: false,
pointRadius: 2,
},
{
label: 'Subscription Reactivations',
backgroundColor: 'rgb(221, 51, 255)',
borderColor: 'rgb(221, 51, 255)',
data: subscriptionReactivatedOverTime?.counts.map((count: { totalCount: any }) => count.totalCount),
fill: false,
pointRadius: 2,
},
],
},
}
const userRegistrationOverTime = data.activityStatisticsOverTime.find(
(a: { name: AnalyticsActivity; period: Period }) =>
a.name === AnalyticsActivity.Register && a.period === Period.Last30Days,
)
const userDeletionOverTime = data.activityStatisticsOverTime.find(
(a: { name: AnalyticsActivity; period: Period }) =>
a.name === AnalyticsActivity.DeleteAccount && a.period === Period.Last30Days,
)
const usersLinerOverTimeConfig = {
type: 'line',
data: {
labels: userRegistrationOverTime?.counts.map((count: { periodKey: any }) => count.periodKey),
datasets: [
{
label: 'User Registrations',
backgroundColor: 'rgb(25, 255, 140)',
borderColor: 'rgb(25, 255, 140)',
data: userRegistrationOverTime?.counts.map((count: { totalCount: any }) => count.totalCount),
fill: false,
pointRadius: 2,
},
{
label: 'Account Deletions',
backgroundColor: 'rgb(255, 99, 132)',
borderColor: 'rgb(255, 99, 132)',
data: userDeletionOverTime?.counts.map((count: { totalCount: any }) => count.totalCount),
fill: false,
pointRadius: 2,
},
],
},
}
const quarters = [Period.Q1ThisYear, Period.Q2ThisYear, Period.Q3ThisYear, Period.Q4ThisYear]
const quarterlyUserRegistrations = []
const quarterlySubscriptionPurchases = []
const quarterlySubscriptionRenewals = []
for (const quarter of quarters) {
const registrations =
data.activityStatisticsOverTime.find(
(a: { name: AnalyticsActivity; period: Period }) =>
a.name === AnalyticsActivity.Register && a.period === quarter,
)?.totalCount ?? 0
const purchases =
data.activityStatisticsOverTime.find(
(a: { name: AnalyticsActivity; period: Period }) =>
a.name === AnalyticsActivity.SubscriptionPurchased && a.period === quarter,
)?.totalCount ?? 0
const renewals =
data.activityStatisticsOverTime.find(
(a: { name: AnalyticsActivity; period: Period }) =>
a.name === AnalyticsActivity.SubscriptionRenewed && a.period === quarter,
)?.totalCount ?? 0
quarterlyUserRegistrations.push(registrations)
quarterlySubscriptionPurchases.push(purchases)
quarterlySubscriptionRenewals.push(renewals)
}
const quarterlyConfig = {
type: 'bar',
data: {
labels: ['Q1', 'Q2', 'Q3', 'Q4'],
datasets: [
{
label: 'User Registrations',
backgroundColor: 'rgba(255, 99, 132, 0.5)',
borderColor: 'rgb(255, 99, 132)',
borderWidth: 1,
data: quarterlyUserRegistrations,
},
{
label: 'Subscription Purchases',
backgroundColor: 'rgba(54, 162, 235, 0.5)',
borderColor: 'rgb(54, 162, 235)',
borderWidth: 1,
data: quarterlySubscriptionPurchases,
},
{
label: 'Subscription Renewals',
backgroundColor: 'rgb(25, 255, 140, 0.5)',
borderColor: 'rgb(25, 255, 140)',
borderWidth: 1,
data: quarterlySubscriptionRenewals,
},
],
},
options: {
title: {
display: true,
text: 'Quarterly Performance',
},
plugins: {
datalabels: {
anchor: 'center',
align: 'center',
color: '#666',
font: {
weight: 'normal',
},
},
},
},
}
const monthlyChurnRates = data.churn.values.map((value: { rate: number }) => +value.rate.toFixed(2))
const churnConfig = {
type: 'bar',
data: {
labels: [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
],
datasets: [
{
label: 'Churn Percent',
backgroundColor: 'rgba(255, 99, 132, 0.5)',
borderColor: 'rgb(255, 99, 132)',
borderWidth: 1,
data: monthlyChurnRates,
},
],
},
options: {
title: {
display: true,
text: 'Monthly Churn Rate',
},
plugins: {
datalabels: {
anchor: 'center',
align: 'center',
color: '#666',
font: {
weight: 'normal',
},
},
},
},
}
const mrrMonthlyOverTime = data.statisticsOverTime
.find((a: { name: string; period: Period }) => a.name === 'mrr' && a.period === Period.ThisYear)
?.counts.map((count: { totalCount: number }) => +count.totalCount.toFixed(2))
const mrrMonthlyConfig = {
type: 'bar',
data: {
labels: [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
],
datasets: [
{
label: 'MRR',
backgroundColor: 'rgba(25, 255, 140, 0.5)',
borderColor: 'rgb(25, 255, 140)',
borderWidth: 1,
data: mrrMonthlyOverTime,
},
],
},
options: {
title: {
display: true,
text: 'Monthly MRR',
},
plugins: {
datalabels: {
anchor: 'center',
align: 'center',
color: '#666',
font: {
weight: 'normal',
},
},
},
},
}
return {
subscriptions: `https://quickchart.io/chart?width=800&c=${encodeURIComponent(
JSON.stringify(subscriptionsLinerOverTimeConfig),
)}`,
users: `https://quickchart.io/chart?width=800&c=${encodeURIComponent(JSON.stringify(usersLinerOverTimeConfig))}`,
quarterlyPerformance: `https://quickchart.io/chart?width=800&c=${encodeURIComponent(
JSON.stringify(quarterlyConfig),
)}`,
churn: `https://quickchart.io/chart?width=800&c=${encodeURIComponent(JSON.stringify(churnConfig))}`,
mrrMonthly: `https://quickchart.io/chart?width=800&c=${encodeURIComponent(JSON.stringify(mrrMonthlyConfig))}`,
}
}
export const html = (data: any, timer: TimerInterface) => {
const chartUrls = getChartUrls(data)
const successfullPaymentsActivity = data.activityStatistics.find(
(a: { name: AnalyticsActivity }) => a.name === AnalyticsActivity.PaymentSuccess && Period.Yesterday,
)
const failedPaymentsActivity = data.activityStatistics.find(
(a: { name: AnalyticsActivity }) => a.name === AnalyticsActivity.PaymentFailed && Period.Yesterday,
)
const limitedDiscountPurchasedActivity = data.activityStatistics.find(
(a: { name: AnalyticsActivity }) => a.name === AnalyticsActivity.LimitedDiscountOfferPurchased && Period.Yesterday,
)
const subscriptionPurchasingOverTime = data.activityStatisticsOverTime.find(
(a: { name: AnalyticsActivity; period: Period }) =>
a.name === AnalyticsActivity.SubscriptionPurchased && a.period === Period.Last30Days,
)
const subscriptionRenewingOverTime = data.activityStatisticsOverTime.find(
(a: { name: AnalyticsActivity; period: Period }) =>
a.name === AnalyticsActivity.SubscriptionRenewed && a.period === Period.Last30Days,
)
const subscriptionRefundingOverTime = data.activityStatisticsOverTime.find(
(a: { name: AnalyticsActivity; period: Period }) =>
a.name === AnalyticsActivity.SubscriptionRefunded && a.period === Period.Last30Days,
)
const subscriptionCancelledOverTime = data.activityStatisticsOverTime.find(
(a: { name: AnalyticsActivity; period: Period }) =>
a.name === AnalyticsActivity.SubscriptionCancelled && a.period === Period.Last30Days,
)
const subscriptionReactivatedOverTime = data.activityStatisticsOverTime.find(
(a: { name: AnalyticsActivity; period: Period }) =>
a.name === AnalyticsActivity.SubscriptionReactivated && a.period === Period.Last30Days,
)
const userRegistrationOverTime = data.activityStatisticsOverTime.find(
(a: { name: AnalyticsActivity; period: Period }) =>
a.name === AnalyticsActivity.Register && a.period === Period.Last30Days,
)
const userDeletionOverTime = data.activityStatisticsOverTime.find(
(a: { name: AnalyticsActivity; period: Period }) =>
a.name === AnalyticsActivity.DeleteAccount && a.period === Period.Last30Days,
)
const incomeMeasureYesterday = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.Income && a.period === Period.Yesterday,
)
const refundMeasureYesterday = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.Refunds && a.period === Period.Yesterday,
)
const incomeYesterday = incomeMeasureYesterday?.totalValue ?? 0
const refundsYesterday = refundMeasureYesterday?.totalValue ?? 0
const revenueYesterday = incomeYesterday - refundsYesterday
const subscriptionLengthMeasureYesterday = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.SubscriptionLength && a.period === Period.Yesterday,
)
const subscriptionLengthDurationYesterday = timer.convertMicrosecondsToTimeStructure(
Math.floor(subscriptionLengthMeasureYesterday?.average ?? 0),
)
const subscriptionRemainingTimePercentageMeasureYesterday = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.RemainingSubscriptionTimePercentage && a.period === Period.Yesterday,
)
const subscriptionRemainingTimePercentageYesterday = Math.floor(
subscriptionRemainingTimePercentageMeasureYesterday?.average ?? 0,
)
const registrationLengthMeasureYesterday = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.RegistrationLength && a.period === Period.Yesterday,
)
const registrationLengthDurationYesterday = timer.convertMicrosecondsToTimeStructure(
Math.floor(registrationLengthMeasureYesterday?.average ?? 0),
)
const registrationToSubscriptionMeasureYesterday = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.RegistrationToSubscriptionTime && a.period === Period.Yesterday,
)
const registrationToSubscriptionDurationYesterday = timer.convertMicrosecondsToTimeStructure(
Math.floor(registrationToSubscriptionMeasureYesterday?.average ?? 0),
)
const incomeMeasureThisMonth = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.Income && a.period === Period.ThisMonth,
)
const refundMeasureThisMonth = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.Refunds && a.period === Period.ThisMonth,
)
const incomeThisMonth = incomeMeasureThisMonth?.totalValue ?? 0
const refundsThisMonth = refundMeasureThisMonth?.totalValue ?? 0
const revenueThisMonth = incomeThisMonth - refundsThisMonth
const subscriptionLengthMeasureThisMonth = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.SubscriptionLength && a.period === Period.ThisMonth,
)
const subscriptionLengthDurationThisMonth = timer.convertMicrosecondsToTimeStructure(
Math.floor(subscriptionLengthMeasureThisMonth?.average ?? 0),
)
const subscriptionRemainingTimePercentageMeasureThisMonth = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.RemainingSubscriptionTimePercentage && a.period === Period.ThisMonth,
)
const subscriptionRemainingTimePercentageThisMonth = Math.floor(
subscriptionRemainingTimePercentageMeasureThisMonth?.average ?? 0,
)
const registrationLengthMeasureThisMonth = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.RegistrationLength && a.period === Period.ThisMonth,
)
const registrationLengthDurationThisMonth = timer.convertMicrosecondsToTimeStructure(
Math.floor(registrationLengthMeasureThisMonth?.average ?? 0),
)
const registrationToSubscriptionMeasureThisMonth = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.RegistrationToSubscriptionTime && a.period === Period.ThisMonth,
)
const registrationToSubscriptionDurationThisMonth = timer.convertMicrosecondsToTimeStructure(
Math.floor(registrationToSubscriptionMeasureThisMonth?.average ?? 0),
)
const plusSubscriptionsInitialAnnualPaymentsYesterday = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.PlusSubscriptionInitialAnnualPaymentsIncome &&
a.period === Period.Yesterday,
)
const plusSubscriptionsInitialMonthlyPaymentsYesterday = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.PlusSubscriptionInitialMonthlyPaymentsIncome &&
a.period === Period.Yesterday,
)
const plusSubscriptionsRenewingAnnualPaymentsYesterday = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.PlusSubscriptionRenewingAnnualPaymentsIncome &&
a.period === Period.Yesterday,
)
const plusSubscriptionsRenewingMonthlyPaymentsYesterday = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.PlusSubscriptionRenewingMonthlyPaymentsIncome &&
a.period === Period.Yesterday,
)
const proSubscriptionsInitialAnnualPaymentsYesterday = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.ProSubscriptionInitialAnnualPaymentsIncome && a.period === Period.Yesterday,
)
const proSubscriptionsInitialMonthlyPaymentsYesterday = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.ProSubscriptionInitialMonthlyPaymentsIncome &&
a.period === Period.Yesterday,
)
const proSubscriptionsRenewingAnnualPaymentsYesterday = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.ProSubscriptionRenewingAnnualPaymentsIncome &&
a.period === Period.Yesterday,
)
const proSubscriptionsRenewingMonthlyPaymentsYesterday = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.ProSubscriptionRenewingMonthlyPaymentsIncome &&
a.period === Period.Yesterday,
)
const plusSubscriptionsInitialAnnualPaymentsThisMonth = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.PlusSubscriptionInitialAnnualPaymentsIncome &&
a.period === Period.ThisMonth,
)
const plusSubscriptionsInitialMonthlyPaymentsThisMonth = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.PlusSubscriptionInitialMonthlyPaymentsIncome &&
a.period === Period.ThisMonth,
)
const plusSubscriptionsRenewingAnnualPaymentsThisMonth = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.PlusSubscriptionRenewingAnnualPaymentsIncome &&
a.period === Period.ThisMonth,
)
const plusSubscriptionsRenewingMonthlyPaymentsThisMonth = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.PlusSubscriptionRenewingMonthlyPaymentsIncome &&
a.period === Period.ThisMonth,
)
const proSubscriptionsInitialAnnualPaymentsThisMonth = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.ProSubscriptionInitialAnnualPaymentsIncome && a.period === Period.ThisMonth,
)
const proSubscriptionsInitialMonthlyPaymentsThisMonth = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.ProSubscriptionInitialMonthlyPaymentsIncome &&
a.period === Period.ThisMonth,
)
const proSubscriptionsRenewingAnnualPaymentsThisMonth = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.ProSubscriptionRenewingAnnualPaymentsIncome &&
a.period === Period.ThisMonth,
)
const proSubscriptionsRenewingMonthlyPaymentsThisMonth = data.statisticMeasures.find(
(a: { name: string; period: Period }) =>
a.name === StatisticMeasureName.NAMES.ProSubscriptionRenewingMonthlyPaymentsIncome &&
a.period === Period.ThisMonth,
)
const mrrOverTime = data.statisticsOverTime.find(
(a: { name: string; period: number }) => a.name === 'mrr' && a.period === 27,
)
const monthlyPlansMrrOverTime = data.statisticsOverTime.find(
(a: { name: string; period: number }) => a.name === 'monthly-plans-mrr' && a.period === 27,
)
const annualPlansMrrOverTime = data.statisticsOverTime.find(
(a: { name: string; period: number }) => a.name === 'annual-plans-mrr' && a.period === 27,
)
const fiveYearPlansMrrOverTime = data.statisticsOverTime.find(
(a: { name: string; period: number }) => a.name === 'five-year-plans-mrr' && a.period === 27,
)
const proPlansMrrOverTime = data.statisticsOverTime.find(
(a: { name: string; period: number }) => a.name === 'pro-plans-mrr' && a.period === 27,
)
const plusPlansMrrOverTime = data.statisticsOverTime.find(
(a: { name: string; period: number }) => a.name === 'plus-plans-mrr' && a.period === 27,
)
const today = new Date()
const thisMonthPeriodKey = `${today.getFullYear().toString()}-${(today.getMonth() + 1).toString()}`
const thisMonthChurn = data.churn.values.find(
(value: { periodKey: string }) => value.periodKey === thisMonthPeriodKey,
)
const totalActiveUsers = countActiveUsers(StatisticMeasureName.NAMES.ActiveUsers, data)
const totalActiveFreeUsers = countActiveUsers(StatisticMeasureName.NAMES.ActiveFreeUsers, data)
const totalActivePlusUsers = countActiveUsers(StatisticMeasureName.NAMES.ActivePlusUsers, data)
const totalActiveProUsers = countActiveUsers(StatisticMeasureName.NAMES.ActiveProUsers, data)
return ` <div>
<p>Hello,</p>
<p>
<strong>Here are some statistics from yesterday:</strong>
</p>
<ul>
<li>
<b>Active Users</b>
<ul>
<li>
<b>Total:</b> ${totalActiveUsers.yesterday.toLocaleString('en-US')}
</li>
<li>
<b>By Subscription Type:</b>
<ul>
<li>
<b>FREE:</b> ${totalActiveFreeUsers.yesterday.toLocaleString('en-US')}
</li>
<li>
<b>PLUS:</b> ${totalActivePlusUsers.yesterday.toLocaleString('en-US')}
</li>
<li>
<b>PRO:</b> ${totalActiveProUsers.yesterday.toLocaleString('en-US')}
</li>
</ul>
</li>
</ul>
</li>
<li>
<b>Payments</b>
<ul>
<li>
Revenue: <b>$${revenueYesterday.toLocaleString('en-US')}</b> (Income: $
${incomeYesterday.toLocaleString('en-US')}, Refunds: $${refundsYesterday.toLocaleString('en-US')})
</li>
<li>
Successfull payments: <b>${successfullPaymentsActivity?.totalCount.toLocaleString('en-US')}</b>
</li>
<li>
Failed payments: <b>${failedPaymentsActivity?.totalCount.toLocaleString('en-US')}</b>
</li>
</ul>
</li>
<li>
<b>MRR Breakdown</b>
<ul>
<li>
<b>Total:</b> $${mrrOverTime?.counts[mrrOverTime?.counts.length - 1].totalCount.toLocaleString('en-US')}
</li>
<li>
<b>By Subscription Type:</b>
<ul>
<li>
<b>PLUS:</b> $
${plusPlansMrrOverTime?.counts[plusPlansMrrOverTime?.counts.length - 1].totalCount.toLocaleString('en-US')}
</li>
<li>
<b>PRO:</b> $
${proPlansMrrOverTime?.counts[proPlansMrrOverTime?.counts.length - 1].totalCount.toLocaleString('en-US')}
</li>
</ul>
</li>
<li>
<b>By Billing Frequency:</b>
<ul>
<li>
<b>Monthly:</b> $
${monthlyPlansMrrOverTime?.counts[monthlyPlansMrrOverTime?.counts.length - 1].totalCount.toLocaleString(
'en-US',
)}
</li>
<li>
<b>Annual:</b> $
${annualPlansMrrOverTime?.counts[annualPlansMrrOverTime?.counts.length - 1].totalCount.toLocaleString(
'en-US',
)}
</li>
<li>
<b>5-year:</b> $
${fiveYearPlansMrrOverTime?.counts[fiveYearPlansMrrOverTime?.counts.length - 1].totalCount.toLocaleString(
'en-US',
)}
</li>
</ul>
</li>
</ul>
</li>
<li>
<b>Income Breakdown</b>
<ul>
<li>
<b>Plus Subscription:</b>
<ul>
<li>
<b>${plusSubscriptionsInitialMonthlyPaymentsYesterday?.increments.toLocaleString('en-US')}</b>${' '}
<i>initial</i> payments on <u>monhtly</u> plan, totaling${' '}
<b>$${plusSubscriptionsInitialMonthlyPaymentsYesterday?.totalValue.toLocaleString('en-US')}</b>
</li>
<li>
<b>${plusSubscriptionsInitialAnnualPaymentsYesterday?.increments.toLocaleString('en-US')}</b>${' '}
<i>initial</i> payments on <u>annual</u> plan, totaling${' '}
<b>$${plusSubscriptionsInitialAnnualPaymentsYesterday?.totalValue.toLocaleString('en-US')}</b>
</li>
<li>
<b>${plusSubscriptionsRenewingMonthlyPaymentsYesterday?.increments.toLocaleString('en-US')}</b>${' '}
<i>renewing</i> payments on <u>monthly</u> plan, totaling${' '}
<b>$${plusSubscriptionsRenewingMonthlyPaymentsYesterday?.totalValue.toLocaleString('en-US')}</b>
</li>
<li>
<b>${plusSubscriptionsRenewingAnnualPaymentsYesterday?.increments.toLocaleString('en-US')}</b>${' '}
<i>renewing</i> payments on <u>annual</u> plan, totaling${' '}
<b>$${plusSubscriptionsRenewingAnnualPaymentsYesterday?.totalValue.toLocaleString('en-US')}</b>
</li>
</ul>
</li>
<li>
<b>Pro Subscription:</b>
<ul>
<li>
<b>${proSubscriptionsInitialMonthlyPaymentsYesterday?.increments.toLocaleString('en-US')}</b>${' '}
<i>initial</i> payments on <u>monhtly</u> plan, totaling${' '}
<b>$${proSubscriptionsInitialMonthlyPaymentsYesterday?.totalValue.toLocaleString('en-US')}</b>
</li>
<li>
<b>${proSubscriptionsInitialAnnualPaymentsYesterday?.increments.toLocaleString('en-US')}</b>${' '}
<i>initial</i> payments on <u>annual</u> plan, totaling${' '}
<b>$${proSubscriptionsInitialAnnualPaymentsYesterday?.totalValue.toLocaleString('en-US')}</b>
</li>
<li>
<b>${proSubscriptionsRenewingMonthlyPaymentsYesterday?.increments.toLocaleString('en-US')}</b>${' '}
<i>renewing</i> payments on <u>monthly</u> plan, totaling${' '}
<b>$${proSubscriptionsRenewingMonthlyPaymentsYesterday?.totalValue.toLocaleString('en-US')}</b>
</li>
<li>
<b>${proSubscriptionsRenewingAnnualPaymentsYesterday?.increments.toLocaleString('en-US')}</b>${' '}
<i>renewing</i> payments on <u>annual</u> plan, totaling${' '}
<b>$${proSubscriptionsRenewingAnnualPaymentsYesterday?.totalValue.toLocaleString('en-US')}</b>
</li>
</ul>
</li>
</ul>
</li>
<li>
<b>Users</b>
<ul>
<li>
Number of users registered:${' '}
<b>
${userRegistrationOverTime?.counts[userRegistrationOverTime?.counts.length - 1]?.totalCount.toLocaleString(
'en-US',
)}
</b>
</li>
<li>
Number of users unregistered:${' '}
<b>
${userDeletionOverTime?.counts[userDeletionOverTime?.counts.length - 1]?.totalCount.toLocaleString('en-US')}
</b>${' '}
(average account duration: ${registrationLengthDurationYesterday.days} days${' '}
${registrationLengthDurationYesterday.hours} hours ${registrationLengthDurationYesterday.minutes} minutes)
</li>
</ul>
</li>
<li>
<b>Subscriptions</b>
<ul>
<li>
Number of subscriptions purchased:${' '}
<b>
${subscriptionPurchasingOverTime?.counts[
subscriptionPurchasingOverTime?.counts.length - 1
]?.totalCount.toLocaleString('en-US')}
</b>${' '}
(includes <b>${limitedDiscountPurchasedActivity?.totalCount.toLocaleString('en-US')}</b> limited time
offer purchases)
</li>
<li>
Number of subscriptions renewed:${' '}
<b>
${subscriptionRenewingOverTime?.counts[
subscriptionRenewingOverTime?.counts.length - 1
]?.totalCount.toLocaleString('en-US')}
</b>
</li>
<li>
Number of subscriptions refunded:${' '}
<b>
${subscriptionRefundingOverTime?.counts[
subscriptionRefundingOverTime?.counts.length - 1
]?.totalCount.toLocaleString('en-US')}
</b>
</li>
<li>
Number of subscriptions cancelled:${' '}
<b>
${subscriptionCancelledOverTime?.counts[
subscriptionCancelledOverTime?.counts.length - 1
]?.totalCount.toLocaleString('en-US')}
</b>${' '}
(average subscription duration: ${subscriptionLengthDurationYesterday.days} days${' '}
${subscriptionLengthDurationYesterday.hours} hours ${subscriptionLengthDurationYesterday.minutes} minutes,
average remaining subscription percentage: ${subscriptionRemainingTimePercentageYesterday}%)
</li>
<li>
Number of subscriptions reactivated:${' '}
<b>
${subscriptionReactivatedOverTime?.counts[
subscriptionReactivatedOverTime?.counts.length - 1
]?.totalCount.toLocaleString('en-US')}
</b>
</li>
<li>
Average time from registration to subscription purchase:${' '}
<b>
${registrationToSubscriptionDurationYesterday.days} days${' '}
${registrationToSubscriptionDurationYesterday.hours} hours${' '}
${registrationToSubscriptionDurationYesterday.minutes} minutes
</b>
</li>
</ul>
</li>
</ul>
<p>
<strong>Here are some statistics from last 30 days:</strong>
</p>
<ul>
<li>
<b>Active Users (Average)</b>
<ul>
<li>
<b>Total:</b> ${totalActiveUsers.last30Days.toLocaleString('en-US')}
</li>
<li>
<b>By Subscription Type:</b>
<ul>
<li>
<b>FREE:</b> ${totalActiveFreeUsers.last30Days.toLocaleString('en-US')}
</li>
<li>
<b>PLUS:</b> ${totalActivePlusUsers.last30Days.toLocaleString('en-US')}
</li>
<li>
<b>PRO:</b> ${totalActiveProUsers.last30Days.toLocaleString('en-US')}
</li>
</ul>
</li>
</ul>
</li>
<li>
<b>Payments (This Month)</b>
<ul>
<li>
Revenue: <b>$${revenueThisMonth.toLocaleString('en-US')}</b>
</li>
<li>
Income: <b>$${incomeThisMonth.toLocaleString('en-US')}</b>
</li>
<li>
Refunds: <b>$${refundsThisMonth.toLocaleString('en-US')}</b>
</li>
</ul>
</li>
<li>
<b>Income Breakdown (This Month)</b>
<ul>
<li>
<b>Plus Subscription:</b>
<ul>
<li>
<b>${plusSubscriptionsInitialMonthlyPaymentsThisMonth?.increments.toLocaleString('en-US')}</b>${' '}
<i>initial</i> payments on <u>monhtly</u> plan, totaling${' '}
<b>$${plusSubscriptionsInitialMonthlyPaymentsThisMonth?.totalValue.toLocaleString('en-US')}</b>
</li>
<li>
<b>${plusSubscriptionsInitialAnnualPaymentsThisMonth?.increments.toLocaleString('en-US')}</b>${' '}
<i>initial</i> payments on <u>annual</u> plan, totaling${' '}
<b>$${plusSubscriptionsInitialAnnualPaymentsThisMonth?.totalValue.toLocaleString('en-US')}</b>
</li>
<li>
<b>${plusSubscriptionsRenewingMonthlyPaymentsThisMonth?.increments.toLocaleString('en-US')}</b>${' '}
<i>renewing</i> payments on <u>monthly</u> plan, totaling${' '}
<b>$${plusSubscriptionsRenewingMonthlyPaymentsThisMonth?.totalValue.toLocaleString('en-US')}</b>
</li>
<li>
<b>${plusSubscriptionsRenewingAnnualPaymentsThisMonth?.increments.toLocaleString('en-US')}</b>${' '}
<i>renewing</i> payments on <u>annual</u> plan, totaling${' '}
<b>$${plusSubscriptionsRenewingAnnualPaymentsThisMonth?.totalValue.toLocaleString('en-US')}</b>
</li>
</ul>
</li>
<li>
<b>Pro Subscription:</b>
<ul>
<li>
<b>${proSubscriptionsInitialMonthlyPaymentsThisMonth?.increments.toLocaleString('en-US')}</b>${' '}
<i>initial</i> payments on <u>monhtly</u> plan, totaling${' '}
<b>$${proSubscriptionsInitialMonthlyPaymentsThisMonth?.totalValue.toLocaleString('en-US')}</b>
</li>
<li>
<b>${proSubscriptionsInitialAnnualPaymentsThisMonth?.increments.toLocaleString('en-US')}</b>${' '}
<i>initial</i> payments on <u>annual</u> plan, totaling${' '}
<b>$${proSubscriptionsInitialAnnualPaymentsThisMonth?.totalValue.toLocaleString('en-US')}</b>
</li>
<li>
<b>${proSubscriptionsRenewingMonthlyPaymentsThisMonth?.increments.toLocaleString('en-US')}</b>${' '}
<i>renewing</i> payments on <u>monthly</u> plan, totaling${' '}
<b>$${proSubscriptionsRenewingMonthlyPaymentsThisMonth?.totalValue.toLocaleString('en-US')}</b>
</li>
<li>
<b>${proSubscriptionsRenewingAnnualPaymentsThisMonth?.increments.toLocaleString('en-US')}</b>${' '}
<i>renewing</i> payments on <u>annual</u> plan, totaling${' '}
<b>$${proSubscriptionsRenewingAnnualPaymentsThisMonth?.totalValue.toLocaleString('en-US')}</b>
</li>
</ul>
</li>
</ul>
</li>
<li>
<b>Users</b>
<ul>
<li>
Number of users registered: <b>${userRegistrationOverTime?.totalCount.toLocaleString('en-US')}</b>
</li>
<li>
Number of users unregistered: <b>${userDeletionOverTime?.totalCount.toLocaleString('en-US')}</b>
</li>
<li>
Average account duration this month:${' '}
<b>
${registrationLengthDurationThisMonth.days} days ${registrationLengthDurationThisMonth.hours} hours${' '}
${registrationLengthDurationThisMonth.minutes} minutes
</b>
</li>
</ul>
</li>
<li>
<b>Subscriptions</b>
<ul>
<li>
Number of subscriptions purchased:${' '}
<b>${subscriptionPurchasingOverTime?.totalCount.toLocaleString('en-US')}</b>
</li>
<li>
Number of subscriptions renewed:${' '}
<b>${subscriptionRenewingOverTime?.totalCount.toLocaleString('en-US')}</b>
</li>
<li>
Number of subscriptions refunded:${' '}
<b>${subscriptionRefundingOverTime?.totalCount.toLocaleString('en-US')}</b>
</li>
<li>
Number of subscriptions cancelled:${' '}
<b>${subscriptionCancelledOverTime?.totalCount.toLocaleString('en-US')}</b>
</li>
<li>
Number of subscriptions reactivated:${' '}
<b>${subscriptionReactivatedOverTime?.totalCount.toLocaleString('en-US')}</b>
</li>
<li>
Average subscription duration this month:${' '}
<b>
${subscriptionLengthDurationThisMonth.days} days ${subscriptionLengthDurationThisMonth.hours} hours${' '}
${subscriptionLengthDurationThisMonth.minutes} minutes
</b>
</li>
<li>
Average subscription remaining percentage this month:${' '}
<b>${subscriptionRemainingTimePercentageThisMonth}%</b>
</li>
<li>
Average time from registration to subscription purchase this month:${' '}
<b>
${registrationToSubscriptionDurationThisMonth.days} days${' '}
${registrationToSubscriptionDurationThisMonth.hours} hours${' '}
${registrationToSubscriptionDurationThisMonth.minutes} minutes
</b>
</li>
</ul>
</li>
</ul>
<p>
<strong>Here is the MRR Monthly chart this year:</strong>
</p>
<img src=${chartUrls.mrrMonthly}></img>
<p>
<strong>Here is the subscription chart over 30 days:</strong>
</p>
<img src=${chartUrls.subscriptions}></img>
<p>
<strong>Here is the users chart over 30 days:</strong>
</p>
<img src=${chartUrls.users}></img>
<p>
<strong>Here is the monthly churn rate percentage:</strong>
</p>
<p>✅ GREAT! Up to 7% 🔶 OKAY: 8-10% 🩸 BAD: 11 -15 % 🚨 TERRIBLE! 16-20%</p>
<p>Churn is calculated by the following formula:</p>
<p>
( Existing Customers Churn [${thisMonthChurn?.existingCustomersChurn}] + New Customers Churn [
${thisMonthChurn?.newCustomersChurn}] ) * 100 / Average Customers Count This Month [
${thisMonthChurn?.averageCustomersCount}]
</p>
<img src=${chartUrls.churn}></img>
<p>
<strong>Here is quarterly performance chart:</strong>
</p>
<img src=${chartUrls.quarterlyPerformance}></img>
<p>Thanks,SN</p>
</div>`
}
@@ -18,5 +18,5 @@ export class AnalyticsEntity {
nullable: true, nullable: true,
}) })
@Index('email') @Index('email')
declare username: string declare userEmail: string
} }
@@ -0,0 +1,140 @@
import 'reflect-metadata'
import { TimerInterface } from '@standardnotes/time'
import { AnalyticsActivity } from '../Analytics/AnalyticsActivity'
import { StatisticsMeasure } from '../Statistics/StatisticsMeasure'
import { Period } from '../Time/Period'
import { DomainEventFactory } from './DomainEventFactory'
describe('DomainEventFactory', () => {
let timer: TimerInterface
const createFactory = () => new DomainEventFactory(timer)
beforeEach(() => {
timer = {} as jest.Mocked<TimerInterface>
timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(1)
timer.getUTCDate = jest.fn().mockReturnValue(new Date(1))
})
it('should create a DAILY_ANALYTICS_REPORT_GENERATED event', () => {
expect(
createFactory().createDailyAnalyticsReportGeneratedEvent({
activityStatistics: [
{
name: AnalyticsActivity.Register,
retention: 24,
totalCount: 45,
},
],
statisticMeasures: [
{
name: StatisticsMeasure.Income,
totalValue: 43,
average: 23,
increments: 5,
period: Period.Today,
},
],
activityStatisticsOverTime: [
{
name: AnalyticsActivity.Register,
period: Period.Last30Days,
counts: [
{
periodKey: '2022-10-9',
totalCount: 3,
},
],
totalCount: 123,
},
],
statisticsOverTime: [
{
name: StatisticsMeasure.MRR,
period: Period.Last30Days,
counts: [
{
periodKey: '2022-10-9',
totalCount: 3,
},
],
},
],
churn: {
periodKeys: ['2022-10-9'],
values: [
{
rate: 12,
periodKey: '2022-10-9',
},
],
},
}),
).toEqual({
createdAt: expect.any(Date),
meta: {
correlation: {
userIdentifier: '',
userIdentifierType: 'uuid',
},
origin: 'analytics',
},
payload: {
activityStatistics: [
{
name: 'register',
retention: 24,
totalCount: 45,
},
],
activityStatisticsOverTime: [
{
counts: [
{
periodKey: '2022-10-9',
totalCount: 3,
},
],
name: 'register',
period: 9,
totalCount: 123,
},
],
statisticsOverTime: [
{
counts: [
{
periodKey: '2022-10-9',
totalCount: 3,
},
],
name: 'mrr',
period: 9,
},
],
churn: {
periodKeys: ['2022-10-9'],
values: [
{
periodKey: '2022-10-9',
rate: 12,
},
],
},
statisticMeasures: [
{
average: 23,
increments: 5,
name: 'income',
period: 0,
totalValue: 43,
},
],
},
type: 'DAILY_ANALYTICS_REPORT_GENERATED',
})
})
})
@@ -1,6 +1,4 @@
/* istanbul ignore file */ import { DomainEventService, DailyAnalyticsReportGeneratedEvent } from '@standardnotes/domain-events'
import { DomainEventService, EmailRequestedEvent } from '@standardnotes/domain-events'
import { TimerInterface } from '@standardnotes/time' import { TimerInterface } from '@standardnotes/time'
import { inject, injectable } from 'inversify' import { inject, injectable } from 'inversify'
import TYPES from '../../Bootstrap/Types' import TYPES from '../../Bootstrap/Types'
@@ -9,20 +7,52 @@ import { DomainEventFactoryInterface } from './DomainEventFactoryInterface'
@injectable() @injectable()
export class DomainEventFactory implements DomainEventFactoryInterface { export class DomainEventFactory implements DomainEventFactoryInterface {
constructor(@inject(TYPES.Timer) private timer: TimerInterface) {} constructor(@inject(TYPES.Timer) private timer: TimerInterface) {}
createEmailRequestedEvent(dto: {
userEmail: string createDailyAnalyticsReportGeneratedEvent(dto: {
messageIdentifier: string activityStatistics: Array<{
level: string name: string
body: string retention: number
subject: string totalCount: number
}): EmailRequestedEvent { }>
statisticMeasures: Array<{
name: string
totalValue: number
average: number
increments: number
period: number
}>
activityStatisticsOverTime: Array<{
name: string
period: number
counts: Array<{
periodKey: string
totalCount: number
}>
totalCount: number
}>
statisticsOverTime: Array<{
name: string
period: number
counts: Array<{
periodKey: string
totalCount: number
}>
}>
churn: {
periodKeys: Array<string>
values: Array<{
rate: number
periodKey: string
}>
}
}): DailyAnalyticsReportGeneratedEvent {
return { return {
type: 'EMAIL_REQUESTED', type: 'DAILY_ANALYTICS_REPORT_GENERATED',
createdAt: this.timer.getUTCDate(), createdAt: this.timer.getUTCDate(),
meta: { meta: {
correlation: { correlation: {
userIdentifier: dto.userEmail, userIdentifier: '',
userIdentifierType: 'email', userIdentifierType: 'uuid',
}, },
origin: DomainEventService.Analytics, origin: DomainEventService.Analytics,
}, },
@@ -1,11 +1,42 @@
import { EmailRequestedEvent } from '@standardnotes/domain-events' import { DailyAnalyticsReportGeneratedEvent } from '@standardnotes/domain-events'
export interface DomainEventFactoryInterface { export interface DomainEventFactoryInterface {
createEmailRequestedEvent(dto: { createDailyAnalyticsReportGeneratedEvent(dto: {
userEmail: string activityStatistics: Array<{
messageIdentifier: string name: string
level: string retention: number
body: string totalCount: number
subject: string }>
}): EmailRequestedEvent statisticMeasures: Array<{
name: string
totalValue: number
average: number
increments: number
period: number
}>
activityStatisticsOverTime: Array<{
name: string
period: number
counts: Array<{
periodKey: string
totalCount: number
}>
totalCount: number
}>
statisticsOverTime: Array<{
name: string
period: number
counts: Array<{
periodKey: string
totalCount: number
}>
}>
churn: {
periodKeys: Array<string>
values: Array<{
rate: number
periodKey: string
}>
}
}): DailyAnalyticsReportGeneratedEvent
} }
@@ -0,0 +1,69 @@
import 'reflect-metadata'
import { AccountDeletionRequestedEvent } from '@standardnotes/domain-events'
import { AccountDeletionRequestedEventHandler } from './AccountDeletionRequestedEventHandler'
import { TimerInterface } from '@standardnotes/time'
import { AnalyticsStoreInterface } from '../Analytics/AnalyticsStoreInterface'
import { StatisticsStoreInterface } from '../Statistics/StatisticsStoreInterface'
import { Period } from '../Time/Period'
import { AnalyticsEntityRepositoryInterface } from '../Entity/AnalyticsEntityRepositoryInterface'
describe('AccountDeletionRequestedEventHandler', () => {
let event: AccountDeletionRequestedEvent
let analyticsEntityRepository: AnalyticsEntityRepositoryInterface
let analyticsStore: AnalyticsStoreInterface
let statisticsStore: StatisticsStoreInterface
let timer: TimerInterface
const createHandler = () =>
new AccountDeletionRequestedEventHandler(analyticsEntityRepository, analyticsStore, statisticsStore, timer)
beforeEach(() => {
event = {} as jest.Mocked<AccountDeletionRequestedEvent>
event.createdAt = new Date(1)
event.payload = {
userUuid: '1-2-3',
userCreatedAtTimestamp: 1,
regularSubscriptionUuid: '2-3-4',
}
analyticsEntityRepository = {} as jest.Mocked<AnalyticsEntityRepositoryInterface>
analyticsEntityRepository.findOneByUserUuid = jest.fn().mockReturnValue({ id: 3 })
analyticsEntityRepository.remove = jest.fn()
analyticsStore = {} as jest.Mocked<AnalyticsStoreInterface>
analyticsStore.markActivity = jest.fn()
statisticsStore = {} as jest.Mocked<StatisticsStoreInterface>
statisticsStore.incrementMeasure = jest.fn()
timer = {} as jest.Mocked<TimerInterface>
timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(123)
})
it('should mark account deletion and registration length', async () => {
await createHandler().handle(event)
expect(analyticsStore.markActivity).toHaveBeenCalledWith(['DeleteAccount'], 3, [
Period.Today,
Period.ThisWeek,
Period.ThisMonth,
])
expect(statisticsStore.incrementMeasure).toHaveBeenCalledWith('registration-length', 122, [
Period.Today,
Period.ThisWeek,
Period.ThisMonth,
])
expect(analyticsEntityRepository.remove).toHaveBeenCalled()
})
it('should not mark anything if entity is not found', async () => {
analyticsEntityRepository.findOneByUserUuid = jest.fn().mockReturnValue(null)
await createHandler().handle(event)
expect(analyticsStore.markActivity).not.toHaveBeenCalled()
expect(statisticsStore.incrementMeasure).not.toHaveBeenCalled()
expect(analyticsEntityRepository.remove).not.toHaveBeenCalled()
})
})
@@ -1,13 +1,12 @@
import { AccountDeletionRequestedEvent, DomainEventHandlerInterface } from '@standardnotes/domain-events' import { AccountDeletionRequestedEvent, DomainEventHandlerInterface } from '@standardnotes/domain-events'
import { TimerInterface } from '@standardnotes/time' import { TimerInterface } from '@standardnotes/time'
import { inject, injectable, optional } from 'inversify' import { inject, injectable } from 'inversify'
import { Mixpanel } from 'mixpanel'
import TYPES from '../../Bootstrap/Types' import TYPES from '../../Bootstrap/Types'
import { AnalyticsActivity } from '../Analytics/AnalyticsActivity' import { AnalyticsActivity } from '../Analytics/AnalyticsActivity'
import { AnalyticsStoreInterface } from '../Analytics/AnalyticsStoreInterface' import { AnalyticsStoreInterface } from '../Analytics/AnalyticsStoreInterface'
import { AnalyticsEntityRepositoryInterface } from '../Entity/AnalyticsEntityRepositoryInterface' import { AnalyticsEntityRepositoryInterface } from '../Entity/AnalyticsEntityRepositoryInterface'
import { StatisticMeasureName } from '../Statistics/StatisticMeasureName' import { StatisticsMeasure } from '../Statistics/StatisticsMeasure'
import { StatisticsStoreInterface } from '../Statistics/StatisticsStoreInterface' import { StatisticsStoreInterface } from '../Statistics/StatisticsStoreInterface'
import { Period } from '../Time/Period' import { Period } from '../Time/Period'
@@ -18,7 +17,6 @@ export class AccountDeletionRequestedEventHandler implements DomainEventHandlerI
@inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface, @inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface,
@inject(TYPES.StatisticsStore) private statisticsStore: StatisticsStoreInterface, @inject(TYPES.StatisticsStore) private statisticsStore: StatisticsStoreInterface,
@inject(TYPES.Timer) private timer: TimerInterface, @inject(TYPES.Timer) private timer: TimerInterface,
@inject(TYPES.MixpanelClient) @optional() private mixpanelClient: Mixpanel | null,
) {} ) {}
async handle(event: AccountDeletionRequestedEvent): Promise<void> { async handle(event: AccountDeletionRequestedEvent): Promise<void> {
@@ -35,19 +33,12 @@ export class AccountDeletionRequestedEventHandler implements DomainEventHandlerI
]) ])
const registrationLength = this.timer.getTimestampInMicroseconds() - event.payload.userCreatedAtTimestamp const registrationLength = this.timer.getTimestampInMicroseconds() - event.payload.userCreatedAtTimestamp
await this.statisticsStore.incrementMeasure(StatisticMeasureName.NAMES.RegistrationLength, registrationLength, [ await this.statisticsStore.incrementMeasure(StatisticsMeasure.RegistrationLength, registrationLength, [
Period.Today, Period.Today,
Period.ThisWeek, Period.ThisWeek,
Period.ThisMonth, Period.ThisMonth,
]) ])
await this.analyticsEntityRepository.remove(analyticsEntity) await this.analyticsEntityRepository.remove(analyticsEntity)
if (this.mixpanelClient !== null) {
this.mixpanelClient.track(event.type, {
distinct_id: analyticsEntity.id.toString(),
user_created_at: this.timer.convertMicrosecondsToDate(event.payload.userCreatedAtTimestamp),
})
}
} }
} }
@@ -0,0 +1,34 @@
import 'reflect-metadata'
import { PaymentFailedEvent } from '@standardnotes/domain-events'
import { PaymentFailedEventHandler } from './PaymentFailedEventHandler'
import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
import { AnalyticsStoreInterface } from '../Analytics/AnalyticsStoreInterface'
describe('PaymentFailedEventHandler', () => {
let event: PaymentFailedEvent
let getUserAnalyticsId: GetUserAnalyticsId
let analyticsStore: AnalyticsStoreInterface
const createHandler = () => new PaymentFailedEventHandler(getUserAnalyticsId, analyticsStore)
beforeEach(() => {
getUserAnalyticsId = {} as jest.Mocked<GetUserAnalyticsId>
getUserAnalyticsId.execute = jest.fn().mockReturnValue({ analyticsId: 3 })
analyticsStore = {} as jest.Mocked<AnalyticsStoreInterface>
analyticsStore.markActivity = jest.fn()
event = {} as jest.Mocked<PaymentFailedEvent>
event.payload = {
userEmail: 'test@test.com',
}
})
it('should mark payment failed for analytics', async () => {
await createHandler().handle(event)
expect(analyticsStore.markActivity).toHaveBeenCalled()
})
})
@@ -1,6 +1,5 @@
import { DomainEventHandlerInterface, PaymentFailedEvent } from '@standardnotes/domain-events' import { DomainEventHandlerInterface, PaymentFailedEvent } from '@standardnotes/domain-events'
import { inject, injectable, optional } from 'inversify' import { inject, injectable } from 'inversify'
import { Mixpanel } from 'mixpanel'
import TYPES from '../../Bootstrap/Types' import TYPES from '../../Bootstrap/Types'
import { AnalyticsActivity } from '../Analytics/AnalyticsActivity' import { AnalyticsActivity } from '../Analytics/AnalyticsActivity'
@@ -13,7 +12,6 @@ export class PaymentFailedEventHandler implements DomainEventHandlerInterface {
constructor( constructor(
@inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId, @inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId,
@inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface, @inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface,
@inject(TYPES.MixpanelClient) @optional() private mixpanelClient: Mixpanel | null,
) {} ) {}
async handle(event: PaymentFailedEvent): Promise<void> { async handle(event: PaymentFailedEvent): Promise<void> {
@@ -23,11 +21,5 @@ export class PaymentFailedEventHandler implements DomainEventHandlerInterface {
Period.ThisWeek, Period.ThisWeek,
Period.ThisMonth, Period.ThisMonth,
]) ])
if (this.mixpanelClient !== null) {
this.mixpanelClient.track(event.type, {
distinct_id: analyticsId.toString(),
})
}
} }
} }
@@ -0,0 +1,75 @@
import 'reflect-metadata'
import { PaymentSuccessEvent } from '@standardnotes/domain-events'
import { PaymentSuccessEventHandler } from './PaymentSuccessEventHandler'
import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
import { Logger } from 'winston'
import { StatisticsStoreInterface } from '../Statistics/StatisticsStoreInterface'
import { AnalyticsStoreInterface } from '../Analytics/AnalyticsStoreInterface'
import { Period } from '../Time/Period'
describe('PaymentSuccessEventHandler', () => {
let event: PaymentSuccessEvent
let getUserAnalyticsId: GetUserAnalyticsId
let analyticsStore: AnalyticsStoreInterface
let statisticsStore: StatisticsStoreInterface
let logger: Logger
const createHandler = () =>
new PaymentSuccessEventHandler(getUserAnalyticsId, analyticsStore, statisticsStore, logger)
beforeEach(() => {
getUserAnalyticsId = {} as jest.Mocked<GetUserAnalyticsId>
getUserAnalyticsId.execute = jest.fn().mockReturnValue({ analyticsId: 3 })
analyticsStore = {} as jest.Mocked<AnalyticsStoreInterface>
analyticsStore.markActivity = jest.fn()
statisticsStore = {} as jest.Mocked<StatisticsStoreInterface>
statisticsStore.incrementMeasure = jest.fn()
event = {} as jest.Mocked<PaymentSuccessEvent>
event.payload = {
userEmail: 'test@test.com',
amount: 12.45,
billingFrequency: 12,
paymentType: 'initial',
subscriptionName: 'PRO_PLAN',
}
logger = {} as jest.Mocked<Logger>
logger.warn = jest.fn()
})
it('should mark payment success for analytics', async () => {
await createHandler().handle(event)
expect(analyticsStore.markActivity).toHaveBeenCalled()
expect(statisticsStore.incrementMeasure).toHaveBeenNthCalledWith(
2,
'pro-subscription-initial-annual-payments-income',
12.45,
[Period.Today, Period.ThisWeek, Period.ThisMonth],
)
})
it('should mark non-detailed payment success statistics for analytics', async () => {
event.payload = {
userEmail: 'test@test.com',
amount: 12.45,
billingFrequency: 13,
paymentType: 'initial',
subscriptionName: 'PRO_PLAN',
}
await createHandler().handle(event)
expect(statisticsStore.incrementMeasure).toBeCalledTimes(1)
expect(statisticsStore.incrementMeasure).toHaveBeenNthCalledWith(1, 'income', 12.45, [
Period.Today,
Period.ThisWeek,
Period.ThisMonth,
])
})
})
@@ -1,13 +1,12 @@
import { PaymentType, SubscriptionBillingFrequency, SubscriptionName } from '@standardnotes/common' import { PaymentType, SubscriptionBillingFrequency, SubscriptionName } from '@standardnotes/common'
import { DomainEventHandlerInterface, PaymentSuccessEvent } from '@standardnotes/domain-events' import { DomainEventHandlerInterface, PaymentSuccessEvent } from '@standardnotes/domain-events'
import { inject, injectable, optional } from 'inversify' import { inject, injectable } from 'inversify'
import { Mixpanel } from 'mixpanel'
import { Logger } from 'winston' import { Logger } from 'winston'
import TYPES from '../../Bootstrap/Types' import TYPES from '../../Bootstrap/Types'
import { AnalyticsActivity } from '../Analytics/AnalyticsActivity' import { AnalyticsActivity } from '../Analytics/AnalyticsActivity'
import { AnalyticsStoreInterface } from '../Analytics/AnalyticsStoreInterface' import { AnalyticsStoreInterface } from '../Analytics/AnalyticsStoreInterface'
import { StatisticMeasureName } from '../Statistics/StatisticMeasureName' import { StatisticsMeasure } from '../Statistics/StatisticsMeasure'
import { StatisticsStoreInterface } from '../Statistics/StatisticsStoreInterface' import { StatisticsStoreInterface } from '../Statistics/StatisticsStoreInterface'
import { Period } from '../Time/Period' import { Period } from '../Time/Period'
import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId' import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
@@ -21,27 +20,15 @@ export class PaymentSuccessEventHandler implements DomainEventHandlerInterface {
[ [
PaymentType.Initial, PaymentType.Initial,
new Map([ new Map([
[ [SubscriptionBillingFrequency.Monthly, StatisticsMeasure.PlusSubscriptionInitialMonthlyPaymentsIncome],
SubscriptionBillingFrequency.Monthly, [SubscriptionBillingFrequency.Annual, StatisticsMeasure.PlusSubscriptionInitialAnnualPaymentsIncome],
StatisticMeasureName.NAMES.PlusSubscriptionInitialMonthlyPaymentsIncome,
],
[
SubscriptionBillingFrequency.Annual,
StatisticMeasureName.NAMES.PlusSubscriptionInitialAnnualPaymentsIncome,
],
]), ]),
], ],
[ [
PaymentType.Renewal, PaymentType.Renewal,
new Map([ new Map([
[ [SubscriptionBillingFrequency.Monthly, StatisticsMeasure.PlusSubscriptionRenewingMonthlyPaymentsIncome],
SubscriptionBillingFrequency.Monthly, [SubscriptionBillingFrequency.Annual, StatisticsMeasure.PlusSubscriptionRenewingAnnualPaymentsIncome],
StatisticMeasureName.NAMES.PlusSubscriptionRenewingMonthlyPaymentsIncome,
],
[
SubscriptionBillingFrequency.Annual,
StatisticMeasureName.NAMES.PlusSubscriptionRenewingAnnualPaymentsIncome,
],
]), ]),
], ],
]), ]),
@@ -52,27 +39,15 @@ export class PaymentSuccessEventHandler implements DomainEventHandlerInterface {
[ [
PaymentType.Initial, PaymentType.Initial,
new Map([ new Map([
[ [SubscriptionBillingFrequency.Monthly, StatisticsMeasure.ProSubscriptionInitialMonthlyPaymentsIncome],
SubscriptionBillingFrequency.Monthly, [SubscriptionBillingFrequency.Annual, StatisticsMeasure.ProSubscriptionInitialAnnualPaymentsIncome],
StatisticMeasureName.NAMES.ProSubscriptionInitialMonthlyPaymentsIncome,
],
[
SubscriptionBillingFrequency.Annual,
StatisticMeasureName.NAMES.ProSubscriptionInitialAnnualPaymentsIncome,
],
]), ]),
], ],
[ [
PaymentType.Renewal, PaymentType.Renewal,
new Map([ new Map([
[ [SubscriptionBillingFrequency.Monthly, StatisticsMeasure.ProSubscriptionRenewingMonthlyPaymentsIncome],
SubscriptionBillingFrequency.Monthly, [SubscriptionBillingFrequency.Annual, StatisticsMeasure.ProSubscriptionRenewingAnnualPaymentsIncome],
StatisticMeasureName.NAMES.ProSubscriptionRenewingMonthlyPaymentsIncome,
],
[
SubscriptionBillingFrequency.Annual,
StatisticMeasureName.NAMES.ProSubscriptionRenewingAnnualPaymentsIncome,
],
]), ]),
], ],
]), ]),
@@ -84,7 +59,6 @@ export class PaymentSuccessEventHandler implements DomainEventHandlerInterface {
@inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface, @inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface,
@inject(TYPES.StatisticsStore) private statisticsStore: StatisticsStoreInterface, @inject(TYPES.StatisticsStore) private statisticsStore: StatisticsStoreInterface,
@inject(TYPES.Logger) private logger: Logger, @inject(TYPES.Logger) private logger: Logger,
@inject(TYPES.MixpanelClient) @optional() private mixpanelClient: Mixpanel | null,
) {} ) {}
async handle(event: PaymentSuccessEvent): Promise<void> { async handle(event: PaymentSuccessEvent): Promise<void> {
@@ -95,7 +69,7 @@ export class PaymentSuccessEventHandler implements DomainEventHandlerInterface {
Period.ThisMonth, Period.ThisMonth,
]) ])
const statisticMeasures = [StatisticMeasureName.NAMES.Income] const statisticMeasures = [StatisticsMeasure.Income]
const detailedMeasure = this.DETAILED_MEASURES.get(event.payload.subscriptionName as SubscriptionName) const detailedMeasure = this.DETAILED_MEASURES.get(event.payload.subscriptionName as SubscriptionName)
?.get(event.payload.paymentType as PaymentType) ?.get(event.payload.paymentType as PaymentType)
@@ -115,19 +89,5 @@ export class PaymentSuccessEventHandler implements DomainEventHandlerInterface {
Period.ThisMonth, Period.ThisMonth,
]) ])
} }
if (this.mixpanelClient !== null) {
this.mixpanelClient.track(event.type, {
distinct_id: analyticsId.toString(),
amount: event.payload.amount,
billing_frequency: event.payload.billingFrequency,
payment_type: event.payload.paymentType,
subscription_name: event.payload.subscriptionName,
})
this.mixpanelClient.people.track_charge(analyticsId.toString(), event.payload.amount)
this.mixpanelClient.people.set(analyticsId.toString(), 'subscription', event.payload.subscriptionName)
}
} }
} }
@@ -0,0 +1,36 @@
import 'reflect-metadata'
import { RefundProcessedEvent } from '@standardnotes/domain-events'
import { RefundProcessedEventHandler } from './RefundProcessedEventHandler'
import { StatisticsMeasure } from '../Statistics/StatisticsMeasure'
import { StatisticsStoreInterface } from '../Statistics/StatisticsStoreInterface'
import { Period } from '../Time/Period'
describe('RefundProcessedEventHandler', () => {
let event: RefundProcessedEvent
let statisticsStore: StatisticsStoreInterface
const createHandler = () => new RefundProcessedEventHandler(statisticsStore)
beforeEach(() => {
statisticsStore = {} as jest.Mocked<StatisticsStoreInterface>
statisticsStore.incrementMeasure = jest.fn()
event = {} as jest.Mocked<RefundProcessedEvent>
event.payload = {
userEmail: 'test@test.com',
amount: 12.45,
}
})
it('should mark refunds for statistics', async () => {
await createHandler().handle(event)
expect(statisticsStore.incrementMeasure).toHaveBeenCalledWith(StatisticsMeasure.Refunds, 12.45, [
Period.Today,
Period.ThisWeek,
Period.ThisMonth,
])
})
})

Some files were not shown because too many files have changed in this diff Show More