feat(analytics): initial Habitica-owned solution
This commit is contained in:
committed by
Kalista Payne
parent
746fcfff49
commit
e6ffd69148
@@ -21,3 +21,4 @@ services:
|
|||||||
timeout: 30s
|
timeout: 30s
|
||||||
start_period: 0s
|
start_period: 0s
|
||||||
retries: 30
|
retries: 30
|
||||||
|
|
||||||
Generated
+164
-249
@@ -57,7 +57,7 @@
|
|||||||
"micromustache": "^8.0.3",
|
"micromustache": "^8.0.3",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"moment-recur": "git://github.com/HabitRPG/moment-recur.git#d3e8e6da0806f13b74dd2e4d7d9053e6a63db119",
|
"moment-recur": "git://github.com/HabitRPG/moment-recur.git#d3e8e6da0806f13b74dd2e4d7d9053e6a63db119",
|
||||||
"mongoose": "^8.9.5",
|
"mongoose": "^8.23.0",
|
||||||
"morgan": "^1.10.1",
|
"morgan": "^1.10.1",
|
||||||
"nan": "^2.25.0",
|
"nan": "^2.25.0",
|
||||||
"nconf": "^0.12.1",
|
"nconf": "^0.12.1",
|
||||||
@@ -2623,6 +2623,19 @@
|
|||||||
"node": ">=12.0.0"
|
"node": ">=12.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@google-cloud/trace-agent/node_modules/gcp-metadata": {
|
||||||
|
"version": "5.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz",
|
||||||
|
"integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"gaxios": "^5.0.0",
|
||||||
|
"json-bigint": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@google-cloud/trace-agent/node_modules/lru-cache": {
|
"node_modules/@google-cloud/trace-agent/node_modules/lru-cache": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||||
@@ -3051,9 +3064,9 @@
|
|||||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||||
},
|
},
|
||||||
"node_modules/@mongodb-js/saslprep": {
|
"node_modules/@mongodb-js/saslprep": {
|
||||||
"version": "1.3.2",
|
"version": "1.4.6",
|
||||||
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.6.tgz",
|
||||||
"integrity": "sha512-QgA5AySqB27cGTXBFmnpifAi7HxoGUeezwo6p9dI03MuDB6Pp33zgclqVb6oVK3j6I9Vesg0+oojW2XxB59SGg==",
|
"integrity": "sha512-y+x3H1xBZd38n10NZF/rEBlvDOOMQ6LKUTHqr8R9VkJ+mmQOYtJFxIlkkK8fZrtOiL6VixbOBWMbZGBdal3Z1g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"sparse-bitfield": "^3.0.3"
|
"sparse-bitfield": "^3.0.3"
|
||||||
@@ -6299,6 +6312,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz",
|
||||||
"integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==",
|
"integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"readable-stream": "^2.3.5",
|
"readable-stream": "^2.3.5",
|
||||||
"safe-buffer": "^5.1.1"
|
"safe-buffer": "^5.1.1"
|
||||||
@@ -6308,13 +6322,15 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/bl/node_modules/readable-stream": {
|
"node_modules/bl/node_modules/readable-stream": {
|
||||||
"version": "2.3.8",
|
"version": "2.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-util-is": "~1.0.0",
|
"core-util-is": "~1.0.0",
|
||||||
"inherits": "~2.0.3",
|
"inherits": "~2.0.3",
|
||||||
@@ -6330,6 +6346,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"safe-buffer": "~5.1.0"
|
"safe-buffer": "~5.1.0"
|
||||||
}
|
}
|
||||||
@@ -11207,18 +11224,6 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/gcp-metadata": {
|
|
||||||
"version": "5.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz",
|
|
||||||
"integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==",
|
|
||||||
"dependencies": {
|
|
||||||
"gaxios": "^5.0.0",
|
|
||||||
"json-bigint": "^1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/gensync": {
|
"node_modules/gensync": {
|
||||||
"version": "1.0.0-beta.2",
|
"version": "1.0.0-beta.2",
|
||||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
||||||
@@ -11962,6 +11967,19 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/google-auth-library/node_modules/gcp-metadata": {
|
||||||
|
"version": "5.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz",
|
||||||
|
"integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"gaxios": "^5.0.0",
|
||||||
|
"json-bigint": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/google-auth-library/node_modules/lru-cache": {
|
"node_modules/google-auth-library/node_modules/lru-cache": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||||
@@ -15522,143 +15540,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mongodb": {
|
"node_modules/mongodb": {
|
||||||
"version": "3.7.4",
|
"version": "6.20.0",
|
||||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.7.4.tgz",
|
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.20.0.tgz",
|
||||||
"integrity": "sha512-K5q8aBqEXMwWdVNh94UQTwZ6BejVbFhh1uB6c5FKtPE9eUMZPUO3sRZdgIEcHSrAWmxzpG/FeODDKL388sqRmw==",
|
"integrity": "sha512-Tl6MEIU3K4Rq3TSHd+sZQqRBoGlFsOgNrH5ltAcFBV62Re3Fd+FcaVf8uSEQFOJ51SDowDVttBTONMfoYWrWlQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"bl": "^2.2.1",
|
|
||||||
"bson": "^1.1.4",
|
|
||||||
"denque": "^1.4.1",
|
|
||||||
"optional-require": "^1.1.8",
|
|
||||||
"safe-buffer": "^5.1.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"saslprep": "^1.0.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"aws4": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"bson-ext": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"kerberos": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"mongodb-client-encryption": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"mongodb-extjson": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"snappy": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mongodb-connection-string-url": {
|
|
||||||
"version": "3.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz",
|
|
||||||
"integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==",
|
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/whatwg-url": "^11.0.2",
|
"@mongodb-js/saslprep": "^1.3.0",
|
||||||
"whatwg-url": "^14.1.0 || ^13.0.0"
|
"bson": "^6.10.4",
|
||||||
}
|
"mongodb-connection-string-url": "^3.0.2"
|
||||||
},
|
|
||||||
"node_modules/mongodb-connection-string-url/node_modules/tr46": {
|
|
||||||
"version": "5.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz",
|
|
||||||
"integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"punycode": "^2.3.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mongodb-connection-string-url/node_modules/webidl-conversions": {
|
|
||||||
"version": "7.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
|
||||||
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
|
|
||||||
"license": "BSD-2-Clause",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mongodb-connection-string-url/node_modules/whatwg-url": {
|
|
||||||
"version": "14.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.0.tgz",
|
|
||||||
"integrity": "sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"tr46": "^5.0.0",
|
|
||||||
"webidl-conversions": "^7.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mongodb/node_modules/bson": {
|
|
||||||
"version": "1.1.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz",
|
|
||||||
"integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.6.19"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mongoose": {
|
|
||||||
"version": "8.9.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.9.7.tgz",
|
|
||||||
"integrity": "sha512-mvNXmU0V8qZzMR/qoK2mjT4Ti2ALdtfS0teK+twxhlGkwzOD76V02/zWajTu2MJ7QyEmZe9OWvnJsIY0iAuX3Q==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"bson": "^6.10.1",
|
|
||||||
"kareem": "2.6.3",
|
|
||||||
"mongodb": "~6.12.0",
|
|
||||||
"mpath": "0.9.0",
|
|
||||||
"mquery": "5.0.0",
|
|
||||||
"ms": "2.1.3",
|
|
||||||
"sift": "17.1.3"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16.20.1"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/mongoose"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mongoose/node_modules/kerberos": {
|
|
||||||
"version": "2.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/kerberos/-/kerberos-2.2.2.tgz",
|
|
||||||
"integrity": "sha512-42O7+/1Zatsc3MkxaMPpXcIl/ukIrbQaGoArZEAr6GcEi2qhfprOBYOPhj+YvSMJkEkdpTjApUx+2DuWaKwRhg==",
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"optional": true,
|
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
|
||||||
"node-addon-api": "^6.1.0",
|
|
||||||
"prebuild-install": "^7.1.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mongoose/node_modules/mongodb": {
|
|
||||||
"version": "6.12.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.12.0.tgz",
|
|
||||||
"integrity": "sha512-RM7AHlvYfS7jv7+BXund/kR64DryVI+cHbVAy9P61fnb1RcWZqOW1/Wj2YhqMCx+MuYhqTRGv7AwHBzmsCKBfA==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@mongodb-js/saslprep": "^1.1.9",
|
|
||||||
"bson": "^6.10.1",
|
|
||||||
"mongodb-connection-string-url": "^3.0.0"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.20.1"
|
"node": ">=16.20.1"
|
||||||
@@ -15669,7 +15558,7 @@
|
|||||||
"gcp-metadata": "^5.2.0",
|
"gcp-metadata": "^5.2.0",
|
||||||
"kerberos": "^2.0.1",
|
"kerberos": "^2.0.1",
|
||||||
"mongodb-client-encryption": ">=6.0.0 <7",
|
"mongodb-client-encryption": ">=6.0.0 <7",
|
||||||
"snappy": "^7.2.2",
|
"snappy": "^7.3.2",
|
||||||
"socks": "^2.7.1"
|
"socks": "^2.7.1"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
@@ -15696,104 +15585,77 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mongodb-connection-string-url": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/whatwg-url": "^11.0.2",
|
||||||
|
"whatwg-url": "^14.1.0 || ^13.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mongodb-connection-string-url/node_modules/tr46": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"punycode": "^2.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mongodb-connection-string-url/node_modules/webidl-conversions": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mongodb-connection-string-url/node_modules/whatwg-url": {
|
||||||
|
"version": "14.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
|
||||||
|
"integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tr46": "^5.1.0",
|
||||||
|
"webidl-conversions": "^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mongoose": {
|
||||||
|
"version": "8.23.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.23.0.tgz",
|
||||||
|
"integrity": "sha512-Bul4Ha6J8IqzFrb0B1xpVzkC3S0sk43dmLSnhFOn8eJlZiLwL5WO6cRymmjaADdCMjUcCpj2ce8hZI6O4ZFSug==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"bson": "^6.10.4",
|
||||||
|
"kareem": "2.6.3",
|
||||||
|
"mongodb": "~6.20.0",
|
||||||
|
"mpath": "0.9.0",
|
||||||
|
"mquery": "5.0.0",
|
||||||
|
"ms": "2.1.3",
|
||||||
|
"sift": "17.1.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mongoose"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/mongoose/node_modules/ms": {
|
"node_modules/mongoose/node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||||
},
|
},
|
||||||
"node_modules/mongoose/node_modules/napi-build-utils": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
|
|
||||||
"optional": true,
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"node_modules/mongoose/node_modules/node-abi": {
|
|
||||||
"version": "3.87.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.87.0.tgz",
|
|
||||||
"integrity": "sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==",
|
|
||||||
"optional": true,
|
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
|
||||||
"semver": "^7.3.5"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mongoose/node_modules/node-addon-api": {
|
|
||||||
"version": "6.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz",
|
|
||||||
"integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==",
|
|
||||||
"optional": true,
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"node_modules/mongoose/node_modules/prebuild-install": {
|
|
||||||
"version": "7.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
|
|
||||||
"integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",
|
|
||||||
"optional": true,
|
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
|
||||||
"detect-libc": "^2.0.0",
|
|
||||||
"expand-template": "^2.0.3",
|
|
||||||
"github-from-package": "0.0.0",
|
|
||||||
"minimist": "^1.2.3",
|
|
||||||
"mkdirp-classic": "^0.5.3",
|
|
||||||
"napi-build-utils": "^2.0.0",
|
|
||||||
"node-abi": "^3.3.0",
|
|
||||||
"pump": "^3.0.0",
|
|
||||||
"rc": "^1.2.7",
|
|
||||||
"simple-get": "^4.0.0",
|
|
||||||
"tar-fs": "^2.0.0",
|
|
||||||
"tunnel-agent": "^0.6.0"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"prebuild-install": "bin.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mongoose/node_modules/semver": {
|
|
||||||
"version": "7.7.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
|
||||||
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
|
||||||
"optional": true,
|
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
|
||||||
"semver": "bin/semver.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mongoose/node_modules/simple-get": {
|
|
||||||
"version": "4.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
|
|
||||||
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/feross"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "patreon",
|
|
||||||
"url": "https://www.patreon.com/feross"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "consulting",
|
|
||||||
"url": "https://feross.org/support"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"optional": true,
|
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
|
||||||
"decompress-response": "^6.0.0",
|
|
||||||
"once": "^1.3.1",
|
|
||||||
"simple-concat": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/monk": {
|
"node_modules/monk": {
|
||||||
"version": "7.3.4",
|
"version": "7.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/monk/-/monk-7.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/monk/-/monk-7.3.4.tgz",
|
||||||
@@ -15848,6 +15710,56 @@
|
|||||||
"integrity": "sha512-jSTz73B/+pGTTvhu5Ym8xsG6+QqaWab53UXnXdNNlTijTdLvcHABCLJXudQiJxob5N1Mzr5EOSx5ziwn2sihPQ==",
|
"integrity": "sha512-jSTz73B/+pGTTvhu5Ym8xsG6+QqaWab53UXnXdNNlTijTdLvcHABCLJXudQiJxob5N1Mzr5EOSx5ziwn2sihPQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/monk/node_modules/bson": {
|
||||||
|
"version": "1.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz",
|
||||||
|
"integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.6.19"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/monk/node_modules/mongodb": {
|
||||||
|
"version": "3.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.7.4.tgz",
|
||||||
|
"integrity": "sha512-K5q8aBqEXMwWdVNh94UQTwZ6BejVbFhh1uB6c5FKtPE9eUMZPUO3sRZdgIEcHSrAWmxzpG/FeODDKL388sqRmw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"bl": "^2.2.1",
|
||||||
|
"bson": "^1.1.4",
|
||||||
|
"denque": "^1.4.1",
|
||||||
|
"optional-require": "^1.1.8",
|
||||||
|
"safe-buffer": "^5.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"saslprep": "^1.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"aws4": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"bson-ext": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"kerberos": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"mongodb-client-encryption": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"mongodb-extjson": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"snappy": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/morgan": {
|
"node_modules/morgan": {
|
||||||
"version": "1.10.1",
|
"version": "1.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz",
|
||||||
@@ -17194,10 +17106,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/optional-require": {
|
"node_modules/optional-require": {
|
||||||
"version": "1.1.8",
|
"version": "1.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.8.tgz",
|
"resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.10.tgz",
|
||||||
"integrity": "sha512-jq83qaUb0wNg9Krv1c5OQ+58EK+vHde6aBPzLvPPqJm89UQWsvSuFy9X/OSNJnFeSOKo7btE0n8Nl2+nE+z5nA==",
|
"integrity": "sha512-0r3OB9EIQsP+a5HVATHq2ExIy2q/Vaffoo4IAikW1spCYswhLxqWQS0i3GwS3AdY/OIP4SWZHLGz8CMU558PGw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"require-at": "^1.0.6"
|
"require-at": "^1.0.6"
|
||||||
},
|
},
|
||||||
@@ -18679,6 +18592,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz",
|
||||||
"integrity": "sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g==",
|
"integrity": "sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
@@ -18977,6 +18891,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
|
||||||
"integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==",
|
"integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"sparse-bitfield": "^3.0.3"
|
"sparse-bitfield": "^3.0.3"
|
||||||
|
|||||||
+1
-1
@@ -52,7 +52,7 @@
|
|||||||
"micromustache": "^8.0.3",
|
"micromustache": "^8.0.3",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"moment-recur": "git://github.com/HabitRPG/moment-recur.git#d3e8e6da0806f13b74dd2e4d7d9053e6a63db119",
|
"moment-recur": "git://github.com/HabitRPG/moment-recur.git#d3e8e6da0806f13b74dd2e4d7d9053e6a63db119",
|
||||||
"mongoose": "^8.9.5",
|
"mongoose": "^8.23.0",
|
||||||
"morgan": "^1.10.1",
|
"morgan": "^1.10.1",
|
||||||
"nan": "^2.25.0",
|
"nan": "^2.25.0",
|
||||||
"nconf": "^0.12.1",
|
"nconf": "^0.12.1",
|
||||||
|
|||||||
@@ -1,560 +0,0 @@
|
|||||||
/* eslint-disable camelcase */
|
|
||||||
import nconf from 'nconf';
|
|
||||||
import Amplitude from 'amplitude';
|
|
||||||
import * as analyticsService from '../../../../website/server/libs/analyticsService';
|
|
||||||
|
|
||||||
describe('analyticsService', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
sandbox.stub(Amplitude.prototype, 'track').returns(Promise.resolve());
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
sandbox.restore();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#getServiceByEnvironment', () => {
|
|
||||||
it('returns mock methods when not in production', () => {
|
|
||||||
sandbox.stub(nconf, 'get').withArgs('IS_PROD').returns(false);
|
|
||||||
expect(analyticsService.getAnalyticsServiceByEnvironment())
|
|
||||||
.to.equal(analyticsService.mockAnalyticsService);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns real methods when in production', () => {
|
|
||||||
sandbox.stub(nconf, 'get').withArgs('IS_PROD').returns(true);
|
|
||||||
expect(analyticsService.getAnalyticsServiceByEnvironment().track)
|
|
||||||
.to.equal(analyticsService.track);
|
|
||||||
expect(analyticsService.getAnalyticsServiceByEnvironment().trackPurchase)
|
|
||||||
.to.equal(analyticsService.trackPurchase);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#track', () => {
|
|
||||||
let eventType; let
|
|
||||||
data;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
eventType = 'Cron';
|
|
||||||
data = {
|
|
||||||
category: 'behavior',
|
|
||||||
uuid: 'unique-user-id',
|
|
||||||
resting: true,
|
|
||||||
cronCount: 5,
|
|
||||||
headers: {
|
|
||||||
'x-client': 'habitica-web',
|
|
||||||
'user-agent': '',
|
|
||||||
},
|
|
||||||
user: {
|
|
||||||
preferences: {
|
|
||||||
analyticsConsent: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
context('Amplitude', () => {
|
|
||||||
it('calls out to amplitude', () => analyticsService.track(eventType, data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledOnce;
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('uses a dummy user id if none is provided', () => {
|
|
||||||
delete data.uuid;
|
|
||||||
|
|
||||||
return analyticsService.track(eventType, data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
user_id: 'no-user-id-was-provided',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
context('platform', () => {
|
|
||||||
it('logs web platform', () => {
|
|
||||||
data.headers = { 'x-client': 'habitica-web' };
|
|
||||||
|
|
||||||
return analyticsService.track(eventType, data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
platform: 'Web',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('logs iOS platform', () => {
|
|
||||||
data.headers = { 'x-client': 'habitica-ios' };
|
|
||||||
|
|
||||||
return analyticsService.track(eventType, data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
platform: 'iOS',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('logs Android platform', () => {
|
|
||||||
data.headers = { 'x-client': 'habitica-android' };
|
|
||||||
|
|
||||||
return analyticsService.track(eventType, data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
platform: 'Android',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('logs 3rd Party platform', () => {
|
|
||||||
data.headers = { 'x-client': 'some-third-party' };
|
|
||||||
|
|
||||||
return analyticsService.track(eventType, data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
platform: '3rd Party',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('logs unknown if headers are not passed in', () => {
|
|
||||||
delete data.headers;
|
|
||||||
|
|
||||||
return analyticsService.track(eventType, data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
platform: 'Unknown',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
context('Operating System', () => {
|
|
||||||
it('sets default', () => {
|
|
||||||
data.headers = {
|
|
||||||
'x-client': 'third-party',
|
|
||||||
'user-agent': 'foo',
|
|
||||||
};
|
|
||||||
|
|
||||||
return analyticsService.track(eventType, data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
os_name: 'Other',
|
|
||||||
os_version: '0',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sets iOS', () => {
|
|
||||||
data.headers = {
|
|
||||||
'x-client': 'habitica-ios',
|
|
||||||
'user-agent': 'Habitica/148 (iPhone; iOS 9.3; Scale/2.00)',
|
|
||||||
};
|
|
||||||
|
|
||||||
return analyticsService.track(eventType, data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
os_name: 'iOS',
|
|
||||||
os_version: '9.3.0',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sets Android', () => {
|
|
||||||
data.headers = {
|
|
||||||
'x-client': 'habitica-android',
|
|
||||||
'user-agent': 'Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19',
|
|
||||||
};
|
|
||||||
|
|
||||||
return analyticsService.track(eventType, data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
os_name: 'Android',
|
|
||||||
os_version: '4.0.4',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sets Unknown if headers are not passed in', () => {
|
|
||||||
delete data.headers;
|
|
||||||
|
|
||||||
return analyticsService.track(eventType, data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
os_name: undefined,
|
|
||||||
os_version: undefined,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sends details about event', () => analyticsService.track(eventType, data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
event_properties: {
|
|
||||||
category: 'behavior',
|
|
||||||
resting: true,
|
|
||||||
cronCount: 5,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('sends english item name for gear if itemKey is provided', () => {
|
|
||||||
data.itemKey = 'headAccessory_special_foxEars';
|
|
||||||
|
|
||||||
return analyticsService.track(eventType, data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
event_properties: {
|
|
||||||
itemKey: data.itemKey,
|
|
||||||
itemName: 'Fox Ears',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sends english item name for egg if itemKey is provided', () => {
|
|
||||||
data.itemKey = 'Wolf';
|
|
||||||
|
|
||||||
return analyticsService.track(eventType, data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
event_properties: {
|
|
||||||
itemKey: data.itemKey,
|
|
||||||
itemName: 'Wolf Egg',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sends english item name for food if itemKey is provided', () => {
|
|
||||||
data.itemKey = 'Cake_Skeleton';
|
|
||||||
|
|
||||||
return analyticsService.track(eventType, data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
event_properties: {
|
|
||||||
itemKey: data.itemKey,
|
|
||||||
itemName: 'Bare Bones Cake',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sends english item name for hatching potion if itemKey is provided', () => {
|
|
||||||
data.itemKey = 'Golden';
|
|
||||||
|
|
||||||
return analyticsService.track(eventType, data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
event_properties: {
|
|
||||||
itemKey: data.itemKey,
|
|
||||||
itemName: 'Golden Hatching Potion',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sends english item name for quest if itemKey is provided', () => {
|
|
||||||
data.itemKey = 'atom1';
|
|
||||||
|
|
||||||
return analyticsService.track(eventType, data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
event_properties: {
|
|
||||||
itemKey: data.itemKey,
|
|
||||||
itemName: 'Attack of the Mundane, Part 1: Dish Disaster!',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sends english item name for purchased spell if itemKey is provided', () => {
|
|
||||||
data.itemKey = 'seafoam';
|
|
||||||
|
|
||||||
return analyticsService.track(eventType, data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
event_properties: {
|
|
||||||
itemKey: data.itemKey,
|
|
||||||
itemName: 'Seafoam',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sends user data if provided', () => {
|
|
||||||
const stats = {
|
|
||||||
class: 'wizard', exp: 5, gp: 23, hp: 10, lvl: 4, mp: 30,
|
|
||||||
};
|
|
||||||
const user = {
|
|
||||||
stats,
|
|
||||||
contributor: { level: 1 },
|
|
||||||
purchased: { plan: { planId: 'foo-plan' } },
|
|
||||||
flags: { tour: { intro: -2 } },
|
|
||||||
habits: [{ _id: 'habit' }],
|
|
||||||
dailys: [{ _id: 'daily' }],
|
|
||||||
todos: [{ _id: 'todo' }],
|
|
||||||
rewards: [{ _id: 'reward' }],
|
|
||||||
balance: 12,
|
|
||||||
loginIncentives: 1,
|
|
||||||
preferences: {
|
|
||||||
analyticsConsent: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
data.user = user;
|
|
||||||
|
|
||||||
return analyticsService.track(eventType, data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
user_properties: {
|
|
||||||
Class: 'wizard',
|
|
||||||
Experience: 5,
|
|
||||||
Gold: 23,
|
|
||||||
Health: 10,
|
|
||||||
Level: 4,
|
|
||||||
Mana: 30,
|
|
||||||
tutorialComplete: true,
|
|
||||||
'Number Of Tasks': {
|
|
||||||
habits: 1,
|
|
||||||
dailys: 1,
|
|
||||||
todos: 1,
|
|
||||||
rewards: 1,
|
|
||||||
},
|
|
||||||
contributorLevel: 1,
|
|
||||||
subscription: 'foo-plan',
|
|
||||||
balance: 12,
|
|
||||||
balanceGemAmount: 48,
|
|
||||||
loginIncentives: 1,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#trackPurchase', () => {
|
|
||||||
let data;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
data = {
|
|
||||||
uuid: 'user-id',
|
|
||||||
sku: 'paypal-checkout',
|
|
||||||
paymentMethod: 'PayPal',
|
|
||||||
itemPurchased: 'Gems',
|
|
||||||
purchaseValue: 8,
|
|
||||||
purchaseType: 'checkout',
|
|
||||||
gift: false,
|
|
||||||
quantity: 1,
|
|
||||||
headers: {
|
|
||||||
'x-client': 'habitica-web',
|
|
||||||
'user-agent': '',
|
|
||||||
},
|
|
||||||
user: {
|
|
||||||
preferences: {
|
|
||||||
analyticsConsent: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
context('Amplitude', () => {
|
|
||||||
it('calls out to amplitude', () => analyticsService.trackPurchase(data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledOnce;
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('uses a dummy user id if none is provided', () => {
|
|
||||||
delete data.uuid;
|
|
||||||
|
|
||||||
return analyticsService.trackPurchase(data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
user_id: 'no-user-id-was-provided',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
context('platform', () => {
|
|
||||||
it('logs web platform', () => {
|
|
||||||
data.headers = { 'x-client': 'habitica-web' };
|
|
||||||
|
|
||||||
return analyticsService.trackPurchase(data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
platform: 'Web',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('logs iOS platform', () => {
|
|
||||||
data.headers = { 'x-client': 'habitica-ios' };
|
|
||||||
|
|
||||||
return analyticsService.trackPurchase(data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
platform: 'iOS',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('logs Android platform', () => {
|
|
||||||
data.headers = { 'x-client': 'habitica-android' };
|
|
||||||
|
|
||||||
return analyticsService.trackPurchase(data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
platform: 'Android',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('logs 3rd Party platform', () => {
|
|
||||||
data.headers = { 'x-client': 'some-third-party' };
|
|
||||||
|
|
||||||
return analyticsService.trackPurchase(data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
platform: '3rd Party',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('logs unknown if headers are not passed in', () => {
|
|
||||||
delete data.headers;
|
|
||||||
|
|
||||||
return analyticsService.trackPurchase(data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
platform: 'Unknown',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
context('Operating System', () => {
|
|
||||||
it('sets default', () => {
|
|
||||||
data.headers = {
|
|
||||||
'x-client': 'third-party',
|
|
||||||
'user-agent': 'foo',
|
|
||||||
};
|
|
||||||
|
|
||||||
return analyticsService.trackPurchase(data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
os_name: 'Other',
|
|
||||||
os_version: '0',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sets iOS', () => {
|
|
||||||
data.headers = {
|
|
||||||
'x-client': 'habitica-ios',
|
|
||||||
'user-agent': 'Habitica/148 (iPhone; iOS 9.3; Scale/2.00)',
|
|
||||||
};
|
|
||||||
|
|
||||||
return analyticsService.trackPurchase(data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
os_name: 'iOS',
|
|
||||||
os_version: '9.3.0',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sets Android', () => {
|
|
||||||
data.headers = {
|
|
||||||
'x-client': 'habitica-android',
|
|
||||||
'user-agent': 'Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19',
|
|
||||||
};
|
|
||||||
|
|
||||||
return analyticsService.trackPurchase(data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
os_name: 'Android',
|
|
||||||
os_version: '4.0.4',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sets Unknown if headers are not passed in', () => {
|
|
||||||
delete data.headers;
|
|
||||||
|
|
||||||
return analyticsService.trackPurchase(data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
os_name: undefined,
|
|
||||||
os_version: undefined,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sends details about purchase', () => analyticsService.trackPurchase(data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
event_properties: {
|
|
||||||
gift: false,
|
|
||||||
itemPurchased: 'Gems',
|
|
||||||
paymentMethod: 'PayPal',
|
|
||||||
purchaseType: 'checkout',
|
|
||||||
quantity: 1,
|
|
||||||
sku: 'paypal-checkout',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('sends user data if provided', () => {
|
|
||||||
const stats = {
|
|
||||||
class: 'wizard', exp: 5, gp: 23, hp: 10, lvl: 4, mp: 30,
|
|
||||||
};
|
|
||||||
const user = {
|
|
||||||
stats,
|
|
||||||
contributor: { level: 1 },
|
|
||||||
purchased: { plan: { planId: 'foo-plan' } },
|
|
||||||
flags: { tour: { intro: -2 } },
|
|
||||||
habits: [{ _id: 'habit' }],
|
|
||||||
dailys: [{ _id: 'daily' }],
|
|
||||||
todos: [{ _id: 'todo' }],
|
|
||||||
rewards: [{ _id: 'reward' }],
|
|
||||||
preferences: {
|
|
||||||
analyticsConsent: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
data.user = user;
|
|
||||||
|
|
||||||
return analyticsService.trackPurchase(data)
|
|
||||||
.then(() => {
|
|
||||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
|
||||||
user_properties: {
|
|
||||||
Class: 'wizard',
|
|
||||||
Experience: 5,
|
|
||||||
Gold: 23,
|
|
||||||
Health: 10,
|
|
||||||
Level: 4,
|
|
||||||
Mana: 30,
|
|
||||||
tutorialComplete: true,
|
|
||||||
'Number Of Tasks': {
|
|
||||||
habits: 1,
|
|
||||||
dailys: 1,
|
|
||||||
todos: 1,
|
|
||||||
rewards: 1,
|
|
||||||
},
|
|
||||||
contributorLevel: 1,
|
|
||||||
subscription: 'foo-plan',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('mockAnalyticsService', () => {
|
|
||||||
it('has stubbed track method', () => {
|
|
||||||
expect(analyticsService.mockAnalyticsService).to.respondTo('track');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has stubbed trackPurchase method', () => {
|
|
||||||
expect(analyticsService.mockAnalyticsService).to.respondTo('trackPurchase');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
+116
-136
@@ -13,7 +13,6 @@ import { cron, cronWrapper } from '../../../../website/server/libs/cron';
|
|||||||
import { model as User } from '../../../../website/server/models/user';
|
import { model as User } from '../../../../website/server/models/user';
|
||||||
import * as Tasks from '../../../../website/server/models/task';
|
import * as Tasks from '../../../../website/server/models/task';
|
||||||
import common from '../../../../website/common';
|
import common from '../../../../website/common';
|
||||||
import * as analytics from '../../../../website/server/libs/analyticsService';
|
|
||||||
import { model as Group } from '../../../../website/server/models/group';
|
import { model as Group } from '../../../../website/server/models/group';
|
||||||
|
|
||||||
const CRON_TIMEOUT_WAIT = new Date(5 * 60 * 1000).getTime();
|
const CRON_TIMEOUT_WAIT = new Date(5 * 60 * 1000).getTime();
|
||||||
@@ -41,20 +40,17 @@ describe('cron', async () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
sinon.spy(analytics, 'track');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
if (clock !== null) clock.restore();
|
if (clock !== null) clock.restore();
|
||||||
analytics.track.restore();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('updates user.preferences.timezoneOffsetAtLastCron', async () => {
|
it('updates user.preferences.timezoneOffsetAtLastCron', async () => {
|
||||||
const timezoneUtcOffsetFromUserPrefs = -1;
|
const timezoneUtcOffsetFromUserPrefs = -1;
|
||||||
|
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics, timezoneUtcOffsetFromUserPrefs,
|
user, tasksByType, daysMissed, timezoneUtcOffsetFromUserPrefs,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(user.preferences.timezoneOffsetAtLastCron).to.equal(1);
|
expect(user.preferences.timezoneOffsetAtLastCron).to.equal(1);
|
||||||
@@ -63,7 +59,7 @@ describe('cron', async () => {
|
|||||||
it('resets user.items.lastDrop.count', async () => {
|
it('resets user.items.lastDrop.count', async () => {
|
||||||
user.items.lastDrop.count = 4;
|
user.items.lastDrop.count = 4;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.items.lastDrop.count).to.equal(0);
|
expect(user.items.lastDrop.count).to.equal(0);
|
||||||
});
|
});
|
||||||
@@ -71,26 +67,11 @@ describe('cron', async () => {
|
|||||||
it('increments user cron count', async () => {
|
it('increments user cron count', async () => {
|
||||||
const cronCountBefore = user.flags.cronCount;
|
const cronCountBefore = user.flags.cronCount;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.flags.cronCount).to.be.greaterThan(cronCountBefore);
|
expect(user.flags.cronCount).to.be.greaterThan(cronCountBefore);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls analytics', async () => {
|
|
||||||
await cron({
|
|
||||||
user, tasksByType, daysMissed, analytics,
|
|
||||||
});
|
|
||||||
expect(analytics.track.callCount).to.equal(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('calls analytics when user is sleeping', async () => {
|
|
||||||
user.preferences.sleep = true;
|
|
||||||
await cron({
|
|
||||||
user, tasksByType, daysMissed, analytics,
|
|
||||||
});
|
|
||||||
expect(analytics.track.callCount).to.equal(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('end of the month perks', async () => {
|
describe('end of the month perks', async () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
user.purchased.plan.customerId = 'subscribedId';
|
user.purchased.plan.customerId = 'subscribedId';
|
||||||
@@ -101,7 +82,7 @@ describe('cron', async () => {
|
|||||||
user.purchased.plan.dateUpdated = new Date('2018-12-11');
|
user.purchased.plan.dateUpdated = new Date('2018-12-11');
|
||||||
clock = sinon.useFakeTimers(new Date('2019-01-29'));
|
clock = sinon.useFakeTimers(new Date('2019-01-29'));
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.purchased.plan.mysteryItems.length).to.eql(2);
|
expect(user.purchased.plan.mysteryItems.length).to.eql(2);
|
||||||
const filteredNotifications = user.notifications.filter(n => n.type === 'NEW_MYSTERY_ITEMS');
|
const filteredNotifications = user.notifications.filter(n => n.type === 'NEW_MYSTERY_ITEMS');
|
||||||
@@ -112,7 +93,7 @@ describe('cron', async () => {
|
|||||||
user.purchased.plan.dateUpdated = new Date('2018-11-11');
|
user.purchased.plan.dateUpdated = new Date('2018-11-11');
|
||||||
clock = sinon.useFakeTimers(new Date('2019-01-29'));
|
clock = sinon.useFakeTimers(new Date('2019-01-29'));
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.purchased.plan.mysteryItems.length).to.eql(4);
|
expect(user.purchased.plan.mysteryItems.length).to.eql(4);
|
||||||
const filteredNotifications = user.notifications.filter(n => n.type === 'NEW_MYSTERY_ITEMS');
|
const filteredNotifications = user.notifications.filter(n => n.type === 'NEW_MYSTERY_ITEMS');
|
||||||
@@ -122,7 +103,7 @@ describe('cron', async () => {
|
|||||||
it('resets plan.gemsBought on a new month', async () => {
|
it('resets plan.gemsBought on a new month', async () => {
|
||||||
user.purchased.plan.gemsBought = 10;
|
user.purchased.plan.gemsBought = 10;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.purchased.plan.gemsBought).to.equal(0);
|
expect(user.purchased.plan.gemsBought).to.equal(0);
|
||||||
});
|
});
|
||||||
@@ -131,7 +112,7 @@ describe('cron', async () => {
|
|||||||
user.purchased.plan.gemsBought = 10;
|
user.purchased.plan.gemsBought = 10;
|
||||||
user.purchased.plan.dateUpdated = undefined;
|
user.purchased.plan.dateUpdated = undefined;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.purchased.plan.gemsBought).to.equal(0);
|
expect(user.purchased.plan.gemsBought).to.equal(0);
|
||||||
});
|
});
|
||||||
@@ -142,7 +123,7 @@ describe('cron', async () => {
|
|||||||
|
|
||||||
user.purchased.plan.gemsBought = 10;
|
user.purchased.plan.gemsBought = 10;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.purchased.plan.gemsBought).to.equal(10);
|
expect(user.purchased.plan.gemsBought).to.equal(10);
|
||||||
});
|
});
|
||||||
@@ -150,7 +131,7 @@ describe('cron', async () => {
|
|||||||
it('resets plan.dateUpdated on a new month', async () => {
|
it('resets plan.dateUpdated on a new month', async () => {
|
||||||
const currentMonth = moment().startOf('month');
|
const currentMonth = moment().startOf('month');
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(moment(user.purchased.plan.dateUpdated).startOf('month').isSame(currentMonth)).to.eql(true);
|
expect(moment(user.purchased.plan.dateUpdated).startOf('month').isSame(currentMonth)).to.eql(true);
|
||||||
});
|
});
|
||||||
@@ -158,7 +139,7 @@ describe('cron', async () => {
|
|||||||
it('increments plan.consecutive.count', async () => {
|
it('increments plan.consecutive.count', async () => {
|
||||||
user.purchased.plan.consecutive.count = 0;
|
user.purchased.plan.consecutive.count = 0;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.purchased.plan.consecutive.count).to.equal(1);
|
expect(user.purchased.plan.consecutive.count).to.equal(1);
|
||||||
});
|
});
|
||||||
@@ -166,7 +147,7 @@ describe('cron', async () => {
|
|||||||
it('increments plan.cumulativeCount', async () => {
|
it('increments plan.cumulativeCount', async () => {
|
||||||
user.purchased.plan.cumulativeCount = 0;
|
user.purchased.plan.cumulativeCount = 0;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.purchased.plan.cumulativeCount).to.equal(1);
|
expect(user.purchased.plan.cumulativeCount).to.equal(1);
|
||||||
});
|
});
|
||||||
@@ -175,7 +156,7 @@ describe('cron', async () => {
|
|||||||
user.purchased.plan.dateUpdated = moment().subtract(2, 'months').toDate();
|
user.purchased.plan.dateUpdated = moment().subtract(2, 'months').toDate();
|
||||||
user.purchased.plan.consecutive.count = 0;
|
user.purchased.plan.consecutive.count = 0;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.purchased.plan.consecutive.count).to.equal(2);
|
expect(user.purchased.plan.consecutive.count).to.equal(2);
|
||||||
});
|
});
|
||||||
@@ -184,7 +165,7 @@ describe('cron', async () => {
|
|||||||
user.purchased.plan.dateUpdated = moment().subtract(3, 'months').toDate();
|
user.purchased.plan.dateUpdated = moment().subtract(3, 'months').toDate();
|
||||||
user.purchased.plan.cumulativeCount = 0;
|
user.purchased.plan.cumulativeCount = 0;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.purchased.plan.cumulativeCount).to.equal(3);
|
expect(user.purchased.plan.cumulativeCount).to.equal(3);
|
||||||
});
|
});
|
||||||
@@ -196,7 +177,7 @@ describe('cron', async () => {
|
|||||||
user.purchased.plan.consecutive.trinkets = 1;
|
user.purchased.plan.consecutive.trinkets = 1;
|
||||||
|
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(user.purchased.plan.consecutive.trinkets).to.equal(1);
|
expect(user.purchased.plan.consecutive.trinkets).to.equal(1);
|
||||||
@@ -206,7 +187,7 @@ describe('cron', async () => {
|
|||||||
user.purchased.plan.consecutive.gemCapExtra = 26;
|
user.purchased.plan.consecutive.gemCapExtra = 26;
|
||||||
user.purchased.plan.consecutive.count = 5;
|
user.purchased.plan.consecutive.count = 5;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.purchased.plan.consecutive.gemCapExtra).to.equal(26);
|
expect(user.purchased.plan.consecutive.gemCapExtra).to.equal(26);
|
||||||
});
|
});
|
||||||
@@ -214,7 +195,7 @@ describe('cron', async () => {
|
|||||||
it('does not reset plan stats if we are before the last day of the cancelled month', async () => {
|
it('does not reset plan stats if we are before the last day of the cancelled month', async () => {
|
||||||
user.purchased.plan.dateTerminated = moment(new Date()).add({ days: 1 });
|
user.purchased.plan.dateTerminated = moment(new Date()).add({ days: 1 });
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.purchased.plan.customerId).to.exist;
|
expect(user.purchased.plan.customerId).to.exist;
|
||||||
});
|
});
|
||||||
@@ -225,7 +206,7 @@ describe('cron', async () => {
|
|||||||
user.purchased.plan.consecutive.count = 5;
|
user.purchased.plan.consecutive.count = 5;
|
||||||
|
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(user.purchased.plan.customerId).to.not.exist;
|
expect(user.purchased.plan.customerId).to.not.exist;
|
||||||
@@ -264,7 +245,7 @@ describe('cron', async () => {
|
|||||||
// Add 2 days so that we're sure we're not affected by any start-of-month effects
|
// Add 2 days so that we're sure we're not affected by any start-of-month effects
|
||||||
// e.g., from time zone oddness.
|
// e.g., from time zone oddness.
|
||||||
await cron({
|
await cron({
|
||||||
user: user1, tasksByType, daysMissed, analytics,
|
user: user1, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user1.purchased.plan.consecutive.count).to.equal(1);
|
expect(user1.purchased.plan.consecutive.count).to.equal(1);
|
||||||
expect(user1.purchased.plan.consecutive.trinkets).to.equal(2);
|
expect(user1.purchased.plan.consecutive.trinkets).to.equal(2);
|
||||||
@@ -276,7 +257,7 @@ describe('cron', async () => {
|
|||||||
.add(2, 'days')
|
.add(2, 'days')
|
||||||
.toDate());
|
.toDate());
|
||||||
await cron({
|
await cron({
|
||||||
user: user1, tasksByType, daysMissed, analytics,
|
user: user1, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user1.purchased.plan.consecutive.count).to.equal(10);
|
expect(user1.purchased.plan.consecutive.count).to.equal(10);
|
||||||
expect(user1.purchased.plan.consecutive.trinkets).to.equal(11);
|
expect(user1.purchased.plan.consecutive.trinkets).to.equal(11);
|
||||||
@@ -311,7 +292,7 @@ describe('cron', async () => {
|
|||||||
.add(2, 'days')
|
.add(2, 'days')
|
||||||
.toDate());
|
.toDate());
|
||||||
await cron({
|
await cron({
|
||||||
user: user3, tasksByType, daysMissed, analytics,
|
user: user3, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user3.purchased.plan.consecutive.count).to.equal(1);
|
expect(user3.purchased.plan.consecutive.count).to.equal(1);
|
||||||
expect(user3.purchased.plan.consecutive.trinkets).to.equal(2);
|
expect(user3.purchased.plan.consecutive.trinkets).to.equal(2);
|
||||||
@@ -323,7 +304,7 @@ describe('cron', async () => {
|
|||||||
.add(2, 'days')
|
.add(2, 'days')
|
||||||
.toDate());
|
.toDate());
|
||||||
await cron({
|
await cron({
|
||||||
user: user3, tasksByType, daysMissed, analytics,
|
user: user3, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user3.purchased.plan.consecutive.count).to.equal(10);
|
expect(user3.purchased.plan.consecutive.count).to.equal(10);
|
||||||
expect(user3.purchased.plan.consecutive.trinkets).to.equal(11);
|
expect(user3.purchased.plan.consecutive.trinkets).to.equal(11);
|
||||||
@@ -358,7 +339,7 @@ describe('cron', async () => {
|
|||||||
.add(2, 'days')
|
.add(2, 'days')
|
||||||
.toDate());
|
.toDate());
|
||||||
await cron({
|
await cron({
|
||||||
user: user6, tasksByType, daysMissed, analytics,
|
user: user6, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user6.purchased.plan.consecutive.count).to.equal(1);
|
expect(user6.purchased.plan.consecutive.count).to.equal(1);
|
||||||
expect(user6.purchased.plan.consecutive.trinkets).to.equal(2);
|
expect(user6.purchased.plan.consecutive.trinkets).to.equal(2);
|
||||||
@@ -391,7 +372,7 @@ describe('cron', async () => {
|
|||||||
.add(2, 'days')
|
.add(2, 'days')
|
||||||
.toDate());
|
.toDate());
|
||||||
await cron({
|
await cron({
|
||||||
user: user12, tasksByType, daysMissed, analytics,
|
user: user12, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user12.purchased.plan.consecutive.count).to.equal(1);
|
expect(user12.purchased.plan.consecutive.count).to.equal(1);
|
||||||
expect(user12.purchased.plan.consecutive.trinkets).to.equal(2);
|
expect(user12.purchased.plan.consecutive.trinkets).to.equal(2);
|
||||||
@@ -403,7 +384,7 @@ describe('cron', async () => {
|
|||||||
.add(2, 'days')
|
.add(2, 'days')
|
||||||
.toDate());
|
.toDate());
|
||||||
await cron({
|
await cron({
|
||||||
user: user12, tasksByType, daysMissed, analytics,
|
user: user12, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user12.purchased.plan.consecutive.count).to.equal(10);
|
expect(user12.purchased.plan.consecutive.count).to.equal(10);
|
||||||
expect(user12.purchased.plan.consecutive.trinkets).to.equal(11);
|
expect(user12.purchased.plan.consecutive.trinkets).to.equal(11);
|
||||||
@@ -439,7 +420,7 @@ describe('cron', async () => {
|
|||||||
.add(2, 'days')
|
.add(2, 'days')
|
||||||
.toDate());
|
.toDate());
|
||||||
await cron({
|
await cron({
|
||||||
user: user3g, tasksByType, daysMissed, analytics,
|
user: user3g, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user3g.purchased.plan.consecutive.count).to.equal(1);
|
expect(user3g.purchased.plan.consecutive.count).to.equal(1);
|
||||||
expect(user3g.purchased.plan.cumulativeCount).to.equal(1);
|
expect(user3g.purchased.plan.cumulativeCount).to.equal(1);
|
||||||
@@ -452,7 +433,7 @@ describe('cron', async () => {
|
|||||||
.add(2, 'days')
|
.add(2, 'days')
|
||||||
.toDate());
|
.toDate());
|
||||||
await cron({
|
await cron({
|
||||||
user: user3g, tasksByType, daysMissed, analytics,
|
user: user3g, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
// subscription has been erased by now
|
// subscription has been erased by now
|
||||||
expect(user3g.purchased.plan.consecutive.count).to.equal(0);
|
expect(user3g.purchased.plan.consecutive.count).to.equal(0);
|
||||||
@@ -471,7 +452,7 @@ describe('cron', async () => {
|
|||||||
it('resets plan.gemsBought on a new month', async () => {
|
it('resets plan.gemsBought on a new month', async () => {
|
||||||
user.purchased.plan.gemsBought = 10;
|
user.purchased.plan.gemsBought = 10;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.purchased.plan.gemsBought).to.equal(0);
|
expect(user.purchased.plan.gemsBought).to.equal(0);
|
||||||
});
|
});
|
||||||
@@ -482,14 +463,14 @@ describe('cron', async () => {
|
|||||||
|
|
||||||
user.purchased.plan.gemsBought = 10;
|
user.purchased.plan.gemsBought = 10;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.purchased.plan.gemsBought).to.equal(10);
|
expect(user.purchased.plan.gemsBought).to.equal(10);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not reset plan.dateUpdated on a new month', async () => {
|
it('does not reset plan.dateUpdated on a new month', async () => {
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.purchased.plan.dateUpdated).to.be.empty;
|
expect(user.purchased.plan.dateUpdated).to.be.empty;
|
||||||
});
|
});
|
||||||
@@ -497,7 +478,7 @@ describe('cron', async () => {
|
|||||||
it('does not increment plan.consecutive.count', async () => {
|
it('does not increment plan.consecutive.count', async () => {
|
||||||
user.purchased.plan.consecutive.count = 0;
|
user.purchased.plan.consecutive.count = 0;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.purchased.plan.consecutive.count).to.equal(0);
|
expect(user.purchased.plan.consecutive.count).to.equal(0);
|
||||||
});
|
});
|
||||||
@@ -505,7 +486,7 @@ describe('cron', async () => {
|
|||||||
it('does not increment plan.cumulativeCount', async () => {
|
it('does not increment plan.cumulativeCount', async () => {
|
||||||
user.purchased.plan.cumulativeCount = 0;
|
user.purchased.plan.cumulativeCount = 0;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.purchased.plan.cumulativeCount).to.equal(0);
|
expect(user.purchased.plan.cumulativeCount).to.equal(0);
|
||||||
});
|
});
|
||||||
@@ -513,7 +494,7 @@ describe('cron', async () => {
|
|||||||
it('does not increment plan.consecutive.trinkets when user has reached a month that is a multiple of 3', async () => {
|
it('does not increment plan.consecutive.trinkets when user has reached a month that is a multiple of 3', async () => {
|
||||||
user.purchased.plan.consecutive.count = 5;
|
user.purchased.plan.consecutive.count = 5;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.purchased.plan.consecutive.trinkets).to.equal(0);
|
expect(user.purchased.plan.consecutive.trinkets).to.equal(0);
|
||||||
});
|
});
|
||||||
@@ -521,7 +502,7 @@ describe('cron', async () => {
|
|||||||
it('does not increment plan.consecutive.gemCapExtra when user has reached a month that is a multiple of 3', async () => {
|
it('does not increment plan.consecutive.gemCapExtra when user has reached a month that is a multiple of 3', async () => {
|
||||||
user.purchased.plan.consecutive.count = 5;
|
user.purchased.plan.consecutive.count = 5;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.purchased.plan.consecutive.gemCapExtra).to.equal(0);
|
expect(user.purchased.plan.consecutive.gemCapExtra).to.equal(0);
|
||||||
});
|
});
|
||||||
@@ -530,7 +511,7 @@ describe('cron', async () => {
|
|||||||
user.purchased.plan.consecutive.gemCapExtra = 26;
|
user.purchased.plan.consecutive.gemCapExtra = 26;
|
||||||
user.purchased.plan.consecutive.count = 5;
|
user.purchased.plan.consecutive.count = 5;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.purchased.plan.consecutive.gemCapExtra).to.equal(26);
|
expect(user.purchased.plan.consecutive.gemCapExtra).to.equal(26);
|
||||||
});
|
});
|
||||||
@@ -538,7 +519,7 @@ describe('cron', async () => {
|
|||||||
it('does nothing to plan stats if we are before the last day of the cancelled month', async () => {
|
it('does nothing to plan stats if we are before the last day of the cancelled month', async () => {
|
||||||
user.purchased.plan.dateTerminated = moment(new Date()).add({ days: 1 });
|
user.purchased.plan.dateTerminated = moment(new Date()).add({ days: 1 });
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.purchased.plan.customerId).to.not.exist;
|
expect(user.purchased.plan.customerId).to.not.exist;
|
||||||
});
|
});
|
||||||
@@ -564,7 +545,7 @@ describe('cron', async () => {
|
|||||||
it('should make uncompleted todos redder', async () => {
|
it('should make uncompleted todos redder', async () => {
|
||||||
const valueBefore = tasksByType.todos[0].value;
|
const valueBefore = tasksByType.todos[0].value;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(tasksByType.todos[0].value).to.be.lessThan(valueBefore);
|
expect(tasksByType.todos[0].value).to.be.lessThan(valueBefore);
|
||||||
});
|
});
|
||||||
@@ -573,7 +554,7 @@ describe('cron', async () => {
|
|||||||
tasksByType.todos[0].completed = true;
|
tasksByType.todos[0].completed = true;
|
||||||
const valueBefore = tasksByType.todos[0].value;
|
const valueBefore = tasksByType.todos[0].value;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(tasksByType.todos[0].value).to.equal(valueBefore);
|
expect(tasksByType.todos[0].value).to.equal(valueBefore);
|
||||||
});
|
});
|
||||||
@@ -582,7 +563,7 @@ describe('cron', async () => {
|
|||||||
tasksByType.todos[0].completed = true;
|
tasksByType.todos[0].completed = true;
|
||||||
|
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(user.history.todos).to.be.lengthOf(1);
|
expect(user.history.todos).to.be.lengthOf(1);
|
||||||
@@ -608,7 +589,7 @@ describe('cron', async () => {
|
|||||||
expect(user.tasksOrder.todos).to.be.lengthOf(3);
|
expect(user.tasksOrder.todos).to.be.lengthOf(3);
|
||||||
|
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
// user.tasksOrder.todos should be filtered while tasks by type remains unchanged
|
// user.tasksOrder.todos should be filtered while tasks by type remains unchanged
|
||||||
@@ -635,7 +616,7 @@ describe('cron', async () => {
|
|||||||
const original = user.tasksOrder.todos; // Preserve the original order
|
const original = user.tasksOrder.todos; // Preserve the original order
|
||||||
|
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
let listsAreEqual = true;
|
let listsAreEqual = true;
|
||||||
@@ -675,7 +656,7 @@ describe('cron', async () => {
|
|||||||
tasksByType.dailys[0].everyX = 5;
|
tasksByType.dailys[0].everyX = 5;
|
||||||
tasksByType.dailys[0].startDate = moment().add(1, 'days').toDate();
|
tasksByType.dailys[0].startDate = moment().add(1, 'days').toDate();
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(tasksByType.dailys[0].isDue).to.be.false;
|
expect(tasksByType.dailys[0].isDue).to.be.false;
|
||||||
});
|
});
|
||||||
@@ -686,7 +667,7 @@ describe('cron', async () => {
|
|||||||
tasksByType.dailys[0].everyX = 5;
|
tasksByType.dailys[0].everyX = 5;
|
||||||
tasksByType.dailys[0].startDate = moment().toDate();
|
tasksByType.dailys[0].startDate = moment().toDate();
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(tasksByType.dailys[0].isDue).to.exist;
|
expect(tasksByType.dailys[0].isDue).to.exist;
|
||||||
});
|
});
|
||||||
@@ -696,14 +677,14 @@ describe('cron', async () => {
|
|||||||
tasksByType.dailys[0].everyX = 5;
|
tasksByType.dailys[0].everyX = 5;
|
||||||
tasksByType.dailys[0].startDate = moment().add(1, 'days').toDate();
|
tasksByType.dailys[0].startDate = moment().add(1, 'days').toDate();
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(tasksByType.dailys[0].nextDue.length).to.eql(6);
|
expect(tasksByType.dailys[0].nextDue.length).to.eql(6);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add history', async () => {
|
it('should add history', async () => {
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(tasksByType.dailys[0].history).to.be.lengthOf(1);
|
expect(tasksByType.dailys[0].history).to.be.lengthOf(1);
|
||||||
});
|
});
|
||||||
@@ -711,7 +692,7 @@ describe('cron', async () => {
|
|||||||
it('should set tasks completed to false', async () => {
|
it('should set tasks completed to false', async () => {
|
||||||
tasksByType.dailys[0].completed = true;
|
tasksByType.dailys[0].completed = true;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(tasksByType.dailys[0].completed).to.be.false;
|
expect(tasksByType.dailys[0].completed).to.be.false;
|
||||||
});
|
});
|
||||||
@@ -720,7 +701,7 @@ describe('cron', async () => {
|
|||||||
user.preferences.sleep = true;
|
user.preferences.sleep = true;
|
||||||
tasksByType.dailys[0].completed = true;
|
tasksByType.dailys[0].completed = true;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(tasksByType.dailys[0].completed).to.be.false;
|
expect(tasksByType.dailys[0].completed).to.be.false;
|
||||||
});
|
});
|
||||||
@@ -729,7 +710,7 @@ describe('cron', async () => {
|
|||||||
tasksByType.dailys[0].checklist.push({ title: 'test', completed: false });
|
tasksByType.dailys[0].checklist.push({ title: 'test', completed: false });
|
||||||
tasksByType.dailys[0].completed = true;
|
tasksByType.dailys[0].completed = true;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(tasksByType.dailys[0].checklist[0].completed).to.be.false;
|
expect(tasksByType.dailys[0].checklist[0].completed).to.be.false;
|
||||||
});
|
});
|
||||||
@@ -739,7 +720,7 @@ describe('cron', async () => {
|
|||||||
tasksByType.dailys[0].checklist.push({ title: 'test', completed: false });
|
tasksByType.dailys[0].checklist.push({ title: 'test', completed: false });
|
||||||
tasksByType.dailys[0].completed = true;
|
tasksByType.dailys[0].completed = true;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(tasksByType.dailys[0].checklist[0].completed).to.be.false;
|
expect(tasksByType.dailys[0].checklist[0].completed).to.be.false;
|
||||||
});
|
});
|
||||||
@@ -749,7 +730,7 @@ describe('cron', async () => {
|
|||||||
tasksByType.dailys[0].checklist.push({ title: 'test', completed: false });
|
tasksByType.dailys[0].checklist.push({ title: 'test', completed: false });
|
||||||
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
|
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(tasksByType.dailys[0].checklist[0].completed).to.be.false;
|
expect(tasksByType.dailys[0].checklist[0].completed).to.be.false;
|
||||||
});
|
});
|
||||||
@@ -759,7 +740,7 @@ describe('cron', async () => {
|
|||||||
const hpBefore = user.stats.hp;
|
const hpBefore = user.stats.hp;
|
||||||
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
|
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.stats.hp).to.be.lessThan(hpBefore);
|
expect(user.stats.hp).to.be.lessThan(hpBefore);
|
||||||
});
|
});
|
||||||
@@ -770,7 +751,7 @@ describe('cron', async () => {
|
|||||||
const hpBefore = user.stats.hp;
|
const hpBefore = user.stats.hp;
|
||||||
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
|
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.stats.hp).to.equal(hpBefore);
|
expect(user.stats.hp).to.equal(hpBefore);
|
||||||
});
|
});
|
||||||
@@ -784,7 +765,7 @@ describe('cron', async () => {
|
|||||||
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
|
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
|
||||||
|
|
||||||
cronOverride({
|
cronOverride({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(user.stats.hp).to.equal(hpBefore);
|
expect(user.stats.hp).to.equal(hpBefore);
|
||||||
@@ -797,7 +778,7 @@ describe('cron', async () => {
|
|||||||
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
|
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
|
||||||
|
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(user.stats.hp).to.equal(hpBefore);
|
expect(user.stats.hp).to.equal(hpBefore);
|
||||||
@@ -808,7 +789,7 @@ describe('cron', async () => {
|
|||||||
let hpBefore = user.stats.hp;
|
let hpBefore = user.stats.hp;
|
||||||
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
|
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
const hpDifferenceOfFullyIncompleteDaily = hpBefore - user.stats.hp;
|
const hpDifferenceOfFullyIncompleteDaily = hpBefore - user.stats.hp;
|
||||||
|
|
||||||
@@ -816,7 +797,7 @@ describe('cron', async () => {
|
|||||||
tasksByType.dailys[0].checklist.push({ title: 'test', completed: true });
|
tasksByType.dailys[0].checklist.push({ title: 'test', completed: true });
|
||||||
tasksByType.dailys[0].checklist.push({ title: 'test2', completed: false });
|
tasksByType.dailys[0].checklist.push({ title: 'test2', completed: false });
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
const hpDifferenceOfPartiallyIncompleteDaily = hpBefore - user.stats.hp;
|
const hpDifferenceOfPartiallyIncompleteDaily = hpBefore - user.stats.hp;
|
||||||
|
|
||||||
@@ -829,7 +810,7 @@ describe('cron', async () => {
|
|||||||
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
|
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
|
||||||
|
|
||||||
const progress = await cron({
|
const progress = await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(progress.down).to.equal(-1);
|
expect(progress.down).to.equal(-1);
|
||||||
@@ -841,7 +822,7 @@ describe('cron', async () => {
|
|||||||
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
|
tasksByType.dailys[0].startDate = moment(new Date()).subtract({ days: 1 });
|
||||||
|
|
||||||
const progress = await cron({
|
const progress = await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(progress.down).to.equal(0);
|
expect(progress.down).to.equal(0);
|
||||||
@@ -862,7 +843,7 @@ describe('cron', async () => {
|
|||||||
tasksByType.dailys[1].frequency = 'daily';
|
tasksByType.dailys[1].frequency = 'daily';
|
||||||
|
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(user.stats.hp).to.equal(48);
|
expect(user.stats.hp).to.equal(48);
|
||||||
@@ -886,7 +867,7 @@ describe('cron', async () => {
|
|||||||
tasksByType.habits[0].down = false;
|
tasksByType.habits[0].down = false;
|
||||||
|
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tasksByType.habits[0].value).to.be.lessThan(1);
|
expect(tasksByType.habits[0].value).to.be.lessThan(1);
|
||||||
@@ -897,7 +878,7 @@ describe('cron', async () => {
|
|||||||
tasksByType.habits[0].up = false;
|
tasksByType.habits[0].up = false;
|
||||||
|
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tasksByType.habits[0].value).to.be.lessThan(1);
|
expect(tasksByType.habits[0].value).to.be.lessThan(1);
|
||||||
@@ -909,7 +890,7 @@ describe('cron', async () => {
|
|||||||
tasksByType.habits[0].down = true;
|
tasksByType.habits[0].down = true;
|
||||||
|
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tasksByType.habits[0].value).to.equal(1);
|
expect(tasksByType.habits[0].value).to.equal(1);
|
||||||
@@ -928,7 +909,7 @@ describe('cron', async () => {
|
|||||||
tasksByType.habits[0].counterDown = 1;
|
tasksByType.habits[0].counterDown = 1;
|
||||||
|
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
||||||
@@ -941,7 +922,7 @@ describe('cron', async () => {
|
|||||||
tasksByType.habits[0].counterDown = 1;
|
tasksByType.habits[0].counterDown = 1;
|
||||||
|
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
||||||
@@ -955,7 +936,7 @@ describe('cron', async () => {
|
|||||||
|
|
||||||
// should not reset
|
// should not reset
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tasksByType.habits[0].counterUp).to.equal(1);
|
expect(tasksByType.habits[0].counterUp).to.equal(1);
|
||||||
@@ -964,7 +945,7 @@ describe('cron', async () => {
|
|||||||
// should reset
|
// should reset
|
||||||
daysMissed = 8;
|
daysMissed = 8;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
||||||
@@ -988,7 +969,7 @@ describe('cron', async () => {
|
|||||||
|
|
||||||
// should not reset
|
// should not reset
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tasksByType.habits[0].counterUp).to.equal(1);
|
expect(tasksByType.habits[0].counterUp).to.equal(1);
|
||||||
@@ -1002,7 +983,7 @@ describe('cron', async () => {
|
|||||||
|
|
||||||
// should reset after user CDS
|
// should reset after user CDS
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
||||||
@@ -1026,7 +1007,7 @@ describe('cron', async () => {
|
|||||||
|
|
||||||
// should not reset
|
// should not reset
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tasksByType.habits[0].counterUp).to.equal(1);
|
expect(tasksByType.habits[0].counterUp).to.equal(1);
|
||||||
@@ -1036,7 +1017,7 @@ describe('cron', async () => {
|
|||||||
// should reset
|
// should reset
|
||||||
daysMissed = 2;
|
daysMissed = 2;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
||||||
@@ -1060,7 +1041,7 @@ describe('cron', async () => {
|
|||||||
|
|
||||||
// should reset
|
// should reset
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
||||||
@@ -1084,7 +1065,7 @@ describe('cron', async () => {
|
|||||||
|
|
||||||
// should not reset
|
// should not reset
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tasksByType.habits[0].counterUp).to.equal(1);
|
expect(tasksByType.habits[0].counterUp).to.equal(1);
|
||||||
@@ -1098,7 +1079,7 @@ describe('cron', async () => {
|
|||||||
|
|
||||||
// should not reset
|
// should not reset
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tasksByType.habits[0].counterUp).to.equal(1);
|
expect(tasksByType.habits[0].counterUp).to.equal(1);
|
||||||
@@ -1107,7 +1088,7 @@ describe('cron', async () => {
|
|||||||
// should reset
|
// should reset
|
||||||
daysMissed = 32;
|
daysMissed = 32;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
||||||
@@ -1132,7 +1113,7 @@ describe('cron', async () => {
|
|||||||
|
|
||||||
// should reset
|
// should reset
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
||||||
@@ -1156,7 +1137,7 @@ describe('cron', async () => {
|
|||||||
|
|
||||||
// should not reset
|
// should not reset
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tasksByType.habits[0].counterUp).to.equal(1);
|
expect(tasksByType.habits[0].counterUp).to.equal(1);
|
||||||
@@ -1166,7 +1147,7 @@ describe('cron', async () => {
|
|||||||
// should reset
|
// should reset
|
||||||
daysMissed = 2;
|
daysMissed = 2;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
expect(tasksByType.habits[0].counterUp).to.equal(0);
|
||||||
@@ -1199,7 +1180,7 @@ describe('cron', async () => {
|
|||||||
user.stats.lvl = 2;
|
user.stats.lvl = 2;
|
||||||
|
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(user.history.exp).to.have.lengthOf(1);
|
expect(user.history.exp).to.have.lengthOf(1);
|
||||||
@@ -1212,7 +1193,7 @@ describe('cron', async () => {
|
|||||||
tasksByType.dailys[0].isDue = true;
|
tasksByType.dailys[0].isDue = true;
|
||||||
|
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(user.achievements.perfect).to.equal(1);
|
expect(user.achievements.perfect).to.equal(1);
|
||||||
@@ -1224,7 +1205,7 @@ describe('cron', async () => {
|
|||||||
tasksByType.dailys[0].isDue = false;
|
tasksByType.dailys[0].isDue = false;
|
||||||
|
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(user.achievements.perfect).to.equal(0);
|
expect(user.achievements.perfect).to.equal(0);
|
||||||
@@ -1238,7 +1219,7 @@ describe('cron', async () => {
|
|||||||
const previousBuffs = user.stats.buffs.toObject();
|
const previousBuffs = user.stats.buffs.toObject();
|
||||||
|
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(user.stats.buffs.str).to.be.greaterThan(previousBuffs.str);
|
expect(user.stats.buffs.str).to.be.greaterThan(previousBuffs.str);
|
||||||
@@ -1256,7 +1237,7 @@ describe('cron', async () => {
|
|||||||
const previousBuffs = user.stats.buffs.toObject();
|
const previousBuffs = user.stats.buffs.toObject();
|
||||||
|
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(user.stats.buffs.str).to.be.greaterThan(previousBuffs.str);
|
expect(user.stats.buffs.str).to.be.greaterThan(previousBuffs.str);
|
||||||
@@ -1280,7 +1261,7 @@ describe('cron', async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(user.stats.buffs.str).to.equal(0);
|
expect(user.stats.buffs.str).to.equal(0);
|
||||||
@@ -1307,7 +1288,7 @@ describe('cron', async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(user.stats.buffs.str).to.equal(0);
|
expect(user.stats.buffs.str).to.equal(0);
|
||||||
@@ -1333,7 +1314,7 @@ describe('cron', async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(user.stats.buffs.str).to.equal(0);
|
expect(user.stats.buffs.str).to.equal(0);
|
||||||
@@ -1360,7 +1341,7 @@ describe('cron', async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(user.stats.buffs.str).to.equal(0);
|
expect(user.stats.buffs.str).to.equal(0);
|
||||||
@@ -1381,7 +1362,7 @@ describe('cron', async () => {
|
|||||||
const previousBuffs = user.stats.buffs.toObject();
|
const previousBuffs = user.stats.buffs.toObject();
|
||||||
|
|
||||||
cronOverride({
|
cronOverride({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(user.stats.buffs.str).to.be.greaterThan(previousBuffs.str);
|
expect(user.stats.buffs.str).to.be.greaterThan(previousBuffs.str);
|
||||||
@@ -1401,7 +1382,7 @@ describe('cron', async () => {
|
|||||||
const previousBuffs = user.stats.buffs.toObject();
|
const previousBuffs = user.stats.buffs.toObject();
|
||||||
|
|
||||||
cronOverride({
|
cronOverride({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(user.stats.buffs.str).to.be.greaterThan(previousBuffs.str);
|
expect(user.stats.buffs.str).to.be.greaterThan(previousBuffs.str);
|
||||||
@@ -1420,7 +1401,7 @@ describe('cron', async () => {
|
|||||||
tasksByType.dailys[0].completed = true;
|
tasksByType.dailys[0].completed = true;
|
||||||
stubbedStatsComputed.returns(Object.assign(statsComputedRes, { maxMP: 100 }));
|
stubbedStatsComputed.returns(Object.assign(statsComputedRes, { maxMP: 100 }));
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.stats.mp).to.be.greaterThan(mpBefore);
|
expect(user.stats.mp).to.be.greaterThan(mpBefore);
|
||||||
|
|
||||||
@@ -1436,7 +1417,7 @@ describe('cron', async () => {
|
|||||||
tasksByType.dailys[0].completed = true;
|
tasksByType.dailys[0].completed = true;
|
||||||
stubbedStatsComputed.returns(Object.assign(statsComputedRes, { maxMP: 100 }));
|
stubbedStatsComputed.returns(Object.assign(statsComputedRes, { maxMP: 100 }));
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.stats.mp).to.equal(mpBefore);
|
expect(user.stats.mp).to.equal(mpBefore);
|
||||||
|
|
||||||
@@ -1449,7 +1430,7 @@ describe('cron', async () => {
|
|||||||
user.stats.mp = 120;
|
user.stats.mp = 120;
|
||||||
stubbedStatsComputed.returns(Object.assign(statsComputedRes, { maxMP: 100 }));
|
stubbedStatsComputed.returns(Object.assign(statsComputedRes, { maxMP: 100 }));
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.stats.mp).to.equal(common.statsComputed(user).maxMP);
|
expect(user.stats.mp).to.equal(common.statsComputed(user).maxMP);
|
||||||
|
|
||||||
@@ -1482,7 +1463,7 @@ describe('cron', async () => {
|
|||||||
|
|
||||||
it('resets user progress', async () => {
|
it('resets user progress', async () => {
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.party.quest.progress.up).to.equal(0);
|
expect(user.party.quest.progress.up).to.equal(0);
|
||||||
expect(user.party.quest.progress.down).to.equal(0);
|
expect(user.party.quest.progress.down).to.equal(0);
|
||||||
@@ -1491,7 +1472,7 @@ describe('cron', async () => {
|
|||||||
|
|
||||||
it('applies the user progress', async () => {
|
it('applies the user progress', async () => {
|
||||||
const progress = await cron({
|
const progress = await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(progress.down).to.equal(-1);
|
expect(progress.down).to.equal(-1);
|
||||||
});
|
});
|
||||||
@@ -1529,19 +1510,19 @@ describe('cron', async () => {
|
|||||||
describe('login incentives', async () => {
|
describe('login incentives', async () => {
|
||||||
it('increments incentive counter each cron', async () => {
|
it('increments incentive counter each cron', async () => {
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.loginIncentives).to.eql(1);
|
expect(user.loginIncentives).to.eql(1);
|
||||||
user.lastCron = moment(new Date()).subtract({ days: 1 });
|
user.lastCron = moment(new Date()).subtract({ days: 1 });
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.loginIncentives).to.eql(2);
|
expect(user.loginIncentives).to.eql(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('pushes a notification of the day\'s incentive each cron', async () => {
|
it('pushes a notification of the day\'s incentive each cron', async () => {
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.notifications.length).to.eql(1);
|
expect(user.notifications.length).to.eql(1);
|
||||||
expect(user.notifications[0].type).to.eql('LOGIN_INCENTIVE');
|
expect(user.notifications[0].type).to.eql('LOGIN_INCENTIVE');
|
||||||
@@ -1549,13 +1530,13 @@ describe('cron', async () => {
|
|||||||
|
|
||||||
it('replaces previous notifications', async () => {
|
it('replaces previous notifications', async () => {
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
|
|
||||||
const filteredNotifications = user.notifications.filter(n => n.type === 'LOGIN_INCENTIVE');
|
const filteredNotifications = user.notifications.filter(n => n.type === 'LOGIN_INCENTIVE');
|
||||||
@@ -1566,7 +1547,7 @@ describe('cron', async () => {
|
|||||||
it('increments loginIncentives by 1 even if days are skipped in between', async () => {
|
it('increments loginIncentives by 1 even if days are skipped in between', async () => {
|
||||||
daysMissed = 3;
|
daysMissed = 3;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.loginIncentives).to.eql(1);
|
expect(user.loginIncentives).to.eql(1);
|
||||||
});
|
});
|
||||||
@@ -1574,14 +1555,14 @@ describe('cron', async () => {
|
|||||||
it('increments loginIncentives by 1 even if user is sleeping', async () => {
|
it('increments loginIncentives by 1 even if user is sleeping', async () => {
|
||||||
user.preferences.sleep = true;
|
user.preferences.sleep = true;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.loginIncentives).to.eql(1);
|
expect(user.loginIncentives).to.eql(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('awards user bard robes if login incentive is 1', async () => {
|
it('awards user bard robes if login incentive is 1', async () => {
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.loginIncentives).to.eql(1);
|
expect(user.loginIncentives).to.eql(1);
|
||||||
expect(user.items.gear.owned.armor_special_bardRobes).to.eql(true);
|
expect(user.items.gear.owned.armor_special_bardRobes).to.eql(true);
|
||||||
@@ -1591,7 +1572,7 @@ describe('cron', async () => {
|
|||||||
it('awards user incentive backgrounds if login incentive is 2', async () => {
|
it('awards user incentive backgrounds if login incentive is 2', async () => {
|
||||||
user.loginIncentives = 1;
|
user.loginIncentives = 1;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.loginIncentives).to.eql(2);
|
expect(user.loginIncentives).to.eql(2);
|
||||||
expect(user.purchased.background.blue).to.eql(true);
|
expect(user.purchased.background.blue).to.eql(true);
|
||||||
@@ -1605,7 +1586,7 @@ describe('cron', async () => {
|
|||||||
it('awards user Bard Hat if login incentive is 3', async () => {
|
it('awards user Bard Hat if login incentive is 3', async () => {
|
||||||
user.loginIncentives = 2;
|
user.loginIncentives = 2;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.loginIncentives).to.eql(3);
|
expect(user.loginIncentives).to.eql(3);
|
||||||
expect(user.items.gear.owned.head_special_bardHat).to.eql(true);
|
expect(user.items.gear.owned.head_special_bardHat).to.eql(true);
|
||||||
@@ -1615,7 +1596,7 @@ describe('cron', async () => {
|
|||||||
it('awards user RoyalPurple Hatching Potion if login incentive is 4', async () => {
|
it('awards user RoyalPurple Hatching Potion if login incentive is 4', async () => {
|
||||||
user.loginIncentives = 3;
|
user.loginIncentives = 3;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.loginIncentives).to.eql(4);
|
expect(user.loginIncentives).to.eql(4);
|
||||||
expect(user.items.hatchingPotions.RoyalPurple).to.eql(1);
|
expect(user.items.hatchingPotions.RoyalPurple).to.eql(1);
|
||||||
@@ -1625,7 +1606,7 @@ describe('cron', async () => {
|
|||||||
it('awards user a Chocolate, Meat and Pink Contton Candy if login incentive is 5', async () => {
|
it('awards user a Chocolate, Meat and Pink Contton Candy if login incentive is 5', async () => {
|
||||||
user.loginIncentives = 4;
|
user.loginIncentives = 4;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.loginIncentives).to.eql(5);
|
expect(user.loginIncentives).to.eql(5);
|
||||||
|
|
||||||
@@ -1639,7 +1620,7 @@ describe('cron', async () => {
|
|||||||
it('awards user moon quest if login incentive is 7', async () => {
|
it('awards user moon quest if login incentive is 7', async () => {
|
||||||
user.loginIncentives = 6;
|
user.loginIncentives = 6;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.loginIncentives).to.eql(7);
|
expect(user.loginIncentives).to.eql(7);
|
||||||
expect(user.items.quests.moon1).to.eql(1);
|
expect(user.items.quests.moon1).to.eql(1);
|
||||||
@@ -1649,7 +1630,7 @@ describe('cron', async () => {
|
|||||||
it('awards user RoyalPurple Hatching Potion if login incentive is 10', async () => {
|
it('awards user RoyalPurple Hatching Potion if login incentive is 10', async () => {
|
||||||
user.loginIncentives = 9;
|
user.loginIncentives = 9;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.loginIncentives).to.eql(10);
|
expect(user.loginIncentives).to.eql(10);
|
||||||
expect(user.items.hatchingPotions.RoyalPurple).to.eql(1);
|
expect(user.items.hatchingPotions.RoyalPurple).to.eql(1);
|
||||||
@@ -1659,7 +1640,7 @@ describe('cron', async () => {
|
|||||||
it('awards user a Strawberry, Patato and Blue Contton Candy if login incentive is 14', async () => {
|
it('awards user a Strawberry, Patato and Blue Contton Candy if login incentive is 14', async () => {
|
||||||
user.loginIncentives = 13;
|
user.loginIncentives = 13;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.loginIncentives).to.eql(14);
|
expect(user.loginIncentives).to.eql(14);
|
||||||
|
|
||||||
@@ -1673,7 +1654,7 @@ describe('cron', async () => {
|
|||||||
it('awards user a bard instrument if login incentive is 18', async () => {
|
it('awards user a bard instrument if login incentive is 18', async () => {
|
||||||
user.loginIncentives = 17;
|
user.loginIncentives = 17;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.loginIncentives).to.eql(18);
|
expect(user.loginIncentives).to.eql(18);
|
||||||
expect(user.items.gear.owned.weapon_special_bardInstrument).to.eql(true);
|
expect(user.items.gear.owned.weapon_special_bardInstrument).to.eql(true);
|
||||||
@@ -1683,7 +1664,7 @@ describe('cron', async () => {
|
|||||||
it('awards user second moon quest if login incentive is 22', async () => {
|
it('awards user second moon quest if login incentive is 22', async () => {
|
||||||
user.loginIncentives = 21;
|
user.loginIncentives = 21;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.loginIncentives).to.eql(22);
|
expect(user.loginIncentives).to.eql(22);
|
||||||
expect(user.items.quests.moon2).to.eql(1);
|
expect(user.items.quests.moon2).to.eql(1);
|
||||||
@@ -1693,7 +1674,7 @@ describe('cron', async () => {
|
|||||||
it('awards user a RoyalPurple hatching potion if login incentive is 26', async () => {
|
it('awards user a RoyalPurple hatching potion if login incentive is 26', async () => {
|
||||||
user.loginIncentives = 25;
|
user.loginIncentives = 25;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.loginIncentives).to.eql(26);
|
expect(user.loginIncentives).to.eql(26);
|
||||||
expect(user.items.hatchingPotions.RoyalPurple).to.eql(1);
|
expect(user.items.hatchingPotions.RoyalPurple).to.eql(1);
|
||||||
@@ -1703,7 +1684,7 @@ describe('cron', async () => {
|
|||||||
it('awards user Fish, Milk, Rotten Meat and Honey if login incentive is 30', async () => {
|
it('awards user Fish, Milk, Rotten Meat and Honey if login incentive is 30', async () => {
|
||||||
user.loginIncentives = 29;
|
user.loginIncentives = 29;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.loginIncentives).to.eql(30);
|
expect(user.loginIncentives).to.eql(30);
|
||||||
|
|
||||||
@@ -1718,7 +1699,7 @@ describe('cron', async () => {
|
|||||||
it('awards user a RoyalPurple hatching potion if login incentive is 35', async () => {
|
it('awards user a RoyalPurple hatching potion if login incentive is 35', async () => {
|
||||||
user.loginIncentives = 34;
|
user.loginIncentives = 34;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.loginIncentives).to.eql(35);
|
expect(user.loginIncentives).to.eql(35);
|
||||||
expect(user.items.hatchingPotions.RoyalPurple).to.eql(1);
|
expect(user.items.hatchingPotions.RoyalPurple).to.eql(1);
|
||||||
@@ -1728,7 +1709,7 @@ describe('cron', async () => {
|
|||||||
it('awards user the third moon quest if login incentive is 40', async () => {
|
it('awards user the third moon quest if login incentive is 40', async () => {
|
||||||
user.loginIncentives = 39;
|
user.loginIncentives = 39;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.loginIncentives).to.eql(40);
|
expect(user.loginIncentives).to.eql(40);
|
||||||
expect(user.items.quests.moon3).to.eql(1);
|
expect(user.items.quests.moon3).to.eql(1);
|
||||||
@@ -1738,7 +1719,7 @@ describe('cron', async () => {
|
|||||||
it('awards user a RoyalPurple hatching potion if login incentive is 45', async () => {
|
it('awards user a RoyalPurple hatching potion if login incentive is 45', async () => {
|
||||||
user.loginIncentives = 44;
|
user.loginIncentives = 44;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.loginIncentives).to.eql(45);
|
expect(user.loginIncentives).to.eql(45);
|
||||||
expect(user.items.hatchingPotions.RoyalPurple).to.eql(1);
|
expect(user.items.hatchingPotions.RoyalPurple).to.eql(1);
|
||||||
@@ -1748,7 +1729,7 @@ describe('cron', async () => {
|
|||||||
it('awards user a saddle if login incentive is 50', async () => {
|
it('awards user a saddle if login incentive is 50', async () => {
|
||||||
user.loginIncentives = 49;
|
user.loginIncentives = 49;
|
||||||
await cron({
|
await cron({
|
||||||
user, tasksByType, daysMissed, analytics,
|
user, tasksByType, daysMissed,
|
||||||
});
|
});
|
||||||
expect(user.loginIncentives).to.eql(50);
|
expect(user.loginIncentives).to.eql(50);
|
||||||
expect(user.items.food.Saddle).to.eql(1);
|
expect(user.items.food.Saddle).to.eql(1);
|
||||||
@@ -1766,7 +1747,6 @@ describe('cron wrapper', () => {
|
|||||||
res = generateRes();
|
res = generateRes();
|
||||||
req = generateReq();
|
req = generateReq();
|
||||||
user = await res.locals.user.save();
|
user = await res.locals.user.save();
|
||||||
res.analytics = analytics;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
import nconf from 'nconf';
|
||||||
|
import requireAgain from 'require-again';
|
||||||
|
import { model as User } from '../../../../website/server/models/user';
|
||||||
|
import { RegistrationEventModel } from '../../../../website/server/models/analytics/registrationEvent';
|
||||||
|
import { SubscriptionEventModel } from '../../../../website/server/models/analytics/subscriptionEvent';
|
||||||
|
|
||||||
|
describe('localAnalytics', () => {
|
||||||
|
let user;
|
||||||
|
let localAnalytics;
|
||||||
|
before(() => {
|
||||||
|
const nconfGetStub = sandbox.stub(nconf, 'get');
|
||||||
|
nconfGetStub.withArgs('ANALYTICS_DB').returns('analytics');
|
||||||
|
nconfGetStub.withArgs('DISABLE_LOCAL_ANALYTICS').returns(false);
|
||||||
|
localAnalytics = requireAgain('../../../../website/server/libs/localAnalytics');
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = new User({
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: 'username',
|
||||||
|
email: 'email@example.com',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
registeredThrough: 'habitica-web',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('trackRegistrationEvent', () => {
|
||||||
|
afterEach(async () => {
|
||||||
|
await RegistrationEventModel.deleteMany({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a registration event when a user registers', async () => {
|
||||||
|
user._id = '00000000-0000-0000-0000-000000000001';
|
||||||
|
await localAnalytics.trackRegistrationEvent({ user, ipAddress: '127.0.0.1' });
|
||||||
|
|
||||||
|
const registrationEvents = await RegistrationEventModel.find({ userId: user._id });
|
||||||
|
expect(registrationEvents).to.have.lengthOf(1);
|
||||||
|
expect(registrationEvents[0]).to.have.property('userId', user._id);
|
||||||
|
expect(registrationEvents[0]).to.have.property('ipAddress', '127.0.0.1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('saves the correct data to the database', async () => {
|
||||||
|
user._id = '00000000-0000-0000-0000-000000000002';
|
||||||
|
user.auth.google = { id: 'abc', emails: [{ value: 'email@example.com' }] };
|
||||||
|
await localAnalytics.trackRegistrationEvent({ user, ipAddress: '127.0.0.2' });
|
||||||
|
|
||||||
|
const registrationEvent = await RegistrationEventModel.findOne({ userId: user._id });
|
||||||
|
expect(registrationEvent).to.have.property('userId', user._id);
|
||||||
|
expect(registrationEvent).to.have.property('ipAddress', '127.0.0.2');
|
||||||
|
expect(registrationEvent).to.have.property('authenticationMethod', 'google');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('trackSubscriptionEvent', () => {
|
||||||
|
afterEach(async () => {
|
||||||
|
await SubscriptionEventModel.deleteMany({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a subscription event when a user subscribes', async () => {
|
||||||
|
user._id = '00000000-0000-0000-0000-000000000003';
|
||||||
|
await localAnalytics.trackSubscriptionEvent({
|
||||||
|
eventType: 'subscribed',
|
||||||
|
user,
|
||||||
|
paymentMethod: 'stripe',
|
||||||
|
customerId: 'cus_123',
|
||||||
|
planId: 'plan_123',
|
||||||
|
});
|
||||||
|
|
||||||
|
const subscriptionEvents = await SubscriptionEventModel.find({ userId: user._id });
|
||||||
|
expect(subscriptionEvents).to.have.lengthOf(1);
|
||||||
|
expect(subscriptionEvents[0]).to.have.property('userId', user._id);
|
||||||
|
expect(subscriptionEvents[0]).to.have.property('eventType', 'subscribed');
|
||||||
|
expect(subscriptionEvents[0]).to.have.property('paymentMethod', 'stripe');
|
||||||
|
expect(subscriptionEvents[0]).to.have.property('customerId', 'cus_123');
|
||||||
|
expect(subscriptionEvents[0]).to.have.property('planId', 'plan_123');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a subscription event with cancellation reason when a user cancels', async () => {
|
||||||
|
user._id = '00000000-0000-0000-0000-000000000004';
|
||||||
|
await localAnalytics.trackSubscriptionEvent({
|
||||||
|
eventType: 'cancelled',
|
||||||
|
user,
|
||||||
|
paymentMethod: 'stripe',
|
||||||
|
customerId: 'cus_456',
|
||||||
|
planId: 'plan_456',
|
||||||
|
cancellationReason: 'No longer needed',
|
||||||
|
});
|
||||||
|
|
||||||
|
const subscriptionEvent = await SubscriptionEventModel.findOne({ userId: user._id });
|
||||||
|
expect(subscriptionEvent).to.have.property('userId', user._id);
|
||||||
|
expect(subscriptionEvent).to.have.property('eventType', 'cancelled');
|
||||||
|
expect(subscriptionEvent).to.have.property('paymentMethod', 'stripe');
|
||||||
|
expect(subscriptionEvent).to.have.property('customerId', 'cus_456');
|
||||||
|
expect(subscriptionEvent).to.have.property('planId', 'plan_456');
|
||||||
|
expect(subscriptionEvent).to.have.property('cancellationReason', 'No longer needed');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -3,7 +3,6 @@ import moment from 'moment';
|
|||||||
import * as sender from '../../../../../website/server/libs/email';
|
import * as sender from '../../../../../website/server/libs/email';
|
||||||
import common from '../../../../../website/common';
|
import common from '../../../../../website/common';
|
||||||
import api from '../../../../../website/server/libs/payments/payments';
|
import api from '../../../../../website/server/libs/payments/payments';
|
||||||
import * as analytics from '../../../../../website/server/libs/analyticsService';
|
|
||||||
import * as notifications from '../../../../../website/server/libs/pushNotifications';
|
import * as notifications from '../../../../../website/server/libs/pushNotifications';
|
||||||
import { model as User } from '../../../../../website/server/models/user';
|
import { model as User } from '../../../../../website/server/models/user';
|
||||||
import { translate as t } from '../../../../helpers/api-integration/v3';
|
import { translate as t } from '../../../../helpers/api-integration/v3';
|
||||||
@@ -13,6 +12,7 @@ import {
|
|||||||
import * as worldState from '../../../../../website/server/libs/worldState';
|
import * as worldState from '../../../../../website/server/libs/worldState';
|
||||||
import { TransactionModel } from '../../../../../website/server/models/transaction';
|
import { TransactionModel } from '../../../../../website/server/models/transaction';
|
||||||
import { REPEATING_EVENTS } from '../../../../../website/common/script/content/constants/events';
|
import { REPEATING_EVENTS } from '../../../../../website/common/script/content/constants/events';
|
||||||
|
import { SubscriptionEventModel } from '../../../../../website/server/models/analytics/subscriptionEvent';
|
||||||
|
|
||||||
describe('payments/index', () => {
|
describe('payments/index', () => {
|
||||||
let user;
|
let user;
|
||||||
@@ -36,8 +36,6 @@ describe('payments/index', () => {
|
|||||||
|
|
||||||
sandbox.stub(sender, 'sendTxn');
|
sandbox.stub(sender, 'sendTxn');
|
||||||
sandbox.stub(user, 'sendMessage');
|
sandbox.stub(user, 'sendMessage');
|
||||||
sandbox.stub(analytics.mockAnalyticsService, 'trackPurchase');
|
|
||||||
sandbox.stub(analytics.mockAnalyticsService, 'track');
|
|
||||||
sandbox.stub(notifications, 'sendNotification');
|
sandbox.stub(notifications, 'sendNotification');
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
@@ -97,6 +95,16 @@ describe('payments/index', () => {
|
|||||||
expect(recipient.items.pets['Jackalope-RoyalPurple']).to.eql(5);
|
expect(recipient.items.pets['Jackalope-RoyalPurple']).to.eql(5);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('tracks subscription events', async () => {
|
||||||
|
await api.createSubscription(data);
|
||||||
|
const subscriptionEvent = await SubscriptionEventModel.findOne({ userId: recipient._id });
|
||||||
|
expect(subscriptionEvent).to.exist;
|
||||||
|
expect(subscriptionEvent).to.have.property('eventType', 'subscribed');
|
||||||
|
expect(subscriptionEvent).to.have.property('userId', recipient._id);
|
||||||
|
expect(subscriptionEvent).to.have.property('planId', 'basic_3mo');
|
||||||
|
expect(subscriptionEvent).to.have.property('paymentMethod', 'Payment Method');
|
||||||
|
});
|
||||||
|
|
||||||
it('adds extra months to an existing subscription', async () => {
|
it('adds extra months to an existing subscription', async () => {
|
||||||
recipient.purchased.plan = plan;
|
recipient.purchased.plan = plan;
|
||||||
|
|
||||||
@@ -298,28 +306,6 @@ describe('payments/index', () => {
|
|||||||
expect(notifications.sendNotification).to.be.calledOnce;
|
expect(notifications.sendNotification).to.be.calledOnce;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tracks subscription purchase as gift', async () => {
|
|
||||||
await api.createSubscription(data);
|
|
||||||
|
|
||||||
expect(analytics.mockAnalyticsService.trackPurchase).to.be.calledOnce;
|
|
||||||
expect(analytics.mockAnalyticsService.trackPurchase).to.be.calledWith({
|
|
||||||
uuid: user._id,
|
|
||||||
groupId: undefined,
|
|
||||||
itemPurchased: 'Subscription',
|
|
||||||
sku: 'payment method-subscription',
|
|
||||||
purchaseType: 'subscribe',
|
|
||||||
paymentMethod: data.paymentMethod,
|
|
||||||
quantity: 1,
|
|
||||||
gift: true,
|
|
||||||
purchaseValue: 15,
|
|
||||||
firstPurchase: true,
|
|
||||||
headers: {
|
|
||||||
'x-client': 'habitica-web',
|
|
||||||
'user-agent': '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
context('No Active Promotion', () => {
|
context('No Active Promotion', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
sinon.stub(worldState, 'getCurrentEventList').returns([]);
|
sinon.stub(worldState, 'getCurrentEventList').returns([]);
|
||||||
@@ -455,6 +441,16 @@ describe('payments/index', () => {
|
|||||||
expect(user.purchased.plan.dateCreated).to.exist;
|
expect(user.purchased.plan.dateCreated).to.exist;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('tracks subscription events', async () => {
|
||||||
|
await api.createSubscription(data);
|
||||||
|
const subscriptionEvent = await SubscriptionEventModel.findOne({ userId: user._id });
|
||||||
|
expect(subscriptionEvent).to.exist;
|
||||||
|
expect(subscriptionEvent).to.have.property('userId', user._id);
|
||||||
|
expect(subscriptionEvent).to.have.property('ipAddress');
|
||||||
|
expect(subscriptionEvent).to.have.property('planId', 'basic_3mo');
|
||||||
|
expect(subscriptionEvent).to.have.property('paymentMethod', 'Payment Method');
|
||||||
|
});
|
||||||
|
|
||||||
it('sets plan.dateCreated if it did not previously exist', async () => {
|
it('sets plan.dateCreated if it did not previously exist', async () => {
|
||||||
expect(user.purchased.plan.dateCreated).to.not.exist;
|
expect(user.purchased.plan.dateCreated).to.not.exist;
|
||||||
|
|
||||||
@@ -543,29 +539,24 @@ describe('payments/index', () => {
|
|||||||
expect(sender.sendTxn).to.be.calledWith(data.user, 'subscription-begins');
|
expect(sender.sendTxn).to.be.calledWith(data.user, 'subscription-begins');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tracks subscription purchase', async () => {
|
|
||||||
await api.createSubscription(data);
|
|
||||||
|
|
||||||
expect(analytics.mockAnalyticsService.trackPurchase).to.be.calledOnce;
|
|
||||||
expect(analytics.mockAnalyticsService.trackPurchase).to.be.calledWith({
|
|
||||||
uuid: user._id,
|
|
||||||
groupId: undefined,
|
|
||||||
itemPurchased: 'Subscription',
|
|
||||||
sku: 'payment method-subscription',
|
|
||||||
purchaseType: 'subscribe',
|
|
||||||
paymentMethod: data.paymentMethod,
|
|
||||||
quantity: 1,
|
|
||||||
gift: false,
|
|
||||||
purchaseValue: 15,
|
|
||||||
firstPurchase: true,
|
|
||||||
headers: {
|
|
||||||
'x-client': 'habitica-web',
|
|
||||||
'user-agent': '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
context('Upgrades subscription', () => {
|
context('Upgrades subscription', () => {
|
||||||
|
it('tracks subscription events', async () => {
|
||||||
|
data.sub.key = 'basic_earned';
|
||||||
|
expect(user.purchased.plan.planId).to.not.exist;
|
||||||
|
|
||||||
|
await api.createSubscription(data);
|
||||||
|
|
||||||
|
data.sub.key = 'basic_6mo';
|
||||||
|
data.updatedFrom = { key: 'basic_earned' };
|
||||||
|
await api.createSubscription(data);
|
||||||
|
|
||||||
|
const subscriptionEvent = await SubscriptionEventModel.findOne({ userId: user._id, planId: 'basic_6mo' });
|
||||||
|
expect(subscriptionEvent).to.exist;
|
||||||
|
expect(subscriptionEvent).to.have.property('eventType', 'upgraded');
|
||||||
|
expect(subscriptionEvent).to.have.property('userId', user._id);
|
||||||
|
expect(subscriptionEvent).to.have.property('paymentMethod', 'Payment Method');
|
||||||
|
});
|
||||||
|
|
||||||
it('from basic_earned to basic_6mo', async () => {
|
it('from basic_earned to basic_6mo', async () => {
|
||||||
data.sub.key = 'basic_earned';
|
data.sub.key = 'basic_earned';
|
||||||
expect(user.purchased.plan.planId).to.not.exist;
|
expect(user.purchased.plan.planId).to.not.exist;
|
||||||
@@ -608,6 +599,23 @@ describe('payments/index', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
context('Downgrades subscription', () => {
|
context('Downgrades subscription', () => {
|
||||||
|
it('tracks subscription events', async () => {
|
||||||
|
data.sub.key = 'basic_6mo';
|
||||||
|
expect(user.purchased.plan.planId).to.not.exist;
|
||||||
|
|
||||||
|
await api.createSubscription(data);
|
||||||
|
|
||||||
|
data.sub.key = 'basic_earned';
|
||||||
|
data.updatedFrom = { key: 'basic_6mo' };
|
||||||
|
await api.createSubscription(data);
|
||||||
|
|
||||||
|
const subscriptionEvent = await SubscriptionEventModel.findOne({ userId: user._id, planId: 'basic_earned' });
|
||||||
|
expect(subscriptionEvent).to.exist;
|
||||||
|
expect(subscriptionEvent).to.have.property('eventType', 'downgraded');
|
||||||
|
expect(subscriptionEvent).to.have.property('userId', user._id);
|
||||||
|
expect(subscriptionEvent).to.have.property('paymentMethod', 'Payment Method');
|
||||||
|
});
|
||||||
|
|
||||||
it('from basic_6mo to basic_earned', async () => {
|
it('from basic_6mo to basic_earned', async () => {
|
||||||
data.sub.key = 'basic_6mo';
|
data.sub.key = 'basic_6mo';
|
||||||
expect(user.purchased.plan.planId).to.not.exist;
|
expect(user.purchased.plan.planId).to.not.exist;
|
||||||
@@ -1136,6 +1144,15 @@ describe('payments/index', () => {
|
|||||||
expect(daysTillTermination).to.be.within(29, 30); // 1 month +/- 1 days
|
expect(daysTillTermination).to.be.within(29, 30); // 1 month +/- 1 days
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('tracks subscription events', async () => {
|
||||||
|
await api.cancelSubscription(data);
|
||||||
|
|
||||||
|
const subscriptionEvent = await SubscriptionEventModel.findOne({ userId: user._id });
|
||||||
|
expect(subscriptionEvent).to.exist;
|
||||||
|
expect(subscriptionEvent).to.have.property('eventType', 'cancelled');
|
||||||
|
expect(subscriptionEvent).to.have.property('userId', user._id);
|
||||||
|
});
|
||||||
|
|
||||||
it('adds extraMonths to dateTerminated value', async () => {
|
it('adds extraMonths to dateTerminated value', async () => {
|
||||||
user.purchased.plan.extraMonths = 2;
|
user.purchased.plan.extraMonths = 2;
|
||||||
|
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
/* eslint-disable global-require */
|
|
||||||
import nconf from 'nconf';
|
|
||||||
import requireAgain from 'require-again';
|
|
||||||
import {
|
|
||||||
generateRes,
|
|
||||||
generateReq,
|
|
||||||
generateNext,
|
|
||||||
} from '../../../helpers/api-unit.helper';
|
|
||||||
import * as analyticsService from '../../../../website/server/libs/analyticsService';
|
|
||||||
|
|
||||||
describe('analytics middleware', () => {
|
|
||||||
let res; let req; let
|
|
||||||
next;
|
|
||||||
const pathToAnalyticsMiddleware = '../../../../website/server/middlewares/analytics';
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
res = generateRes();
|
|
||||||
req = generateReq();
|
|
||||||
next = generateNext();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('attaches analytics object to res', () => {
|
|
||||||
const attachAnalytics = requireAgain(pathToAnalyticsMiddleware).default;
|
|
||||||
|
|
||||||
attachAnalytics(req, res, next);
|
|
||||||
|
|
||||||
expect(res.analytics).to.exist;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('attaches stubbed methods for non-prod environments', () => {
|
|
||||||
sandbox.stub(nconf, 'get').withArgs('IS_PROD').returns(false);
|
|
||||||
const attachAnalytics = requireAgain(pathToAnalyticsMiddleware).default;
|
|
||||||
|
|
||||||
attachAnalytics(req, res, next);
|
|
||||||
|
|
||||||
expect(res.analytics.track).to.eql(analyticsService.mockAnalyticsService.track);
|
|
||||||
expect(res.analytics.trackPurchase).to.eql(analyticsService.mockAnalyticsService.trackPurchase);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('attaches real methods for prod environments', () => {
|
|
||||||
sandbox.stub(nconf, 'get').withArgs('IS_PROD').returns(true);
|
|
||||||
|
|
||||||
const attachAnalytics = requireAgain(pathToAnalyticsMiddleware).default;
|
|
||||||
|
|
||||||
attachAnalytics(req, res, next);
|
|
||||||
|
|
||||||
expect(res.analytics.track).to.eql(analyticsService.track);
|
|
||||||
expect(res.analytics.trackPurchase).to.eql(analyticsService.trackPurchase);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import {
|
|
||||||
generateUser,
|
|
||||||
requester,
|
|
||||||
} from '../../../../helpers/api-integration/v3';
|
|
||||||
import { mockAnalyticsService as analytics } from '../../../../../website/server/libs/analyticsService';
|
|
||||||
|
|
||||||
describe('POST /analytics/track/:eventName', () => {
|
|
||||||
it('calls res.analytics', async () => {
|
|
||||||
const user = await generateUser();
|
|
||||||
sandbox.spy(analytics, 'track');
|
|
||||||
|
|
||||||
const requestWithHeaders = requester(user, { 'x-client': 'habitica-web' });
|
|
||||||
await requestWithHeaders.post('/analytics/track/eventName', { data: 'example' }, { 'x-client': 'habitica-web' });
|
|
||||||
expect(analytics.track).to.be.calledOnce;
|
|
||||||
expect(analytics.track).to.be.calledWith('eventName', sandbox.match({ data: 'example' }));
|
|
||||||
|
|
||||||
sandbox.restore();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
generateUser,
|
generateUser,
|
||||||
} from '../../../../helpers/api-integration/v3';
|
} from '../../../../helpers/api-integration/v3';
|
||||||
import { mockAnalyticsService as analytics } from '../../../../../website/server/libs/analyticsService';
|
|
||||||
|
|
||||||
describe('POST /user/sleep', () => {
|
describe('POST /user/sleep', () => {
|
||||||
let user;
|
let user;
|
||||||
@@ -23,15 +22,4 @@ describe('POST /user/sleep', () => {
|
|||||||
await user.sync();
|
await user.sync();
|
||||||
expect(user.preferences.sleep).to.be.false;
|
expect(user.preferences.sleep).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sends sleep status to analytics service', async () => {
|
|
||||||
sandbox.spy(analytics, 'track');
|
|
||||||
|
|
||||||
await user.post('/user/sleep');
|
|
||||||
await user.sync();
|
|
||||||
expect(analytics.track).to.be.calledOnce;
|
|
||||||
expect(analytics.track).to.be.calledWith('sleep', sandbox.match.has('status', user.preferences.sleep));
|
|
||||||
|
|
||||||
sandbox.restore();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
} from '../../../../../helpers/api-integration/v3';
|
} from '../../../../../helpers/api-integration/v3';
|
||||||
import { ApiUser } from '../../../../../helpers/api-integration/api-classes';
|
import { ApiUser } from '../../../../../helpers/api-integration/api-classes';
|
||||||
import { encrypt } from '../../../../../../website/server/libs/encryption';
|
import { encrypt } from '../../../../../../website/server/libs/encryption';
|
||||||
|
import { RegistrationEventModel } from '../../../../../../website/server/models/analytics/registrationEvent';
|
||||||
|
|
||||||
function generateRandomUserName () {
|
function generateRandomUserName () {
|
||||||
return (Date.now() + uuid()).substring(0, 20);
|
return (Date.now() + uuid()).substring(0, 20);
|
||||||
@@ -41,6 +42,25 @@ describe('POST /user/auth/local/register', () => {
|
|||||||
expect(user.newUser).to.eql(true);
|
expect(user.newUser).to.eql(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('tracks a registration event', async () => {
|
||||||
|
const username = generateRandomUserName();
|
||||||
|
const email = `${username}@example.com`;
|
||||||
|
const password = 'password';
|
||||||
|
|
||||||
|
const user = await api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
});
|
||||||
|
|
||||||
|
const registrationEvent = await RegistrationEventModel.findOne({ userId: user._id });
|
||||||
|
expect(registrationEvent).to.exist;
|
||||||
|
expect(registrationEvent).to.have.property('userId', user._id);
|
||||||
|
expect(registrationEvent).to.have.property('ipAddress');
|
||||||
|
expect(registrationEvent).to.have.property('authenticationMethod', 'local');
|
||||||
|
});
|
||||||
|
|
||||||
it('registers a new user and sets verifiedUsername to true', async () => {
|
it('registers a new user and sets verifiedUsername to true', async () => {
|
||||||
const username = generateRandomUserName();
|
const username = generateRandomUserName();
|
||||||
const email = `${username}@example.com`;
|
const email = `${username}@example.com`;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
getProperty,
|
getProperty,
|
||||||
} from '../../../../../helpers/api-integration/v3';
|
} from '../../../../../helpers/api-integration/v3';
|
||||||
import apiErrorMessages from '../../../../../../website/common/script/errors/apiErrorMessages';
|
import apiErrorMessages from '../../../../../../website/common/script/errors/apiErrorMessages';
|
||||||
|
import { RegistrationEventModel } from '../../../../../../website/server/models/analytics/registrationEvent';
|
||||||
|
|
||||||
describe('POST /user/auth/social', () => {
|
describe('POST /user/auth/social', () => {
|
||||||
let api;
|
let api;
|
||||||
@@ -65,6 +66,19 @@ describe('POST /user/auth/social', () => {
|
|||||||
await expect(getProperty('users', response.id, 'profile.name')).to.eventually.equal('a google user');
|
await expect(getProperty('users', response.id, 'profile.name')).to.eventually.equal('a google user');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('tracks a registration event', async () => {
|
||||||
|
const socialUser = await api.post(endpoint, {
|
||||||
|
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
|
||||||
|
network,
|
||||||
|
});
|
||||||
|
|
||||||
|
const registrationEvent = await RegistrationEventModel.findOne({ userId: socialUser.id });
|
||||||
|
expect(registrationEvent).to.exist;
|
||||||
|
expect(registrationEvent).to.have.property('userId', socialUser.id);
|
||||||
|
expect(registrationEvent).to.have.property('ipAddress');
|
||||||
|
expect(registrationEvent).to.have.property('authenticationMethod', 'google');
|
||||||
|
});
|
||||||
|
|
||||||
it('includes sanitized version of provided username', async () => {
|
it('includes sanitized version of provided username', async () => {
|
||||||
const response = await api.post(endpoint, {
|
const response = await api.post(endpoint, {
|
||||||
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
|
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
|
||||||
@@ -231,6 +245,17 @@ describe('POST /user/auth/social', () => {
|
|||||||
expect(response.newUser).to.be.false;
|
expect(response.newUser).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not track a registration event for existing users', async () => {
|
||||||
|
const beforeEvents = await RegistrationEventModel.find({ userId: user._id });
|
||||||
|
await user.post(endpoint, {
|
||||||
|
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
|
||||||
|
network,
|
||||||
|
});
|
||||||
|
|
||||||
|
const registrationEvents = await RegistrationEventModel.find({ userId: user._id });
|
||||||
|
expect(registrationEvents).to.have.lengthOf(beforeEvents.length);
|
||||||
|
});
|
||||||
|
|
||||||
it('does not log into other account if social auth already exists', async () => {
|
it('does not log into other account if social auth already exists', async () => {
|
||||||
const registerResponse = await api.post(endpoint, {
|
const registerResponse = await api.post(endpoint, {
|
||||||
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
|
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import { errorMessage } from '../../../../website/common/script/libs/errorMessag
|
|||||||
|
|
||||||
describe('shared.ops.buy', () => {
|
describe('shared.ops.buy', () => {
|
||||||
let user;
|
let user;
|
||||||
const analytics = { track () {} };
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
user = generateUser({
|
user = generateUser({
|
||||||
@@ -32,12 +31,6 @@ describe('shared.ops.buy', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
sinon.stub(analytics, 'track');
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
analytics.track.restore();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns error when key is not provided', async () => {
|
it('returns error when key is not provided', async () => {
|
||||||
@@ -51,10 +44,8 @@ describe('shared.ops.buy', () => {
|
|||||||
|
|
||||||
it('buys health potion', async () => {
|
it('buys health potion', async () => {
|
||||||
user.stats.hp = 30;
|
user.stats.hp = 30;
|
||||||
await buy(user, { params: { key: 'potion' } }, analytics);
|
await buy(user, { params: { key: 'potion' } });
|
||||||
expect(user.stats.hp).to.eql(45);
|
expect(user.stats.hp).to.eql(45);
|
||||||
|
|
||||||
expect(analytics.track).to.be.calledOnce;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('adds equipment to inventory', async () => {
|
it('adds equipment to inventory', async () => {
|
||||||
|
|||||||
@@ -29,10 +29,9 @@ describe('shared.ops.buyArmoire', () => {
|
|||||||
const YIELD_EQUIPMENT = 0.5;
|
const YIELD_EQUIPMENT = 0.5;
|
||||||
const YIELD_FOOD = 0.7;
|
const YIELD_FOOD = 0.7;
|
||||||
const YIELD_EXP = 0.9;
|
const YIELD_EXP = 0.9;
|
||||||
const analytics = { track () {} };
|
|
||||||
|
|
||||||
async function buyArmoire (_user, _req, _analytics) {
|
async function buyArmoire (_user, _req) {
|
||||||
const buyOp = new BuyArmoireOperation(_user, _req, _analytics);
|
const buyOp = new BuyArmoireOperation(_user, _req);
|
||||||
|
|
||||||
return buyOp.purchase();
|
return buyOp.purchase();
|
||||||
}
|
}
|
||||||
@@ -50,12 +49,10 @@ describe('shared.ops.buyArmoire', () => {
|
|||||||
user.items.food = {};
|
user.items.food = {};
|
||||||
|
|
||||||
sandbox.stub(randomValFns, 'trueRandom');
|
sandbox.stub(randomValFns, 'trueRandom');
|
||||||
sinon.stub(analytics, 'track');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
randomValFns.trueRandom.restore();
|
randomValFns.trueRandom.restore();
|
||||||
analytics.track.restore();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
context('failure conditions', () => {
|
context('failure conditions', () => {
|
||||||
@@ -147,7 +144,7 @@ describe('shared.ops.buyArmoire', () => {
|
|||||||
|
|
||||||
expect(_.size(user.items.gear.owned)).to.equal(2);
|
expect(_.size(user.items.gear.owned)).to.equal(2);
|
||||||
|
|
||||||
await buyArmoire(user, {}, analytics);
|
await buyArmoire(user, {});
|
||||||
|
|
||||||
expect(_.size(user.items.gear.owned)).to.equal(3);
|
expect(_.size(user.items.gear.owned)).to.equal(3);
|
||||||
|
|
||||||
@@ -155,7 +152,6 @@ describe('shared.ops.buyArmoire', () => {
|
|||||||
|
|
||||||
expect(armoireCount).to.eql(_.size(getFullArmoire()) - 2);
|
expect(armoireCount).to.eql(_.size(getFullArmoire()) - 2);
|
||||||
expect(user.stats.gp).to.eql(100);
|
expect(user.stats.gp).to.eql(100);
|
||||||
expect(analytics.track).to.be.calledTwice;
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
/* eslint-disable camelcase */
|
/* eslint-disable camelcase */
|
||||||
|
|
||||||
import sinon from 'sinon'; // eslint-disable-line no-shadow
|
|
||||||
import {
|
import {
|
||||||
generateUser,
|
generateUser,
|
||||||
} from '../../../helpers/common.helper';
|
} from '../../../helpers/common.helper';
|
||||||
@@ -11,15 +10,14 @@ import i18n from '../../../../website/common/script/i18n';
|
|||||||
import { BuyGemOperation } from '../../../../website/common/script/ops/buy/buyGem';
|
import { BuyGemOperation } from '../../../../website/common/script/ops/buy/buyGem';
|
||||||
import planGemLimits from '../../../../website/common/script/libs/planGemLimits';
|
import planGemLimits from '../../../../website/common/script/libs/planGemLimits';
|
||||||
|
|
||||||
async function buyGem (user, req, analytics) {
|
async function buyGem (user, req) {
|
||||||
const buyOp = new BuyGemOperation(user, req, analytics);
|
const buyOp = new BuyGemOperation(user, req);
|
||||||
|
|
||||||
return buyOp.purchase();
|
return buyOp.purchase();
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('shared.ops.buyGem', () => {
|
describe('shared.ops.buyGem', () => {
|
||||||
let user;
|
let user;
|
||||||
const analytics = { track () {} };
|
|
||||||
const goldPoints = 40;
|
const goldPoints = 40;
|
||||||
const gemsBought = 40;
|
const gemsBought = 40;
|
||||||
const userGemAmount = 10;
|
const userGemAmount = 10;
|
||||||
@@ -35,23 +33,16 @@ describe('shared.ops.buyGem', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
sinon.stub(analytics, 'track');
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
analytics.track.restore();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
context('Gems', () => {
|
context('Gems', () => {
|
||||||
it('purchases gems', async () => {
|
it('purchases gems', async () => {
|
||||||
const [, message] = await buyGem(user, { params: { type: 'gems', key: 'gem' } }, analytics);
|
const [, message] = await buyGem(user, { params: { type: 'gems', key: 'gem' } });
|
||||||
|
|
||||||
expect(message).to.equal(i18n.t('plusGem', { count: 1 }));
|
expect(message).to.equal(i18n.t('plusGem', { count: 1 }));
|
||||||
expect(user.balance).to.equal(userGemAmount + 0.25);
|
expect(user.balance).to.equal(userGemAmount + 0.25);
|
||||||
expect(user.purchased.plan.gemsBought).to.equal(1);
|
expect(user.purchased.plan.gemsBought).to.equal(1);
|
||||||
expect(user.stats.gp).to.equal(goldPoints - planGemLimits.convRate);
|
expect(user.stats.gp).to.equal(goldPoints - planGemLimits.convRate);
|
||||||
expect(analytics.track).to.be.calledOnce;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('purchases gems with a different language than the default', async () => {
|
it('purchases gems with a different language than the default', async () => {
|
||||||
|
|||||||
@@ -10,10 +10,9 @@ import i18n from '../../../../website/common/script/i18n';
|
|||||||
|
|
||||||
describe('shared.ops.buyHealthPotion', () => {
|
describe('shared.ops.buyHealthPotion', () => {
|
||||||
let user;
|
let user;
|
||||||
const analytics = { track () {} };
|
|
||||||
|
|
||||||
async function buyHealthPotion (_user, _req, _analytics) {
|
async function buyHealthPotion (_user, _req) {
|
||||||
const buyOp = new BuyHealthPotionOperation(_user, _req, _analytics);
|
const buyOp = new BuyHealthPotionOperation(_user, _req);
|
||||||
|
|
||||||
return buyOp.purchase();
|
return buyOp.purchase();
|
||||||
}
|
}
|
||||||
@@ -32,19 +31,13 @@ describe('shared.ops.buyHealthPotion', () => {
|
|||||||
},
|
},
|
||||||
stats: { gp: 200 },
|
stats: { gp: 200 },
|
||||||
});
|
});
|
||||||
sinon.stub(analytics, 'track');
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
analytics.track.restore();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
context('Potion', () => {
|
context('Potion', () => {
|
||||||
it('recovers 15 hp', async () => {
|
it('recovers 15 hp', async () => {
|
||||||
user.stats.hp = 30;
|
user.stats.hp = 30;
|
||||||
await buyHealthPotion(user, {}, analytics);
|
await buyHealthPotion(user, {});
|
||||||
expect(user.stats.hp).to.eql(45);
|
expect(user.stats.hp).to.eql(45);
|
||||||
expect(analytics.track).to.be.calledOnce;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not increase hp above 50', async () => {
|
it('does not increase hp above 50', async () => {
|
||||||
|
|||||||
@@ -13,15 +13,14 @@ import {
|
|||||||
import i18n from '../../../../website/common/script/i18n';
|
import i18n from '../../../../website/common/script/i18n';
|
||||||
import { errorMessage } from '../../../../website/common/script/libs/errorMessage';
|
import { errorMessage } from '../../../../website/common/script/libs/errorMessage';
|
||||||
|
|
||||||
async function buyGear (user, req, analytics) {
|
async function buyGear (user, req) {
|
||||||
const buyOp = new BuyMarketGearOperation(user, req, analytics);
|
const buyOp = new BuyMarketGearOperation(user, req);
|
||||||
|
|
||||||
return buyOp.purchase();
|
return buyOp.purchase();
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('shared.ops.buyMarketGear', () => {
|
describe('shared.ops.buyMarketGear', () => {
|
||||||
let user;
|
let user;
|
||||||
const analytics = { track () {} };
|
|
||||||
let clock;
|
let clock;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -47,14 +46,12 @@ describe('shared.ops.buyMarketGear', () => {
|
|||||||
sinon.stub(shared, 'randomVal');
|
sinon.stub(shared, 'randomVal');
|
||||||
sinon.stub(shared.onboarding, 'checkOnboardingStatus');
|
sinon.stub(shared.onboarding, 'checkOnboardingStatus');
|
||||||
sinon.stub(shared.fns, 'predictableRandom');
|
sinon.stub(shared.fns, 'predictableRandom');
|
||||||
sinon.stub(analytics, 'track');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
shared.randomVal.restore();
|
shared.randomVal.restore();
|
||||||
shared.fns.predictableRandom.restore();
|
shared.fns.predictableRandom.restore();
|
||||||
shared.onboarding.checkOnboardingStatus.restore();
|
shared.onboarding.checkOnboardingStatus.restore();
|
||||||
analytics.track.restore();
|
|
||||||
|
|
||||||
if (clock) {
|
if (clock) {
|
||||||
clock.restore();
|
clock.restore();
|
||||||
@@ -65,7 +62,7 @@ describe('shared.ops.buyMarketGear', () => {
|
|||||||
it('adds equipment to inventory', async () => {
|
it('adds equipment to inventory', async () => {
|
||||||
user.stats.gp = 31;
|
user.stats.gp = 31;
|
||||||
|
|
||||||
await buyGear(user, { params: { key: 'armor_warrior_1' } }, analytics);
|
await buyGear(user, { params: { key: 'armor_warrior_1' } });
|
||||||
|
|
||||||
expect(user.items.gear.owned).to.eql({
|
expect(user.items.gear.owned).to.eql({
|
||||||
weapon_warrior_0: true,
|
weapon_warrior_0: true,
|
||||||
@@ -92,13 +89,12 @@ describe('shared.ops.buyMarketGear', () => {
|
|||||||
eyewear_special_whiteHalfMoon: true,
|
eyewear_special_whiteHalfMoon: true,
|
||||||
eyewear_special_yellowHalfMoon: true,
|
eyewear_special_yellowHalfMoon: true,
|
||||||
});
|
});
|
||||||
expect(analytics.track).to.be.calledOnce;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('adds the onboarding achievement to the user and checks the onboarding status', async () => {
|
it('adds the onboarding achievement to the user and checks the onboarding status', async () => {
|
||||||
user.stats.gp = 31;
|
user.stats.gp = 31;
|
||||||
|
|
||||||
await buyGear(user, { params: { key: 'armor_warrior_1' } }, analytics);
|
await buyGear(user, { params: { key: 'armor_warrior_1' } });
|
||||||
|
|
||||||
expect(user.addAchievement).to.be.calledOnce;
|
expect(user.addAchievement).to.be.calledOnce;
|
||||||
expect(user.addAchievement).to.be.calledWith('purchasedEquipment');
|
expect(user.addAchievement).to.be.calledWith('purchasedEquipment');
|
||||||
@@ -111,7 +107,7 @@ describe('shared.ops.buyMarketGear', () => {
|
|||||||
user.stats.gp = 31;
|
user.stats.gp = 31;
|
||||||
user.achievements.purchasedEquipment = true;
|
user.achievements.purchasedEquipment = true;
|
||||||
|
|
||||||
await buyGear(user, { params: { key: 'armor_warrior_1' } }, analytics);
|
await buyGear(user, { params: { key: 'armor_warrior_1' } });
|
||||||
|
|
||||||
expect(user.addAchievement).to.not.be.called;
|
expect(user.addAchievement).to.not.be.called;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import { errorMessage } from '../../../../website/common/script/libs/errorMessag
|
|||||||
|
|
||||||
describe('shared.ops.buyMysterySet', () => {
|
describe('shared.ops.buyMysterySet', () => {
|
||||||
let user;
|
let user;
|
||||||
const analytics = { track () {} };
|
|
||||||
let clock;
|
let clock;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -27,11 +26,9 @@ describe('shared.ops.buyMysterySet', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
sinon.stub(analytics, 'track');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
analytics.track.restore();
|
|
||||||
if (clock) {
|
if (clock) {
|
||||||
clock.restore();
|
clock.restore();
|
||||||
}
|
}
|
||||||
@@ -93,7 +90,7 @@ describe('shared.ops.buyMysterySet', () => {
|
|||||||
context('successful purchases', () => {
|
context('successful purchases', () => {
|
||||||
it('buys Steampunk Accessories Set', async () => {
|
it('buys Steampunk Accessories Set', async () => {
|
||||||
user.purchased.plan.consecutive.trinkets = 1;
|
user.purchased.plan.consecutive.trinkets = 1;
|
||||||
await buyMysterySet(user, { params: { key: '301404' } }, analytics);
|
await buyMysterySet(user, { params: { key: '301404' } });
|
||||||
|
|
||||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
|
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
|
||||||
expect(user.items.gear.owned).to.have.property('weapon_warrior_0', true);
|
expect(user.items.gear.owned).to.have.property('weapon_warrior_0', true);
|
||||||
@@ -106,7 +103,7 @@ describe('shared.ops.buyMysterySet', () => {
|
|||||||
it('buys mystery set if it is available', async () => {
|
it('buys mystery set if it is available', async () => {
|
||||||
clock = sinon.useFakeTimers(new Date('2024-01-16'));
|
clock = sinon.useFakeTimers(new Date('2024-01-16'));
|
||||||
user.purchased.plan.consecutive.trinkets = 1;
|
user.purchased.plan.consecutive.trinkets = 1;
|
||||||
await buyMysterySet(user, { params: { key: '201601' } }, analytics);
|
await buyMysterySet(user, { params: { key: '201601' } });
|
||||||
|
|
||||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
|
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
|
||||||
expect(user.items.gear.owned).to.have.property('shield_mystery_201601', true);
|
expect(user.items.gear.owned).to.have.property('shield_mystery_201601', true);
|
||||||
|
|||||||
@@ -12,10 +12,9 @@ describe('shared.ops.buyQuestGems', () => {
|
|||||||
let user;
|
let user;
|
||||||
let clock;
|
let clock;
|
||||||
const goldPoints = 40;
|
const goldPoints = 40;
|
||||||
const analytics = { track () {} };
|
|
||||||
|
|
||||||
async function buyQuest (_user, _req, _analytics) {
|
async function buyQuest (_user, _req) {
|
||||||
const buyOp = new BuyQuestWithGemOperation(_user, _req, _analytics);
|
const buyOp = new BuyQuestWithGemOperation(_user, _req);
|
||||||
|
|
||||||
return buyOp.purchase();
|
return buyOp.purchase();
|
||||||
}
|
}
|
||||||
@@ -25,13 +24,11 @@ describe('shared.ops.buyQuestGems', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
sinon.stub(analytics, 'track');
|
|
||||||
sinon.spy(pinnedGearUtils, 'removeItemByPath');
|
sinon.spy(pinnedGearUtils, 'removeItemByPath');
|
||||||
clock = sinon.useFakeTimers(new Date('2024-01-16'));
|
clock = sinon.useFakeTimers(new Date('2024-01-16'));
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
analytics.track.restore();
|
|
||||||
pinnedGearUtils.removeItemByPath.restore();
|
pinnedGearUtils.removeItemByPath.restore();
|
||||||
clock.restore();
|
clock.restore();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,21 +12,15 @@ import { errorMessage } from '../../../../website/common/script/libs/errorMessag
|
|||||||
|
|
||||||
describe('shared.ops.buyQuest', () => {
|
describe('shared.ops.buyQuest', () => {
|
||||||
let user;
|
let user;
|
||||||
const analytics = { track () {} };
|
|
||||||
|
|
||||||
async function buyQuest (_user, _req, _analytics) {
|
async function buyQuest (_user, _req) {
|
||||||
const buyOp = new BuyQuestWithGoldOperation(_user, _req, _analytics);
|
const buyOp = new BuyQuestWithGoldOperation(_user, _req);
|
||||||
|
|
||||||
return buyOp.purchase();
|
return buyOp.purchase();
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
user = generateUser();
|
user = generateUser();
|
||||||
sinon.stub(analytics, 'track');
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
analytics.track.restore();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('buys a Quest scroll', async () => {
|
it('buys a Quest scroll', async () => {
|
||||||
@@ -35,12 +29,11 @@ describe('shared.ops.buyQuest', () => {
|
|||||||
params: {
|
params: {
|
||||||
key: 'dilatoryDistress1',
|
key: 'dilatoryDistress1',
|
||||||
},
|
},
|
||||||
}, analytics);
|
});
|
||||||
expect(user.items.quests).to.eql({
|
expect(user.items.quests).to.eql({
|
||||||
dilatoryDistress1: 1,
|
dilatoryDistress1: 1,
|
||||||
});
|
});
|
||||||
expect(user.stats.gp).to.equal(5);
|
expect(user.stats.gp).to.equal(5);
|
||||||
expect(analytics.track).to.be.calledOnce;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('if a user\'s count of a quest scroll is negative, it will be reset to 0 before incrementing when they buy a new one.', async () => {
|
it('if a user\'s count of a quest scroll is negative, it will be reset to 0 before incrementing when they buy a new one.', async () => {
|
||||||
@@ -49,10 +42,9 @@ describe('shared.ops.buyQuest', () => {
|
|||||||
user.items.quests[key] = -1;
|
user.items.quests[key] = -1;
|
||||||
await buyQuest(user, {
|
await buyQuest(user, {
|
||||||
params: { key },
|
params: { key },
|
||||||
}, analytics);
|
});
|
||||||
expect(user.items.quests[key]).to.equal(1);
|
expect(user.items.quests[key]).to.equal(1);
|
||||||
expect(user.stats.gp).to.equal(5);
|
expect(user.stats.gp).to.equal(5);
|
||||||
expect(analytics.track).to.be.calledOnce;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('buys a Quest scroll with the right quantity if a string is passed for quantity', async () => {
|
it('buys a Quest scroll with the right quantity if a string is passed for quantity', async () => {
|
||||||
@@ -61,13 +53,13 @@ describe('shared.ops.buyQuest', () => {
|
|||||||
params: {
|
params: {
|
||||||
key: 'dilatoryDistress1',
|
key: 'dilatoryDistress1',
|
||||||
},
|
},
|
||||||
}, analytics);
|
});
|
||||||
await buyQuest(user, {
|
await buyQuest(user, {
|
||||||
params: {
|
params: {
|
||||||
key: 'dilatoryDistress1',
|
key: 'dilatoryDistress1',
|
||||||
},
|
},
|
||||||
quantity: '3',
|
quantity: '3',
|
||||||
}, analytics);
|
});
|
||||||
|
|
||||||
expect(user.items.quests).to.eql({
|
expect(user.items.quests).to.eql({
|
||||||
dilatoryDistress1: 4,
|
dilatoryDistress1: 4,
|
||||||
@@ -82,7 +74,7 @@ describe('shared.ops.buyQuest', () => {
|
|||||||
key: 'dilatoryDistress1',
|
key: 'dilatoryDistress1',
|
||||||
},
|
},
|
||||||
quantity: 'a',
|
quantity: 'a',
|
||||||
}, analytics);
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.equal(i18n.t('invalidQuantity'));
|
expect(err.message).to.equal(i18n.t('invalidQuantity'));
|
||||||
@@ -187,12 +179,11 @@ describe('shared.ops.buyQuest', () => {
|
|||||||
params: {
|
params: {
|
||||||
key: 'dilatoryDistress3',
|
key: 'dilatoryDistress3',
|
||||||
},
|
},
|
||||||
}, analytics);
|
});
|
||||||
|
|
||||||
expect(user.items.quests).to.eql({
|
expect(user.items.quests).to.eql({
|
||||||
dilatoryDistress3: 1,
|
dilatoryDistress3: 1,
|
||||||
});
|
});
|
||||||
expect(user.stats.gp).to.equal(100);
|
expect(user.stats.gp).to.equal(100);
|
||||||
expect(analytics.track).to.be.calledOnce;
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,20 +14,17 @@ import { errorMessage } from '../../../../website/common/script/libs/errorMessag
|
|||||||
describe('shared.ops.buySpecialSpell', () => {
|
describe('shared.ops.buySpecialSpell', () => {
|
||||||
let user;
|
let user;
|
||||||
let clock;
|
let clock;
|
||||||
const analytics = { track () {} };
|
|
||||||
|
|
||||||
async function buySpecialSpell (_user, _req, _analytics) {
|
async function buySpecialSpell (_user, _req) {
|
||||||
const buyOp = new BuySpellOperation(_user, _req, _analytics);
|
const buyOp = new BuySpellOperation(_user, _req);
|
||||||
|
|
||||||
return buyOp.purchase();
|
return buyOp.purchase();
|
||||||
}
|
}
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
user = generateUser();
|
user = generateUser();
|
||||||
sinon.stub(analytics, 'track');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
analytics.track.restore();
|
|
||||||
if (clock) {
|
if (clock) {
|
||||||
clock.restore();
|
clock.restore();
|
||||||
}
|
}
|
||||||
@@ -78,7 +75,7 @@ describe('shared.ops.buySpecialSpell', () => {
|
|||||||
params: {
|
params: {
|
||||||
key: 'thankyou',
|
key: 'thankyou',
|
||||||
},
|
},
|
||||||
}, analytics);
|
});
|
||||||
|
|
||||||
expect(user.stats.gp).to.equal(1);
|
expect(user.stats.gp).to.equal(1);
|
||||||
expect(user.items.special.thankyou).to.equal(1);
|
expect(user.items.special.thankyou).to.equal(1);
|
||||||
@@ -89,7 +86,6 @@ describe('shared.ops.buySpecialSpell', () => {
|
|||||||
expect(message).to.equal(i18n.t('messageBought', {
|
expect(message).to.equal(i18n.t('messageBought', {
|
||||||
itemText: item.text(),
|
itemText: item.text(),
|
||||||
}));
|
}));
|
||||||
expect(analytics.track).to.be.calledOnce;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('buys a limited card when it is available', async () => {
|
it('buys a limited card when it is available', async () => {
|
||||||
@@ -101,7 +97,7 @@ describe('shared.ops.buySpecialSpell', () => {
|
|||||||
params: {
|
params: {
|
||||||
key: 'nye',
|
key: 'nye',
|
||||||
},
|
},
|
||||||
}, analytics);
|
});
|
||||||
|
|
||||||
expect(user.stats.gp).to.equal(1);
|
expect(user.stats.gp).to.equal(1);
|
||||||
expect(user.items.special.nye).to.equal(1);
|
expect(user.items.special.nye).to.equal(1);
|
||||||
@@ -112,7 +108,6 @@ describe('shared.ops.buySpecialSpell', () => {
|
|||||||
expect(message).to.equal(i18n.t('messageBought', {
|
expect(message).to.equal(i18n.t('messageBought', {
|
||||||
itemText: item.text(),
|
itemText: item.text(),
|
||||||
}));
|
}));
|
||||||
expect(analytics.track).to.be.calledOnce;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws an error if the card is not currently available', async () => {
|
it('throws an error if the card is not currently available', async () => {
|
||||||
@@ -140,7 +135,7 @@ describe('shared.ops.buySpecialSpell', () => {
|
|||||||
params: {
|
params: {
|
||||||
key: 'seafoam',
|
key: 'seafoam',
|
||||||
},
|
},
|
||||||
}, analytics);
|
});
|
||||||
|
|
||||||
expect(user.stats.gp).to.equal(1);
|
expect(user.stats.gp).to.equal(1);
|
||||||
expect(user.items.special.seafoam).to.equal(1);
|
expect(user.items.special.seafoam).to.equal(1);
|
||||||
@@ -151,7 +146,6 @@ describe('shared.ops.buySpecialSpell', () => {
|
|||||||
expect(message).to.equal(i18n.t('messageBought', {
|
expect(message).to.equal(i18n.t('messageBought', {
|
||||||
itemText: item.text(),
|
itemText: item.text(),
|
||||||
}));
|
}));
|
||||||
expect(analytics.track).to.be.calledOnce;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws an error if the spell is not currently available', async () => {
|
it('throws an error if the spell is not currently available', async () => {
|
||||||
|
|||||||
@@ -13,21 +13,15 @@ import { BuyHourglassMountOperation } from '../../../../website/common/script/op
|
|||||||
|
|
||||||
describe('common.ops.hourglassPurchase', () => {
|
describe('common.ops.hourglassPurchase', () => {
|
||||||
let user;
|
let user;
|
||||||
const analytics = { track () {} };
|
|
||||||
|
|
||||||
async function buyMount (_user, _req, _analytics) {
|
async function buyMount (_user, _req) {
|
||||||
const buyOp = new BuyHourglassMountOperation(_user, _req, _analytics);
|
const buyOp = new BuyHourglassMountOperation(_user, _req);
|
||||||
|
|
||||||
return buyOp.purchase();
|
return buyOp.purchase();
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
user = generateUser();
|
user = generateUser();
|
||||||
sinon.stub(analytics, 'track');
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
analytics.track.restore();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
context('failure conditions', () => {
|
context('failure conditions', () => {
|
||||||
@@ -131,12 +125,11 @@ describe('common.ops.hourglassPurchase', () => {
|
|||||||
it('buys a pet', async () => {
|
it('buys a pet', async () => {
|
||||||
user.purchased.plan.consecutive.trinkets = 2;
|
user.purchased.plan.consecutive.trinkets = 2;
|
||||||
|
|
||||||
const [, message] = await hourglassPurchase(user, { params: { type: 'pets', key: 'MantisShrimp-Base' } }, analytics);
|
const [, message] = await hourglassPurchase(user, { params: { type: 'pets', key: 'MantisShrimp-Base' } });
|
||||||
|
|
||||||
expect(message).to.eql(i18n.t('hourglassPurchase'));
|
expect(message).to.eql(i18n.t('hourglassPurchase'));
|
||||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
|
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
|
||||||
expect(user.items.pets).to.eql({ 'MantisShrimp-Base': 5 });
|
expect(user.items.pets).to.eql({ 'MantisShrimp-Base': 5 });
|
||||||
expect(analytics.track).to.be.calledOnce;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('buys a mount', async () => {
|
it('buys a mount', async () => {
|
||||||
|
|||||||
@@ -17,20 +17,17 @@ describe('shared.ops.purchase', () => {
|
|||||||
let user;
|
let user;
|
||||||
let clock;
|
let clock;
|
||||||
const goldPoints = 40;
|
const goldPoints = 40;
|
||||||
const analytics = { track () {} };
|
|
||||||
|
|
||||||
before(() => {
|
before(() => {
|
||||||
user = generateUser({ 'stats.class': 'rogue' });
|
user = generateUser({ 'stats.class': 'rogue' });
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
sinon.stub(analytics, 'track');
|
|
||||||
sinon.spy(pinnedGearUtils, 'removeItemByPath');
|
sinon.spy(pinnedGearUtils, 'removeItemByPath');
|
||||||
clock = sandbox.useFakeTimers(new Date('2024-01-10'));
|
clock = sandbox.useFakeTimers(new Date('2024-01-10'));
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
analytics.track.restore();
|
|
||||||
pinnedGearUtils.removeItemByPath.restore();
|
pinnedGearUtils.removeItemByPath.restore();
|
||||||
clock.restore();
|
clock.restore();
|
||||||
});
|
});
|
||||||
@@ -187,11 +184,10 @@ describe('shared.ops.purchase', () => {
|
|||||||
const type = 'eggs';
|
const type = 'eggs';
|
||||||
const key = 'Wolf';
|
const key = 'Wolf';
|
||||||
|
|
||||||
await purchase(user, { params: { type, key } }, analytics);
|
await purchase(user, { params: { type, key } });
|
||||||
|
|
||||||
expect(user.items[type][key]).to.equal(1);
|
expect(user.items[type][key]).to.equal(1);
|
||||||
expect(pinnedGearUtils.removeItemByPath.notCalled).to.equal(true);
|
expect(pinnedGearUtils.removeItemByPath.notCalled).to.equal(true);
|
||||||
expect(analytics.track).to.be.calledOnce;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('purchases hatchingPotions', async () => {
|
it('purchases hatchingPotions', async () => {
|
||||||
@@ -332,7 +328,7 @@ describe('shared.ops.purchase', () => {
|
|||||||
const key = 'Wolf';
|
const key = 'Wolf';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await purchase(user, { params: { type, key }, quantity: 'jamboree' }, analytics);
|
await purchase(user, { params: { type, key }, quantity: 'jamboree' });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.equal(i18n.t('invalidQuantity'));
|
expect(err.message).to.equal(i18n.t('invalidQuantity'));
|
||||||
@@ -345,7 +341,7 @@ describe('shared.ops.purchase', () => {
|
|||||||
user.balance = 10;
|
user.balance = 10;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await purchase(user, { params: { type, key }, quantity: -2 }, analytics);
|
await purchase(user, { params: { type, key }, quantity: -2 });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.equal(i18n.t('invalidQuantity'));
|
expect(err.message).to.equal(i18n.t('invalidQuantity'));
|
||||||
@@ -358,7 +354,7 @@ describe('shared.ops.purchase', () => {
|
|||||||
user.balance = 10;
|
user.balance = 10;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await purchase(user, { params: { type, key }, quantity: 2.9 }, analytics);
|
await purchase(user, { params: { type, key }, quantity: 2.9 });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.equal(i18n.t('invalidQuantity'));
|
expect(err.message).to.equal(i18n.t('invalidQuantity'));
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ function _requestMaker (user, method, additionalSets = {}) {
|
|||||||
|| route.indexOf('/paypal') === 0
|
|| route.indexOf('/paypal') === 0
|
||||||
|| route.indexOf('/amazon') === 0
|
|| route.indexOf('/amazon') === 0
|
||||||
|| route.indexOf('/stripe') === 0
|
|| route.indexOf('/stripe') === 0
|
||||||
|| route.indexOf('/analytics') === 0
|
|
||||||
) {
|
) {
|
||||||
url += `${route}`;
|
url += `${route}`;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -198,7 +198,6 @@ import dailyIcon from '@/assets/svg/daily.svg?raw';
|
|||||||
import todoIcon from '@/assets/svg/todo.svg?raw';
|
import todoIcon from '@/assets/svg/todo.svg?raw';
|
||||||
import rewardIcon from '@/assets/svg/reward.svg?raw';
|
import rewardIcon from '@/assets/svg/reward.svg?raw';
|
||||||
|
|
||||||
import * as Analytics from '@/libs/analytics';
|
|
||||||
import { mapState } from '@/libs/store';
|
import { mapState } from '@/libs/store';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -438,14 +437,6 @@ export default {
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
changeMirrorPreference (newVal) {
|
changeMirrorPreference (newVal) {
|
||||||
Analytics.track({
|
|
||||||
eventName: 'mirror tasks',
|
|
||||||
eventAction: 'mirror tasks',
|
|
||||||
eventCategory: 'behavior',
|
|
||||||
hitType: 'event',
|
|
||||||
mirror: newVal,
|
|
||||||
group: this.group._id,
|
|
||||||
}, { trackOnClient: true });
|
|
||||||
const groupsToMirror = this.user.preferences.tasks.mirrorGroupTasks || [];
|
const groupsToMirror = this.user.preferences.tasks.mirrorGroupTasks || [];
|
||||||
if (newVal) { // we're turning copy ON for this group
|
if (newVal) { // we're turning copy ON for this group
|
||||||
groupsToMirror.push(this.group._id);
|
groupsToMirror.push(this.group._id);
|
||||||
|
|||||||
@@ -240,7 +240,6 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from '@/libs/store';
|
import { mapState } from '@/libs/store';
|
||||||
import * as Analytics from '@/libs/analytics';
|
|
||||||
import notifications from '@/mixins/notifications';
|
import notifications from '@/mixins/notifications';
|
||||||
import closeX from '../ui/closeX';
|
import closeX from '../ui/closeX';
|
||||||
|
|
||||||
@@ -276,11 +275,6 @@ export default {
|
|||||||
this.$store.state.party.data = party;
|
this.$store.state.party.data = party;
|
||||||
this.user.party._id = party._id;
|
this.user.party._id = party._id;
|
||||||
|
|
||||||
Analytics.updateUser({
|
|
||||||
partyID: party._id,
|
|
||||||
partySize: 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$root.$emit('bv::hide::modal', 'create-party-modal');
|
this.$root.$emit('bv::hide::modal', 'create-party-modal');
|
||||||
await this.$router.push('/party');
|
await this.$router.push('/party');
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -314,7 +314,6 @@ import extend from 'lodash/extend';
|
|||||||
import groupUtilities from '@/mixins/groupsUtilities';
|
import groupUtilities from '@/mixins/groupsUtilities';
|
||||||
import styleHelper from '@/mixins/styleHelper';
|
import styleHelper from '@/mixins/styleHelper';
|
||||||
import { mapGetters } from '@/libs/store';
|
import { mapGetters } from '@/libs/store';
|
||||||
import * as Analytics from '@/libs/analytics';
|
|
||||||
import participantListModal from './participantListModal';
|
import participantListModal from './participantListModal';
|
||||||
import groupFormModal from './groupFormModal';
|
import groupFormModal from './groupFormModal';
|
||||||
import groupGemsModal from '@/components/groups/groupGemsModal';
|
import groupGemsModal from '@/components/groups/groupGemsModal';
|
||||||
@@ -560,7 +559,6 @@ export default {
|
|||||||
|
|
||||||
if (this.isParty) {
|
if (this.isParty) {
|
||||||
data.type = 'party';
|
data.type = 'party';
|
||||||
Analytics.updateUser({ partySize: null, partyID: null });
|
|
||||||
this.$store.state.partyMembers = [];
|
this.$store.state.partyMembers = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -334,7 +334,6 @@ import orderBy from 'lodash/orderBy';
|
|||||||
import * as quests from '@/../../common/script/content/quests';
|
import * as quests from '@/../../common/script/content/quests';
|
||||||
import getItemInfo from '@/../../common/script/libs/getItemInfo';
|
import getItemInfo from '@/../../common/script/libs/getItemInfo';
|
||||||
import { mapState } from '@/libs/store';
|
import { mapState } from '@/libs/store';
|
||||||
import * as Analytics from '@/libs/analytics';
|
|
||||||
|
|
||||||
import navigationBack from '@/assets/svg/navigation_back.svg?raw';
|
import navigationBack from '@/assets/svg/navigation_back.svg?raw';
|
||||||
import questDialogContent from '../shops/quests/questDialogContent';
|
import questDialogContent from '../shops/quests/questDialogContent';
|
||||||
@@ -421,11 +420,6 @@ export default {
|
|||||||
async questInit () {
|
async questInit () {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
Analytics.updateUser({
|
|
||||||
partyID: this.group._id,
|
|
||||||
partySize: this.group.memberCount,
|
|
||||||
});
|
|
||||||
|
|
||||||
const groupId = this.group._id || this.user.party._id;
|
const groupId = this.group._id || this.user.party._id;
|
||||||
|
|
||||||
const key = this.selectedQuest;
|
const key = this.selectedQuest;
|
||||||
|
|||||||
@@ -123,7 +123,6 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import orderBy from 'lodash/orderBy';
|
import orderBy from 'lodash/orderBy';
|
||||||
import * as Analytics from '@/libs/analytics';
|
|
||||||
import { mapGetters, mapActions } from '@/libs/store';
|
import { mapGetters, mapActions } from '@/libs/store';
|
||||||
import MemberDetails from '../memberDetails';
|
import MemberDetails from '../memberDetails';
|
||||||
import createPartyModal from '../groups/createPartyModal';
|
import createPartyModal from '../groups/createPartyModal';
|
||||||
@@ -236,22 +235,8 @@ export default {
|
|||||||
},
|
},
|
||||||
async createOrInviteParty () {
|
async createOrInviteParty () {
|
||||||
if (this.user.party._id) {
|
if (this.user.party._id) {
|
||||||
await Analytics.track({
|
|
||||||
eventName: 'Header Party CTA',
|
|
||||||
eventAction: 'Header Party CTA',
|
|
||||||
eventCategory: 'behavior',
|
|
||||||
hitType: 'event',
|
|
||||||
state: 'Find Party Members',
|
|
||||||
});
|
|
||||||
this.$router.push('/looking-for-party');
|
this.$router.push('/looking-for-party');
|
||||||
} else {
|
} else {
|
||||||
await Analytics.track({
|
|
||||||
eventName: 'Header Party CTA',
|
|
||||||
eventAction: 'Header Party CTA',
|
|
||||||
eventCategory: 'behavior',
|
|
||||||
hitType: 'event',
|
|
||||||
state: 'Get Started',
|
|
||||||
});
|
|
||||||
this.$root.$emit('bv::show::modal', 'create-party-modal');
|
this.$root.$emit('bv::show::modal', 'create-party-modal');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -114,7 +114,6 @@ import { mapState } from '@/libs/store';
|
|||||||
import notifications from '@/mixins/notifications';
|
import notifications from '@/mixins/notifications';
|
||||||
import guide from '@/mixins/guide';
|
import guide from '@/mixins/guide';
|
||||||
import { CONSTANTS, setLocalSetting } from '@/libs/userlocalManager';
|
import { CONSTANTS, setLocalSetting } from '@/libs/userlocalManager';
|
||||||
import * as Analytics from '@/libs/analytics';
|
|
||||||
|
|
||||||
import yesterdailyModal from './tasks/yesterdailyModal';
|
import yesterdailyModal from './tasks/yesterdailyModal';
|
||||||
import newStuff from './news/modal';
|
import newStuff from './news/modal';
|
||||||
@@ -648,15 +647,6 @@ export default {
|
|||||||
// Reset daily analytics actions
|
// Reset daily analytics actions
|
||||||
setLocalSetting(CONSTANTS.keyConstants.TASKS_SCORED_COUNT, 0);
|
setLocalSetting(CONSTANTS.keyConstants.TASKS_SCORED_COUNT, 0);
|
||||||
setLocalSetting(CONSTANTS.keyConstants.TASKS_CREATED_COUNT, 0);
|
setLocalSetting(CONSTANTS.keyConstants.TASKS_CREATED_COUNT, 0);
|
||||||
} else {
|
|
||||||
// Note a failed cron event, for our records and investigation
|
|
||||||
Analytics.track({
|
|
||||||
eventName: 'cron failed',
|
|
||||||
eventAction: 'cron failed',
|
|
||||||
eventCategory: 'behavior',
|
|
||||||
hitType: 'event',
|
|
||||||
responseCode: response.status,
|
|
||||||
}, { trackOnClient: true });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync
|
// Sync
|
||||||
|
|||||||
@@ -433,9 +433,6 @@ import lockableLabel from '@/components/tasks/modal-controls/lockableLabel';
|
|||||||
import notificationsMixin from '@/mixins/notifications';
|
import notificationsMixin from '@/mixins/notifications';
|
||||||
import paymentsMixin from '@/mixins/payments';
|
import paymentsMixin from '@/mixins/payments';
|
||||||
|
|
||||||
// analytics
|
|
||||||
import * as Analytics from '@/libs/analytics';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
selectTranslatedArray,
|
selectTranslatedArray,
|
||||||
@@ -536,16 +533,6 @@ export default {
|
|||||||
this.close();
|
this.close();
|
||||||
},
|
},
|
||||||
submit () {
|
submit () {
|
||||||
if (this.paymentData.group && !this.paymentData.newGroup) {
|
|
||||||
Analytics.track({
|
|
||||||
hitType: 'event',
|
|
||||||
eventName: 'group plan upgrade',
|
|
||||||
eventAction: 'group plan upgrade',
|
|
||||||
eventCategory: 'behavior',
|
|
||||||
demographics: this.upgradedGroup.demographics,
|
|
||||||
type: this.paymentData.group.type,
|
|
||||||
}, { trackOnClient: true });
|
|
||||||
}
|
|
||||||
this.paymentData = {};
|
this.paymentData = {};
|
||||||
this.$root.$emit('bv::hide::modal', 'payments-success-modal');
|
this.$root.$emit('bv::hide::modal', 'payments-success-modal');
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { mapState } from '@/libs/store';
|
|||||||
import encodeParams from '@/libs/encodeParams';
|
import encodeParams from '@/libs/encodeParams';
|
||||||
import notificationsMixin from '@/mixins/notifications';
|
import notificationsMixin from '@/mixins/notifications';
|
||||||
import { CONSTANTS, setLocalSetting } from '@/libs/userlocalManager';
|
import { CONSTANTS, setLocalSetting } from '@/libs/userlocalManager';
|
||||||
import * as Analytics from '@/libs/analytics';
|
|
||||||
|
|
||||||
const { STRIPE_PUB_KEY } = import.meta.env;
|
const { STRIPE_PUB_KEY } = import.meta.env;
|
||||||
|
|
||||||
@@ -207,16 +206,6 @@ export default {
|
|||||||
alert(`Error while redirecting to Stripe: ${checkoutSessionResult.error.message}`);
|
alert(`Error while redirecting to Stripe: ${checkoutSessionResult.error.message}`);
|
||||||
throw checkoutSessionResult.error;
|
throw checkoutSessionResult.error;
|
||||||
}
|
}
|
||||||
if (paymentType === 'groupPlan') {
|
|
||||||
Analytics.track({
|
|
||||||
hitType: 'event',
|
|
||||||
eventName: 'group plan create',
|
|
||||||
eventAction: 'group plan create',
|
|
||||||
eventCategory: 'behavior',
|
|
||||||
demographics: appState.newGroup.demographics,
|
|
||||||
type: appState.newGroup.type,
|
|
||||||
}, { trackOnClient: true });
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error while redirecting to Stripe', err); // eslint-disable-line
|
console.error('Error while redirecting to Stripe', err); // eslint-disable-line
|
||||||
alert(`Error while redirecting to Stripe: ${err.message}`);
|
alert(`Error while redirecting to Stripe: ${err.message}`);
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import Vue from 'vue';
|
|||||||
import scoreTask from '@/../../common/script/ops/scoreTask';
|
import scoreTask from '@/../../common/script/ops/scoreTask';
|
||||||
import notifications from './notifications';
|
import notifications from './notifications';
|
||||||
import { mapState } from '@/libs/store';
|
import { mapState } from '@/libs/store';
|
||||||
import * as Analytics from '@/libs/analytics';
|
|
||||||
import { CONSTANTS, getLocalSetting, setLocalSetting } from '@/libs/userlocalManager';
|
import { CONSTANTS, getLocalSetting, setLocalSetting } from '@/libs/userlocalManager';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -58,15 +57,6 @@ export default {
|
|||||||
|
|
||||||
const tasksScoredCount = getLocalSetting(CONSTANTS.keyConstants.TASKS_SCORED_COUNT);
|
const tasksScoredCount = getLocalSetting(CONSTANTS.keyConstants.TASKS_SCORED_COUNT);
|
||||||
if (!tasksScoredCount || tasksScoredCount < 2) {
|
if (!tasksScoredCount || tasksScoredCount < 2) {
|
||||||
Analytics.track({
|
|
||||||
eventName: 'task scored',
|
|
||||||
eventAction: 'task scored',
|
|
||||||
eventCategory: 'behavior',
|
|
||||||
hitType: 'event',
|
|
||||||
uuid: user._id,
|
|
||||||
taskType: task.type,
|
|
||||||
direction,
|
|
||||||
}, { trackOnClient: true });
|
|
||||||
if (!tasksScoredCount) {
|
if (!tasksScoredCount) {
|
||||||
setLocalSetting(CONSTANTS.keyConstants.TASKS_SCORED_COUNT, 1);
|
setLocalSetting(CONSTANTS.keyConstants.TASKS_SCORED_COUNT, 1);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -130,7 +130,6 @@ import PrivacyBanner from '@/components/header/banners/privacy';
|
|||||||
import AppFooter from '@/components/appFooter';
|
import AppFooter from '@/components/appFooter';
|
||||||
import notificationsDisplay from '@/components/notifications';
|
import notificationsDisplay from '@/components/notifications';
|
||||||
import { mapState } from '@/libs/store';
|
import { mapState } from '@/libs/store';
|
||||||
import * as Analytics from '@/libs/analytics';
|
|
||||||
import BuyModal from '@/components/shops/buyModal.vue';
|
import BuyModal from '@/components/shops/buyModal.vue';
|
||||||
import SelectMembersModal from '@/components/selectMembersModal.vue';
|
import SelectMembersModal from '@/components/selectMembersModal.vue';
|
||||||
import notifications from '@/mixins/notifications';
|
import notifications from '@/mixins/notifications';
|
||||||
@@ -276,7 +275,6 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Analytics.updateUser();
|
|
||||||
return this.loadAllTranslations();
|
return this.loadAllTranslations();
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.$store.state.isUserLoaded = true;
|
this.$store.state.isUserLoaded = true;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import VueRouter from 'vue-router';
|
import VueRouter from 'vue-router';
|
||||||
import * as Analytics from '@/libs/analytics';
|
|
||||||
import getStore from '@/store';
|
import getStore from '@/store';
|
||||||
import handleRedirect from './handleRedirect';
|
import handleRedirect from './handleRedirect';
|
||||||
|
|
||||||
@@ -318,15 +317,6 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
router.app.$root.$emit('update-party');
|
router.app.$root.$emit('update-party');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (to.name === 'lookingForParty') {
|
|
||||||
Analytics.track({
|
|
||||||
hitType: 'event',
|
|
||||||
eventName: 'View Find Members',
|
|
||||||
eventAction: 'View Find Members',
|
|
||||||
eventCategory: 'behavior',
|
|
||||||
}, { trackOnClient: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Redirect old guild urls
|
// Redirect old guild urls
|
||||||
if (to.hash.indexOf('#/options/groups/guilds/') !== -1) {
|
if (to.hash.indexOf('#/options/groups/guilds/') !== -1) {
|
||||||
const splits = to.hash.split('/');
|
const splits = to.hash.split('/');
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import * as Analytics from '@/libs/analytics';
|
|
||||||
|
|
||||||
export async function getChat (store, payload) {
|
export async function getChat (store, payload) {
|
||||||
const response = await axios.get(`/api/v4/groups/${payload.groupId}/chat?limit=400`);
|
const response = await axios.get(`/api/v4/groups/${payload.groupId}/chat?limit=400`);
|
||||||
@@ -17,13 +16,6 @@ export async function postChat (store, payload) {
|
|||||||
url += `?previousMsg=${payload.previousMsg}`;
|
url += `?previousMsg=${payload.previousMsg}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (group.type === 'party') {
|
|
||||||
Analytics.updateUser({
|
|
||||||
partyID: group.id,
|
|
||||||
partySize: group.memberCount,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await axios.post(url, {
|
const response = await axios.post(url, {
|
||||||
message: payload.message,
|
message: payload.message,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import omit from 'lodash/omit';
|
import omit from 'lodash/omit';
|
||||||
import findIndex from 'lodash/findIndex';
|
import findIndex from 'lodash/findIndex';
|
||||||
import * as Analytics from '@/libs/analytics';
|
|
||||||
import { loadAsyncResource } from '@/libs/asyncResource';
|
import { loadAsyncResource } from '@/libs/asyncResource';
|
||||||
|
|
||||||
export async function getPublicGuilds (store, payload) {
|
export async function getPublicGuilds (store, payload) {
|
||||||
@@ -74,7 +73,6 @@ export async function join (store, payload) {
|
|||||||
if (invitationI !== -1) invitations.parties.splice(invitationI, 1);
|
if (invitationI !== -1) invitations.parties.splice(invitationI, 1);
|
||||||
|
|
||||||
user.party._id = groupId;
|
user.party._id = groupId;
|
||||||
Analytics.updateUser({ partyID: groupId });
|
|
||||||
// load the party members so that they get shown in the header
|
// load the party members so that they get shown in the header
|
||||||
store.dispatch('party:getMembers');
|
store.dispatch('party:getMembers');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import * as shops from './shops';
|
|||||||
import * as snackbars from './snackbars';
|
import * as snackbars from './snackbars';
|
||||||
import * as worldState from './worldState';
|
import * as worldState from './worldState';
|
||||||
import * as news from './news';
|
import * as news from './news';
|
||||||
import * as analytics from './analytics';
|
|
||||||
import * as faq from './faq';
|
import * as faq from './faq';
|
||||||
import * as blockers from './blockers';
|
import * as blockers from './blockers';
|
||||||
|
|
||||||
@@ -44,7 +43,6 @@ const actions = flattenAndNamespace({
|
|||||||
snackbars,
|
snackbars,
|
||||||
worldState,
|
worldState,
|
||||||
news,
|
news,
|
||||||
analytics,
|
|
||||||
faq,
|
faq,
|
||||||
blockers,
|
blockers,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,26 +1,6 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import * as Analytics from '@/libs/analytics';
|
|
||||||
|
|
||||||
// export async function initQuest (store) {
|
|
||||||
// }
|
|
||||||
|
|
||||||
export async function sendAction (store, payload) { // eslint-disable-line import/prefer-default-export, max-len
|
export async function sendAction (store, payload) { // eslint-disable-line import/prefer-default-export, max-len
|
||||||
// @TODO: Maybe move this to server
|
|
||||||
let partyData = {};
|
|
||||||
if (store.state.party && store.state.party.data) {
|
|
||||||
partyData = {
|
|
||||||
partyID: store.state.party.data._id,
|
|
||||||
partySize: store.state.party.data.memberCount,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
partyData = {
|
|
||||||
partyID: store.state.user.data.party._id,
|
|
||||||
partySize: store.state.partyMembers.data.length,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Analytics.updateUser(partyData);
|
|
||||||
|
|
||||||
const response = await axios.post(`/api/v4/groups/${payload.groupId}/${payload.action}`);
|
const response = await axios.post(`/api/v4/groups/${payload.groupId}/${payload.action}`);
|
||||||
|
|
||||||
// @TODO: Update user?
|
// @TODO: Update user?
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import Vue from 'vue';
|
|||||||
import compact from 'lodash/compact';
|
import compact from 'lodash/compact';
|
||||||
import omit from 'lodash/omit';
|
import omit from 'lodash/omit';
|
||||||
import { loadAsyncResource } from '@/libs/asyncResource';
|
import { loadAsyncResource } from '@/libs/asyncResource';
|
||||||
import * as Analytics from '@/libs/analytics';
|
|
||||||
import { CONSTANTS, getLocalSetting, setLocalSetting } from '@/libs/userlocalManager';
|
import { CONSTANTS, getLocalSetting, setLocalSetting } from '@/libs/userlocalManager';
|
||||||
|
|
||||||
export function fetchUserTasks (store, options = {}) {
|
export function fetchUserTasks (store, options = {}) {
|
||||||
@@ -112,15 +111,6 @@ export async function create (store, createdTask) {
|
|||||||
}
|
}
|
||||||
const tasksCreatedCount = getLocalSetting(CONSTANTS.keyConstants.TASKS_CREATED_COUNT);
|
const tasksCreatedCount = getLocalSetting(CONSTANTS.keyConstants.TASKS_CREATED_COUNT);
|
||||||
if (!tasksCreatedCount || tasksCreatedCount < 2) {
|
if (!tasksCreatedCount || tasksCreatedCount < 2) {
|
||||||
const uuid = store.state.user.data._id;
|
|
||||||
Analytics.track({
|
|
||||||
eventName: 'task created',
|
|
||||||
eventAction: 'task created',
|
|
||||||
eventCategory: 'behavior',
|
|
||||||
hitType: 'event',
|
|
||||||
uuid,
|
|
||||||
taskType: taskRes.type,
|
|
||||||
}, { trackOnClient: true });
|
|
||||||
if (!tasksCreatedCount) {
|
if (!tasksCreatedCount) {
|
||||||
setLocalSetting(CONSTANTS.keyConstants.TASKS_CREATED_COUNT, 1);
|
setLocalSetting(CONSTANTS.keyConstants.TASKS_CREATED_COUNT, 1);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -159,10 +159,6 @@ export default defineConfig({
|
|||||||
target: DEV_BASE_URL,
|
target: DEV_BASE_URL,
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
},
|
},
|
||||||
'^/analytics': {
|
|
||||||
target: DEV_BASE_URL,
|
|
||||||
changeOrigin: true,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -3,10 +3,8 @@ import isFunction from 'lodash/isFunction';
|
|||||||
import min from 'lodash/min';
|
import min from 'lodash/min';
|
||||||
import reduce from 'lodash/reduce';
|
import reduce from 'lodash/reduce';
|
||||||
import filter from 'lodash/filter';
|
import filter from 'lodash/filter';
|
||||||
import pick from 'lodash/pick';
|
|
||||||
import pickBy from 'lodash/pickBy';
|
import pickBy from 'lodash/pickBy';
|
||||||
import size from 'lodash/size';
|
import size from 'lodash/size';
|
||||||
import moment from 'moment';
|
|
||||||
import content from '../content/index';
|
import content from '../content/index';
|
||||||
import i18n from '../i18n';
|
import i18n from '../i18n';
|
||||||
import { daysSince } from '../cron';
|
import { daysSince } from '../cron';
|
||||||
@@ -28,7 +26,7 @@ function trueRandom () {
|
|||||||
return Math.random();
|
return Math.random();
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function randomDrop (user, options, req = {}, analytics) {
|
export default function randomDrop (user, options, req = {}) {
|
||||||
let acceptableDrops;
|
let acceptableDrops;
|
||||||
let drop;
|
let drop;
|
||||||
let dropMultiplier;
|
let dropMultiplier;
|
||||||
@@ -157,15 +155,5 @@ export default function randomDrop (user, options, req = {}, analytics) {
|
|||||||
user._tmp.drop = drop;
|
user._tmp.drop = drop;
|
||||||
user.items.lastDrop.date = Number(new Date());
|
user.items.lastDrop.date = Number(new Date());
|
||||||
user.items.lastDrop.count += 1;
|
user.items.lastDrop.count += 1;
|
||||||
|
|
||||||
if (analytics && moment().diff(user.auth.timestamps.created, 'days') < 7) {
|
|
||||||
analytics.track('dropped item', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
itemKey: drop.key,
|
|
||||||
category: 'behavior',
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import pick from 'lodash/pick';
|
|
||||||
|
|
||||||
export function hasCompletedOnboarding (user) {
|
export function hasCompletedOnboarding (user) {
|
||||||
return (
|
return (
|
||||||
user.achievements.createdTask === true
|
user.achievements.createdTask === true
|
||||||
@@ -16,18 +14,9 @@ export function onOnboardingComplete (user) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add notification and awards (server)
|
// Add notification and awards (server)
|
||||||
export function checkOnboardingStatus (user, req, analytics) {
|
export function checkOnboardingStatus (user) {
|
||||||
if (hasCompletedOnboarding(user) && user.addNotification) {
|
if (hasCompletedOnboarding(user) && user.addNotification) {
|
||||||
user.addNotification('ONBOARDING_COMPLETE');
|
user.addNotification('ONBOARDING_COMPLETE');
|
||||||
if (analytics) {
|
|
||||||
analytics.track('onboarding complete', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
hitType: 'event',
|
|
||||||
category: 'behavior',
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
onOnboardingComplete(user);
|
onOnboardingComplete(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
/* eslint-disable max-classes-per-file */
|
/* eslint-disable max-classes-per-file */
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import merge from 'lodash/merge';
|
|
||||||
import pick from 'lodash/pick';
|
|
||||||
import i18n from '../../i18n';
|
import i18n from '../../i18n';
|
||||||
import {
|
import {
|
||||||
NotAuthorized,
|
NotAuthorized,
|
||||||
@@ -15,12 +13,10 @@ export class AbstractBuyOperation {
|
|||||||
/**
|
/**
|
||||||
* @param {User} user - the User-Object
|
* @param {User} user - the User-Object
|
||||||
* @param {Request} req - the Request-Object
|
* @param {Request} req - the Request-Object
|
||||||
* @param {analytics} analytics
|
|
||||||
*/
|
*/
|
||||||
constructor (user, req, analytics) {
|
constructor (user, req) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.req = req || {};
|
this.req = req || {};
|
||||||
this.analytics = analytics;
|
|
||||||
|
|
||||||
const quantity = get(req, 'quantity');
|
const quantity = get(req, 'quantity');
|
||||||
|
|
||||||
@@ -87,10 +83,6 @@ export class AbstractBuyOperation {
|
|||||||
throw new NotImplementedError('executeChanges');
|
throw new NotImplementedError('executeChanges');
|
||||||
}
|
}
|
||||||
|
|
||||||
analyticsData () { // eslint-disable-line class-methods-use-this
|
|
||||||
throw new NotImplementedError('sendToAnalytics');
|
|
||||||
}
|
|
||||||
|
|
||||||
async purchase () {
|
async purchase () {
|
||||||
if (!this.multiplePurchaseAllowed() && this.quantity > 1) {
|
if (!this.multiplePurchaseAllowed() && this.quantity > 1) {
|
||||||
throw new NotAuthorized(this.i18n('messageNotAbleToBuyInBulk'));
|
throw new NotAuthorized(this.i18n('messageNotAbleToBuyInBulk'));
|
||||||
@@ -98,34 +90,10 @@ export class AbstractBuyOperation {
|
|||||||
|
|
||||||
this.extractAndValidateParams(this.user, this.req);
|
this.extractAndValidateParams(this.user, this.req);
|
||||||
|
|
||||||
const resultObj = await this.executeChanges(this.user, this.item, this.req, this.analytics);
|
const resultObj = await this.executeChanges(this.user, this.item, this.req);
|
||||||
|
|
||||||
if (this.analytics) {
|
|
||||||
this.sendToAnalytics(this.analyticsData());
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultObj;
|
return resultObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
analyticsLabel () { // eslint-disable-line class-methods-use-this
|
|
||||||
return 'buy';
|
|
||||||
}
|
|
||||||
|
|
||||||
sendToAnalytics (additionalData = {}) {
|
|
||||||
// spread-operator produces an "unexpected token" error
|
|
||||||
const analyticsData = merge(additionalData, {
|
|
||||||
user: pick(this.user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: this.user._id,
|
|
||||||
category: 'behavior',
|
|
||||||
headers: this.req.headers,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.multiplePurchaseAllowed()) {
|
|
||||||
analyticsData.quantityPurchased = this.quantity;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.analytics.track(this.analyticsLabel(), analyticsData);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AbstractGoldItemOperation extends AbstractBuyOperation {
|
export class AbstractGoldItemOperation extends AbstractBuyOperation {
|
||||||
@@ -149,15 +117,6 @@ export class AbstractGoldItemOperation extends AbstractBuyOperation {
|
|||||||
|
|
||||||
user.stats.gp -= itemValue * this.quantity;
|
user.stats.gp -= itemValue * this.quantity;
|
||||||
}
|
}
|
||||||
|
|
||||||
analyticsData () {
|
|
||||||
return {
|
|
||||||
itemKey: this.getItemKey(this.item),
|
|
||||||
itemType: this.getItemType(this.item),
|
|
||||||
currency: 'Gold',
|
|
||||||
goldCost: this.getItemValue(this.item),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AbstractGemItemOperation extends AbstractBuyOperation {
|
export class AbstractGemItemOperation extends AbstractBuyOperation {
|
||||||
@@ -179,15 +138,6 @@ export class AbstractGemItemOperation extends AbstractBuyOperation {
|
|||||||
|
|
||||||
await updateUserBalance(user, -(itemValue * this.quantity), 'spend', item.key, item.text());
|
await updateUserBalance(user, -(itemValue * this.quantity), 'spend', item.key, item.text());
|
||||||
}
|
}
|
||||||
|
|
||||||
analyticsData () {
|
|
||||||
return {
|
|
||||||
itemKey: this.getItemKey(this.item),
|
|
||||||
itemType: this.getItemType(this.item),
|
|
||||||
currency: 'Gems',
|
|
||||||
gemCost: this.getItemValue(this.item) * 4,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AbstractHourglassItemOperation extends AbstractBuyOperation {
|
export class AbstractHourglassItemOperation extends AbstractBuyOperation {
|
||||||
@@ -202,11 +152,4 @@ export class AbstractHourglassItemOperation extends AbstractBuyOperation {
|
|||||||
async subtractCurrency (user, item) { // eslint-disable-line class-methods-use-this
|
async subtractCurrency (user, item) { // eslint-disable-line class-methods-use-this
|
||||||
await updateUserHourglasses(user, -1, 'spend', item.key);
|
await updateUserHourglasses(user, -1, 'spend', item.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
analyticsData () {
|
|
||||||
return {
|
|
||||||
itemKey: this.item.key,
|
|
||||||
currency: 'Hourglass',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import { BuyHourglassMountOperation } from './buyMount';
|
|||||||
export default async function buy (
|
export default async function buy (
|
||||||
user,
|
user,
|
||||||
req = {},
|
req = {},
|
||||||
analytics,
|
|
||||||
options = { quantity: 1, hourglass: false },
|
options = { quantity: 1, hourglass: false },
|
||||||
) {
|
) {
|
||||||
const key = get(req, 'params.key');
|
const key = get(req, 'params.key');
|
||||||
@@ -42,35 +41,35 @@ export default async function buy (
|
|||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'armoire': {
|
case 'armoire': {
|
||||||
const buyOp = new BuyArmoireOperation(user, req, analytics);
|
const buyOp = new BuyArmoireOperation(user, req);
|
||||||
|
|
||||||
buyRes = await buyOp.purchase();
|
buyRes = await buyOp.purchase();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'backgrounds':
|
case 'backgrounds':
|
||||||
if (!hourglass) throw new BadRequest(errorMessage('useUnlockForCosmetics'));
|
if (!hourglass) throw new BadRequest(errorMessage('useUnlockForCosmetics'));
|
||||||
buyRes = await hourglassPurchase(user, req, analytics);
|
buyRes = await hourglassPurchase(user, req);
|
||||||
break;
|
break;
|
||||||
case 'mystery':
|
case 'mystery':
|
||||||
buyRes = await buyMysterySet(user, req, analytics);
|
buyRes = await buyMysterySet(user, req);
|
||||||
break;
|
break;
|
||||||
case 'potion': {
|
case 'potion': {
|
||||||
const buyOp = new BuyHealthPotionOperation(user, req, analytics);
|
const buyOp = new BuyHealthPotionOperation(user, req);
|
||||||
|
|
||||||
buyRes = await buyOp.purchase();
|
buyRes = await buyOp.purchase();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'gems': {
|
case 'gems': {
|
||||||
const buyOp = new BuyGemOperation(user, req, analytics);
|
const buyOp = new BuyGemOperation(user, req);
|
||||||
|
|
||||||
buyRes = await buyOp.purchase();
|
buyRes = await buyOp.purchase();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'quests': {
|
case 'quests': {
|
||||||
if (hourglass) {
|
if (hourglass) {
|
||||||
buyRes = await hourglassPurchase(user, req, analytics, quantity);
|
buyRes = await hourglassPurchase(user, req, quantity);
|
||||||
} else {
|
} else {
|
||||||
const buyOp = new BuyQuestWithGemOperation(user, req, analytics);
|
const buyOp = new BuyQuestWithGemOperation(user, req);
|
||||||
|
|
||||||
buyRes = await buyOp.purchase();
|
buyRes = await buyOp.purchase();
|
||||||
}
|
}
|
||||||
@@ -81,36 +80,36 @@ export default async function buy (
|
|||||||
case 'food':
|
case 'food':
|
||||||
case 'gear':
|
case 'gear':
|
||||||
case 'bundles':
|
case 'bundles':
|
||||||
buyRes = await purchaseOp(user, req, analytics);
|
buyRes = await purchaseOp(user, req);
|
||||||
break;
|
break;
|
||||||
case 'mounts': {
|
case 'mounts': {
|
||||||
const buyOp = new BuyHourglassMountOperation(user, req, analytics);
|
const buyOp = new BuyHourglassMountOperation(user, req);
|
||||||
|
|
||||||
buyRes = await buyOp.purchase();
|
buyRes = await buyOp.purchase();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'pets':
|
case 'pets':
|
||||||
if (key === 'Gryphatrice-Jubilant') {
|
if (key === 'Gryphatrice-Jubilant') {
|
||||||
const buyOp = new BuyPetWithGemOperation(user, req, analytics);
|
const buyOp = new BuyPetWithGemOperation(user, req);
|
||||||
buyRes = await buyOp.purchase();
|
buyRes = await buyOp.purchase();
|
||||||
} else {
|
} else {
|
||||||
buyRes = hourglassPurchase(user, req, analytics);
|
buyRes = hourglassPurchase(user, req);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'quest': {
|
case 'quest': {
|
||||||
const buyOp = new BuyQuestWithGoldOperation(user, req, analytics);
|
const buyOp = new BuyQuestWithGoldOperation(user, req);
|
||||||
|
|
||||||
buyRes = await buyOp.purchase();
|
buyRes = await buyOp.purchase();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'special': {
|
case 'special': {
|
||||||
const buyOp = new BuySpellOperation(user, req, analytics);
|
const buyOp = new BuySpellOperation(user, req);
|
||||||
|
|
||||||
buyRes = await buyOp.purchase();
|
buyRes = await buyOp.purchase();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
const buyOp = new BuyMarketGearOperation(user, req, analytics);
|
const buyOp = new BuyMarketGearOperation(user, req);
|
||||||
|
|
||||||
buyRes = await buyOp.purchase();
|
buyRes = await buyOp.purchase();
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -69,19 +69,6 @@ export class BuyArmoireOperation extends AbstractGoldItemOperation { // eslint-d
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
_trackDropAnalytics (user, key) {
|
|
||||||
this.analytics.track(
|
|
||||||
'Enchanted Armoire',
|
|
||||||
{
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
itemKey: key,
|
|
||||||
category: 'behavior',
|
|
||||||
headers: this.req.headers,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_gearResult (user, eligibleEquipment) {
|
_gearResult (user, eligibleEquipment) {
|
||||||
const emptied = eligibleEquipment.length === 1;
|
const emptied = eligibleEquipment.length === 1;
|
||||||
eligibleEquipment.sort();
|
eligibleEquipment.sort();
|
||||||
@@ -105,10 +92,6 @@ export class BuyArmoireOperation extends AbstractGoldItemOperation { // eslint-d
|
|||||||
|
|
||||||
removeItemByPath(user, `gear.flat.${drop.key}`);
|
removeItemByPath(user, `gear.flat.${drop.key}`);
|
||||||
|
|
||||||
if (this.analytics) {
|
|
||||||
this._trackDropAnalytics(user, drop.key);
|
|
||||||
}
|
|
||||||
|
|
||||||
const armoireResp = {
|
const armoireResp = {
|
||||||
type: 'gear',
|
type: 'gear',
|
||||||
dropKey: drop.key,
|
dropKey: drop.key,
|
||||||
@@ -134,9 +117,6 @@ export class BuyArmoireOperation extends AbstractGoldItemOperation { // eslint-d
|
|||||||
user.items.food[drop.key] += 1;
|
user.items.food[drop.key] += 1;
|
||||||
if (user.markModified) user.markModified('items.food');
|
if (user.markModified) user.markModified('items.food');
|
||||||
|
|
||||||
if (this.analytics) {
|
|
||||||
this._trackDropAnalytics(user, drop.key);
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
message: this.i18n('armoireFood', {
|
message: this.i18n('armoireFood', {
|
||||||
image: `<span class="Pet_Food_${drop.key} pull-left"></span>`,
|
image: `<span class="Pet_Food_${drop.key} pull-left"></span>`,
|
||||||
|
|||||||
@@ -70,8 +70,4 @@ export class BuyGemOperation extends AbstractGoldItemOperation { // eslint-disab
|
|||||||
this.i18n('plusGem', { count: this.quantity }),
|
this.i18n('plusGem', { count: this.quantity }),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
analyticsLabel () { // eslint-disable-line class-methods-use-this
|
|
||||||
return 'purchase gems';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export class BuyMarketGearOperation extends AbstractGoldItemOperation { // eslin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
executeChanges (user, item, req, analytics) {
|
executeChanges (user, item, req) {
|
||||||
let message;
|
let message;
|
||||||
|
|
||||||
if (user.preferences.autoEquip) {
|
if (user.preferences.autoEquip) {
|
||||||
@@ -70,7 +70,7 @@ export class BuyMarketGearOperation extends AbstractGoldItemOperation { // eslin
|
|||||||
|
|
||||||
if (!user.achievements.purchasedEquipment && user.addAchievement) {
|
if (!user.achievements.purchasedEquipment && user.addAchievement) {
|
||||||
user.addAchievement('purchasedEquipment');
|
user.addAchievement('purchasedEquipment');
|
||||||
checkOnboardingStatus(user, req, analytics);
|
checkOnboardingStatus(user, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
removePinnedGearAddPossibleNewOnes(user, `gear.flat.${item.key}`, item.key);
|
removePinnedGearAddPossibleNewOnes(user, `gear.flat.${item.key}`, item.key);
|
||||||
|
|||||||
@@ -49,10 +49,4 @@ export class BuyHourglassMountOperation extends AbstractHourglassItemOperation {
|
|||||||
message,
|
message,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
analyticsData () {
|
|
||||||
const data = super.analyticsData();
|
|
||||||
data.itemType = 'mounts';
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import each from 'lodash/each';
|
import each from 'lodash/each';
|
||||||
import pick from 'lodash/pick';
|
|
||||||
import i18n from '../../i18n';
|
import i18n from '../../i18n';
|
||||||
import content from '../../content/index';
|
import content from '../../content/index';
|
||||||
import {
|
import {
|
||||||
@@ -13,7 +12,7 @@ import updateUserHourglasses from '../updateUserHourglasses';
|
|||||||
import { removeItemByPath } from '../pinnedGearUtils';
|
import { removeItemByPath } from '../pinnedGearUtils';
|
||||||
import getItemInfo from '../../libs/getItemInfo';
|
import getItemInfo from '../../libs/getItemInfo';
|
||||||
|
|
||||||
export default async function buyMysterySet (user, req = {}, analytics) {
|
export default async function buyMysterySet (user, req = {}) {
|
||||||
const key = get(req, 'params.key');
|
const key = get(req, 'params.key');
|
||||||
if (!key) throw new BadRequest(errorMessage('missingKeyParam'));
|
if (!key) throw new BadRequest(errorMessage('missingKeyParam'));
|
||||||
|
|
||||||
@@ -35,18 +34,6 @@ export default async function buyMysterySet (user, req = {}, analytics) {
|
|||||||
const itemInfo = getItemInfo(user, 'mystery_set', mysterySet);
|
const itemInfo = getItemInfo(user, 'mystery_set', mysterySet);
|
||||||
removeItemByPath(user, itemInfo.path);
|
removeItemByPath(user, itemInfo.path);
|
||||||
|
|
||||||
if (analytics) {
|
|
||||||
analytics.track('buy', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
itemKey: mysterySet.key,
|
|
||||||
itemType: 'Subscriber Gear',
|
|
||||||
currency: 'Hourglass',
|
|
||||||
category: 'behavior',
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Here we need to trigger vue reactivity through reassign object
|
// Here we need to trigger vue reactivity through reassign object
|
||||||
user.items.gear.owned = {
|
user.items.gear.owned = {
|
||||||
...user.items.gear.owned,
|
...user.items.gear.owned,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import includes from 'lodash/includes';
|
import includes from 'lodash/includes';
|
||||||
import keys from 'lodash/keys';
|
import keys from 'lodash/keys';
|
||||||
import pick from 'lodash/pick';
|
|
||||||
import i18n from '../../i18n';
|
import i18n from '../../i18n';
|
||||||
import content from '../../content/index';
|
import content from '../../content/index';
|
||||||
import {
|
import {
|
||||||
@@ -13,7 +12,7 @@ import getItemInfo from '../../libs/getItemInfo';
|
|||||||
import { removeItemByPath } from '../pinnedGearUtils';
|
import { removeItemByPath } from '../pinnedGearUtils';
|
||||||
import updateUserHourglasses from '../updateUserHourglasses';
|
import updateUserHourglasses from '../updateUserHourglasses';
|
||||||
|
|
||||||
export default async function purchaseHourglass (user, req = {}, analytics, quantity = 1) {
|
export default async function purchaseHourglass (user, req = {}, quantity = 1) {
|
||||||
const key = get(req, 'params.key');
|
const key = get(req, 'params.key');
|
||||||
if (!key) throw new BadRequest(errorMessage('missingKeyParam'));
|
if (!key) throw new BadRequest(errorMessage('missingKeyParam'));
|
||||||
|
|
||||||
@@ -94,18 +93,6 @@ export default async function purchaseHourglass (user, req = {}, analytics, quan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (analytics) {
|
|
||||||
analytics.track('buy', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
itemKey: key,
|
|
||||||
itemType: type,
|
|
||||||
currency: 'Hourglass',
|
|
||||||
category: 'behavior',
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{ items: user.items, purchasedPlanConsecutive: user.purchased.plan.consecutive },
|
{ items: user.items, purchasedPlanConsecutive: user.purchased.plan.consecutive },
|
||||||
i18n.t('hourglassPurchase', req.language),
|
i18n.t('hourglassPurchase', req.language),
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ async function purchaseItem (user, item, price, type, key) {
|
|||||||
|
|
||||||
const acceptedTypes = ['eggs', 'hatchingPotions', 'food', 'gear', 'bundles'];
|
const acceptedTypes = ['eggs', 'hatchingPotions', 'food', 'gear', 'bundles'];
|
||||||
const singlePurchaseTypes = ['gear'];
|
const singlePurchaseTypes = ['gear'];
|
||||||
export default async function purchase (user, req = {}, analytics) {
|
export default async function purchase (user, req = {}) {
|
||||||
const type = get(req.params, 'type');
|
const type = get(req.params, 'type');
|
||||||
const key = get(req.params, 'key');
|
const key = get(req.params, 'key');
|
||||||
|
|
||||||
@@ -130,19 +130,6 @@ export default async function purchase (user, req = {}, analytics) {
|
|||||||
await purchaseItem(user, item, price, type, key);
|
await purchaseItem(user, item, price, type, key);
|
||||||
}
|
}
|
||||||
/* eslint-enable no-await-in-loop */
|
/* eslint-enable no-await-in-loop */
|
||||||
if (analytics) {
|
|
||||||
analytics.track('buy', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
itemKey: key,
|
|
||||||
itemType: type,
|
|
||||||
currency: 'Gems',
|
|
||||||
gemCost: price * 4,
|
|
||||||
quantityPurchased: quantity,
|
|
||||||
category: 'behavior',
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
pick(user, splitWhitespace('items balance')),
|
pick(user, splitWhitespace('items balance')),
|
||||||
|
|||||||
@@ -34,19 +34,18 @@ async function resetClass (user, req = {}) {
|
|||||||
return balanceRemoved;
|
return balanceRemoved;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function changeClass (user, req = {}, analytics) {
|
export default async function changeClass (user, req = {}) {
|
||||||
const klass = get(req, 'query.class');
|
const klass = get(req, 'query.class');
|
||||||
let balanceRemoved = 0;
|
|
||||||
// user.flags.classSelected is set to false after the user paid the 3 gems
|
// user.flags.classSelected is set to false after the user paid the 3 gems
|
||||||
if (user.stats.lvl < 10) {
|
if (user.stats.lvl < 10) {
|
||||||
throw new NotAuthorized(i18n.t('lvl10ChangeClass', req.language));
|
throw new NotAuthorized(i18n.t('lvl10ChangeClass', req.language));
|
||||||
} else if (!klass) {
|
} else if (!klass) {
|
||||||
// if no class is specified, reset points and set user.flags.classSelected to false.
|
// if no class is specified, reset points and set user.flags.classSelected to false.
|
||||||
// User will have paid 3 gems and will be prompted to select class.
|
// User will have paid 3 gems and will be prompted to select class.
|
||||||
balanceRemoved = await resetClass(user, req);
|
await resetClass(user, req);
|
||||||
} else if (klass === 'warrior' || klass === 'rogue' || klass === 'wizard' || klass === 'healer') {
|
} else if (klass === 'warrior' || klass === 'rogue' || klass === 'wizard' || klass === 'healer') {
|
||||||
if (user.flags.classSelected) {
|
if (user.flags.classSelected) {
|
||||||
balanceRemoved = await resetClass(user, req);
|
await resetClass(user, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
user.stats.class = klass;
|
user.stats.class = klass;
|
||||||
@@ -67,17 +66,6 @@ export default async function changeClass (user, req = {}, analytics) {
|
|||||||
if (user.markModified) user.markModified('items.gear.owned');
|
if (user.markModified) user.markModified('items.gear.owned');
|
||||||
|
|
||||||
removePinnedItemsByOwnedGear(user);
|
removePinnedItemsByOwnedGear(user);
|
||||||
|
|
||||||
if (analytics) {
|
|
||||||
analytics.track('change class', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
class: klass,
|
|
||||||
currency: balanceRemoved === 0 ? 'Free' : 'Gems',
|
|
||||||
category: 'behavior',
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// if invalid class is specified, throw an error.
|
// if invalid class is specified, throw an error.
|
||||||
throw new BadRequest(i18n.t('invalidClass', req.language));
|
throw new BadRequest(i18n.t('invalidClass', req.language));
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ import forEach from 'lodash/forEach';
|
|||||||
import findIndex from 'lodash/findIndex';
|
import findIndex from 'lodash/findIndex';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import keys from 'lodash/keys';
|
import keys from 'lodash/keys';
|
||||||
import pick from 'lodash/pick';
|
|
||||||
import upperFirst from 'lodash/upperFirst';
|
import upperFirst from 'lodash/upperFirst';
|
||||||
import moment from 'moment';
|
|
||||||
import i18n from '../i18n';
|
import i18n from '../i18n';
|
||||||
import content from '../content/index';
|
import content from '../content/index';
|
||||||
import {
|
import {
|
||||||
@@ -36,7 +34,7 @@ function evolve (user, pet, req) {
|
|||||||
}, req.language);
|
}, req.language);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function feed (user, req = {}, analytics) {
|
export default function feed (user, req = {}) {
|
||||||
let pet = get(req, 'params.pet');
|
let pet = get(req, 'params.pet');
|
||||||
const foodK = get(req, 'params.food');
|
const foodK = get(req, 'params.food');
|
||||||
let amount = Number(get(req.query, 'amount', 1));
|
let amount = Number(get(req.query, 'amount', 1));
|
||||||
@@ -116,7 +114,7 @@ export default function feed (user, req = {}, analytics) {
|
|||||||
|
|
||||||
if (!user.achievements.fedPet && user.addAchievement) {
|
if (!user.achievements.fedPet && user.addAchievement) {
|
||||||
user.addAchievement('fedPet');
|
user.addAchievement('fedPet');
|
||||||
checkOnboardingStatus(user, req, analytics);
|
checkOnboardingStatus(user, req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,17 +139,6 @@ export default function feed (user, req = {}, analytics) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (analytics && moment().diff(user.auth.timestamps.created, 'days') < 7) {
|
|
||||||
analytics.track('pet feed', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
foodKey: food.key,
|
|
||||||
petKey: pet.key,
|
|
||||||
category: 'behavior',
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
user.items.pets[pet.key],
|
user.items.pets[pet.key],
|
||||||
message,
|
message,
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ import findIndex from 'lodash/findIndex';
|
|||||||
import forEach from 'lodash/forEach';
|
import forEach from 'lodash/forEach';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import keys from 'lodash/keys';
|
import keys from 'lodash/keys';
|
||||||
import pick from 'lodash/pick';
|
|
||||||
import upperFirst from 'lodash/upperFirst';
|
import upperFirst from 'lodash/upperFirst';
|
||||||
import moment from 'moment';
|
|
||||||
import i18n from '../i18n';
|
import i18n from '../i18n';
|
||||||
import content from '../content/index';
|
import content from '../content/index';
|
||||||
import {
|
import {
|
||||||
@@ -15,7 +13,7 @@ import {
|
|||||||
import { errorMessage } from '../libs/errorMessage';
|
import { errorMessage } from '../libs/errorMessage';
|
||||||
import { checkOnboardingStatus } from '../libs/onboarding';
|
import { checkOnboardingStatus } from '../libs/onboarding';
|
||||||
|
|
||||||
export default function hatch (user, req = {}, analytics) {
|
export default function hatch (user, req = {}) {
|
||||||
const egg = get(req, 'params.egg');
|
const egg = get(req, 'params.egg');
|
||||||
const hatchingPotion = get(req, 'params.hatchingPotion');
|
const hatchingPotion = get(req, 'params.hatchingPotion');
|
||||||
|
|
||||||
@@ -57,7 +55,7 @@ export default function hatch (user, req = {}, analytics) {
|
|||||||
|
|
||||||
if (!user.achievements.hatchedPet && user.addAchievement) {
|
if (!user.achievements.hatchedPet && user.addAchievement) {
|
||||||
user.addAchievement('hatchedPet');
|
user.addAchievement('hatchedPet');
|
||||||
checkOnboardingStatus(user, req, analytics);
|
checkOnboardingStatus(user, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content.dropEggs[egg]) {
|
if (content.dropEggs[egg]) {
|
||||||
@@ -152,16 +150,6 @@ export default function hatch (user, req = {}, analytics) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (analytics && moment().diff(user.auth.timestamps.created, 'days') < 7) {
|
|
||||||
analytics.track('pet hatch', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
petKey: pet,
|
|
||||||
category: 'behavior',
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
user.items,
|
user.items,
|
||||||
i18n.t('messageHatched', req.language),
|
i18n.t('messageHatched', req.language),
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import each from 'lodash/each';
|
import each from 'lodash/each';
|
||||||
import pick from 'lodash/pick';
|
|
||||||
import i18n from '../i18n';
|
import i18n from '../i18n';
|
||||||
import { capByLevel } from '../statHelpers';
|
import { capByLevel } from '../statHelpers';
|
||||||
import {
|
import {
|
||||||
@@ -13,31 +12,15 @@ import updateUserBalance from './updateUserBalance';
|
|||||||
|
|
||||||
const USERSTATSLIST = ['per', 'int', 'con', 'str', 'points', 'gp', 'exp', 'mp'];
|
const USERSTATSLIST = ['per', 'int', 'con', 'str', 'points', 'gp', 'exp', 'mp'];
|
||||||
|
|
||||||
export default async function rebirth (user, tasks = [], req = {}, analytics) {
|
export default async function rebirth (user, tasks = [], req = {}) {
|
||||||
const notFree = !isFreeRebirth(user);
|
const notFree = !isFreeRebirth(user);
|
||||||
|
|
||||||
if (user.balance < 1.5 && notFree) {
|
if (user.balance < 1.5 && notFree) {
|
||||||
throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
|
throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
|
||||||
}
|
}
|
||||||
|
|
||||||
const analyticsData = {
|
|
||||||
uuid: user._id,
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
category: 'behavior',
|
|
||||||
};
|
|
||||||
|
|
||||||
if (notFree) {
|
if (notFree) {
|
||||||
await updateUserBalance(user, -1.5, 'rebirth');
|
await updateUserBalance(user, -1.5, 'rebirth');
|
||||||
analyticsData.currency = 'Gems';
|
|
||||||
analyticsData.gemCost = 6;
|
|
||||||
} else {
|
|
||||||
analyticsData.currency = 'Free';
|
|
||||||
analyticsData.gemCost = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (analytics) {
|
|
||||||
analyticsData.headers = req.headers;
|
|
||||||
analytics.track('Rebirth', analyticsData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const lvl = capByLevel(user.stats.lvl);
|
const lvl = capByLevel(user.stats.lvl);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import pick from 'lodash/pick';
|
|
||||||
import content from '../content/index';
|
import content from '../content/index';
|
||||||
import { mountMasterProgress } from '../count';
|
import { mountMasterProgress } from '../count';
|
||||||
import i18n from '../i18n';
|
import i18n from '../i18n';
|
||||||
@@ -7,7 +6,7 @@ import {
|
|||||||
} from '../libs/errors';
|
} from '../libs/errors';
|
||||||
import updateUserBalance from './updateUserBalance';
|
import updateUserBalance from './updateUserBalance';
|
||||||
|
|
||||||
export default async function releaseMounts (user, req = {}, analytics) {
|
export default async function releaseMounts (user, req = {}) {
|
||||||
if (user.balance < 1) {
|
if (user.balance < 1) {
|
||||||
throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
|
throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
|
||||||
}
|
}
|
||||||
@@ -42,17 +41,6 @@ export default async function releaseMounts (user, req = {}, analytics) {
|
|||||||
user.achievements.mountMasterCount += 1;
|
user.achievements.mountMasterCount += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (analytics) {
|
|
||||||
analytics.track('release mounts', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
currency: 'Gems',
|
|
||||||
gemCost: 4,
|
|
||||||
category: 'behavior',
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
user.items.mounts,
|
user.items.mounts,
|
||||||
i18n.t('mountsReleased'),
|
i18n.t('mountsReleased'),
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import pick from 'lodash/pick';
|
|
||||||
import content from '../content/index';
|
import content from '../content/index';
|
||||||
import { beastMasterProgress } from '../count';
|
import { beastMasterProgress } from '../count';
|
||||||
import i18n from '../i18n';
|
import i18n from '../i18n';
|
||||||
@@ -7,7 +6,7 @@ import {
|
|||||||
} from '../libs/errors';
|
} from '../libs/errors';
|
||||||
import updateUserBalance from './updateUserBalance';
|
import updateUserBalance from './updateUserBalance';
|
||||||
|
|
||||||
export default function releasePets (user, req = {}, analytics) {
|
export default function releasePets (user, req = {}) {
|
||||||
if (user.balance < 1) {
|
if (user.balance < 1) {
|
||||||
throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
|
throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
|
||||||
}
|
}
|
||||||
@@ -42,17 +41,6 @@ export default function releasePets (user, req = {}, analytics) {
|
|||||||
user.achievements.beastMasterCount += 1;
|
user.achievements.beastMasterCount += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (analytics) {
|
|
||||||
analytics.track('release pets', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
currency: 'Gems',
|
|
||||||
gemCost: 4,
|
|
||||||
category: 'behavior',
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
user.items.pets,
|
user.items.pets,
|
||||||
i18n.t('petsReleased'),
|
i18n.t('petsReleased'),
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import each from 'lodash/each';
|
import each from 'lodash/each';
|
||||||
import pick from 'lodash/pick';
|
|
||||||
import i18n from '../i18n';
|
import i18n from '../i18n';
|
||||||
import {
|
import {
|
||||||
NotAuthorized,
|
NotAuthorized,
|
||||||
} from '../libs/errors';
|
} from '../libs/errors';
|
||||||
import updateUserBalance from './updateUserBalance';
|
import updateUserBalance from './updateUserBalance';
|
||||||
|
|
||||||
export default async function reroll (user, tasks = [], req = {}, analytics) {
|
export default async function reroll (user, tasks = [], req = {}) {
|
||||||
if (user.balance < 1) {
|
if (user.balance < 1) {
|
||||||
throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
|
throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
|
||||||
}
|
}
|
||||||
@@ -22,17 +21,6 @@ export default async function reroll (user, tasks = [], req = {}, analytics) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (analytics) {
|
|
||||||
analytics.track('Fortify Potion', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
currency: 'Gems',
|
|
||||||
gemCost: 4,
|
|
||||||
category: 'behavior',
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{ user, tasks },
|
{ user, tasks },
|
||||||
i18n.t('fortifyComplete'),
|
i18n.t('fortifyComplete'),
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import merge from 'lodash/merge';
|
import merge from 'lodash/merge';
|
||||||
import pick from 'lodash/pick';
|
|
||||||
import reduce from 'lodash/reduce';
|
import reduce from 'lodash/reduce';
|
||||||
import each from 'lodash/each';
|
import each from 'lodash/each';
|
||||||
import i18n from '../i18n';
|
import i18n from '../i18n';
|
||||||
@@ -13,7 +12,7 @@ import predictableRandom from '../fns/predictableRandom';
|
|||||||
import { removePinnedGearByClass, addPinnedGearByClass, addPinnedGear } from './pinnedGearUtils';
|
import { removePinnedGearByClass, addPinnedGearByClass, addPinnedGear } from './pinnedGearUtils';
|
||||||
import getItemInfo from '../libs/getItemInfo';
|
import getItemInfo from '../libs/getItemInfo';
|
||||||
|
|
||||||
export default function revive (user, req = {}, analytics) {
|
export default function revive (user, req = {}) {
|
||||||
if (user.stats.hp > 0) {
|
if (user.stats.hp > 0) {
|
||||||
throw new NotAuthorized(i18n.t('cannotRevive', req.language));
|
throw new NotAuthorized(i18n.t('cannotRevive', req.language));
|
||||||
}
|
}
|
||||||
@@ -110,16 +109,6 @@ export default function revive (user, req = {}, analytics) {
|
|||||||
message = i18n.t('messageLostItem', { itemText: item.text(req.language) }, req.language);
|
message = i18n.t('messageLostItem', { itemText: item.text(req.language) }, req.language);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (analytics) {
|
|
||||||
analytics.track('Death', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
lostItem,
|
|
||||||
category: 'behavior',
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
user.items,
|
user.items,
|
||||||
message,
|
message,
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ function _updateLastHistoryEntry (lastHistoryEntry, task, direction, times) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function scoreTask (options = {}, req = {}, analytics) {
|
export default function scoreTask (options = {}, req = {}) {
|
||||||
const {
|
const {
|
||||||
user, task, direction, times = 1, cron = false,
|
user, task, direction, times = 1, cron = false,
|
||||||
} = options;
|
} = options;
|
||||||
@@ -425,7 +425,7 @@ export default function scoreTask (options = {}, req = {}, analytics) {
|
|||||||
|
|
||||||
if (!user.achievements.completedTask && cron === false && direction === 'up' && user.addAchievement) {
|
if (!user.achievements.completedTask && cron === false && direction === 'up' && user.addAchievement) {
|
||||||
user.addAchievement('completedTask');
|
user.addAchievement('completedTask');
|
||||||
checkOnboardingStatus(user, req, analytics);
|
checkOnboardingStatus(user, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
return delta;
|
return delta;
|
||||||
|
|||||||
@@ -1,17 +1,4 @@
|
|||||||
import pick from 'lodash/pick';
|
export function sleep (user) {
|
||||||
|
|
||||||
export function sleep (user, req = {}, analytics) {
|
|
||||||
user.preferences.sleep = !user.preferences.sleep;
|
user.preferences.sleep = !user.preferences.sleep;
|
||||||
|
|
||||||
if (analytics) {
|
|
||||||
analytics.track('sleep', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
status: user.preferences.sleep,
|
|
||||||
category: 'behavior',
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return [user.preferences.sleep];
|
return [user.preferences.sleep];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import pick from 'lodash/pick';
|
|
||||||
import setWith from 'lodash/setWith';
|
import setWith from 'lodash/setWith';
|
||||||
import i18n from '../i18n';
|
import i18n from '../i18n';
|
||||||
import { NotAuthorized, BadRequest } from '../libs/errors';
|
import { NotAuthorized, BadRequest } from '../libs/errors';
|
||||||
@@ -208,7 +207,7 @@ function buildResponse ({ purchased, preference, items }, ownsAlready, language)
|
|||||||
// If item is already purchased -> equip it
|
// If item is already purchased -> equip it
|
||||||
// Otherwise unlock it
|
// Otherwise unlock it
|
||||||
// @TODO refactor and take as parameter the set name, for single items use the buy ops
|
// @TODO refactor and take as parameter the set name, for single items use the buy ops
|
||||||
export default async function unlock (user, req = {}, analytics) {
|
export default async function unlock (user, req = {}) {
|
||||||
const path = get(req.query, 'path');
|
const path = get(req.query, 'path');
|
||||||
|
|
||||||
if (!path) {
|
if (!path) {
|
||||||
@@ -319,19 +318,6 @@ export default async function unlock (user, req = {}, analytics) {
|
|||||||
|
|
||||||
if (!unlockedAlready) {
|
if (!unlockedAlready) {
|
||||||
await updateUserBalance(user, -cost, 'spend', path);
|
await updateUserBalance(user, -cost, 'spend', path);
|
||||||
|
|
||||||
if (analytics) {
|
|
||||||
analytics.track('buy', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
itemKey: path,
|
|
||||||
itemType: 'customization',
|
|
||||||
currency: 'Gems',
|
|
||||||
gemCost: cost / 0.25,
|
|
||||||
category: 'behavior',
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return buildResponse(user, unlockedAlready, req.language);
|
return buildResponse(user, unlockedAlready, req.language);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import validator from 'validator';
|
import validator from 'validator';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import pick from 'lodash/pick';
|
|
||||||
import sortBy from 'lodash/sortBy';
|
import sortBy from 'lodash/sortBy';
|
||||||
import nconf from 'nconf';
|
import nconf from 'nconf';
|
||||||
import {
|
import {
|
||||||
@@ -127,14 +126,6 @@ api.loginLocal = {
|
|||||||
user.auth.timestamps.updated = new Date();
|
user.auth.timestamps.updated = new Date();
|
||||||
await user.save();
|
await user.save();
|
||||||
|
|
||||||
res.analytics.track('login', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
category: 'behavior',
|
|
||||||
type: 'local',
|
|
||||||
uuid: user._id,
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
|
|
||||||
return loginRes(user, req, res);
|
return loginRes(user, req, res);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import cloneDeep from 'lodash/cloneDeep';
|
import cloneDeep from 'lodash/cloneDeep';
|
||||||
import escapeRegExp from 'lodash/escapeRegExp';
|
import escapeRegExp from 'lodash/escapeRegExp';
|
||||||
import merge from 'lodash/merge';
|
import merge from 'lodash/merge';
|
||||||
import pick from 'lodash/pick';
|
|
||||||
import reduce from 'lodash/reduce';
|
import reduce from 'lodash/reduce';
|
||||||
import times from 'lodash/times';
|
import times from 'lodash/times';
|
||||||
import { authWithHeaders, authWithSession } from '../../middlewares/auth';
|
import { authWithHeaders, authWithSession } from '../../middlewares/auth';
|
||||||
@@ -290,19 +289,6 @@ api.createChallenge = {
|
|||||||
};
|
};
|
||||||
response.group = getChallengeGroupResponse(group);
|
response.group = getChallengeGroupResponse(group);
|
||||||
|
|
||||||
res.analytics.track('challenge create', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
hitType: 'event',
|
|
||||||
category: 'behavior',
|
|
||||||
challengeID: response._id,
|
|
||||||
groupID: group._id,
|
|
||||||
groupName: group.privacy === 'private' ? null : group.name,
|
|
||||||
groupType: group._id === TAVERN_ID ? 'tavern' : group.type,
|
|
||||||
prize: response.prize,
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
|
|
||||||
res.respond(201, response);
|
res.respond(201, response);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -359,18 +345,6 @@ api.joinChallenge = {
|
|||||||
const chalLeader = await User.findById(response.leader).select(nameFields).exec();
|
const chalLeader = await User.findById(response.leader).select(nameFields).exec();
|
||||||
response.leader = chalLeader ? chalLeader.toJSON({ minimize: true }) : null;
|
response.leader = chalLeader ? chalLeader.toJSON({ minimize: true }) : null;
|
||||||
|
|
||||||
res.analytics.track('challenge join', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
hitType: 'event',
|
|
||||||
category: 'behavior',
|
|
||||||
challengeID: challenge._id,
|
|
||||||
groupID: group._id,
|
|
||||||
groupName: group.privacy === 'private' ? null : group.name,
|
|
||||||
groupType: group._id === TAVERN_ID ? 'tavern' : group.type,
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
|
|
||||||
res.respond(200, response);
|
res.respond(200, response);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -410,18 +384,6 @@ api.leaveChallenge = {
|
|||||||
// Unlink challenge's tasks from user's tasks and save the challenge
|
// Unlink challenge's tasks from user's tasks and save the challenge
|
||||||
await challenge.unlinkTasks(user, keep);
|
await challenge.unlinkTasks(user, keep);
|
||||||
|
|
||||||
res.analytics.track('challenge leave', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
hitType: 'event',
|
|
||||||
category: 'behavior',
|
|
||||||
challengeID: challenge._id,
|
|
||||||
groupID: challenge.group._id,
|
|
||||||
groupName: challenge.group.privacy === 'private' ? null : challenge.group.name,
|
|
||||||
groupType: challenge.group._id === TAVERN_ID ? 'tavern' : challenge.group.type,
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
|
|
||||||
res.respond(200, {});
|
res.respond(200, {});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -895,19 +857,6 @@ api.deleteChallenge = {
|
|||||||
// Close channel in background, some ops are run in the background without `await`ing
|
// Close channel in background, some ops are run in the background without `await`ing
|
||||||
await challenge.closeChal({ broken: 'CHALLENGE_DELETED' });
|
await challenge.closeChal({ broken: 'CHALLENGE_DELETED' });
|
||||||
|
|
||||||
res.analytics.track('challenge delete', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
hitType: 'event',
|
|
||||||
category: 'behavior',
|
|
||||||
challengeID: challenge._id,
|
|
||||||
groupID: challenge.group._id,
|
|
||||||
groupName: challenge.group.privacy === 'private' ? null : challenge.group.name,
|
|
||||||
groupType: challenge.group._id === TAVERN_ID ? 'tavern' : challenge.group.type,
|
|
||||||
prize: challenge.prize,
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
|
|
||||||
res.respond(200, {});
|
res.respond(200, {});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -956,20 +905,6 @@ api.selectChallengeWinner = {
|
|||||||
// Close channel in background, some ops are run in the background without `await`ing
|
// Close channel in background, some ops are run in the background without `await`ing
|
||||||
await challenge.closeChal({ broken: 'CHALLENGE_CLOSED', winner });
|
await challenge.closeChal({ broken: 'CHALLENGE_CLOSED', winner });
|
||||||
|
|
||||||
res.analytics.track('challenge close', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
hitType: 'event',
|
|
||||||
category: 'behavior',
|
|
||||||
challengeID: challenge._id,
|
|
||||||
challengeWinnerID: winner._id,
|
|
||||||
groupID: challenge.group._id,
|
|
||||||
groupName: challenge.group.privacy === 'private' ? null : challenge.group.name,
|
|
||||||
groupType: challenge.group._id === TAVERN_ID ? 'tavern' : challenge.group.type,
|
|
||||||
prize: challenge.prize,
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
|
|
||||||
res.respond(200, {});
|
res.respond(200, {});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import pick from 'lodash/pick';
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import nconf from 'nconf';
|
import nconf from 'nconf';
|
||||||
import { authWithHeaders, chatPrivilegesRequired } from '../../middlewares/auth';
|
import { authWithHeaders, chatPrivilegesRequired } from '../../middlewares/auth';
|
||||||
@@ -23,9 +22,6 @@ import { getMatchesByWordArray } from '../../libs/stringUtils';
|
|||||||
import bannedSlurs from '../../libs/bannedSlurs';
|
import bannedSlurs from '../../libs/bannedSlurs';
|
||||||
import { apiError } from '../../libs/apiError';
|
import { apiError } from '../../libs/apiError';
|
||||||
import highlightMentions from '../../libs/highlightMentions';
|
import highlightMentions from '../../libs/highlightMentions';
|
||||||
import { getAnalyticsServiceByEnvironment } from '../../libs/analyticsService';
|
|
||||||
|
|
||||||
const analytics = getAnalyticsServiceByEnvironment();
|
|
||||||
|
|
||||||
const ACCOUNT_MIN_CHAT_AGE = Number(nconf.get('ACCOUNT_MIN_CHAT_AGE'));
|
const ACCOUNT_MIN_CHAT_AGE = Number(nconf.get('ACCOUNT_MIN_CHAT_AGE'));
|
||||||
|
|
||||||
@@ -187,13 +183,6 @@ api.postChat = {
|
|||||||
|
|
||||||
// Check if account is newer than the minimum age for chat participation
|
// Check if account is newer than the minimum age for chat participation
|
||||||
if (moment().diff(user.auth.timestamps.created, 'minutes') < ACCOUNT_MIN_CHAT_AGE) {
|
if (moment().diff(user.auth.timestamps.created, 'minutes') < ACCOUNT_MIN_CHAT_AGE) {
|
||||||
analytics.track('chat age error', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
hitType: 'event',
|
|
||||||
category: 'behavior',
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
throw new BadRequest(res.t('chatTemporarilyUnavailable'));
|
throw new BadRequest(res.t('chatTemporarilyUnavailable'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,27 +228,6 @@ api.postChat = {
|
|||||||
|
|
||||||
await Promise.all(toSave);
|
await Promise.all(toSave);
|
||||||
|
|
||||||
const analyticsObject = {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
hitType: 'event',
|
|
||||||
category: 'behavior',
|
|
||||||
groupType: group.type,
|
|
||||||
privacy: group.privacy,
|
|
||||||
headers: req.headers,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (mentions) {
|
|
||||||
analyticsObject.mentionsCount = mentions.length;
|
|
||||||
} else {
|
|
||||||
analyticsObject.mentionsCount = 0;
|
|
||||||
}
|
|
||||||
if (group.privacy === 'public') {
|
|
||||||
analyticsObject.groupName = group.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
res.analytics.track('group chat', analyticsObject);
|
|
||||||
|
|
||||||
if (chatUpdated) {
|
if (chatUpdated) {
|
||||||
res.respond(200, { chat: chatRes.chat });
|
res.respond(200, { chat: chatRes.chat });
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import findIndex from 'lodash/findIndex';
|
|||||||
import includes from 'lodash/includes';
|
import includes from 'lodash/includes';
|
||||||
import isArray from 'lodash/isArray';
|
import isArray from 'lodash/isArray';
|
||||||
import mergeWith from 'lodash/mergeWith';
|
import mergeWith from 'lodash/mergeWith';
|
||||||
import pick from 'lodash/pick';
|
|
||||||
import uniqBy from 'lodash/uniqBy';
|
import uniqBy from 'lodash/uniqBy';
|
||||||
import nconf from 'nconf';
|
import nconf from 'nconf';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
@@ -166,25 +165,6 @@ api.createGroup = {
|
|||||||
profile: { name: user.profile.name },
|
profile: { name: user.profile.name },
|
||||||
};
|
};
|
||||||
|
|
||||||
const analyticsObject = {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
hitType: 'event',
|
|
||||||
category: 'behavior',
|
|
||||||
owner: true,
|
|
||||||
groupId: savedGroup._id,
|
|
||||||
groupType: savedGroup.type,
|
|
||||||
privacy: savedGroup.privacy,
|
|
||||||
headers: req.headers,
|
|
||||||
invited: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (savedGroup.privacy === 'public') {
|
|
||||||
analyticsObject.groupName = savedGroup.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
res.analytics.track('join group', analyticsObject);
|
|
||||||
|
|
||||||
res.respond(201, response); // do not remove chat flags data as we've just created the group
|
res.respond(201, response); // do not remove chat flags data as we've just created the group
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -217,19 +197,6 @@ api.createGroupPlan = {
|
|||||||
const results = await Promise.all([user.save(), group.save()]);
|
const results = await Promise.all([user.save(), group.save()]);
|
||||||
const savedGroup = results[1];
|
const savedGroup = results[1];
|
||||||
|
|
||||||
res.analytics.track('join group', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
hitType: 'event',
|
|
||||||
category: 'behavior',
|
|
||||||
owner: true,
|
|
||||||
groupId: savedGroup._id,
|
|
||||||
groupType: savedGroup.type,
|
|
||||||
privacy: savedGroup.privacy,
|
|
||||||
headers: req.headers,
|
|
||||||
invited: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
// do not remove chat flags data as we've just created the group
|
// do not remove chat flags data as we've just created the group
|
||||||
const groupResponse = savedGroup.toJSON();
|
const groupResponse = savedGroup.toJSON();
|
||||||
// the leader is the authenticated user
|
// the leader is the authenticated user
|
||||||
@@ -585,7 +552,6 @@ api.joinGroup = {
|
|||||||
if (!group) throw new NotFound(res.t('groupNotFound'));
|
if (!group) throw new NotFound(res.t('groupNotFound'));
|
||||||
|
|
||||||
let isUserInvited = false;
|
let isUserInvited = false;
|
||||||
const seekingParty = Boolean(user.party.seeking);
|
|
||||||
|
|
||||||
if (group.type === 'party') {
|
if (group.type === 'party') {
|
||||||
// Check if was invited to party
|
// Check if was invited to party
|
||||||
@@ -710,20 +676,6 @@ api.joinGroup = {
|
|||||||
|
|
||||||
promises.push(group.save());
|
promises.push(group.save());
|
||||||
|
|
||||||
const analyticsObject = {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
hitType: 'event',
|
|
||||||
category: 'behavior',
|
|
||||||
owner: false,
|
|
||||||
groupId: group._id,
|
|
||||||
groupType: group.type,
|
|
||||||
privacy: group.privacy,
|
|
||||||
headers: req.headers,
|
|
||||||
invited: isUserInvited,
|
|
||||||
seekingParty: group.type === 'party' ? seekingParty : null,
|
|
||||||
};
|
|
||||||
|
|
||||||
promises = await Promise.all(promises);
|
promises = await Promise.all(promises);
|
||||||
|
|
||||||
if (group.hasNotCancelled()) {
|
if (group.hasNotCancelled()) {
|
||||||
@@ -737,8 +689,6 @@ api.joinGroup = {
|
|||||||
response.leader = leader.toJSON({ minimize: true });
|
response.leader = leader.toJSON({ minimize: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
res.analytics.track('join group', analyticsObject);
|
|
||||||
|
|
||||||
res.respond(200, response);
|
res.respond(200, response);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import escapeRegExp from 'lodash/escapeRegExp';
|
import escapeRegExp from 'lodash/escapeRegExp';
|
||||||
import pick from 'lodash/pick';
|
|
||||||
import { authWithHeaders } from '../../middlewares/auth';
|
import { authWithHeaders } from '../../middlewares/auth';
|
||||||
import {
|
import {
|
||||||
model as User,
|
model as User,
|
||||||
@@ -734,17 +733,6 @@ api.transferGems = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
res.respond(200, {});
|
res.respond(200, {});
|
||||||
|
|
||||||
if (res.analytics) {
|
|
||||||
res.analytics.track('transfer gems', {
|
|
||||||
user: pick(sender, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: sender._id,
|
|
||||||
hitType: 'event',
|
|
||||||
category: 'behavior',
|
|
||||||
headers: req.headers,
|
|
||||||
quantity: gemAmount,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import each from 'lodash/each';
|
import each from 'lodash/each';
|
||||||
import every from 'lodash/every';
|
import every from 'lodash/every';
|
||||||
import isBoolean from 'lodash/isBoolean';
|
import isBoolean from 'lodash/isBoolean';
|
||||||
import pick from 'lodash/pick';
|
|
||||||
import { authWithHeaders } from '../../middlewares/auth';
|
import { authWithHeaders } from '../../middlewares/auth';
|
||||||
import { getAnalyticsServiceByEnvironment } from '../../libs/analyticsService';
|
|
||||||
import {
|
import {
|
||||||
model as Group,
|
model as Group,
|
||||||
basicFields as basicGroupFields,
|
basicFields as basicGroupFields,
|
||||||
@@ -24,8 +22,6 @@ import { apiError } from '../../libs/apiError';
|
|||||||
import { questActivityWebhook } from '../../libs/webhook';
|
import { questActivityWebhook } from '../../libs/webhook';
|
||||||
import { model as UserHistory } from '../../models/userHistory';
|
import { model as UserHistory } from '../../models/userHistory';
|
||||||
|
|
||||||
const analytics = getAnalyticsServiceByEnvironment();
|
|
||||||
|
|
||||||
const questScrolls = common.content.quests;
|
const questScrolls = common.content.quests;
|
||||||
|
|
||||||
function canStartQuestAutomatically (group) {
|
function canStartQuestAutomatically (group) {
|
||||||
@@ -166,17 +162,6 @@ api.inviteToQuest = {
|
|||||||
quest,
|
quest,
|
||||||
});
|
});
|
||||||
|
|
||||||
// track that the inviting user has accepted the quest
|
|
||||||
analytics.track('quest', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
category: 'behavior',
|
|
||||||
headers: req.headers,
|
|
||||||
owner: true,
|
|
||||||
questName: questKey,
|
|
||||||
response: 'accept',
|
|
||||||
});
|
|
||||||
|
|
||||||
await UserHistory.beginUserHistoryUpdate(user._id, req.headers)
|
await UserHistory.beginUserHistoryUpdate(user._id, req.headers)
|
||||||
.withQuestInviteResponse(group.quest.key, 'invite')
|
.withQuestInviteResponse(group.quest.key, 'invite')
|
||||||
.commit();
|
.commit();
|
||||||
@@ -231,17 +216,6 @@ api.acceptQuest = {
|
|||||||
|
|
||||||
res.respond(200, savedGroup.quest);
|
res.respond(200, savedGroup.quest);
|
||||||
|
|
||||||
// track that a user has accepted the quest
|
|
||||||
analytics.track('quest', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
category: 'behavior',
|
|
||||||
owner: false,
|
|
||||||
response: 'accept',
|
|
||||||
questName: group.quest.key,
|
|
||||||
uuid: user._id,
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
|
|
||||||
await UserHistory.beginUserHistoryUpdate(user._id, req.headers)
|
await UserHistory.beginUserHistoryUpdate(user._id, req.headers)
|
||||||
.withQuestInviteResponse(group.quest.key, 'accept')
|
.withQuestInviteResponse(group.quest.key, 'accept')
|
||||||
.commit();
|
.commit();
|
||||||
@@ -297,16 +271,6 @@ api.rejectQuest = {
|
|||||||
|
|
||||||
res.respond(200, savedGroup.quest);
|
res.respond(200, savedGroup.quest);
|
||||||
|
|
||||||
analytics.track('quest', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
category: 'behavior',
|
|
||||||
owner: false,
|
|
||||||
response: 'reject',
|
|
||||||
questName: group.quest.key,
|
|
||||||
uuid: user._id,
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
|
|
||||||
await UserHistory.beginUserHistoryUpdate(user._id, req.headers)
|
await UserHistory.beginUserHistoryUpdate(user._id, req.headers)
|
||||||
.withQuestInviteResponse(group.quest.key, 'reject')
|
.withQuestInviteResponse(group.quest.key, 'reject')
|
||||||
.commit();
|
.commit();
|
||||||
@@ -360,16 +324,6 @@ api.forceStart = {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
res.respond(200, savedGroup.quest);
|
res.respond(200, savedGroup.quest);
|
||||||
|
|
||||||
analytics.track('quest', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
category: 'behavior',
|
|
||||||
owner: user._id === group.quest.leader,
|
|
||||||
response: 'force-start',
|
|
||||||
questName: group.quest.key,
|
|
||||||
uuid: user._id,
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import assign from 'lodash/assign';
|
import assign from 'lodash/assign';
|
||||||
import find from 'lodash/find';
|
import find from 'lodash/find';
|
||||||
import merge from 'lodash/merge';
|
import merge from 'lodash/merge';
|
||||||
import pick from 'lodash/pick';
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { authWithHeaders } from '../../middlewares/auth';
|
import { authWithHeaders } from '../../middlewares/auth';
|
||||||
import {
|
import {
|
||||||
@@ -330,17 +329,6 @@ api.createChallengeTasks = {
|
|||||||
|
|
||||||
// If adding tasks to a challenge -> sync users
|
// If adding tasks to a challenge -> sync users
|
||||||
if (challenge) challenge.addTasks(tasks);
|
if (challenge) challenge.addTasks(tasks);
|
||||||
|
|
||||||
tasks.forEach(task => {
|
|
||||||
res.analytics.track('challenge task created', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
hitType: 'event',
|
|
||||||
category: 'behavior',
|
|
||||||
taskType: task.type,
|
|
||||||
challengeID: challenge._id,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -700,17 +688,6 @@ api.updateTask = {
|
|||||||
task: savedTask,
|
task: savedTask,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (group) {
|
|
||||||
res.analytics.track('task edit', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
hitType: 'event',
|
|
||||||
category: 'behavior',
|
|
||||||
taskType: task.type,
|
|
||||||
groupID: group._id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import pick from 'lodash/pick';
|
|
||||||
import isUUID from 'validator/lib/isUUID';
|
import isUUID from 'validator/lib/isUUID';
|
||||||
import { authWithHeaders } from '../../../middlewares/auth';
|
import { authWithHeaders } from '../../../middlewares/auth';
|
||||||
import * as Tasks from '../../../models/task';
|
import * as Tasks from '../../../models/task';
|
||||||
@@ -61,18 +60,6 @@ api.createGroupTasks = {
|
|||||||
const tasks = await createTasks(req, res, { user, group });
|
const tasks = await createTasks(req, res, { user, group });
|
||||||
|
|
||||||
res.respond(201, tasks.length === 1 ? tasks[0] : tasks);
|
res.respond(201, tasks.length === 1 ? tasks[0] : tasks);
|
||||||
|
|
||||||
tasks.forEach(task => {
|
|
||||||
res.analytics.track('team task created', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
hitType: 'event',
|
|
||||||
category: 'behavior',
|
|
||||||
taskType: task.type,
|
|
||||||
groupID: group._id,
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -251,16 +238,6 @@ api.assignTask = {
|
|||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
|
|
||||||
res.respond(200, task);
|
res.respond(200, task);
|
||||||
|
|
||||||
res.analytics.track('task assign', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
hitType: 'event',
|
|
||||||
category: 'behavior',
|
|
||||||
taskType: task.type,
|
|
||||||
groupID: group._id,
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import cloneDeep from 'lodash/cloneDeep';
|
import cloneDeep from 'lodash/cloneDeep';
|
||||||
import forEach from 'lodash/forEach';
|
import forEach from 'lodash/forEach';
|
||||||
import isFunction from 'lodash/isFunction';
|
import isFunction from 'lodash/isFunction';
|
||||||
import pick from 'lodash/pick';
|
|
||||||
import nconf from 'nconf';
|
import nconf from 'nconf';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import { authWithHeaders } from '../../middlewares/auth';
|
import { authWithHeaders } from '../../middlewares/auth';
|
||||||
@@ -27,7 +26,6 @@ import * as inboxLib from '../../libs/inbox';
|
|||||||
import * as userLib from '../../libs/user';
|
import * as userLib from '../../libs/user';
|
||||||
import { model as UserHistory } from '../../models/userHistory';
|
import { model as UserHistory } from '../../models/userHistory';
|
||||||
|
|
||||||
const OFFICIAL_PLATFORMS = ['habitica-web', 'habitica-ios', 'habitica-android'];
|
|
||||||
const TECH_ASSISTANCE_EMAIL = nconf.get('EMAILS_TECH_ASSISTANCE_EMAIL');
|
const TECH_ASSISTANCE_EMAIL = nconf.get('EMAILS_TECH_ASSISTANCE_EMAIL');
|
||||||
const DELETE_CONFIRMATION = 'DELETE';
|
const DELETE_CONFIRMATION = 'DELETE';
|
||||||
|
|
||||||
@@ -325,13 +323,6 @@ api.deleteUser = {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
res.analytics.track('account delete', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
hitType: 'event',
|
|
||||||
category: 'behavior',
|
|
||||||
});
|
|
||||||
|
|
||||||
res.respond(200, {});
|
res.respond(200, {});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -441,7 +432,7 @@ api.sleep = {
|
|||||||
url: '/user/sleep',
|
url: '/user/sleep',
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
const sleepRes = common.ops.sleep(user, req, res.analytics);
|
const sleepRes = common.ops.sleep(user, req);
|
||||||
await user.save();
|
await user.save();
|
||||||
res.respond(200, ...sleepRes);
|
res.respond(200, ...sleepRes);
|
||||||
},
|
},
|
||||||
@@ -500,10 +491,7 @@ api.buy = {
|
|||||||
let quantity = 1;
|
let quantity = 1;
|
||||||
if (req.body.quantity) quantity = req.body.quantity;
|
if (req.body.quantity) quantity = req.body.quantity;
|
||||||
req.quantity = quantity;
|
req.quantity = quantity;
|
||||||
if (OFFICIAL_PLATFORMS.indexOf(req.headers['x-client']) === -1) {
|
const buyRes = await common.ops.buy(user, req);
|
||||||
res.analytics = undefined;
|
|
||||||
}
|
|
||||||
const buyRes = await common.ops.buy(user, req, res.analytics);
|
|
||||||
|
|
||||||
await user.save();
|
await user.save();
|
||||||
|
|
||||||
@@ -558,7 +546,7 @@ api.buyGear = {
|
|||||||
url: '/user/buy-gear/:key',
|
url: '/user/buy-gear/:key',
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
const buyGearRes = await common.ops.buy(user, req, res.analytics);
|
const buyGearRes = await common.ops.buy(user, req);
|
||||||
await user.save();
|
await user.save();
|
||||||
res.respond(200, ...buyGearRes);
|
res.respond(200, ...buyGearRes);
|
||||||
},
|
},
|
||||||
@@ -600,10 +588,7 @@ api.buyArmoire = {
|
|||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
req.type = 'armoire';
|
req.type = 'armoire';
|
||||||
req.params.key = 'armoire';
|
req.params.key = 'armoire';
|
||||||
if (OFFICIAL_PLATFORMS.indexOf(req.headers['x-client']) === -1) {
|
const buyArmoireResponse = await common.ops.buy(user, req);
|
||||||
res.analytics = undefined;
|
|
||||||
}
|
|
||||||
const buyArmoireResponse = await common.ops.buy(user, req, res.analytics);
|
|
||||||
await user.save();
|
await user.save();
|
||||||
await UserHistory.beginUserHistoryUpdate(user._id, req.headers)
|
await UserHistory.beginUserHistoryUpdate(user._id, req.headers)
|
||||||
.withArmoire(buyArmoireResponse[0].armoire.dropKey || 'experience')
|
.withArmoire(buyArmoireResponse[0].armoire.dropKey || 'experience')
|
||||||
@@ -646,7 +631,7 @@ api.buyHealthPotion = {
|
|||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
req.type = 'potion';
|
req.type = 'potion';
|
||||||
req.params.key = 'potion';
|
req.params.key = 'potion';
|
||||||
const buyHealthPotionResponse = await common.ops.buy(user, req, res.analytics);
|
const buyHealthPotionResponse = await common.ops.buy(user, req);
|
||||||
await user.save();
|
await user.save();
|
||||||
res.respond(200, ...buyHealthPotionResponse);
|
res.respond(200, ...buyHealthPotionResponse);
|
||||||
},
|
},
|
||||||
@@ -688,7 +673,7 @@ api.buyMysterySet = {
|
|||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
req.type = 'mystery';
|
req.type = 'mystery';
|
||||||
const buyMysterySetRes = await common.ops.buy(user, req, res.analytics);
|
const buyMysterySetRes = await common.ops.buy(user, req);
|
||||||
await user.save();
|
await user.save();
|
||||||
res.respond(200, ...buyMysterySetRes);
|
res.respond(200, ...buyMysterySetRes);
|
||||||
},
|
},
|
||||||
@@ -731,7 +716,7 @@ api.buyQuest = {
|
|||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
req.type = 'quest';
|
req.type = 'quest';
|
||||||
const buyQuestRes = await common.ops.buy(user, req, res.analytics);
|
const buyQuestRes = await common.ops.buy(user, req);
|
||||||
await user.save();
|
await user.save();
|
||||||
res.respond(200, ...buyQuestRes);
|
res.respond(200, ...buyQuestRes);
|
||||||
},
|
},
|
||||||
@@ -818,7 +803,7 @@ api.hatch = {
|
|||||||
url: '/user/hatch/:egg/:hatchingPotion',
|
url: '/user/hatch/:egg/:hatchingPotion',
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
const hatchRes = common.ops.hatch(user, req, res.analytics);
|
const hatchRes = common.ops.hatch(user, req);
|
||||||
|
|
||||||
await user.save();
|
await user.save();
|
||||||
|
|
||||||
@@ -916,7 +901,7 @@ api.feed = {
|
|||||||
url: '/user/feed/:pet/:food',
|
url: '/user/feed/:pet/:food',
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
const feedRes = common.ops.feed(user, req, res.analytics);
|
const feedRes = common.ops.feed(user, req);
|
||||||
|
|
||||||
await user.save();
|
await user.save();
|
||||||
|
|
||||||
@@ -964,7 +949,7 @@ api.changeClass = {
|
|||||||
url: '/user/change-class',
|
url: '/user/change-class',
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
const changeClassRes = await common.ops.changeClass(user, req, res.analytics);
|
const changeClassRes = await common.ops.changeClass(user, req);
|
||||||
await user.save();
|
await user.save();
|
||||||
res.respond(200, ...changeClassRes);
|
res.respond(200, ...changeClassRes);
|
||||||
},
|
},
|
||||||
@@ -1040,7 +1025,7 @@ api.purchase = {
|
|||||||
if (req.body.quantity) quantity = req.body.quantity;
|
if (req.body.quantity) quantity = req.body.quantity;
|
||||||
req.quantity = quantity;
|
req.quantity = quantity;
|
||||||
|
|
||||||
const purchaseRes = await common.ops.buy(user, req, res.analytics);
|
const purchaseRes = await common.ops.buy(user, req);
|
||||||
await user.save();
|
await user.save();
|
||||||
res.respond(200, ...purchaseRes);
|
res.respond(200, ...purchaseRes);
|
||||||
},
|
},
|
||||||
@@ -1083,7 +1068,6 @@ api.userPurchaseHourglass = {
|
|||||||
const purchaseHourglassRes = await common.ops.buy(
|
const purchaseHourglassRes = await common.ops.buy(
|
||||||
user,
|
user,
|
||||||
req,
|
req,
|
||||||
res.analytics,
|
|
||||||
{ quantity, hourglass: true },
|
{ quantity, hourglass: true },
|
||||||
);
|
);
|
||||||
await user.save();
|
await user.save();
|
||||||
@@ -1180,7 +1164,7 @@ api.userOpenMysteryItem = {
|
|||||||
url: '/user/open-mystery-item',
|
url: '/user/open-mystery-item',
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
const openMysteryItemRes = common.ops.openMysteryItem(user, req, res.analytics);
|
const openMysteryItemRes = common.ops.openMysteryItem(user, req);
|
||||||
await user.save();
|
await user.save();
|
||||||
res.respond(200, ...openMysteryItemRes);
|
res.respond(200, ...openMysteryItemRes);
|
||||||
},
|
},
|
||||||
@@ -1212,7 +1196,7 @@ api.userReleasePets = {
|
|||||||
url: '/user/release-pets',
|
url: '/user/release-pets',
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
const releasePetsRes = await common.ops.releasePets(user, req, res.analytics);
|
const releasePetsRes = await common.ops.releasePets(user, req);
|
||||||
await user.save();
|
await user.save();
|
||||||
res.respond(200, ...releasePetsRes);
|
res.respond(200, ...releasePetsRes);
|
||||||
},
|
},
|
||||||
@@ -1261,7 +1245,7 @@ api.userReleaseBoth = {
|
|||||||
url: '/user/release-both',
|
url: '/user/release-both',
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
const releaseBothRes = common.ops.releaseBoth(user, req, res.analytics);
|
const releaseBothRes = common.ops.releaseBoth(user, req);
|
||||||
await user.save();
|
await user.save();
|
||||||
res.respond(200, ...releaseBothRes);
|
res.respond(200, ...releaseBothRes);
|
||||||
},
|
},
|
||||||
@@ -1297,7 +1281,7 @@ api.userReleaseMounts = {
|
|||||||
url: '/user/release-mounts',
|
url: '/user/release-mounts',
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
const releaseMountsRes = await common.ops.releaseMounts(user, req, res.analytics);
|
const releaseMountsRes = await common.ops.releaseMounts(user, req);
|
||||||
await user.save();
|
await user.save();
|
||||||
res.respond(200, ...releaseMountsRes);
|
res.respond(200, ...releaseMountsRes);
|
||||||
},
|
},
|
||||||
@@ -1373,7 +1357,7 @@ api.userUnlock = {
|
|||||||
url: '/user/unlock',
|
url: '/user/unlock',
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
const unlockRes = await common.ops.unlock(user, req, res.analytics);
|
const unlockRes = await common.ops.unlock(user, req);
|
||||||
await user.save();
|
await user.save();
|
||||||
res.respond(200, ...unlockRes);
|
res.respond(200, ...unlockRes);
|
||||||
},
|
},
|
||||||
@@ -1399,7 +1383,7 @@ api.userRevive = {
|
|||||||
url: '/user/revive',
|
url: '/user/revive',
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
const reviveRes = common.ops.revive(user, req, res.analytics);
|
const reviveRes = common.ops.revive(user, req);
|
||||||
await user.save();
|
await user.save();
|
||||||
res.respond(200, ...reviveRes);
|
res.respond(200, ...reviveRes);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
import pick from 'lodash/pick';
|
|
||||||
import {
|
|
||||||
NotAuthorized,
|
|
||||||
} from '../../libs/errors';
|
|
||||||
import {
|
|
||||||
authWithHeaders,
|
|
||||||
} from '../../middlewares/auth';
|
|
||||||
|
|
||||||
const api = {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @apiIgnore Analytics are considered part of the private API
|
|
||||||
* @api {post} /analytics/track/:eventName Track a generic analytics event
|
|
||||||
* @apiName AnalyticsTrack
|
|
||||||
* @apiGroup Analytics
|
|
||||||
*
|
|
||||||
* @apiSuccess {Object} data An empty object
|
|
||||||
* */
|
|
||||||
api.trackEvent = {
|
|
||||||
method: 'POST',
|
|
||||||
url: '/analytics/track/:eventName',
|
|
||||||
// we authenticate these requests to make sure they actually came from a real user
|
|
||||||
middlewares: [authWithHeaders()],
|
|
||||||
async handler (req, res) {
|
|
||||||
// As of now only web can track events using this route
|
|
||||||
if (req.headers['x-client'] !== 'habitica-web') {
|
|
||||||
throw new NotAuthorized('Only habitica.com is allowed to track analytics events.');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { user } = res.locals;
|
|
||||||
const eventProperties = req.body;
|
|
||||||
|
|
||||||
res.analytics.track(req.params.eventName, {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
headers: req.headers,
|
|
||||||
category: 'behavior',
|
|
||||||
...eventProperties,
|
|
||||||
});
|
|
||||||
|
|
||||||
// not using res.respond
|
|
||||||
// because we don't want to send back notifications and other user-related data
|
|
||||||
res.status(200).send({});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default api;
|
|
||||||
@@ -1,270 +0,0 @@
|
|||||||
/* eslint-disable camelcase */
|
|
||||||
import nconf from 'nconf';
|
|
||||||
import Amplitude from 'amplitude';
|
|
||||||
import useragent from 'useragent';
|
|
||||||
import {
|
|
||||||
omit,
|
|
||||||
toArray,
|
|
||||||
} from 'lodash';
|
|
||||||
import common from '../../common';
|
|
||||||
import logger from './logger';
|
|
||||||
|
|
||||||
const LOG_AMPLITUDE_EVENTS = nconf.get('LOG_AMPLITUDE_EVENTS') === 'true';
|
|
||||||
const AMPLITUDE_TOKEN = nconf.get('AMPLITUDE_KEY');
|
|
||||||
const AMPLITUDE_PROPERTIES_TO_SCRUB = [
|
|
||||||
'uuid', 'user', 'purchaseValue',
|
|
||||||
'headers', 'registeredThrough',
|
|
||||||
];
|
|
||||||
|
|
||||||
const PLATFORM_MAP = Object.freeze({
|
|
||||||
'habitica-web': 'Web',
|
|
||||||
'habitica-ios': 'iOS',
|
|
||||||
'habitica-android': 'Android',
|
|
||||||
});
|
|
||||||
|
|
||||||
let amplitude;
|
|
||||||
if (AMPLITUDE_TOKEN) amplitude = new Amplitude(AMPLITUDE_TOKEN);
|
|
||||||
|
|
||||||
const Content = common.content;
|
|
||||||
|
|
||||||
function _lookUpItemName (itemKey) {
|
|
||||||
if (!itemKey) return null;
|
|
||||||
|
|
||||||
const gear = Content.gear.flat[itemKey];
|
|
||||||
const egg = Content.eggs[itemKey];
|
|
||||||
const food = Content.food[itemKey];
|
|
||||||
const hatchingPotion = Content.hatchingPotions[itemKey];
|
|
||||||
const quest = Content.quests[itemKey];
|
|
||||||
const spell = Content.special[itemKey];
|
|
||||||
|
|
||||||
let itemName;
|
|
||||||
|
|
||||||
if (gear) {
|
|
||||||
itemName = gear.text();
|
|
||||||
} else if (egg) {
|
|
||||||
itemName = `${egg.text()} Egg`;
|
|
||||||
} else if (food) {
|
|
||||||
itemName = food.text();
|
|
||||||
} else if (hatchingPotion) {
|
|
||||||
itemName = `${hatchingPotion.text()} Hatching Potion`;
|
|
||||||
} else if (quest) {
|
|
||||||
itemName = quest.text();
|
|
||||||
} else if (spell) {
|
|
||||||
itemName = spell.text();
|
|
||||||
}
|
|
||||||
|
|
||||||
return itemName;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _formatUserData (user) {
|
|
||||||
const properties = {};
|
|
||||||
|
|
||||||
if (user.stats) {
|
|
||||||
properties.Class = user.stats.class;
|
|
||||||
properties.Experience = Math.floor(user.stats.exp);
|
|
||||||
properties.Gold = Math.floor(user.stats.gp);
|
|
||||||
properties.Health = Math.ceil(user.stats.hp);
|
|
||||||
properties.Level = user.stats.lvl;
|
|
||||||
properties.Mana = Math.floor(user.stats.mp);
|
|
||||||
}
|
|
||||||
|
|
||||||
properties.balance = user.balance;
|
|
||||||
properties.balanceGemAmount = properties.balance * 4;
|
|
||||||
properties.tutorialComplete = user.flags && user.flags.tour && user.flags.tour.intro === -2;
|
|
||||||
properties.verifiedUsername = user.flags && user.flags.verifiedUsername;
|
|
||||||
if (properties.verifiedUsername && user.auth && user.auth.local) {
|
|
||||||
properties.username = user.auth.local.lowerCaseUsername;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.habits && user.dailys && user.todos && user.rewards) {
|
|
||||||
properties['Number Of Tasks'] = {
|
|
||||||
habits: user.habits.length,
|
|
||||||
dailys: user.dailys.length,
|
|
||||||
todos: user.todos.length,
|
|
||||||
rewards: user.rewards.length,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.contributor && user.contributor.level) {
|
|
||||||
properties.contributorLevel = user.contributor.level;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.purchased && user.purchased.plan.planId) {
|
|
||||||
properties.subscription = user.purchased.plan.planId;
|
|
||||||
} else {
|
|
||||||
properties.subscription = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user._ABtests) {
|
|
||||||
properties.ABtests = toArray(user._ABtests);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.loginIncentives) {
|
|
||||||
properties.loginIncentives = user.loginIncentives;
|
|
||||||
}
|
|
||||||
|
|
||||||
return properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _formatPlatformForAmplitude (platform) {
|
|
||||||
if (!platform) {
|
|
||||||
return 'Unknown';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (platform in PLATFORM_MAP) {
|
|
||||||
return PLATFORM_MAP[platform];
|
|
||||||
}
|
|
||||||
|
|
||||||
return '3rd Party';
|
|
||||||
}
|
|
||||||
|
|
||||||
function _formatUserAgentForAmplitude (platform, agentString) {
|
|
||||||
if (!agentString) {
|
|
||||||
return 'Unknown';
|
|
||||||
}
|
|
||||||
|
|
||||||
const agent = useragent.lookup(agentString).toJSON();
|
|
||||||
const formattedAgent = {};
|
|
||||||
if (platform === 'iOS' || platform === 'Android') {
|
|
||||||
formattedAgent.name = agent.os.family;
|
|
||||||
formattedAgent.version = `${agent.os.major}.${agent.os.minor}.${agent.os.patch}`;
|
|
||||||
if (platform === 'Android' && formattedAgent.name === 'Other') {
|
|
||||||
formattedAgent.name = 'Android';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
formattedAgent.name = agent.family;
|
|
||||||
formattedAgent.version = agent.major;
|
|
||||||
}
|
|
||||||
|
|
||||||
return formattedAgent;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _formatUUIDForAmplitude (uuid) {
|
|
||||||
return uuid || 'no-user-id-was-provided';
|
|
||||||
}
|
|
||||||
|
|
||||||
function _formatDataForAmplitude (data) {
|
|
||||||
const event_properties = omit(data, AMPLITUDE_PROPERTIES_TO_SCRUB);
|
|
||||||
const platform = _formatPlatformForAmplitude(data.headers && data.headers['x-client']);
|
|
||||||
const agent = _formatUserAgentForAmplitude(platform, data.headers && data.headers['user-agent']);
|
|
||||||
const ampData = {
|
|
||||||
user_id: _formatUUIDForAmplitude(data.uuid),
|
|
||||||
platform,
|
|
||||||
os_name: agent.name,
|
|
||||||
os_version: agent.version,
|
|
||||||
event_properties,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (data.user) {
|
|
||||||
ampData.user_properties = _formatUserData(data.user);
|
|
||||||
}
|
|
||||||
|
|
||||||
const itemName = _lookUpItemName(data.itemKey);
|
|
||||||
|
|
||||||
if (itemName) {
|
|
||||||
ampData.event_properties.itemName = itemName;
|
|
||||||
}
|
|
||||||
return ampData;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _sendDataToAmplitude (eventType, data, loggerOnly) {
|
|
||||||
const amplitudeData = _formatDataForAmplitude(data);
|
|
||||||
|
|
||||||
amplitudeData.event_type = eventType;
|
|
||||||
|
|
||||||
if (LOG_AMPLITUDE_EVENTS) {
|
|
||||||
logger.info('Amplitude Event', amplitudeData);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loggerOnly) return Promise.resolve(null);
|
|
||||||
|
|
||||||
return amplitude
|
|
||||||
.track(amplitudeData)
|
|
||||||
.catch(err => logger.error(err, 'Error while sending data to Amplitude.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
function _sendPurchaseDataToAmplitude (data) {
|
|
||||||
const amplitudeData = _formatDataForAmplitude(data);
|
|
||||||
|
|
||||||
// Stripe transactions come via webhook. We can log these as Web events
|
|
||||||
if (data.paymentMethod === 'Stripe' && amplitudeData.platform === 'Unknown') {
|
|
||||||
amplitudeData.platform = 'Web';
|
|
||||||
}
|
|
||||||
|
|
||||||
amplitudeData.event_type = 'purchase';
|
|
||||||
amplitudeData.revenue = data.purchaseValue;
|
|
||||||
amplitudeData.productId = data.itemPurchased;
|
|
||||||
|
|
||||||
if (LOG_AMPLITUDE_EVENTS) {
|
|
||||||
logger.info('Amplitude Purchase Event', amplitudeData);
|
|
||||||
}
|
|
||||||
|
|
||||||
return amplitude
|
|
||||||
.track(amplitudeData)
|
|
||||||
.catch(err => logger.error(err, 'Error while sending data to Amplitude.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
function _setOnce (dataToSetOnce, uuid) {
|
|
||||||
return amplitude
|
|
||||||
.identify({
|
|
||||||
user_id: _formatUUIDForAmplitude(uuid),
|
|
||||||
user_properties: {
|
|
||||||
$setOnce: dataToSetOnce,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.catch(err => logger.error(err, 'Error while sending data to Amplitude.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// There's no error handling directly here because it's handled inside _sendDataTo{Amplitude|Google}
|
|
||||||
async function track (eventType, data, loggerOnly = false) {
|
|
||||||
const { user } = data;
|
|
||||||
if (!user || !user.preferences || !user.preferences.analyticsConsent) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const promises = [
|
|
||||||
_sendDataToAmplitude(eventType, data, loggerOnly),
|
|
||||||
];
|
|
||||||
if (user.registeredThrough) {
|
|
||||||
promises.push(_setOnce({
|
|
||||||
registeredPlatform: user.registeredThrough,
|
|
||||||
}, data.uuid || user._id));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.all(promises);
|
|
||||||
}
|
|
||||||
|
|
||||||
// There's no error handling directly here because
|
|
||||||
// it's handled inside _sendPurchaseDataTo{Amplitude|Google}
|
|
||||||
async function trackPurchase (data) {
|
|
||||||
const { user } = data;
|
|
||||||
if (!user || !user.preferences || !user.preferences.analyticsConsent) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return Promise.all([
|
|
||||||
_sendPurchaseDataToAmplitude(data),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stub for non-prod environments
|
|
||||||
const mockAnalyticsService = {
|
|
||||||
track: () => { },
|
|
||||||
trackPurchase: () => { },
|
|
||||||
};
|
|
||||||
|
|
||||||
// Return the production or mock service based on the current environment
|
|
||||||
function getServiceByEnvironment () {
|
|
||||||
if (nconf.get('IS_PROD') || (nconf.get('DEBUG_ENABLED') && !nconf.get('BASE_URL').includes('localhost'))) {
|
|
||||||
return {
|
|
||||||
track,
|
|
||||||
trackPurchase,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return mockAnalyticsService;
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
track,
|
|
||||||
trackPurchase,
|
|
||||||
mockAnalyticsService,
|
|
||||||
getServiceByEnvironment as getAnalyticsServiceByEnvironment,
|
|
||||||
};
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import pick from 'lodash/pick';
|
|
||||||
import {
|
import {
|
||||||
BadRequest,
|
BadRequest,
|
||||||
NotAuthorized,
|
NotAuthorized,
|
||||||
@@ -19,6 +18,7 @@ import {
|
|||||||
} from './social';
|
} from './social';
|
||||||
import { loginRes } from './utils';
|
import { loginRes } from './utils';
|
||||||
import { verifyUsername } from '../user/validation';
|
import { verifyUsername } from '../user/validation';
|
||||||
|
import { trackRegistrationEvent } from '../localAnalytics';
|
||||||
|
|
||||||
const USERNAME_LENGTH_MIN = 1;
|
const USERNAME_LENGTH_MIN = 1;
|
||||||
const USERNAME_LENGTH_MAX = 20;
|
const USERNAME_LENGTH_MAX = 20;
|
||||||
@@ -180,6 +180,7 @@ async function registerLocal (req, res, { isV3 = false }) {
|
|||||||
} else {
|
} else {
|
||||||
newUser = new User(newUser);
|
newUser = new User(newUser);
|
||||||
newUser.registeredThrough = req.headers['x-client']; // Not saved, used to create the correct tasks based on the device used
|
newUser.registeredThrough = req.headers['x-client']; // Not saved, used to create the correct tasks based on the device used
|
||||||
|
trackRegistrationEvent({ user: newUser, method: 'local', ipAddress: req.ip });
|
||||||
}
|
}
|
||||||
|
|
||||||
// we check for partyInvite for backward compatibility
|
// we check for partyInvite for backward compatibility
|
||||||
@@ -217,16 +218,6 @@ async function registerLocal (req, res, { isV3 = false }) {
|
|||||||
})
|
})
|
||||||
.catch(err => logger.error(err));
|
.catch(err => logger.error(err));
|
||||||
|
|
||||||
if (!existingUser) {
|
|
||||||
res.analytics.track('register', {
|
|
||||||
user: pick(savedUser, ['preferences', 'registeredThrough']),
|
|
||||||
category: 'acquisition',
|
|
||||||
type: 'local',
|
|
||||||
uuid: savedUser._id,
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import pick from 'lodash/pick';
|
|
||||||
import passport from 'passport';
|
import passport from 'passport';
|
||||||
import common from '../../../common';
|
import common from '../../../common';
|
||||||
import { verifyUsername } from '../user/validation';
|
import { verifyUsername } from '../user/validation';
|
||||||
@@ -13,6 +12,7 @@ import { model as User } from '../../models/user';
|
|||||||
import { model as EmailUnsubscription } from '../../models/emailUnsubscription';
|
import { model as EmailUnsubscription } from '../../models/emailUnsubscription';
|
||||||
import { sendTxn as sendTxnEmail } from '../email';
|
import { sendTxn as sendTxnEmail } from '../email';
|
||||||
import { apiError } from '../apiError';
|
import { apiError } from '../apiError';
|
||||||
|
import { trackRegistrationEvent } from '../localAnalytics';
|
||||||
|
|
||||||
function _passportProfile (network, accessToken) {
|
function _passportProfile (network, accessToken) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@@ -145,6 +145,7 @@ export async function loginSocial (req, res) { // eslint-disable-line import/pre
|
|||||||
};
|
};
|
||||||
user = new User(user);
|
user = new User(user);
|
||||||
user.registeredThrough = req.headers['x-client']; // Not saved, used to create the correct tasks based on the device used
|
user.registeredThrough = req.headers['x-client']; // Not saved, used to create the correct tasks based on the device used
|
||||||
|
trackRegistrationEvent({ user, method: network, ipAddress: req.ip });
|
||||||
}
|
}
|
||||||
|
|
||||||
const savedUser = await user.save();
|
const savedUser = await user.save();
|
||||||
@@ -172,15 +173,5 @@ export async function loginSocial (req, res) { // eslint-disable-line import/pre
|
|||||||
.catch(err => logger.error(err)); // eslint-disable-line max-nested-callbacks
|
.catch(err => logger.error(err)); // eslint-disable-line max-nested-callbacks
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!existingUser) {
|
|
||||||
res.analytics.track('register', {
|
|
||||||
user: pick(savedUser, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: savedUser._id,
|
|
||||||
category: 'acquisition',
|
|
||||||
type: network,
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import mongoose from 'mongoose';
|
import mongoose from 'mongoose';
|
||||||
import pick from 'lodash/pick';
|
|
||||||
import nconf from 'nconf';
|
import nconf from 'nconf';
|
||||||
import { model as User } from '../models/user';
|
import { model as User } from '../models/user';
|
||||||
import * as Tasks from '../models/task';
|
import * as Tasks from '../models/task';
|
||||||
@@ -100,20 +99,6 @@ function processHabits (user, habits, now, daysMissed) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function trackCronAnalytics (analytics, user, _progress, options) {
|
|
||||||
analytics.track('Cron', {
|
|
||||||
category: 'behavior',
|
|
||||||
uuid: user._id,
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
resting: user.preferences.sleep,
|
|
||||||
cronCount: user.flags.cronCount,
|
|
||||||
progressUp: Math.min(_progress.up, 900),
|
|
||||||
progressDown: _progress.down,
|
|
||||||
headers: options.headers,
|
|
||||||
loginIncentives: user.loginIncentives,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function awardLoginIncentives (user) {
|
function awardLoginIncentives (user) {
|
||||||
if (user.loginIncentives > MAX_INCENTIVES) return;
|
if (user.loginIncentives > MAX_INCENTIVES) return;
|
||||||
|
|
||||||
@@ -165,7 +150,7 @@ function awardLoginIncentives (user) {
|
|||||||
// Perform various beginning-of-day reset actions.
|
// Perform various beginning-of-day reset actions.
|
||||||
export async function cron (options = {}) {
|
export async function cron (options = {}) {
|
||||||
const {
|
const {
|
||||||
user, tasksByType, analytics, now = new Date(), daysMissed, timezoneUtcOffsetFromUserPrefs,
|
user, tasksByType, now = new Date(), daysMissed, timezoneUtcOffsetFromUserPrefs,
|
||||||
} = options;
|
} = options;
|
||||||
let _progress = { down: 0, up: 0, collectedItems: 0 };
|
let _progress = { down: 0, up: 0, collectedItems: 0 };
|
||||||
|
|
||||||
@@ -392,9 +377,7 @@ export async function cron (options = {}) {
|
|||||||
user.pinnedItems = common.cleanupPinnedItems(user);
|
user.pinnedItems = common.cleanupPinnedItems(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Analytics
|
|
||||||
user.flags.cronCount += 1;
|
user.flags.cronCount += 1;
|
||||||
trackCronAnalytics(analytics, user, _progress, options);
|
|
||||||
|
|
||||||
await UserHistory.beginUserHistoryUpdate(user._id, options.headers)
|
await UserHistory.beginUserHistoryUpdate(user._id, options.headers)
|
||||||
.withCron(user.flags.cronCount)
|
.withCron(user.flags.cronCount)
|
||||||
@@ -438,7 +421,6 @@ export async function cronWrapper (req, res) {
|
|||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
if (!user) return null; // User might not be available when authentication is not mandatory
|
if (!user) return null; // User might not be available when authentication is not mandatory
|
||||||
|
|
||||||
const { analytics } = res;
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
let session;
|
let session;
|
||||||
|
|
||||||
@@ -488,7 +470,6 @@ export async function cronWrapper (req, res) {
|
|||||||
tasksByType,
|
tasksByType,
|
||||||
now,
|
now,
|
||||||
daysMissed,
|
daysMissed,
|
||||||
analytics,
|
|
||||||
timezoneUtcOffsetFromUserPrefs,
|
timezoneUtcOffsetFromUserPrefs,
|
||||||
headers: req.headers,
|
headers: req.headers,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import find from 'lodash/find';
|
import find from 'lodash/find';
|
||||||
import includes from 'lodash/includes';
|
import includes from 'lodash/includes';
|
||||||
import pick from 'lodash/pick';
|
|
||||||
|
|
||||||
import { encrypt } from '../encryption';
|
import { encrypt } from '../encryption';
|
||||||
import { sendNotification as sendPushNotification } from '../pushNotifications';
|
import { sendNotification as sendPushNotification } from '../pushNotifications';
|
||||||
@@ -143,23 +142,6 @@ async function inviteByUUID (uuid, group, inviter, req, res) {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
const analyticsObject = {
|
|
||||||
user: pick(inviter, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: inviter._id,
|
|
||||||
hitType: 'event',
|
|
||||||
category: 'behavior',
|
|
||||||
invitee: uuid,
|
|
||||||
groupId: group._id,
|
|
||||||
groupType: group.type,
|
|
||||||
headers: req.headers,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (group.type === 'party') {
|
|
||||||
analyticsObject.seekingParty = Boolean(userToInvite.party.seeking);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.analytics.track('group invite', analyticsObject);
|
|
||||||
|
|
||||||
return addInvitationToUser(userToInvite, group, inviter, res);
|
return addInvitationToUser(userToInvite, group, inviter, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,19 +189,6 @@ async function inviteByEmail (invite, group, inviter, req, res) {
|
|||||||
const userIsUnsubscribed = await EmailUnsubscription.findOne({ email: invite.email }).exec();
|
const userIsUnsubscribed = await EmailUnsubscription.findOne({ email: invite.email }).exec();
|
||||||
const groupLabel = group.type === 'guild' ? '-guild' : '';
|
const groupLabel = group.type === 'guild' ? '-guild' : '';
|
||||||
if (!userIsUnsubscribed) sendTxnEmail(invite, `invite-friend${groupLabel}`, variables);
|
if (!userIsUnsubscribed) sendTxnEmail(invite, `invite-friend${groupLabel}`, variables);
|
||||||
|
|
||||||
const analyticsObject = {
|
|
||||||
user: pick(inviter, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: inviter._id,
|
|
||||||
hitType: 'event',
|
|
||||||
category: 'behavior',
|
|
||||||
invitee: 'email',
|
|
||||||
groupId: group._id,
|
|
||||||
groupType: group.type,
|
|
||||||
headers: req.headers,
|
|
||||||
};
|
|
||||||
|
|
||||||
res.analytics.track('group invite', analyticsObject);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return userReturnInfo;
|
return userReturnInfo;
|
||||||
@@ -245,24 +214,6 @@ async function inviteByUserName (username, group, inviter, req, res) {
|
|||||||
{ userId: userToInvite._id, username: userToInvite.profile.name },
|
{ userId: userToInvite._id, username: userToInvite.profile.name },
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
const analyticsObject = {
|
|
||||||
user: pick(inviter, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: inviter._id,
|
|
||||||
hitType: 'event',
|
|
||||||
category: 'behavior',
|
|
||||||
invitee: userToInvite._id,
|
|
||||||
groupId: group._id,
|
|
||||||
groupType: group.type,
|
|
||||||
headers: req.headers,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (group.type === 'party') {
|
|
||||||
analyticsObject.seekingParty = Boolean(userToInvite.party.seeking);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.analytics.track('group invite', analyticsObject);
|
|
||||||
|
|
||||||
return addInvitationToUser(userToInvite, group, inviter, res);
|
return addInvitationToUser(userToInvite, group, inviter, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import nconf from 'nconf';
|
||||||
|
import { RegistrationEventModel } from '../models/analytics/registrationEvent';
|
||||||
|
import { SubscriptionEventModel } from '../models/analytics/subscriptionEvent';
|
||||||
|
|
||||||
|
const LOCAL_ANALYTICS = !nconf.get('DISABLE_LOCAL_ANALYTICS');
|
||||||
|
|
||||||
|
function getAuthenticationMethod (user) {
|
||||||
|
if (user.auth.google && user.auth.google.id) return 'google';
|
||||||
|
if (user.auth.facebook && user.auth.facebook.id) return 'facebook';
|
||||||
|
if (user.auth.apple && user.auth.apple.id) return 'apple';
|
||||||
|
return 'local';
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function trackRegistrationEvent (eventData) {
|
||||||
|
if (!LOCAL_ANALYTICS) return null;
|
||||||
|
|
||||||
|
const { user, ipAddress, method } = eventData;
|
||||||
|
|
||||||
|
const registrationEvent = new RegistrationEventModel({
|
||||||
|
userId: user._id,
|
||||||
|
ipAddress,
|
||||||
|
authenticationMethod: method || getAuthenticationMethod(user),
|
||||||
|
platform: user.registeredThrough,
|
||||||
|
language: user.preferences.language,
|
||||||
|
});
|
||||||
|
return registrationEvent.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function trackSubscriptionEvent (eventData) {
|
||||||
|
if (!LOCAL_ANALYTICS) return null;
|
||||||
|
|
||||||
|
const {
|
||||||
|
eventType,
|
||||||
|
user,
|
||||||
|
paymentMethod,
|
||||||
|
customerId,
|
||||||
|
planId,
|
||||||
|
cancellationReason,
|
||||||
|
} = eventData;
|
||||||
|
|
||||||
|
const subscriptionEvent = new SubscriptionEventModel({
|
||||||
|
userId: user._id,
|
||||||
|
eventType,
|
||||||
|
paymentMethod,
|
||||||
|
customerId,
|
||||||
|
planId,
|
||||||
|
cancellationReason,
|
||||||
|
});
|
||||||
|
return subscriptionEvent.save();
|
||||||
|
}
|
||||||
@@ -38,3 +38,17 @@ export default async function connectToMongoDB () {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let analyticsDb;
|
||||||
|
|
||||||
|
export function getAnalyticsDatabase () {
|
||||||
|
if (!analyticsDb) {
|
||||||
|
const analyticsDbName = nconf.get('ANALYTICS_DB');
|
||||||
|
const analyticsDbUri = nconf.get('ANALYTICS_DB_URI') || connectionUrl;
|
||||||
|
analyticsDb = mongoose.createConnection(analyticsDbUri, {
|
||||||
|
...mongooseOptions,
|
||||||
|
dbName: analyticsDbName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return analyticsDb;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import find from 'lodash/find';
|
import find from 'lodash/find';
|
||||||
import pick from 'lodash/pick';
|
|
||||||
import { getAnalyticsServiceByEnvironment } from '../analyticsService';
|
|
||||||
import { getCurrentEventList } from '../worldState'; // eslint-disable-line import/no-cycle
|
import { getCurrentEventList } from '../worldState'; // eslint-disable-line import/no-cycle
|
||||||
import { // eslint-disable-line import/no-cycle
|
import { // eslint-disable-line import/no-cycle
|
||||||
getUserInfo,
|
getUserInfo,
|
||||||
@@ -13,8 +11,6 @@ import {
|
|||||||
} from '../errors';
|
} from '../errors';
|
||||||
import { apiError } from '../apiError';
|
import { apiError } from '../apiError';
|
||||||
|
|
||||||
const analytics = getAnalyticsServiceByEnvironment();
|
|
||||||
|
|
||||||
function getGiftMessage (data, byUsername, gemAmount, language) {
|
function getGiftMessage (data, byUsername, gemAmount, language) {
|
||||||
const senderMsg = shared.i18n.t('giftedGemsFull', {
|
const senderMsg = shared.i18n.t('giftedGemsFull', {
|
||||||
username: data.gift.member.profile.name,
|
username: data.gift.member.profile.name,
|
||||||
@@ -114,20 +110,6 @@ export async function buyGems (data) {
|
|||||||
|
|
||||||
if (!data.gift) txnEmail(data.user, 'donation');
|
if (!data.gift) txnEmail(data.user, 'donation');
|
||||||
|
|
||||||
analytics.trackPurchase({
|
|
||||||
user: pick(data.user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: data.user._id,
|
|
||||||
itemPurchased: 'Gems',
|
|
||||||
sku: `${data.paymentMethod.toLowerCase()}-checkout`,
|
|
||||||
purchaseType: 'checkout',
|
|
||||||
paymentMethod: data.paymentMethod,
|
|
||||||
quantity: 1,
|
|
||||||
gift: Boolean(data.gift),
|
|
||||||
purchaseValue: amt,
|
|
||||||
headers: data.headers,
|
|
||||||
firstPurchase: data.user.purchased.txnCount === 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (data.gift) await buyGemGift(data);
|
if (data.gift) await buyGemGift(data);
|
||||||
|
|
||||||
await data.user.save();
|
await data.user.save();
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
import pick from 'lodash/pick';
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import {
|
import {
|
||||||
BadRequest,
|
BadRequest,
|
||||||
} from '../errors';
|
} from '../errors';
|
||||||
import shared from '../../../common';
|
import shared from '../../../common';
|
||||||
import { getAnalyticsServiceByEnvironment } from '../analyticsService';
|
|
||||||
import { getGemsBlock, buyGems } from './gems'; // eslint-disable-line import/no-cycle
|
import { getGemsBlock, buyGems } from './gems'; // eslint-disable-line import/no-cycle
|
||||||
|
|
||||||
const analytics = getAnalyticsServiceByEnvironment();
|
|
||||||
|
|
||||||
const RESPONSE_INVALID_ITEM = 'INVALID_ITEM_PURCHASED';
|
const RESPONSE_INVALID_ITEM = 'INVALID_ITEM_PURCHASED';
|
||||||
|
|
||||||
const EVENTS = {
|
const EVENTS = {
|
||||||
@@ -31,19 +27,6 @@ async function buyGryphatrice (data) {
|
|||||||
data.user.items.pets[key] = 5;
|
data.user.items.pets[key] = 5;
|
||||||
data.user.purchased.txnCount += 1;
|
data.user.purchased.txnCount += 1;
|
||||||
|
|
||||||
analytics.trackPurchase({
|
|
||||||
user: pick(data.user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: data.user._id,
|
|
||||||
itemPurchased: 'Gryphatrice',
|
|
||||||
sku: `${data.paymentMethod.toLowerCase()}-checkout`,
|
|
||||||
purchaseType: 'checkout',
|
|
||||||
paymentMethod: data.paymentMethod,
|
|
||||||
quantity: 1,
|
|
||||||
gift: Boolean(data.gift),
|
|
||||||
purchaseValue: 10,
|
|
||||||
headers: data.headers,
|
|
||||||
firstPurchase: data.user.purchased.txnCount === 1,
|
|
||||||
});
|
|
||||||
if (data.user.markModified) data.user.markModified('items.pets');
|
if (data.user.markModified) data.user.markModified('items.pets');
|
||||||
await data.user.save();
|
await data.user.save();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,7 @@
|
|||||||
import defaults from 'lodash/defaults';
|
import defaults from 'lodash/defaults';
|
||||||
import each from 'lodash/each';
|
import each from 'lodash/each';
|
||||||
import find from 'lodash/find';
|
import find from 'lodash/find';
|
||||||
import pick from 'lodash/pick';
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
import { getAnalyticsServiceByEnvironment } from '../analyticsService';
|
|
||||||
import * as slack from '../slack'; // eslint-disable-line import/no-cycle
|
import * as slack from '../slack'; // eslint-disable-line import/no-cycle
|
||||||
import { // eslint-disable-line import/no-cycle
|
import { // eslint-disable-line import/no-cycle
|
||||||
getUserInfo,
|
getUserInfo,
|
||||||
@@ -26,10 +23,10 @@ import calculateSubscriptionTerminationDate from './calculateSubscriptionTermina
|
|||||||
import { getCurrentEventList } from '../worldState'; // eslint-disable-line import/no-cycle
|
import { getCurrentEventList } from '../worldState'; // eslint-disable-line import/no-cycle
|
||||||
import { paymentConstants } from './constants';
|
import { paymentConstants } from './constants';
|
||||||
import { addSubscriptionToGroupUsers, cancelGroupUsersSubscription } from './groupPayments'; // eslint-disable-line import/no-cycle
|
import { addSubscriptionToGroupUsers, cancelGroupUsersSubscription } from './groupPayments'; // eslint-disable-line import/no-cycle
|
||||||
|
import { trackSubscriptionEvent } from '../localAnalytics';
|
||||||
|
|
||||||
// @TODO: Abstract to shared/constant
|
// @TODO: Abstract to shared/constant
|
||||||
const JOINED_GROUP_PLAN = 'joined group plan';
|
const JOINED_GROUP_PLAN = 'joined group plan';
|
||||||
const analytics = getAnalyticsServiceByEnvironment();
|
|
||||||
|
|
||||||
function _findMysteryItems (user, dateMoment) {
|
function _findMysteryItems (user, dateMoment) {
|
||||||
const pushedItems = [];
|
const pushedItems = [];
|
||||||
@@ -81,6 +78,14 @@ async function prepareSubscriptionValues (data) {
|
|||||||
? shared.content.subscriptionBlocks[data.updatedFrom.key]
|
? shared.content.subscriptionBlocks[data.updatedFrom.key]
|
||||||
: undefined;
|
: undefined;
|
||||||
let months;
|
let months;
|
||||||
|
let subscriptionEventType = 'subscribed';
|
||||||
|
if (updatedFrom) {
|
||||||
|
if (Number(updatedFrom.months) > Number(block.months)) {
|
||||||
|
subscriptionEventType = 'downgraded';
|
||||||
|
} else {
|
||||||
|
subscriptionEventType = 'upgraded';
|
||||||
|
}
|
||||||
|
}
|
||||||
if (updatedFrom && Number(updatedFrom.months) !== 1) {
|
if (updatedFrom && Number(updatedFrom.months) !== 1) {
|
||||||
if (Number(updatedFrom.months) > Number(block.months)) {
|
if (Number(updatedFrom.months) > Number(block.months)) {
|
||||||
months = 0;
|
months = 0;
|
||||||
@@ -126,13 +131,6 @@ async function prepareSubscriptionValues (data) {
|
|||||||
user: data.user, groupId: data.groupId, populateLeader: false, groupFields,
|
user: data.user, groupId: data.groupId, populateLeader: false, groupFields,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (group) {
|
|
||||||
analytics.track(
|
|
||||||
data.groupID,
|
|
||||||
data.demographics,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!group) {
|
if (!group) {
|
||||||
throw new NotFound(shared.i18n.t('groupNotFound'));
|
throw new NotFound(shared.i18n.t('groupNotFound'));
|
||||||
}
|
}
|
||||||
@@ -230,6 +228,7 @@ async function prepareSubscriptionValues (data) {
|
|||||||
purchaseType,
|
purchaseType,
|
||||||
emailType,
|
emailType,
|
||||||
isNewSubscription,
|
isNewSubscription,
|
||||||
|
subscriptionEventType,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,10 +241,9 @@ async function createSubscription (data) {
|
|||||||
autoRenews,
|
autoRenews,
|
||||||
group,
|
group,
|
||||||
groupId,
|
groupId,
|
||||||
itemPurchased,
|
|
||||||
purchaseType,
|
|
||||||
emailType,
|
emailType,
|
||||||
isNewSubscription,
|
isNewSubscription,
|
||||||
|
subscriptionEventType,
|
||||||
} = await prepareSubscriptionValues(data);
|
} = await prepareSubscriptionValues(data);
|
||||||
if (recipient !== group) {
|
if (recipient !== group) {
|
||||||
recipient.items.pets['Jackalope-RoyalPurple'] = 5;
|
recipient.items.pets['Jackalope-RoyalPurple'] = 5;
|
||||||
@@ -277,22 +275,6 @@ async function createSubscription (data) {
|
|||||||
|
|
||||||
if (!group && !data.promo) data.user.purchased.txnCount += 1;
|
if (!group && !data.promo) data.user.purchased.txnCount += 1;
|
||||||
|
|
||||||
if (!data.promo) {
|
|
||||||
analytics.trackPurchase({
|
|
||||||
uuid: data.user._id,
|
|
||||||
groupId,
|
|
||||||
itemPurchased,
|
|
||||||
sku: `${data.paymentMethod.toLowerCase()}-subscription`,
|
|
||||||
purchaseType,
|
|
||||||
paymentMethod: data.paymentMethod,
|
|
||||||
quantity: 1,
|
|
||||||
gift: Boolean(data.gift),
|
|
||||||
purchaseValue: block.price,
|
|
||||||
headers: data.headers || { 'x-client': 'habitica-web' },
|
|
||||||
firstPurchase: !group && data.user.purchased.txnCount === 1,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.gift) {
|
if (data.gift) {
|
||||||
const byUserName = getUserInfo(data.user, ['name']).name;
|
const byUserName = getUserInfo(data.user, ['name']).name;
|
||||||
|
|
||||||
@@ -381,6 +363,16 @@ async function createSubscription (data) {
|
|||||||
if (data.user && data.user.isModified()) await data.user.save();
|
if (data.user && data.user.isModified()) await data.user.save();
|
||||||
if (data.gift) await data.gift.member.save();
|
if (data.gift) await data.gift.member.save();
|
||||||
|
|
||||||
|
await trackSubscriptionEvent({
|
||||||
|
eventType: subscriptionEventType,
|
||||||
|
user: data.gift ? data.gift.member : data.user,
|
||||||
|
gifted: data.gift !== undefined,
|
||||||
|
autoRenews,
|
||||||
|
paymentMethod: data.paymentMethod,
|
||||||
|
planId: block.key,
|
||||||
|
customerId: plan.customerId,
|
||||||
|
});
|
||||||
|
|
||||||
slack.sendSubscriptionNotification({
|
slack.sendSubscriptionNotification({
|
||||||
buyer: {
|
buyer: {
|
||||||
id: data.user._id,
|
id: data.user._id,
|
||||||
@@ -403,8 +395,6 @@ async function createSubscription (data) {
|
|||||||
async function cancelSubscription (data) {
|
async function cancelSubscription (data) {
|
||||||
let plan;
|
let plan;
|
||||||
let group;
|
let group;
|
||||||
let cancelType = 'unsubscribe';
|
|
||||||
let groupId;
|
|
||||||
let emailType;
|
let emailType;
|
||||||
const emailMergeData = [];
|
const emailMergeData = [];
|
||||||
let sendEmail = true;
|
let sendEmail = true;
|
||||||
@@ -462,17 +452,13 @@ async function cancelSubscription (data) {
|
|||||||
txnEmail(data.user, emailType, emailMergeData);
|
txnEmail(data.user, emailType, emailMergeData);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (group) {
|
await trackSubscriptionEvent({
|
||||||
cancelType = 'group-unsubscribe';
|
eventType: 'cancelled',
|
||||||
groupId = group._id;
|
user: data.user,
|
||||||
}
|
cancellationReason: data.cancellationReason,
|
||||||
|
paymentMethod: plan.paymentMethod,
|
||||||
analytics.track(cancelType, {
|
planId: plan.planId,
|
||||||
uuid: data.user._id,
|
customerId: plan.customerId,
|
||||||
user: pick(data.user, ['preferences', 'registeredThrough']),
|
|
||||||
groupId,
|
|
||||||
paymentMethod: data.paymentMethod,
|
|
||||||
headers: data.headers,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import cloneDeep from 'lodash/cloneDeep';
|
|||||||
import compact from 'lodash/compact';
|
import compact from 'lodash/compact';
|
||||||
import forEach from 'lodash/forEach';
|
import forEach from 'lodash/forEach';
|
||||||
import keys from 'lodash/keys';
|
import keys from 'lodash/keys';
|
||||||
import pick from 'lodash/pick';
|
|
||||||
import remove from 'lodash/remove';
|
import remove from 'lodash/remove';
|
||||||
import validator from 'validator';
|
import validator from 'validator';
|
||||||
import {
|
import {
|
||||||
@@ -77,7 +76,7 @@ async function createTasks (req, res, options = {}) {
|
|||||||
// are the onboarding ones
|
// are the onboarding ones
|
||||||
if (!user.achievements.createdTask && user.flags.welcomed) {
|
if (!user.achievements.createdTask && user.flags.welcomed) {
|
||||||
user.addAchievement('createdTask');
|
user.addAchievement('createdTask');
|
||||||
shared.onboarding.checkOnboardingStatus(user, req, res.analytics);
|
shared.onboarding.checkOnboardingStatus(user, req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -462,14 +461,14 @@ async function scoreTask (user, task, direction, req, res) {
|
|||||||
task,
|
task,
|
||||||
user: rollbackUser,
|
user: rollbackUser,
|
||||||
direction,
|
direction,
|
||||||
}, req, res.analytics);
|
}, req);
|
||||||
await rollbackUser.save();
|
await rollbackUser.save();
|
||||||
} else {
|
} else {
|
||||||
delta = shared.ops.scoreTask({ task, user, direction }, req, res.analytics);
|
delta = shared.ops.scoreTask({ task, user, direction }, req);
|
||||||
}
|
}
|
||||||
// Drop system (don't run on the client,
|
// Drop system (don't run on the client,
|
||||||
// as it would only be discarded since ops are sent to the API, not the results)
|
// as it would only be discarded since ops are sent to the API, not the results)
|
||||||
if (direction === 'up' && !firstTask) shared.fns.randomDrop(user, { task, delta }, req, res.analytics);
|
if (direction === 'up' && !firstTask) shared.fns.randomDrop(user, { task, delta }, req);
|
||||||
|
|
||||||
// If a todo was completed or uncompleted move it in or out of the user.tasksOrder.todos list
|
// If a todo was completed or uncompleted move it in or out of the user.tasksOrder.todos list
|
||||||
// TODO move to common code?
|
// TODO move to common code?
|
||||||
@@ -506,28 +505,6 @@ async function scoreTask (user, task, direction, req, res) {
|
|||||||
user,
|
user,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (group) {
|
|
||||||
let role;
|
|
||||||
if (group.leader === user._id) {
|
|
||||||
role = 'leader';
|
|
||||||
} else if (group.managers[user._id]) {
|
|
||||||
role = 'manager';
|
|
||||||
} else {
|
|
||||||
role = 'member';
|
|
||||||
}
|
|
||||||
res.analytics.track('team task scored', {
|
|
||||||
user: pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
hitType: 'event',
|
|
||||||
category: 'behavior',
|
|
||||||
taskType: task.type,
|
|
||||||
direction,
|
|
||||||
headers: req.headers,
|
|
||||||
groupID: group._id,
|
|
||||||
role,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
task,
|
task,
|
||||||
delta,
|
delta,
|
||||||
|
|||||||
@@ -116,13 +116,6 @@ export async function update (req, res, { isV3 = false }) {
|
|||||||
if (req.body['party.seeking'] !== undefined && req.body['party.seeking'] !== null) {
|
if (req.body['party.seeking'] !== undefined && req.body['party.seeking'] !== null) {
|
||||||
user.invitations.party = {};
|
user.invitations.party = {};
|
||||||
user.invitations.parties = [];
|
user.invitations.parties = [];
|
||||||
res.analytics.track('Starts Looking for Party', {
|
|
||||||
user: _.pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
hitType: 'event',
|
|
||||||
category: 'behavior',
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let slurWasUsed = false;
|
let slurWasUsed = false;
|
||||||
@@ -200,13 +193,6 @@ export async function update (req, res, { isV3 = false }) {
|
|||||||
|
|
||||||
if (key === 'party.seeking' && val === null) {
|
if (key === 'party.seeking' && val === null) {
|
||||||
user.party.seeking = undefined;
|
user.party.seeking = undefined;
|
||||||
res.analytics.track('Leaves Looking for Party', {
|
|
||||||
user: _.pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
hitType: 'event',
|
|
||||||
category: 'behavior',
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
} else if (key === 'tags') {
|
} else if (key === 'tags') {
|
||||||
if (!Array.isArray(val)) throw new BadRequest('Tag list must be an array.');
|
if (!Array.isArray(val)) throw new BadRequest('Tag list must be an array.');
|
||||||
|
|
||||||
@@ -291,14 +277,6 @@ export async function reset (req, res, { isV3 = false }) {
|
|||||||
user.save(),
|
user.save(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
res.analytics.track('account reset', {
|
|
||||||
user: _.pick(user, ['preferences', 'registeredThrough']),
|
|
||||||
uuid: user._id,
|
|
||||||
hitType: 'event',
|
|
||||||
category: 'behavior',
|
|
||||||
headers: req.headers,
|
|
||||||
});
|
|
||||||
|
|
||||||
res.respond(200, ...resetRes);
|
res.respond(200, ...resetRes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,7 +288,7 @@ export async function reroll (req, res, { isV3 = false }) {
|
|||||||
...Tasks.taskIsGroupOrChallengeQuery,
|
...Tasks.taskIsGroupOrChallengeQuery,
|
||||||
};
|
};
|
||||||
const tasks = await Tasks.Task.find(query).exec();
|
const tasks = await Tasks.Task.find(query).exec();
|
||||||
const rerollRes = await common.ops.reroll(user, tasks, req, res.analytics);
|
const rerollRes = await common.ops.reroll(user, tasks, req);
|
||||||
if (isV3) {
|
if (isV3) {
|
||||||
rerollRes[0].user = await rerollRes[0].user.toJSONWithInbox();
|
rerollRes[0].user = await rerollRes[0].user.toJSONWithInbox();
|
||||||
}
|
}
|
||||||
@@ -331,7 +309,7 @@ export async function rebirth (req, res, { isV3 = false }) {
|
|||||||
...Tasks.taskIsGroupOrChallengeQuery,
|
...Tasks.taskIsGroupOrChallengeQuery,
|
||||||
}).exec();
|
}).exec();
|
||||||
|
|
||||||
const rebirthRes = await common.ops.rebirth(user, tasks, req, res.analytics);
|
const rebirthRes = await common.ops.rebirth(user, tasks, req);
|
||||||
if (isV3) {
|
if (isV3) {
|
||||||
rebirthRes[0].user = await rebirthRes[0].user.toJSONWithInbox();
|
rebirthRes[0].user = await rebirthRes[0].user.toJSONWithInbox();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
import {
|
|
||||||
getAnalyticsServiceByEnvironment,
|
|
||||||
} from '../libs/analyticsService';
|
|
||||||
|
|
||||||
const service = getAnalyticsServiceByEnvironment();
|
|
||||||
|
|
||||||
export default function attachAnalytics (req, res, next) {
|
|
||||||
res.analytics = service;
|
|
||||||
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import expressValidator from 'express-validator';
|
import expressValidator from 'express-validator';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import analytics from './analytics';
|
|
||||||
import setupBody from './setupBody';
|
import setupBody from './setupBody';
|
||||||
import rateLimiter from './rateLimiter';
|
import rateLimiter from './rateLimiter';
|
||||||
import setupExpress from '../libs/setupExpress';
|
import setupExpress from '../libs/setupExpress';
|
||||||
@@ -17,7 +16,6 @@ const app = express();
|
|||||||
setupExpress(app);
|
setupExpress(app);
|
||||||
|
|
||||||
app.use(expressValidator());
|
app.use(expressValidator());
|
||||||
app.use(analytics);
|
|
||||||
app.use(setupBody);
|
app.use(setupBody);
|
||||||
|
|
||||||
const topLevelRouter = express.Router(); // eslint-disable-line new-cap
|
const topLevelRouter = express.Router(); // eslint-disable-line new-cap
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
import validator from 'validator';
|
||||||
|
import baseModel from '../../libs/baseModel';
|
||||||
|
import { getAnalyticsDatabase } from '../../libs/mongoose';
|
||||||
|
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
|
export const schema = new Schema({
|
||||||
|
userId: {
|
||||||
|
$type: String, ref: 'User', required: true, validate: [v => validator.isUUID(v), 'Invalid uuid for user.'],
|
||||||
|
},
|
||||||
|
ipAddress: { $type: String },
|
||||||
|
platform: { $type: String },
|
||||||
|
authenticationMethod: { $type: String },
|
||||||
|
language: { $type: String },
|
||||||
|
}, {
|
||||||
|
strict: true,
|
||||||
|
typeKey: '$type',
|
||||||
|
});
|
||||||
|
|
||||||
|
schema.plugin(baseModel, {
|
||||||
|
noSet: [
|
||||||
|
'id',
|
||||||
|
'_id',
|
||||||
|
'userId',
|
||||||
|
'platform',
|
||||||
|
'authenticationMethod',
|
||||||
|
], // Nothing can be set from the client
|
||||||
|
timestamps: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const RegistrationEventModel = getAnalyticsDatabase().model('RegistrationEvent', schema);
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
import validator from 'validator';
|
||||||
|
import baseModel from '../../libs/baseModel';
|
||||||
|
import { getAnalyticsDatabase } from '../../libs/mongoose';
|
||||||
|
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
const eventTypes = ['subscribed', 'cancelled', 'resubscribed', 'upgraded', 'downgraded'];
|
||||||
|
|
||||||
|
export const schema = new Schema({
|
||||||
|
userId: {
|
||||||
|
$type: String, required: true, validate: [v => validator.isUUID(v), 'Invalid uuid for user.'],
|
||||||
|
},
|
||||||
|
ipAddress: { $type: String },
|
||||||
|
eventType: { $type: String, enum: eventTypes, required: true },
|
||||||
|
paymentMethod: { $type: String },
|
||||||
|
customerId: { $type: String },
|
||||||
|
planId: { $type: String },
|
||||||
|
cancellationReason: { $type: String },
|
||||||
|
}, {
|
||||||
|
strict: true,
|
||||||
|
typeKey: '$type',
|
||||||
|
});
|
||||||
|
|
||||||
|
schema.plugin(baseModel, {
|
||||||
|
noSet: [
|
||||||
|
'id',
|
||||||
|
'_id',
|
||||||
|
'userId',
|
||||||
|
'eventType',
|
||||||
|
'paymentMethod',
|
||||||
|
'customerId',
|
||||||
|
'planId',
|
||||||
|
'cancellationReason',
|
||||||
|
], // Nothing can be set from the client
|
||||||
|
timestamps: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SubscriptionEventModel = getAnalyticsDatabase().model('SubscriptionEvent', schema);
|
||||||
@@ -31,7 +31,9 @@ process.on('SIGTERM', async () => {
|
|||||||
console.log('SIGTERM signal received: closing HTTP server');
|
console.log('SIGTERM signal received: closing HTTP server');
|
||||||
server.close(async () => {
|
server.close(async () => {
|
||||||
await mongoose.disconnect();
|
await mongoose.disconnect();
|
||||||
await redis.quit();
|
if (redis.quit) {
|
||||||
|
await redis.quit();
|
||||||
|
}
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user