From 72e5aaf8094c79c5019b7865cf4b59f2ce95ff4d Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Sat, 10 Apr 2021 12:33:46 +0300 Subject: [PATCH 01/20] discussions migration Signed-off-by: Alexander Ivanov --- .../migration.sql | 55 +++++ packages/core/prisma/schema.prisma | 61 ++++++ .../commons/types/CommonMetadata.type.ts | 2 +- yarn.lock | 190 ++++++++++++++++++ 4 files changed, 307 insertions(+), 1 deletion(-) create mode 100644 packages/core/prisma/migrations/20210410091827_initial_discussions_migration/migration.sql diff --git a/packages/core/prisma/migrations/20210410091827_initial_discussions_migration/migration.sql b/packages/core/prisma/migrations/20210410091827_initial_discussions_migration/migration.sql new file mode 100644 index 000000000..cb235202b --- /dev/null +++ b/packages/core/prisma/migrations/20210410091827_initial_discussions_migration/migration.sql @@ -0,0 +1,55 @@ +-- CreateEnum +CREATE TYPE "DiscussionType" AS ENUM ('ProposalDiscussion', 'CommonDiscussion'); + +-- CreateEnum +CREATE TYPE "DiscussionSubscriptionType" AS ENUM ('AllNotifications', 'OnlyMentions', 'NoNotification'); + +-- CreateEnum +CREATE TYPE "DiscussionMessageType" AS ENUM ('Message'); + +-- CreateTable +CREATE TABLE "Discussion" +( + "id" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "topic" TEXT NOT NULL, + "description" TEXT NOT NULL, + "latestMessage" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "DiscussionSubscription" +( + "id" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "latestMessageSeen" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "type" "DiscussionSubscriptionType" NOT NULL DEFAULT E'AllNotifications', + "discussionId" TEXT NOT NULL, + + PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "DiscussionMessage" +( + "id" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "type" "DiscussionMessageType" NOT NULL DEFAULT E'Message', + "message" TEXT NOT NULL, + "discussionId" TEXT, + + PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "DiscussionSubscription" + ADD FOREIGN KEY ("discussionId") REFERENCES "Discussion" ("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "DiscussionMessage" + ADD FOREIGN KEY ("discussionId") REFERENCES "Discussion" ("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/packages/core/prisma/schema.prisma b/packages/core/prisma/schema.prisma index 5b04b9cef..a5ec55432 100644 --- a/packages/core/prisma/schema.prisma +++ b/packages/core/prisma/schema.prisma @@ -436,3 +436,64 @@ enum EventType { UserCreated } + +// ---- Discussions + +enum DiscussionType { + ProposalDiscussion + CommonDiscussion +} + +model Discussion { + id String @id @default(uuid()) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + topic String + description String + + latestMessage DateTime @default(now()) + + subscriptions DiscussionSubscription[] + messages DiscussionMessage[] +} + +model DiscussionSubscription { + id String @id @default(uuid()) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + latestMessageSeen DateTime @default(now()) + + type DiscussionSubscriptionType @default(AllNotifications) + + discussion Discussion @relation(fields: [discussionId], references: [id]) + + discussionId String +} + +enum DiscussionSubscriptionType { + AllNotifications + OnlyMentions + NoNotification +} + +model DiscussionMessage { + id String @id @default(uuid()) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + type DiscussionMessageType @default(Message) + message String + + discussion Discussion? @relation(fields: [discussionId], references: [id]) + + discussionId String? +} + +enum DiscussionMessageType { + Message +} diff --git a/packages/firebase/functions/src/core/graph/schema/commons/types/CommonMetadata.type.ts b/packages/firebase/functions/src/core/graph/schema/commons/types/CommonMetadata.type.ts index e98e7f2d6..2d7a180dc 100644 --- a/packages/firebase/functions/src/core/graph/schema/commons/types/CommonMetadata.type.ts +++ b/packages/firebase/functions/src/core/graph/schema/commons/types/CommonMetadata.type.ts @@ -11,5 +11,5 @@ export const CommonMetadataType = objectType({ t.field('contributionType', { type: CommonContributionTypeEnum }); - }, + } }); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 45e14d182..9cf7980f2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1701,11 +1701,28 @@ "@firebase/util" "0.3.4" tslib "^1.11.1" +"@firebase/analytics@0.6.8": + version "0.6.8" + resolved "https://registry.yarnpkg.com/@firebase/analytics/-/analytics-0.6.8.tgz#ec69a8673df2e0381bdebc892d28448e2d97ee8a" + integrity sha512-cPbQIQo3uqpImtiGIB42F9s9fw8cPseCj1ZMR3VshL6u/6kzC9ptOpgg8PMCLOgZvBwC993LbT1UOTuufTd49Q== + dependencies: + "@firebase/analytics-types" "0.4.0" + "@firebase/component" "0.4.0" + "@firebase/installations" "0.4.24" + "@firebase/logger" "0.2.6" + "@firebase/util" "0.4.1" + tslib "^2.1.0" + "@firebase/app-types@0.6.1": version "0.6.1" resolved "https://registry.yarnpkg.com/@firebase/app-types/-/app-types-0.6.1.tgz#dcbd23030a71c0c74fc95d4a3f75ba81653850e9" integrity sha512-L/ZnJRAq7F++utfuoTKX4CLBG5YR7tFO3PLzG1/oXXKEezJ0kRL3CMRoueBEmTCzVb/6SIs2Qlaw++uDgi5Xyg== +"@firebase/app-types@0.6.2": + version "0.6.2" + resolved "https://registry.yarnpkg.com/@firebase/app-types/-/app-types-0.6.2.tgz#8578cb1061a83ced4570188be9e225d54e0f27fb" + integrity sha512-2VXvq/K+n8XMdM4L2xy5bYp2ZXMawJXluUIDzUBvMthVR+lhxK4pfFiqr1mmDbv9ydXvEAuFsD+6DpcZuJcSSw== + "@firebase/app@0.6.13": version "0.6.13" resolved "https://registry.yarnpkg.com/@firebase/app/-/app-0.6.13.tgz#f2e9fa9e75815e54161dc34659a60f1fffd9a450" @@ -1732,6 +1749,19 @@ tslib "^1.11.1" xmlhttprequest "1.8.0" +"@firebase/app@0.6.19": + version "0.6.19" + resolved "https://registry.yarnpkg.com/@firebase/app/-/app-0.6.19.tgz#40fe266889436ab0fcf035ee5a415db7339c1936" + integrity sha512-qDimGNoukCuWvGYcsosGV2tOKbJ98RuRHLoK2j4t73TupY6rH+4QeR3tf5E3q1gZ5mtaFZloXc6aZWWOgtfwoQ== + dependencies: + "@firebase/app-types" "0.6.2" + "@firebase/component" "0.4.0" + "@firebase/logger" "0.2.6" + "@firebase/util" "0.4.1" + dom-storage "2.1.0" + tslib "^2.1.0" + xmlhttprequest "1.8.0" + "@firebase/auth-interop-types@0.1.5": version "0.1.5" resolved "https://registry.yarnpkg.com/@firebase/auth-interop-types/-/auth-interop-types-0.1.5.tgz#9fc9bd7c879f16b8d1bb08373a0f48c3a8b74557" @@ -1742,6 +1772,11 @@ resolved "https://registry.yarnpkg.com/@firebase/auth-types/-/auth-types-0.10.1.tgz#7815e71c9c6f072034415524b29ca8f1d1770660" integrity sha512-/+gBHb1O9x/YlG7inXfxff/6X3BPZt4zgBv4kql6HEmdzNQCodIRlEYnI+/da+lN+dha7PjaFH7C7ewMmfV7rw== +"@firebase/auth-types@0.10.2": + version "0.10.2" + resolved "https://registry.yarnpkg.com/@firebase/auth-types/-/auth-types-0.10.2.tgz#3fad953380c447b7545122430a4c7a9bc8355001" + integrity sha512-0GMWVWh5TBCYIQfVerxzDsuvhoFpK0++O9LtP3FWkwYo7EAxp6w0cftAg/8ntU1E5Wg56Ry0b6ti/YGP6g0jlg== + "@firebase/auth@0.16.2": version "0.16.2" resolved "https://registry.yarnpkg.com/@firebase/auth/-/auth-0.16.2.tgz#e8fdd65c7987276bc06676e9ed990346ebe2dc51" @@ -1756,6 +1791,13 @@ dependencies: "@firebase/auth-types" "0.10.1" +"@firebase/auth@0.16.4": + version "0.16.4" + resolved "https://registry.yarnpkg.com/@firebase/auth/-/auth-0.16.4.tgz#6249d80f1e974b0db122930ae9fac885eccead5c" + integrity sha512-zgHPK6/uL6+nAyG9zqammHTF1MQpAN7z/jVRLYkDZS4l81H08b2SzApLbRfW/fmy665xqb5MK7sVH0V1wsiCNw== + dependencies: + "@firebase/auth-types" "0.10.2" + "@firebase/component@0.1.21": version "0.1.21" resolved "https://registry.yarnpkg.com/@firebase/component/-/component-0.1.21.tgz#56062eb0d449dc1e7bbef3c084a9b5fa48c7c14d" @@ -1764,6 +1806,14 @@ "@firebase/util" "0.3.4" tslib "^1.11.1" +"@firebase/component@0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@firebase/component/-/component-0.4.0.tgz#90baa455d75160c8a5134b3e9d642df11f0ac818" + integrity sha512-L7kLKpW1v5qxPfIhx/VqHuVi+vr5IcnDS4zCJFb+/eYe23i6czSOWR1urAoJ4r42Dk0XB5kDt6Idojdd9BGMEA== + dependencies: + "@firebase/util" "0.4.1" + tslib "^2.1.0" + "@firebase/database-types@0.6.1", "@firebase/database-types@^0.6.1": version "0.6.1" resolved "https://registry.yarnpkg.com/@firebase/database-types/-/database-types-0.6.1.tgz#cf1cfc03e617ed4c2561703781f85ba4c707ff65" @@ -1778,6 +1828,13 @@ dependencies: "@firebase/app-types" "0.6.1" +"@firebase/database-types@0.7.1": + version "0.7.1" + resolved "https://registry.yarnpkg.com/@firebase/database-types/-/database-types-0.7.1.tgz#3505e3e8d57e94a3ce6038649a95afe0af040757" + integrity sha512-465ceJXSMqFFMnL2lxYx+YhYajcyk+VpGiXf9T6KNME0lKne5hYuqYr7XmS8/sTeyV0huhmTb8K1nxlA7hiPOg== + dependencies: + "@firebase/app-types" "0.6.2" + "@firebase/database@0.9.0": version "0.9.0" resolved "https://registry.yarnpkg.com/@firebase/database/-/database-0.9.0.tgz#eaf07dbaf3433be6cc27543c1823dc3c88c47572" @@ -1817,6 +1874,19 @@ faye-websocket "0.11.3" tslib "^1.11.1" +"@firebase/database@0.9.8": + version "0.9.8" + resolved "https://registry.yarnpkg.com/@firebase/database/-/database-0.9.8.tgz#244eb897033ecacfc4a1fa5f031cda9b5e4009e5" + integrity sha512-bqZUDR6jIQSQcY7oZVGmI/Bg7SfmUUW/toaZBCfaddWAnniBthaa8o0Hyv1ypPxjEZCu1CfPQwtpMhlSTjG0tA== + dependencies: + "@firebase/auth-interop-types" "0.1.5" + "@firebase/component" "0.4.0" + "@firebase/database-types" "0.7.1" + "@firebase/logger" "0.2.6" + "@firebase/util" "0.4.1" + faye-websocket "0.11.3" + tslib "^2.1.0" + "@firebase/database@^0.8.1": version "0.8.3" resolved "https://registry.yarnpkg.com/@firebase/database/-/database-0.8.3.tgz#4e5efa8fc8df00d6febfd9c8d6d6e409596659f7" @@ -1835,6 +1905,11 @@ resolved "https://registry.yarnpkg.com/@firebase/firestore-types/-/firestore-types-2.1.0.tgz#ad406c6fd7f0eae7ea52979f712daa0166aef665" integrity sha512-jietErBWihMvJkqqEquQy5GgoEwzHnMXXC/TsVoe9FPysXm1/AeJS12taS7ZYvenAtyvL/AEJyKrRKRh4adcJQ== +"@firebase/firestore-types@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@firebase/firestore-types/-/firestore-types-2.2.0.tgz#9a3f3f2906232c3b4a726d988a6ef077f35f9093" + integrity sha512-5kZZtQ32FIRJP1029dw+ZVNRCclKOErHv1+Xn0pw/5Fq3dxroA/ZyFHqDu+uV52AyWHhNLjCqX43ibm4YqOzRw== + "@firebase/firestore@2.1.3": version "2.1.3" resolved "https://registry.yarnpkg.com/@firebase/firestore/-/firestore-2.1.3.tgz#74b99c9365a26ea101045c0600641064621cb341" @@ -1880,6 +1955,21 @@ node-fetch "2.6.1" tslib "^1.11.1" +"@firebase/firestore@2.2.3": + version "2.2.3" + resolved "https://registry.yarnpkg.com/@firebase/firestore/-/firestore-2.2.3.tgz#e76d9191c48ef4c51ae73c2fcce7d547be2a8c17" + integrity sha512-efJxJahP9936QlIHeATvatCO4c3UEk6nz7pc812xxkgTVezkg8K66IDUe0fncV70zbDrIyxUIl8yRcxhXytiGw== + dependencies: + "@firebase/component" "0.4.0" + "@firebase/firestore-types" "2.2.0" + "@firebase/logger" "0.2.6" + "@firebase/util" "0.4.1" + "@firebase/webchannel-wrapper" "0.4.1" + "@grpc/grpc-js" "^1.0.0" + "@grpc/proto-loader" "^0.5.0" + node-fetch "2.6.1" + tslib "^2.1.0" + "@firebase/functions-types@0.4.0": version "0.4.0" resolved "https://registry.yarnpkg.com/@firebase/functions-types/-/functions-types-0.4.0.tgz#0b789f4fe9a9c0b987606c4da10139345b40f6b9" @@ -1896,6 +1986,17 @@ node-fetch "2.6.1" tslib "^1.11.1" +"@firebase/functions@0.6.6": + version "0.6.6" + resolved "https://registry.yarnpkg.com/@firebase/functions/-/functions-0.6.6.tgz#06b786e68b269a615fc83598d99cda7b11ec740e" + integrity sha512-cvZiqcL3X7+6ObkwcRUV54iFHaVxVgio2t610p2qwjzMxyYfiHWDA+GwKPioObDWqyXmNtkU8cw2WLoGf46cnA== + dependencies: + "@firebase/component" "0.4.0" + "@firebase/functions-types" "0.4.0" + "@firebase/messaging-types" "0.5.0" + node-fetch "2.6.1" + tslib "^2.1.0" + "@firebase/installations-types@0.3.4": version "0.3.4" resolved "https://registry.yarnpkg.com/@firebase/installations-types/-/installations-types-0.3.4.tgz#589a941d713f4f64bf9f4feb7f463505bab1afa2" @@ -1912,6 +2013,17 @@ idb "3.0.2" tslib "^1.11.1" +"@firebase/installations@0.4.24": + version "0.4.24" + resolved "https://registry.yarnpkg.com/@firebase/installations/-/installations-0.4.24.tgz#acaf3d48c156f3a3a5ddb53e8e8c63a89fce2f55" + integrity sha512-cMWI3IfnmdJ4SzPav56yaHwEhpPPl5b03AVtv7AeKnmDZ61eBqPzEnYSL8Iso73/FeKpr8BYcZelAx0EyxcJ3Q== + dependencies: + "@firebase/component" "0.4.0" + "@firebase/installations-types" "0.3.4" + "@firebase/util" "0.4.1" + idb "3.0.2" + tslib "^2.1.0" + "@firebase/logger@0.2.6": version "0.2.6" resolved "https://registry.yarnpkg.com/@firebase/logger/-/logger-0.2.6.tgz#3aa2ca4fe10327cabf7808bd3994e88db26d7989" @@ -1934,11 +2046,35 @@ idb "3.0.2" tslib "^1.11.1" +"@firebase/messaging@0.7.8": + version "0.7.8" + resolved "https://registry.yarnpkg.com/@firebase/messaging/-/messaging-0.7.8.tgz#90119a2f1cd5055fd61206732024e0281de80616" + integrity sha512-rXYvVQPZd+rCMV7+/FgpvsHad0HuEhoyH5OQgYxeBgSsgFn6mOyvAtYcoCFjPTvTV5eyGH1I4hQtNOyY8zVzzg== + dependencies: + "@firebase/component" "0.4.0" + "@firebase/installations" "0.4.24" + "@firebase/messaging-types" "0.5.0" + "@firebase/util" "0.4.1" + idb "3.0.2" + tslib "^2.1.0" + "@firebase/performance-types@0.0.13": version "0.0.13" resolved "https://registry.yarnpkg.com/@firebase/performance-types/-/performance-types-0.0.13.tgz#58ce5453f57e34b18186f74ef11550dfc558ede6" integrity sha512-6fZfIGjQpwo9S5OzMpPyqgYAUZcFzZxHFqOyNtorDIgNXq33nlldTL/vtaUZA8iT9TT5cJlCrF/jthKU7X21EA== +"@firebase/performance@0.4.10": + version "0.4.10" + resolved "https://registry.yarnpkg.com/@firebase/performance/-/performance-0.4.10.tgz#c78ed1c15c26884eae23edf1498e930bb729b51f" + integrity sha512-gyAOd9Z/GVlLE5V8U5pVQDZpjr4Msdx5yJr7oQE/xkh6dNZGuYp5qJh1pAmJs2ZI8eMTs+j2bXJEMYk6w7ehRg== + dependencies: + "@firebase/component" "0.4.0" + "@firebase/installations" "0.4.24" + "@firebase/logger" "0.2.6" + "@firebase/performance-types" "0.0.13" + "@firebase/util" "0.4.1" + tslib "^2.1.0" + "@firebase/performance@0.4.5": version "0.4.5" resolved "https://registry.yarnpkg.com/@firebase/performance/-/performance-0.4.5.tgz#3ab89208ed6fb80165e5594058e46dc85113cd78" @@ -1977,6 +2113,18 @@ "@firebase/util" "0.3.4" tslib "^1.11.1" +"@firebase/remote-config@0.1.35": + version "0.1.35" + resolved "https://registry.yarnpkg.com/@firebase/remote-config/-/remote-config-0.1.35.tgz#792b6d9e2d8e5db0a883ee53579629c2412ae1f5" + integrity sha512-szhu48LTyb46S33hUR3sC4kiykEoc+B5M7HWWHhjp7Ne+524G8pH/9+/r9ZA8eVj48c5cihXyQKQ/6yCQotnUA== + dependencies: + "@firebase/component" "0.4.0" + "@firebase/installations" "0.4.24" + "@firebase/logger" "0.2.6" + "@firebase/remote-config-types" "0.1.9" + "@firebase/util" "0.4.1" + tslib "^2.1.0" + "@firebase/rules-unit-testing@^1.0.3": version "1.1.9" resolved "https://registry.yarnpkg.com/@firebase/rules-unit-testing/-/rules-unit-testing-1.1.9.tgz#aa867c3faf8b2fc2310710dabd6df759b32e80aa" @@ -2002,6 +2150,16 @@ "@firebase/util" "0.3.4" tslib "^1.11.1" +"@firebase/storage@0.4.7": + version "0.4.7" + resolved "https://registry.yarnpkg.com/@firebase/storage/-/storage-0.4.7.tgz#541a2d96af6da9c345b190858345c1106650fce0" + integrity sha512-5DFb+VncNBomPzpzYqJzzJjfiZhOWg0FHTBkw90K9OdE2wUfKqzhhbIAjyaXcu+2YLB2hjft8BKbjQfV5BDFnw== + dependencies: + "@firebase/component" "0.4.0" + "@firebase/storage-types" "0.3.13" + "@firebase/util" "0.4.1" + tslib "^2.1.0" + "@firebase/util@0.3.4": version "0.3.4" resolved "https://registry.yarnpkg.com/@firebase/util/-/util-0.3.4.tgz#e389d0e0e2aac88a5235b06ba9431db999d4892b" @@ -2009,6 +2167,13 @@ dependencies: tslib "^1.11.1" +"@firebase/util@0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@firebase/util/-/util-0.4.1.tgz#fe76cf0238901dc5455b341cf02e298e7bf68df4" + integrity sha512-XhYCOwq4AH+YeQBEnDQvigz50WiiBU4LnJh2+//VMt4J2Ybsk0eTgUHNngUzXsmp80EJrwal3ItODg55q1ajWg== + dependencies: + tslib "^2.1.0" + "@firebase/webchannel-wrapper@0.4.1": version "0.4.1" resolved "https://registry.yarnpkg.com/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.4.1.tgz#600f2275ff54739ad5ac0102f1467b8963cd5f71" @@ -11044,6 +11209,26 @@ firebase@^8.2.7: "@firebase/storage" "0.4.2" "@firebase/util" "0.3.4" +firebase@^8.3.2: + version "8.3.3" + resolved "https://registry.yarnpkg.com/firebase/-/firebase-8.3.3.tgz#21d8fb8eec2c43b0d8f98ab6bda5535b7454fa54" + integrity sha512-eRkW7bD25aevlGwtCEsP53xBo5/Fi4wkxvfvmDW6R2/oSHjy+hVLkQILP4kQFFXgFL0LBjxIPOchXoQ5MUbTCA== + dependencies: + "@firebase/analytics" "0.6.8" + "@firebase/app" "0.6.19" + "@firebase/app-types" "0.6.2" + "@firebase/auth" "0.16.4" + "@firebase/database" "0.9.8" + "@firebase/firestore" "2.2.3" + "@firebase/functions" "0.6.6" + "@firebase/installations" "0.4.24" + "@firebase/messaging" "0.7.8" + "@firebase/performance" "0.4.10" + "@firebase/polyfill" "0.3.36" + "@firebase/remote-config" "0.1.35" + "@firebase/storage" "0.4.7" + "@firebase/util" "0.4.1" + flat-arguments@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/flat-arguments/-/flat-arguments-1.0.2.tgz#9baa780adf0501f282d726c9c6a038dba44ea76f" @@ -22896,6 +23081,11 @@ tslib@^2, tslib@^2.0.0, tslib@^2.0.3, tslib@~2.1.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== +tslib@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c" + integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w== + tslib@~2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" From 6714e552f486478e7606a0c5d83b6fb5d82efbee Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Sat, 10 Apr 2021 13:11:09 +0300 Subject: [PATCH 02/20] WIP Signed-off-by: Alexander Ivanov --- .../core/commands/createDiscussionCommand.ts | 26 +++++++++++++++++++ .../core/src/services/discussions/index.ts | 11 ++++++++ 2 files changed, 37 insertions(+) create mode 100644 packages/core/src/services/discussions/core/commands/createDiscussionCommand.ts create mode 100644 packages/core/src/services/discussions/index.ts diff --git a/packages/core/src/services/discussions/core/commands/createDiscussionCommand.ts b/packages/core/src/services/discussions/core/commands/createDiscussionCommand.ts new file mode 100644 index 000000000..58df2c7fc --- /dev/null +++ b/packages/core/src/services/discussions/core/commands/createDiscussionCommand.ts @@ -0,0 +1,26 @@ +import * as z from 'zod'; +import { Discussion } from '@prisma/client'; +import { NotImplementedError } from '@errors'; + +const schema = z.object({ + commonId: z.string() + .uuid(), + + proposalId: z.string() + .uuid() + .optional() + .nullable(), + + usesId: z.string() + .nonempty(), + + topic: z.string() + .nonempty(), + + description: z.string() + .nonempty() +}); + +export const createDiscussionCommand = (payload: z.infer): Promise => { + throw new NotImplementedError(); +}; \ No newline at end of file diff --git a/packages/core/src/services/discussions/index.ts b/packages/core/src/services/discussions/index.ts new file mode 100644 index 000000000..03fe3ebe3 --- /dev/null +++ b/packages/core/src/services/discussions/index.ts @@ -0,0 +1,11 @@ +import { createDiscussionCommand } from './core/commands/createDiscussionCommand'; + +export const discussionService = { + /** + * Create new discussion for the common, + * or for proposal + */ + create: createDiscussionCommand, + + messages: {} +}; \ No newline at end of file From a581bf92d91da300ebf8e41013fe6c9789b10856 Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Sat, 10 Apr 2021 13:19:34 +0300 Subject: [PATCH 03/20] Split the schema Signed-off-by: Alexander Ivanov --- packages/core/.gitignore | 2 + packages/core/package.json | 3 +- packages/core/prisma/base.prisma | 15 ++++++++ .../{schema.prisma => models/all.prisma} | 38 ------------------- packages/core/prisma/models/user.prisma | 22 +++++++++++ 5 files changed, 41 insertions(+), 39 deletions(-) create mode 100644 packages/core/prisma/base.prisma rename packages/core/prisma/{schema.prisma => models/all.prisma} (93%) create mode 100644 packages/core/prisma/models/user.prisma diff --git a/packages/core/.gitignore b/packages/core/.gitignore index 0e74ee0bf..6d8f1a8c9 100644 --- a/packages/core/.gitignore +++ b/packages/core/.gitignore @@ -2,5 +2,7 @@ .env.staging .env.production +prisma/schema.prisma + **/secrets/* diff --git a/packages/core/package.json b/packages/core/package.json index cf4df893c..9847f29eb 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -7,7 +7,8 @@ "dev": "yarn compile:watch", "compile": "tsc -p tsconfig.json && yarn fix:paths", "compile:watch": "tsc-watch --onSuccess \"yarn fix:paths\"", - "fix:paths": "tscpaths -p tsconfig.json -s ./src -o ./dist" + "fix:paths": "tscpaths -p tsconfig.json -s ./src -o ./dist", + "compile:schema": "cat ./prisma/base.prisma ./prisma/models/*.prisma > ./prisma/schema.prisma && prisma format" }, "devDependencies": { "@types/request-ip": "^0.0.35", diff --git a/packages/core/prisma/base.prisma b/packages/core/prisma/base.prisma new file mode 100644 index 000000000..2421b6e05 --- /dev/null +++ b/packages/core/prisma/base.prisma @@ -0,0 +1,15 @@ +datasource db { + provider = "postgresql" + url = env("Database.Url") +} + +generator client { + provider = "prisma-client-js" +} + +// ---- Shared + +enum FundingType { + OneTime + Monthly +} \ No newline at end of file diff --git a/packages/core/prisma/schema.prisma b/packages/core/prisma/models/all.prisma similarity index 93% rename from packages/core/prisma/schema.prisma rename to packages/core/prisma/models/all.prisma index a5ec55432..93e3f5199 100644 --- a/packages/core/prisma/schema.prisma +++ b/packages/core/prisma/models/all.prisma @@ -1,44 +1,6 @@ // This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema -datasource db { - provider = "postgresql" - url = env("Database.Url") -} - -generator client { - provider = "prisma-client-js" -} - -// ---- Shared - -enum FundingType { - OneTime - Monthly -} - -// ---- User related stuff - -model User { - id String @id - - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - firstName String - lastName String - - email String @unique - emailVerified Boolean @default(false) - - cards Card[] - events Event[] - memberships CommonMember[] - - payments Payment[] - proposals Proposal[] - subscriptions Subscription[] -} // ---- Common related stuff diff --git a/packages/core/prisma/models/user.prisma b/packages/core/prisma/models/user.prisma new file mode 100644 index 000000000..050d919a5 --- /dev/null +++ b/packages/core/prisma/models/user.prisma @@ -0,0 +1,22 @@ +// ---- User related stuff + +model User { + id String @id + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + firstName String + lastName String + + email String @unique + emailVerified Boolean @default(false) + + cards Card[] + events Event[] + memberships CommonMember[] + + payments Payment[] + proposals Proposal[] + subscriptions Subscription[] +} \ No newline at end of file From 64ad5e3c45356d304d056bc4d02ce8a61a4825ec Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Sun, 11 Apr 2021 20:26:46 +0300 Subject: [PATCH 04/20] Split the schema Signed-off-by: Alexander Ivanov --- packages/core/package.json | 2 +- packages/core/prisma/models/all.prisma | 354 ------------------ .../core/prisma/models/commons/Common.prisma | 26 ++ .../prisma/models/commons/CommonMember.prisma | 20 + .../commons/enums/CommonMemberRole.prisma | 3 + .../models/discussions/Discussion.prisma | 14 + .../discussions/DiscussionMessage.prisma | 13 + .../discussions/DiscussionSubscription.prisma | 14 + .../enums/DiscussionMessageType.prisma | 3 + .../enums/DiscussionSubscriptionType.prisma | 6 + .../discussions/enums/DiscussionType.prisma | 4 + .../core/prisma/models/events/Event.prisma | 16 + .../prisma/models/events/EventType.prisma | 30 ++ .../models/proposals/FundingProposal.prisma | 15 + .../models/proposals/JoinProposal.prisma | 20 + .../prisma/models/proposals/Proposal.prisma | 40 ++ .../models/proposals/ProposalVote.prisma | 17 + .../enums/ProposalFundingState.prisma | 12 + .../enums/ProposalPaymentState.prisma | 7 + .../proposals/enums/ProposalState.prisma | 8 + .../proposals/enums/ProposalType.prisma | 4 + .../enums/ProposalVoteOutcome.prisma | 4 + .../models/subscriptions/Subscription.prisma | 30 ++ .../enums/SubscriptionPaymentStatus.prisma | 8 + .../enums/SubscriptionStatus.prisma | 10 + .../models/{user.prisma => users/User.prisma} | 2 - 26 files changed, 325 insertions(+), 357 deletions(-) create mode 100644 packages/core/prisma/models/commons/Common.prisma create mode 100644 packages/core/prisma/models/commons/CommonMember.prisma create mode 100644 packages/core/prisma/models/commons/enums/CommonMemberRole.prisma create mode 100644 packages/core/prisma/models/discussions/Discussion.prisma create mode 100644 packages/core/prisma/models/discussions/DiscussionMessage.prisma create mode 100644 packages/core/prisma/models/discussions/DiscussionSubscription.prisma create mode 100644 packages/core/prisma/models/discussions/enums/DiscussionMessageType.prisma create mode 100644 packages/core/prisma/models/discussions/enums/DiscussionSubscriptionType.prisma create mode 100644 packages/core/prisma/models/discussions/enums/DiscussionType.prisma create mode 100644 packages/core/prisma/models/events/Event.prisma create mode 100644 packages/core/prisma/models/events/EventType.prisma create mode 100644 packages/core/prisma/models/proposals/FundingProposal.prisma create mode 100644 packages/core/prisma/models/proposals/JoinProposal.prisma create mode 100644 packages/core/prisma/models/proposals/Proposal.prisma create mode 100644 packages/core/prisma/models/proposals/ProposalVote.prisma create mode 100644 packages/core/prisma/models/proposals/enums/ProposalFundingState.prisma create mode 100644 packages/core/prisma/models/proposals/enums/ProposalPaymentState.prisma create mode 100644 packages/core/prisma/models/proposals/enums/ProposalState.prisma create mode 100644 packages/core/prisma/models/proposals/enums/ProposalType.prisma create mode 100644 packages/core/prisma/models/proposals/enums/ProposalVoteOutcome.prisma create mode 100644 packages/core/prisma/models/subscriptions/Subscription.prisma create mode 100644 packages/core/prisma/models/subscriptions/enums/SubscriptionPaymentStatus.prisma create mode 100644 packages/core/prisma/models/subscriptions/enums/SubscriptionStatus.prisma rename packages/core/prisma/models/{user.prisma => users/User.prisma} (92%) diff --git a/packages/core/package.json b/packages/core/package.json index 9847f29eb..4e1172813 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -8,7 +8,7 @@ "compile": "tsc -p tsconfig.json && yarn fix:paths", "compile:watch": "tsc-watch --onSuccess \"yarn fix:paths\"", "fix:paths": "tscpaths -p tsconfig.json -s ./src -o ./dist", - "compile:schema": "cat ./prisma/base.prisma ./prisma/models/*.prisma > ./prisma/schema.prisma && prisma format" + "compile:schema": "find prisma -name '*.prisma' -not -name \"schema.prisma\" -exec cat {} + > prisma/schema.prisma && prisma format" }, "devDependencies": { "@types/request-ip": "^0.0.35", diff --git a/packages/core/prisma/models/all.prisma b/packages/core/prisma/models/all.prisma index 93e3f5199..3ffee3a29 100644 --- a/packages/core/prisma/models/all.prisma +++ b/packages/core/prisma/models/all.prisma @@ -1,247 +1,3 @@ -// This is your Prisma schema file, -// learn more about it in the docs: https://pris.ly/d/prisma-schema - - -// ---- Common related stuff - -model Common { - id String @id @default(uuid()) - - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - name String - - balance Int @default(0) - raised Int @default(0) - - whitelisted Boolean @default(false) - - fundingType FundingType - fundingMinimumAmount Int - - events Event[] - members CommonMember[] - - payments Payment[] - subscriptions Subscription[] - - proposals Proposal[] -} - -model CommonMember { - id String @id @default(uuid()) - - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - common Common @relation(fields: [commonId], references: [id]) - user User @relation(fields: [userId], references: [id]) - - roles CommonMemberRole[] - - proposals Proposal[] - - commonId String - userId String - - Vote Vote[] - - @@unique([userId, commonId]) -} - -enum CommonMemberRole { - Founder -} - -// ---- Proposals - -model JoinProposal { - id String @id @default(uuid()) - - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - funding Int - fundingType FundingType - paymentState ProposalPaymentState @default(NotAttempted) - - card Card @relation(fields: [cardId], references: [id]) - payment Payment[] - proposal Proposal? - subscription Subscription? @relation(fields: [subscriptionId], references: [id]) - - cardId String - subscriptionId String? -} - -model FundingProposal { - id String @id @default(uuid()) - - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - proposal Proposal? - - amount Int - - - fundingState FundingState @default(NotEligible) - - // @todo Payout -} - -enum FundingState { - NotEligible - Eligible - - AwaitingApproval - Pending - Completed - Confirmed -} - -model Proposal { - id String @id @default(uuid()) - - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - expiresAt DateTime - - title String? - description String? - - link Json? - files Json? - images Json? - - ipAddress String? - - votes Vote[] - - type ProposalType - state ProposalState @default(Countdown) - - votesFor Int @default(0) - votesAgainst Int @default(0) - - join JoinProposal? @relation(fields: [joinId], references: [id]) - funding FundingProposal? @relation(fields: [fundingId], references: [id]) - - user User @relation(fields: [userId], references: [id]) - common Common @relation(fields: [commonId], references: [id]) - commonMember CommonMember? @relation(fields: [commonMemberId], references: [id]) - - - joinId String? @unique - fundingId String? @unique - - userId String - commonId String - commonMemberId String? -} - -enum ProposalType { - FundingRequest - JoinRequest -} - -enum ProposalState { - Countdown - - Finalizing - - Rejected - Accepted -} - -enum ProposalPaymentState { - NotAttempted - Pending - - Successful - Unsuccessful -} - -// ---- Subscriptions -model Subscription { - id String @id @default(uuid()) - - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - card Card @relation(fields: [cardId], references: [id]) - join JoinProposal? - - - user User @relation(fields: [userId], references: [id]) - common Common @relation(fields: [commonId], references: [id]) - - payments Payment[] - - amount Int - - paymentStatus SubscriptionPaymentStatus @default(AwaitingInitialPayment) - status SubscriptionStatus @default(Pending) - - dueDate DateTime - chargedAt DateTime? - - voided Boolean @default(false) - - cardId String - - userId String - commonId String -} - -enum SubscriptionPaymentStatus { - AwaitingInitialPayment - - Pending - - Successful - Unsuccessful -} - -enum SubscriptionStatus { - Pending - - Active - - PaymentFailed - - CanceledByUser - CanceledByPaymentFailure -} - -// ---- Votes - -model Vote { - id String @id @default(uuid()) - - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - outcome VoteOutcome - - proposal Proposal @relation(fields: [proposalId], references: [id]) - commonMember CommonMember @relation(fields: [commonMemberId], references: [id]) - - commonMemberId String - proposalId String - - @@unique([commonMemberId, proposalId]) -} - -enum VoteOutcome { - Approve - Condemn -} - -// ---- Payments and cards - model Payment { id String @id @default(uuid()) @@ -349,113 +105,3 @@ model CardBillingDetail { cardId String } -// ---- Events - -model Event { - id String @id @default(uuid()) - - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - type EventType - - payload Json? - - commonId String? - userId String? - - common Common? @relation(fields: [commonId], references: [id]) - user User? @relation(fields: [userId], references: [id]) -} - -enum EventType { - CommonCreated - - CommonMemberCreated - CommonMemberRoleAdded - CommonMemberRoleRemoved - - JoinRequestCreated - JoinRequestAccepted - JoinRequestRejected - - FundingRequestCreated - FundingRequestAccepted - FundingRequestRejected - - CardCreated - CardCvvVerificationPassed - CardCvvVerificationFailed - - PaymentCreated - PaymentSucceeded - PaymentFailed - - ProposalMajorityReached - ProposalExpired - - VoteCreated - - UserCreated -} - -// ---- Discussions - -enum DiscussionType { - ProposalDiscussion - CommonDiscussion -} - -model Discussion { - id String @id @default(uuid()) - - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - topic String - description String - - latestMessage DateTime @default(now()) - - subscriptions DiscussionSubscription[] - messages DiscussionMessage[] -} - -model DiscussionSubscription { - id String @id @default(uuid()) - - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - latestMessageSeen DateTime @default(now()) - - type DiscussionSubscriptionType @default(AllNotifications) - - discussion Discussion @relation(fields: [discussionId], references: [id]) - - discussionId String -} - -enum DiscussionSubscriptionType { - AllNotifications - OnlyMentions - NoNotification -} - -model DiscussionMessage { - id String @id @default(uuid()) - - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - type DiscussionMessageType @default(Message) - message String - - discussion Discussion? @relation(fields: [discussionId], references: [id]) - - discussionId String? -} - -enum DiscussionMessageType { - Message -} diff --git a/packages/core/prisma/models/commons/Common.prisma b/packages/core/prisma/models/commons/Common.prisma new file mode 100644 index 000000000..eb588b449 --- /dev/null +++ b/packages/core/prisma/models/commons/Common.prisma @@ -0,0 +1,26 @@ +model Common { + id String @id @default(uuid()) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + name String + + balance Int @default(0) + raised Int @default(0) + + whitelisted Boolean @default(false) + + fundingType FundingType + fundingMinimumAmount Int + + events Event[] + members CommonMember[] + + payments Payment[] + subscriptions Subscription[] + + proposals Proposal[] +} + + diff --git a/packages/core/prisma/models/commons/CommonMember.prisma b/packages/core/prisma/models/commons/CommonMember.prisma new file mode 100644 index 000000000..884637514 --- /dev/null +++ b/packages/core/prisma/models/commons/CommonMember.prisma @@ -0,0 +1,20 @@ +model CommonMember { + id String @id @default(uuid()) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + common Common @relation(fields: [commonId], references: [id]) + user User @relation(fields: [userId], references: [id]) + + roles CommonMemberRole[] + + proposals Proposal[] + + commonId String + userId String + + Vote Vote[] + + @@unique([userId, commonId]) +} diff --git a/packages/core/prisma/models/commons/enums/CommonMemberRole.prisma b/packages/core/prisma/models/commons/enums/CommonMemberRole.prisma new file mode 100644 index 000000000..853f2b13e --- /dev/null +++ b/packages/core/prisma/models/commons/enums/CommonMemberRole.prisma @@ -0,0 +1,3 @@ +enum CommonMemberRole { + Founder +} diff --git a/packages/core/prisma/models/discussions/Discussion.prisma b/packages/core/prisma/models/discussions/Discussion.prisma new file mode 100644 index 000000000..a02667471 --- /dev/null +++ b/packages/core/prisma/models/discussions/Discussion.prisma @@ -0,0 +1,14 @@ +model Discussion { + id String @id @default(uuid()) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + topic String + description String + + latestMessage DateTime @default(now()) + + subscriptions DiscussionSubscription[] + messages DiscussionMessage[] +} diff --git a/packages/core/prisma/models/discussions/DiscussionMessage.prisma b/packages/core/prisma/models/discussions/DiscussionMessage.prisma new file mode 100644 index 000000000..fda56e912 --- /dev/null +++ b/packages/core/prisma/models/discussions/DiscussionMessage.prisma @@ -0,0 +1,13 @@ +model DiscussionMessage { + id String @id @default(uuid()) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + type DiscussionMessageType @default(Message) + message String + + discussion Discussion? @relation(fields: [discussionId], references: [id]) + + discussionId String? +} \ No newline at end of file diff --git a/packages/core/prisma/models/discussions/DiscussionSubscription.prisma b/packages/core/prisma/models/discussions/DiscussionSubscription.prisma new file mode 100644 index 000000000..6ade15b50 --- /dev/null +++ b/packages/core/prisma/models/discussions/DiscussionSubscription.prisma @@ -0,0 +1,14 @@ +model DiscussionSubscription { + id String @id @default(uuid()) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + latestMessageSeen DateTime @default(now()) + + type DiscussionSubscriptionType @default(AllNotifications) + + discussion Discussion @relation(fields: [discussionId], references: [id]) + + discussionId String +} diff --git a/packages/core/prisma/models/discussions/enums/DiscussionMessageType.prisma b/packages/core/prisma/models/discussions/enums/DiscussionMessageType.prisma new file mode 100644 index 000000000..af067fbbf --- /dev/null +++ b/packages/core/prisma/models/discussions/enums/DiscussionMessageType.prisma @@ -0,0 +1,3 @@ +enum DiscussionMessageType { + Message +} diff --git a/packages/core/prisma/models/discussions/enums/DiscussionSubscriptionType.prisma b/packages/core/prisma/models/discussions/enums/DiscussionSubscriptionType.prisma new file mode 100644 index 000000000..04f70124a --- /dev/null +++ b/packages/core/prisma/models/discussions/enums/DiscussionSubscriptionType.prisma @@ -0,0 +1,6 @@ +enum DiscussionSubscriptionType { + AllNotifications + OnlyMentions + NoNotification +} + diff --git a/packages/core/prisma/models/discussions/enums/DiscussionType.prisma b/packages/core/prisma/models/discussions/enums/DiscussionType.prisma new file mode 100644 index 000000000..04afa6f03 --- /dev/null +++ b/packages/core/prisma/models/discussions/enums/DiscussionType.prisma @@ -0,0 +1,4 @@ +enum DiscussionType { + ProposalDiscussion + CommonDiscussion +} diff --git a/packages/core/prisma/models/events/Event.prisma b/packages/core/prisma/models/events/Event.prisma new file mode 100644 index 000000000..b8324df06 --- /dev/null +++ b/packages/core/prisma/models/events/Event.prisma @@ -0,0 +1,16 @@ +model Event { + id String @id @default(uuid()) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + type EventType + + payload Json? + + commonId String? + userId String? + + common Common? @relation(fields: [commonId], references: [id]) + user User? @relation(fields: [userId], references: [id]) +} \ No newline at end of file diff --git a/packages/core/prisma/models/events/EventType.prisma b/packages/core/prisma/models/events/EventType.prisma new file mode 100644 index 000000000..d25d322ef --- /dev/null +++ b/packages/core/prisma/models/events/EventType.prisma @@ -0,0 +1,30 @@ +enum EventType { + CommonCreated + + CommonMemberCreated + CommonMemberRoleAdded + CommonMemberRoleRemoved + + JoinRequestCreated + JoinRequestAccepted + JoinRequestRejected + + FundingRequestCreated + FundingRequestAccepted + FundingRequestRejected + + CardCreated + CardCvvVerificationPassed + CardCvvVerificationFailed + + PaymentCreated + PaymentSucceeded + PaymentFailed + + ProposalMajorityReached + ProposalExpired + + VoteCreated + + UserCreated +} \ No newline at end of file diff --git a/packages/core/prisma/models/proposals/FundingProposal.prisma b/packages/core/prisma/models/proposals/FundingProposal.prisma new file mode 100644 index 000000000..ab806e9a3 --- /dev/null +++ b/packages/core/prisma/models/proposals/FundingProposal.prisma @@ -0,0 +1,15 @@ +model FundingProposal { + id String @id @default(uuid()) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + proposal Proposal? + + amount Int + + + fundingState FundingState @default(NotEligible) + + // @todo Payout +} diff --git a/packages/core/prisma/models/proposals/JoinProposal.prisma b/packages/core/prisma/models/proposals/JoinProposal.prisma new file mode 100644 index 000000000..29ba06784 --- /dev/null +++ b/packages/core/prisma/models/proposals/JoinProposal.prisma @@ -0,0 +1,20 @@ +model JoinProposal { + id String @id @default(uuid()) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + funding Int + fundingType FundingType + paymentState ProposalPaymentState @default(NotAttempted) + + card Card @relation(fields: [cardId], references: [id]) + payment Payment[] + proposal Proposal? + subscription Subscription? @relation(fields: [subscriptionId], references: [id]) + + cardId String + subscriptionId String? +} + + diff --git a/packages/core/prisma/models/proposals/Proposal.prisma b/packages/core/prisma/models/proposals/Proposal.prisma new file mode 100644 index 000000000..137c13a0f --- /dev/null +++ b/packages/core/prisma/models/proposals/Proposal.prisma @@ -0,0 +1,40 @@ +model Proposal { + id String @id @default(uuid()) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + expiresAt DateTime + + title String? + description String? + + link Json? + files Json? + images Json? + + ipAddress String? + + votes Vote[] + + type ProposalType + state ProposalState @default(Countdown) + + votesFor Int @default(0) + votesAgainst Int @default(0) + + join JoinProposal? @relation(fields: [joinId], references: [id]) + funding FundingProposal? @relation(fields: [fundingId], references: [id]) + + user User @relation(fields: [userId], references: [id]) + common Common @relation(fields: [commonId], references: [id]) + commonMember CommonMember? @relation(fields: [commonMemberId], references: [id]) + + + joinId String? @unique + fundingId String? @unique + + userId String + commonId String + commonMemberId String? +} \ No newline at end of file diff --git a/packages/core/prisma/models/proposals/ProposalVote.prisma b/packages/core/prisma/models/proposals/ProposalVote.prisma new file mode 100644 index 000000000..0702d3bc3 --- /dev/null +++ b/packages/core/prisma/models/proposals/ProposalVote.prisma @@ -0,0 +1,17 @@ +model Vote { + id String @id @default(uuid()) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + outcome VoteOutcome + + proposal Proposal @relation(fields: [proposalId], references: [id]) + commonMember CommonMember @relation(fields: [commonMemberId], references: [id]) + + commonMemberId String + proposalId String + + @@unique([commonMemberId, proposalId]) +} + diff --git a/packages/core/prisma/models/proposals/enums/ProposalFundingState.prisma b/packages/core/prisma/models/proposals/enums/ProposalFundingState.prisma new file mode 100644 index 000000000..843f2e17a --- /dev/null +++ b/packages/core/prisma/models/proposals/enums/ProposalFundingState.prisma @@ -0,0 +1,12 @@ +enum FundingState { + NotEligible + Eligible + + AwaitingApproval + Pending + Completed + Confirmed +} + + + diff --git a/packages/core/prisma/models/proposals/enums/ProposalPaymentState.prisma b/packages/core/prisma/models/proposals/enums/ProposalPaymentState.prisma new file mode 100644 index 000000000..92a0c31ec --- /dev/null +++ b/packages/core/prisma/models/proposals/enums/ProposalPaymentState.prisma @@ -0,0 +1,7 @@ +enum ProposalPaymentState { + NotAttempted + Pending + + Successful + Unsuccessful +} \ No newline at end of file diff --git a/packages/core/prisma/models/proposals/enums/ProposalState.prisma b/packages/core/prisma/models/proposals/enums/ProposalState.prisma new file mode 100644 index 000000000..dde6a4cda --- /dev/null +++ b/packages/core/prisma/models/proposals/enums/ProposalState.prisma @@ -0,0 +1,8 @@ +enum ProposalState { + Countdown + + Finalizing + + Rejected + Accepted +} \ No newline at end of file diff --git a/packages/core/prisma/models/proposals/enums/ProposalType.prisma b/packages/core/prisma/models/proposals/enums/ProposalType.prisma new file mode 100644 index 000000000..81ea25afa --- /dev/null +++ b/packages/core/prisma/models/proposals/enums/ProposalType.prisma @@ -0,0 +1,4 @@ +enum ProposalType { + FundingRequest + JoinRequest +} diff --git a/packages/core/prisma/models/proposals/enums/ProposalVoteOutcome.prisma b/packages/core/prisma/models/proposals/enums/ProposalVoteOutcome.prisma new file mode 100644 index 000000000..6bcb2699e --- /dev/null +++ b/packages/core/prisma/models/proposals/enums/ProposalVoteOutcome.prisma @@ -0,0 +1,4 @@ +enum VoteOutcome { + Approve + Condemn +} \ No newline at end of file diff --git a/packages/core/prisma/models/subscriptions/Subscription.prisma b/packages/core/prisma/models/subscriptions/Subscription.prisma new file mode 100644 index 000000000..65b95b14e --- /dev/null +++ b/packages/core/prisma/models/subscriptions/Subscription.prisma @@ -0,0 +1,30 @@ +model Subscription { + id String @id @default(uuid()) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + card Card @relation(fields: [cardId], references: [id]) + join JoinProposal? + + + user User @relation(fields: [userId], references: [id]) + common Common @relation(fields: [commonId], references: [id]) + + payments Payment[] + + amount Int + + paymentStatus SubscriptionPaymentStatus @default(AwaitingInitialPayment) + status SubscriptionStatus @default(Pending) + + dueDate DateTime + chargedAt DateTime? + + voided Boolean @default(false) + + cardId String + + userId String + commonId String +} \ No newline at end of file diff --git a/packages/core/prisma/models/subscriptions/enums/SubscriptionPaymentStatus.prisma b/packages/core/prisma/models/subscriptions/enums/SubscriptionPaymentStatus.prisma new file mode 100644 index 000000000..657f915ff --- /dev/null +++ b/packages/core/prisma/models/subscriptions/enums/SubscriptionPaymentStatus.prisma @@ -0,0 +1,8 @@ +enum SubscriptionPaymentStatus { + AwaitingInitialPayment + + Pending + + Successful + Unsuccessful +} diff --git a/packages/core/prisma/models/subscriptions/enums/SubscriptionStatus.prisma b/packages/core/prisma/models/subscriptions/enums/SubscriptionStatus.prisma new file mode 100644 index 000000000..fb777551f --- /dev/null +++ b/packages/core/prisma/models/subscriptions/enums/SubscriptionStatus.prisma @@ -0,0 +1,10 @@ +enum SubscriptionStatus { + Pending + + Active + + PaymentFailed + + CanceledByUser + CanceledByPaymentFailure +} \ No newline at end of file diff --git a/packages/core/prisma/models/user.prisma b/packages/core/prisma/models/users/User.prisma similarity index 92% rename from packages/core/prisma/models/user.prisma rename to packages/core/prisma/models/users/User.prisma index 050d919a5..2f15337f8 100644 --- a/packages/core/prisma/models/user.prisma +++ b/packages/core/prisma/models/users/User.prisma @@ -1,5 +1,3 @@ -// ---- User related stuff - model User { id String @id From dd0891a9b50f528073b3e134ff48a62f43604858 Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Mon, 12 Apr 2021 08:25:30 +0300 Subject: [PATCH 05/20] Create discussion command Signed-off-by: Alexander Ivanov --- packages/core/package.json | 3 +- .../migration.sql | 26 +++++++++ .../migration.sql | 18 ++++++ .../migration.sql | 13 +++++ .../migration.sql | 11 ++++ .../core/prisma/models/commons/Common.prisma | 3 +- .../models/discussions/Discussion.prisma | 6 +- .../discussions/DiscussionMessage.prisma | 6 +- .../discussions/DiscussionSubscription.prisma | 8 +-- .../prisma/models/events/EventType.prisma | 4 ++ .../prisma/models/proposals/Proposal.prisma | 2 + packages/core/prisma/models/users/User.prisma | 10 +++- .../core/commands/createDiscussionCommand.ts | 58 +++++++++++++++++-- 13 files changed, 151 insertions(+), 17 deletions(-) create mode 100644 packages/core/prisma/migrations/20210412050840_discussion_links_migration/migration.sql create mode 100644 packages/core/prisma/migrations/20210412051108_proposal_common_discussion/migration.sql create mode 100644 packages/core/prisma/migrations/20210412052009_discussion_creator/migration.sql create mode 100644 packages/core/prisma/migrations/20210412052219_discussion_event_types/migration.sql diff --git a/packages/core/package.json b/packages/core/package.json index 4e1172813..5ee83f17c 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -8,7 +8,8 @@ "compile": "tsc -p tsconfig.json && yarn fix:paths", "compile:watch": "tsc-watch --onSuccess \"yarn fix:paths\"", "fix:paths": "tscpaths -p tsconfig.json -s ./src -o ./dist", - "compile:schema": "find prisma -name '*.prisma' -not -name \"schema.prisma\" -exec cat {} + > prisma/schema.prisma && prisma format" + "create:schema": "find prisma -name '*.prisma' -not -name \"schema.prisma\" -exec cat {} + > prisma/schema.prisma && prisma format", + "create:migration": "yarn create:schema && prisma migrate dev" }, "devDependencies": { "@types/request-ip": "^0.0.35", diff --git a/packages/core/prisma/migrations/20210412050840_discussion_links_migration/migration.sql b/packages/core/prisma/migrations/20210412050840_discussion_links_migration/migration.sql new file mode 100644 index 000000000..8ede2a148 --- /dev/null +++ b/packages/core/prisma/migrations/20210412050840_discussion_links_migration/migration.sql @@ -0,0 +1,26 @@ +/* + Warnings: + + - You are about to drop the column `latestMessageSeen` on the `DiscussionSubscription` table. All the data in the column will be lost. + - Added the required column `userId` to the `DiscussionMessage` table without a default value. This is not possible if the table is not empty. + - Made the column `discussionId` on table `DiscussionMessage` required. This step will fail if there are existing NULL values in that column. + - Added the required column `userId` to the `DiscussionSubscription` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "DiscussionMessage" + ADD COLUMN "userId" TEXT NOT NULL, + ALTER COLUMN "discussionId" SET NOT NULL; + +-- AlterTable +ALTER TABLE "DiscussionSubscription" + DROP COLUMN "latestMessageSeen", + ADD COLUMN "userId" TEXT NOT NULL; + +-- AddForeignKey +ALTER TABLE "DiscussionMessage" + ADD FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "DiscussionSubscription" + ADD FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/core/prisma/migrations/20210412051108_proposal_common_discussion/migration.sql b/packages/core/prisma/migrations/20210412051108_proposal_common_discussion/migration.sql new file mode 100644 index 000000000..6e812fec5 --- /dev/null +++ b/packages/core/prisma/migrations/20210412051108_proposal_common_discussion/migration.sql @@ -0,0 +1,18 @@ +/* + Warnings: + + - Added the required column `commonId` to the `Discussion` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Discussion" + ADD COLUMN "commonId" TEXT NOT NULL, + ADD COLUMN "proposalId" TEXT; + +-- AddForeignKey +ALTER TABLE "Discussion" + ADD FOREIGN KEY ("commonId") REFERENCES "Common" ("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Discussion" + ADD FOREIGN KEY ("proposalId") REFERENCES "Proposal" ("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/packages/core/prisma/migrations/20210412052009_discussion_creator/migration.sql b/packages/core/prisma/migrations/20210412052009_discussion_creator/migration.sql new file mode 100644 index 000000000..4f9270e95 --- /dev/null +++ b/packages/core/prisma/migrations/20210412052009_discussion_creator/migration.sql @@ -0,0 +1,13 @@ +/* + Warnings: + + - Added the required column `userId` to the `Discussion` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Discussion" + ADD COLUMN "userId" TEXT NOT NULL; + +-- AddForeignKey +ALTER TABLE "Discussion" + ADD FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/core/prisma/migrations/20210412052219_discussion_event_types/migration.sql b/packages/core/prisma/migrations/20210412052219_discussion_event_types/migration.sql new file mode 100644 index 000000000..37a4da8e7 --- /dev/null +++ b/packages/core/prisma/migrations/20210412052219_discussion_event_types/migration.sql @@ -0,0 +1,11 @@ +-- AlterEnum +-- This migration adds more than one value to an enum. +-- With PostgreSQL versions 11 and earlier, this is not possible +-- in a single migration. This can be worked around by creating +-- multiple migrations, each migration adding only one value to +-- the enum. + + +ALTER TYPE "EventType" ADD VALUE 'DiscussionCreated'; +ALTER TYPE "EventType" ADD VALUE 'DiscussionMessageCreated'; +ALTER TYPE "EventType" ADD VALUE 'DiscussionSubscriptionCreated'; diff --git a/packages/core/prisma/models/commons/Common.prisma b/packages/core/prisma/models/commons/Common.prisma index eb588b449..be68913fa 100644 --- a/packages/core/prisma/models/commons/Common.prisma +++ b/packages/core/prisma/models/commons/Common.prisma @@ -20,7 +20,8 @@ model Common { payments Payment[] subscriptions Subscription[] - proposals Proposal[] + proposals Proposal[] + discussions Discussion[] } diff --git a/packages/core/prisma/models/discussions/Discussion.prisma b/packages/core/prisma/models/discussions/Discussion.prisma index a02667471..306a381b9 100644 --- a/packages/core/prisma/models/discussions/Discussion.prisma +++ b/packages/core/prisma/models/discussions/Discussion.prisma @@ -9,6 +9,10 @@ model Discussion { latestMessage DateTime @default(now()) - subscriptions DiscussionSubscription[] + user User + common Common + proposal Proposal? + messages DiscussionMessage[] + subscriptions DiscussionSubscription[] } diff --git a/packages/core/prisma/models/discussions/DiscussionMessage.prisma b/packages/core/prisma/models/discussions/DiscussionMessage.prisma index fda56e912..02509f3c2 100644 --- a/packages/core/prisma/models/discussions/DiscussionMessage.prisma +++ b/packages/core/prisma/models/discussions/DiscussionMessage.prisma @@ -7,7 +7,9 @@ model DiscussionMessage { type DiscussionMessageType @default(Message) message String - discussion Discussion? @relation(fields: [discussionId], references: [id]) + discussion Discussion @relation(fields: [discussionId], references: [id]) + user User @relation(fields: [userId], references: [id]) - discussionId String? + discussionId String + userId String } \ No newline at end of file diff --git a/packages/core/prisma/models/discussions/DiscussionSubscription.prisma b/packages/core/prisma/models/discussions/DiscussionSubscription.prisma index 6ade15b50..02dbc31ec 100644 --- a/packages/core/prisma/models/discussions/DiscussionSubscription.prisma +++ b/packages/core/prisma/models/discussions/DiscussionSubscription.prisma @@ -4,11 +4,11 @@ model DiscussionSubscription { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - latestMessageSeen DateTime @default(now()) - type DiscussionSubscriptionType @default(AllNotifications) - discussion Discussion @relation(fields: [discussionId], references: [id]) - + userId String discussionId String + + user User @relation(fields: [userId], references: [id]) + discussion Discussion @relation(fields: [discussionId], references: [id]) } diff --git a/packages/core/prisma/models/events/EventType.prisma b/packages/core/prisma/models/events/EventType.prisma index d25d322ef..73c83ab80 100644 --- a/packages/core/prisma/models/events/EventType.prisma +++ b/packages/core/prisma/models/events/EventType.prisma @@ -27,4 +27,8 @@ enum EventType { VoteCreated UserCreated + + DiscussionCreated + DiscussionMessageCreated + DiscussionSubscriptionCreated } \ No newline at end of file diff --git a/packages/core/prisma/models/proposals/Proposal.prisma b/packages/core/prisma/models/proposals/Proposal.prisma index 137c13a0f..ccc11c2cd 100644 --- a/packages/core/prisma/models/proposals/Proposal.prisma +++ b/packages/core/prisma/models/proposals/Proposal.prisma @@ -23,6 +23,8 @@ model Proposal { votesFor Int @default(0) votesAgainst Int @default(0) + discussions Discussion[] + join JoinProposal? @relation(fields: [joinId], references: [id]) funding FundingProposal? @relation(fields: [fundingId], references: [id]) diff --git a/packages/core/prisma/models/users/User.prisma b/packages/core/prisma/models/users/User.prisma index 2f15337f8..db46042b6 100644 --- a/packages/core/prisma/models/users/User.prisma +++ b/packages/core/prisma/models/users/User.prisma @@ -14,7 +14,11 @@ model User { events Event[] memberships CommonMember[] - payments Payment[] - proposals Proposal[] - subscriptions Subscription[] + payments Payment[] + proposals Proposal[] + subscriptions Subscription[] + + discussionSubscriptions DiscussionSubscription[] + discussionMessages DiscussionMessage[] + discussions Discussion[] } \ No newline at end of file diff --git a/packages/core/src/services/discussions/core/commands/createDiscussionCommand.ts b/packages/core/src/services/discussions/core/commands/createDiscussionCommand.ts index 58df2c7fc..4c33d33ce 100644 --- a/packages/core/src/services/discussions/core/commands/createDiscussionCommand.ts +++ b/packages/core/src/services/discussions/core/commands/createDiscussionCommand.ts @@ -1,6 +1,9 @@ import * as z from 'zod'; -import { Discussion } from '@prisma/client'; -import { NotImplementedError } from '@errors'; +import { Discussion, EventType } from '@prisma/client'; + +import { prisma } from '@toolkits'; +import { CommonError } from '@errors'; +import { eventService } from '@services'; const schema = z.object({ commonId: z.string() @@ -11,7 +14,7 @@ const schema = z.object({ .optional() .nullable(), - usesId: z.string() + userId: z.string() .nonempty(), topic: z.string() @@ -21,6 +24,51 @@ const schema = z.object({ .nonempty() }); -export const createDiscussionCommand = (payload: z.infer): Promise => { - throw new NotImplementedError(); +/** + * Creates new discussion for a common or proposal. The userId must me of a + * user that is common member of that common, or the common of that proposal + * + * @param payload + */ +export const createDiscussionCommand = async (payload: z.infer): Promise => { + // Validate the payload + schema.parse(payload); + + // Check if the user is common member + if ( + !(await prisma.commonMember.count({ + where: { + userId: payload.userId, + commonId: payload.commonId + } + })) + ) { + throw new CommonError('Cannot create discussion in a common that you are not member of!'); + } + + // Create the discussion + const discussion = await prisma.discussion.create({ + data: { + userId: payload.userId, + commonId: payload.commonId, + + topic: payload.topic.trim(), + description: payload.description.trim() + } + }); + + // Create event about the discussion creation + await eventService.create({ + type: EventType.DiscussionCreated, + commonId: payload.commonId, + userId: payload.userId, + payload: { + discussionId: discussion.id + } + }); + + // @todo Subscribe the creator to the discussion + + // Return the created discussion + return discussion; }; \ No newline at end of file From 9b50581c630a2fd7c0294a48eb1d71b65285dfc5 Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Mon, 12 Apr 2021 09:43:02 +0300 Subject: [PATCH 06/20] Create discussion graphql Signed-off-by: Alexander Ivanov --- .../migration.sql | 8 + .../models/discussions/Discussion.prisma | 2 + packages/core/src/index.ts | 4 + .../core/commands/createDiscussionCommand.ts | 10 +- packages/core/src/services/index.ts | 1 + packages/graphql/src/context/index.ts | 11 +- .../graphql/src/generated/nexus-typegen.ts | 63 ++++- packages/graphql/src/generated/schema.graphql | 264 ++++++++++-------- .../Shared/Interfaces/BaseEntity.interface.ts | 21 ++ .../Mutations/CreateDiscussion.mutation.ts | 47 ++++ .../Types/Discussion/Types/Discussion.type.ts | 8 + .../src/schema/Types/Discussion/index.ts | 10 + packages/graphql/src/schema/index.ts | 9 +- packages/queues/src/jobs/EventsQueue.ts | 6 +- 14 files changed, 329 insertions(+), 135 deletions(-) create mode 100644 packages/core/prisma/migrations/20210412053758_discussion_type/migration.sql create mode 100644 packages/graphql/src/schema/Shared/Interfaces/BaseEntity.interface.ts create mode 100644 packages/graphql/src/schema/Types/Discussion/Mutations/CreateDiscussion.mutation.ts create mode 100644 packages/graphql/src/schema/Types/Discussion/Types/Discussion.type.ts create mode 100644 packages/graphql/src/schema/Types/Discussion/index.ts diff --git a/packages/core/prisma/migrations/20210412053758_discussion_type/migration.sql b/packages/core/prisma/migrations/20210412053758_discussion_type/migration.sql new file mode 100644 index 000000000..800a8a095 --- /dev/null +++ b/packages/core/prisma/migrations/20210412053758_discussion_type/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - Added the required column `type` to the `Discussion` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Discussion" ADD COLUMN "type" "DiscussionType" NOT NULL; diff --git a/packages/core/prisma/models/discussions/Discussion.prisma b/packages/core/prisma/models/discussions/Discussion.prisma index 306a381b9..2a6bb11c6 100644 --- a/packages/core/prisma/models/discussions/Discussion.prisma +++ b/packages/core/prisma/models/discussions/Discussion.prisma @@ -4,6 +4,8 @@ model Discussion { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + type DiscussionType + topic String description String diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 40795ec0b..2903ba897 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -6,6 +6,9 @@ FirebaseToolkit.InitializeFirebase(); export { prisma } from '@toolkits'; export { logger } from '@logger'; +// Domain +export { CommonError } from '@errors'; + // Services export { cardService } from './services/cards'; @@ -15,5 +18,6 @@ export { eventService } from './services/events'; export { commonService } from './services/commons'; export { paymentService } from './services/payments'; export { proposalService } from './services/proposals'; +export { discussionService } from './services/discussions'; export { FirebaseToolkit }; \ No newline at end of file diff --git a/packages/core/src/services/discussions/core/commands/createDiscussionCommand.ts b/packages/core/src/services/discussions/core/commands/createDiscussionCommand.ts index 4c33d33ce..392c58863 100644 --- a/packages/core/src/services/discussions/core/commands/createDiscussionCommand.ts +++ b/packages/core/src/services/discussions/core/commands/createDiscussionCommand.ts @@ -1,5 +1,5 @@ import * as z from 'zod'; -import { Discussion, EventType } from '@prisma/client'; +import { Discussion, EventType, DiscussionType } from '@prisma/client'; import { prisma } from '@toolkits'; import { CommonError } from '@errors'; @@ -46,6 +46,8 @@ export const createDiscussionCommand = async (payload: z.infer): throw new CommonError('Cannot create discussion in a common that you are not member of!'); } + // @todo If there is proposal id check if it is from the common + // Create the discussion const discussion = await prisma.discussion.create({ data: { @@ -53,7 +55,11 @@ export const createDiscussionCommand = async (payload: z.infer): commonId: payload.commonId, topic: payload.topic.trim(), - description: payload.description.trim() + description: payload.description.trim(), + + type: payload.proposalId + ? DiscussionType.ProposalDiscussion + : DiscussionType.CommonDiscussion } }); diff --git a/packages/core/src/services/index.ts b/packages/core/src/services/index.ts index 74203117a..db0e3d18f 100644 --- a/packages/core/src/services/index.ts +++ b/packages/core/src/services/index.ts @@ -4,4 +4,5 @@ export { cardService } from './cards'; export { eventService } from './events'; export { commonService } from './commons'; export { paymentService } from './payments'; +export { discussionService } from './discussions'; export { joinProposalService, proposalService } from './proposals'; diff --git a/packages/graphql/src/context/index.ts b/packages/graphql/src/context/index.ts index 0f6267f43..1f9489890 100644 --- a/packages/graphql/src/context/index.ts +++ b/packages/graphql/src/context/index.ts @@ -41,8 +41,15 @@ export const createRequestContext = ({ req, res }: ExpressContext): IRequestCont prisma, getUserId: async () => { - // @todo Use custom method for that - return (await auth().verifyIdToken(req.headers.authorization as string)).uid; + // @todo Remove this + if (process.env['Authentication.Mock']) { + return req.headers.authorization + ? (await auth().verifyIdToken(req.headers.authorization as string)).uid + : req.headers.uid as string; + } else { + // @todo Use custom method for that + return (await auth().verifyIdToken(req.headers.authorization as string)).uid; + } }, getUserDecodedToken: async () => { diff --git a/packages/graphql/src/generated/nexus-typegen.ts b/packages/graphql/src/generated/nexus-typegen.ts index 25ccf8109..00935be3c 100644 --- a/packages/graphql/src/generated/nexus-typegen.ts +++ b/packages/graphql/src/generated/nexus-typegen.ts @@ -4,10 +4,9 @@ */ -import { IRequestContext } from './../context'; -import { QueryComplexity } from 'nexus/dist/plugins/queryComplexityPlugin'; -import { core } from 'nexus'; - +import { IRequestContext } from "./../context" +import { QueryComplexity } from "nexus/dist/plugins/queryComplexityPlugin" +import { core } from "nexus" declare global { interface NexusGenCustomInputMethods { /** @@ -74,6 +73,12 @@ export interface NexusGenInputs { fundingType: NexusGenEnums['FundingType']; // FundingType! name: string; // String! } + CreateDiscussionInput: { // input type + commonId: string; // ID! + description: string; // String! + proposalId?: string | null; // ID + topic: string; // String! + } CreateFundingProposalInput: { // input type amount: number; // Int! commonId: string; // ID! @@ -137,12 +142,12 @@ export interface NexusGenInputs { } export interface NexusGenEnums { - CommonMemberRole: 'Founder' - EventType: 'CardCreated' | 'CardCvvVerificationFailed' | 'CardCvvVerificationPassed' | 'CommonCreated' | 'CommonMemberCreated' | 'CommonMemberRoleAdded' | 'CommonMemberRoleRemoved' | 'FundingRequestAccepted' | 'FundingRequestCreated' | 'FundingRequestRejected' | 'JoinRequestAccepted' | 'JoinRequestCreated' | 'JoinRequestRejected' | 'PaymentCreated' | 'PaymentFailed' | 'PaymentSucceeded' | 'ProposalExpired' | 'ProposalMajorityReached' | 'UserCreated' | 'VoteCreated' - FundingType: 'Monthly' | 'OneTime' - ProposalType: 'FundingRequest' | 'JoinRequest' - SortOrder: 'asc' | 'desc' - VoteOutcome: 'Approve' | 'Condemn' + CommonMemberRole: "Founder" + EventType: "CardCreated" | "CardCvvVerificationFailed" | "CardCvvVerificationPassed" | "CommonCreated" | "CommonMemberCreated" | "CommonMemberRoleAdded" | "CommonMemberRoleRemoved" | "DiscussionCreated" | "DiscussionMessageCreated" | "DiscussionSubscriptionCreated" | "FundingRequestAccepted" | "FundingRequestCreated" | "FundingRequestRejected" | "JoinRequestAccepted" | "JoinRequestCreated" | "JoinRequestRejected" | "PaymentCreated" | "PaymentFailed" | "PaymentSucceeded" | "ProposalExpired" | "ProposalMajorityReached" | "UserCreated" | "VoteCreated" + FundingType: "Monthly" | "OneTime" + ProposalType: "FundingRequest" | "JoinRequest" + SortOrder: "asc" | "desc" + VoteOutcome: "Approve" | "Condemn" } export interface NexusGenScalars { @@ -175,6 +180,10 @@ export interface NexusGenObjects { roles: NexusGenEnums['CommonMemberRole'][]; // [CommonMemberRole!]! userId: string; // ID! } + Discussion: { // root type + createdAt: NexusGenScalars['DateTime']; // DateTime! + updatedAt: NexusGenScalars['DateTime']; // DateTime! + } Event: { // root type commonId?: string | null; // ID createdAt: NexusGenScalars['DateTime']; // DateTime! @@ -221,12 +230,13 @@ export interface NexusGenObjects { } export interface NexusGenInterfaces { + BaseEntity: NexusGenRootTypes['Discussion']; } export interface NexusGenUnions { } -export type NexusGenRootTypes = NexusGenObjects +export type NexusGenRootTypes = NexusGenInterfaces & NexusGenObjects export type NexusGenAllTypes = NexusGenRootTypes & NexusGenScalars & NexusGenEnums @@ -254,6 +264,11 @@ export interface NexusGenFieldTypes { user: NexusGenRootTypes['User'] | null; // User userId: string; // ID! } + Discussion: { // field return type + createdAt: NexusGenScalars['DateTime']; // DateTime! + id: string; // ID! + updatedAt: NexusGenScalars['DateTime']; // DateTime! + } Event: { // field return type commonId: string | null; // ID createdAt: NexusGenScalars['DateTime']; // DateTime! @@ -280,6 +295,7 @@ export interface NexusGenFieldTypes { Mutation: { // field return type createCard: NexusGenRootTypes['Card']; // Card! createCommon: NexusGenRootTypes['Common']; // Common! + createDiscussion: NexusGenRootTypes['Discussion']; // Discussion! createFundingProposal: NexusGenRootTypes['FundingProposal']; // FundingProposal! createJoinProposal: NexusGenRootTypes['JoinProposal']; // JoinProposal! createUser: NexusGenRootTypes['User']; // User! @@ -312,6 +328,11 @@ export interface NexusGenFieldTypes { id: string; // ID! updatedAt: NexusGenScalars['DateTime']; // DateTime! } + BaseEntity: { // field return type + createdAt: NexusGenScalars['DateTime']; // DateTime! + id: string; // ID! + updatedAt: NexusGenScalars['DateTime']; // DateTime! + } } export interface NexusGenFieldTypeNames { @@ -338,6 +359,11 @@ export interface NexusGenFieldTypeNames { user: 'User' userId: 'ID' } + Discussion: { // field return type name + createdAt: 'DateTime' + id: 'ID' + updatedAt: 'DateTime' + } Event: { // field return type name commonId: 'ID' createdAt: 'DateTime' @@ -364,6 +390,7 @@ export interface NexusGenFieldTypeNames { Mutation: { // field return type name createCard: 'Card' createCommon: 'Common' + createDiscussion: 'Discussion' createFundingProposal: 'FundingProposal' createJoinProposal: 'JoinProposal' createUser: 'User' @@ -396,6 +423,11 @@ export interface NexusGenFieldTypeNames { id: 'ID' updatedAt: 'DateTime' } + BaseEntity: { // field return type name + createdAt: 'DateTime' + id: 'ID' + updatedAt: 'DateTime' + } } export interface NexusGenArgTypes { @@ -423,6 +455,9 @@ export interface NexusGenArgTypes { createCommon: { // args input: NexusGenInputs['CreateCommonInput']; // CreateCommonInput! } + createDiscussion: { // args + input: NexusGenInputs['CreateDiscussionInput']; // CreateDiscussionInput! + } createFundingProposal: { // args input: NexusGenInputs['CreateFundingProposalInput']; // CreateFundingProposalInput! } @@ -465,9 +500,11 @@ export interface NexusGenArgTypes { } export interface NexusGenAbstractTypeMembers { + BaseEntity: "Discussion" } export interface NexusGenTypeInterfaces { + Discussion: "BaseEntity" } export type NexusGenObjectNames = keyof NexusGenObjects; @@ -476,7 +513,7 @@ export type NexusGenInputNames = keyof NexusGenInputs; export type NexusGenEnumNames = keyof NexusGenEnums; -export type NexusGenInterfaceNames = never; +export type NexusGenInterfaceNames = keyof NexusGenInterfaces; export type NexusGenScalarNames = keyof NexusGenScalars; @@ -484,7 +521,7 @@ export type NexusGenUnionNames = never; export type NexusGenObjectsUsingAbstractStrategyIsTypeOf = never; -export type NexusGenAbstractsUsingStrategyResolveType = never; +export type NexusGenAbstractsUsingStrategyResolveType = "BaseEntity"; export type NexusGenFeaturesConfig = { abstractTypeStrategies: { diff --git a/packages/graphql/src/generated/schema.graphql b/packages/graphql/src/generated/schema.graphql index 480b9480b..bbda4dee0 100644 --- a/packages/graphql/src/generated/schema.graphql +++ b/packages/graphql/src/generated/schema.graphql @@ -2,6 +2,17 @@ ### Do not make changes to this file directly +interface BaseEntity { + """The date, at which the item was created""" + createdAt: DateTime! + + """The main identifier of the item""" + id: ID! + + """The date, at which the item was last modified""" + updatedAt: DateTime! +} + input BillingDetailsInput { city: String! country: String! @@ -13,36 +24,36 @@ input BillingDetailsInput { } type Card { - """The date, at which the item was created""" - createdAt: DateTime! + """The date, at which the item was created""" + createdAt: DateTime! - """The main identifier of the item""" - id: ID! + """The main identifier of the item""" + id: ID! - """The date, at which the item was last modified""" - updatedAt: DateTime! + """The date, at which the item was last modified""" + updatedAt: DateTime! } type Common { - """The date, at which the item was created""" - createdAt: DateTime! + """The date, at which the item was created""" + createdAt: DateTime! - """List of events, that occurred in a common""" - events(orderBy: EventOrderByInput, skip: Int = 0, take: Int = 10): [Event!]! + """List of events, that occurred in a common""" + events(orderBy: EventOrderByInput, skip: Int = 0, take: Int = 10): [Event!]! - """The main identifier of the item""" - id: ID! - members(orderBy: CommonMemberOrderByInput, skip: Int, take: Int): [CommonMember]! + """The main identifier of the item""" + id: ID! + members(orderBy: CommonMemberOrderByInput, skip: Int, take: Int): [CommonMember]! - """The name of the common as provided""" - name: String! - proposals(skip: Int = 0, take: Int = 10, where: ProposalWhereInput): [Proposal!]! + """The name of the common as provided""" + name: String! + proposals(skip: Int = 0, take: Int = 10, where: ProposalWhereInput): [Proposal!]! - """The date, at which the item was last modified""" - updatedAt: DateTime! + """The date, at which the item was last modified""" + updatedAt: DateTime! - """The whitelisting state of a common""" - whitelisted: Boolean! + """The whitelisting state of a common""" + whitelisted: Boolean! } type CommonMember { @@ -79,9 +90,23 @@ input CreateCardInput { } input CreateCommonInput { - fundingMinimumAmount: Int! - fundingType: FundingType! - name: String! + fundingMinimumAmount: Int! + fundingType: FundingType! + name: String! +} + +input CreateDiscussionInput { + """The ID of the common, for which we are creating the discussion""" + commonId: ID! + + """Short description about the topic""" + description: String! + + """The ID of the proposal, if this is proposal discussion""" + proposalId: ID + + """The topic of the discussion to be created""" + topic: String! } input CreateFundingProposalInput { @@ -121,25 +146,36 @@ A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the `dat """ scalar DateTime +type Discussion implements BaseEntity { + """The date, at which the item was created""" + createdAt: DateTime! + + """The main identifier of the item""" + id: ID! + + """The date, at which the item was last modified""" + updatedAt: DateTime! +} + type Event { - """The ID of the common, for whom the event was created""" - commonId: ID + """The ID of the common, for whom the event was created""" + commonId: ID - """The date, at which the item was created""" - createdAt: DateTime! + """The date, at which the item was created""" + createdAt: DateTime! - """The main identifier of the item""" - id: ID! - payload: JSON + """The main identifier of the item""" + id: ID! + payload: JSON - """The type of the event in one of the predefined event types""" - type: EventType! + """The type of the event in one of the predefined event types""" + type: EventType! - """The date, at which the item was last modified""" - updatedAt: DateTime! + """The date, at which the item was last modified""" + updatedAt: DateTime! - """The ID of the event creator""" - userId: ID + """The ID of the event creator""" + userId: ID } input EventOrderByInput { @@ -156,6 +192,9 @@ enum EventType { CommonMemberCreated CommonMemberRoleAdded CommonMemberRoleRemoved + DiscussionCreated + DiscussionMessageCreated + DiscussionSubscriptionCreated FundingRequestAccepted FundingRequestCreated FundingRequestRejected @@ -172,20 +211,20 @@ enum EventType { } type FundingProposal { - """The date, at which the item was created""" - createdAt: DateTime! + """The date, at which the item was created""" + createdAt: DateTime! - """The main identifier of the item""" - id: ID! + """The main identifier of the item""" + id: ID! - """The date, at which the item was last modified""" - updatedAt: DateTime! + """The date, at which the item was last modified""" + updatedAt: DateTime! } """The funding type of the common""" enum FundingType { - Monthly - OneTime + Monthly + OneTime } """ @@ -194,14 +233,14 @@ The `JSON` scalar type represents JSON values as specified by [ECMA-404](http:// scalar JSON type JoinProposal { - """The date, at which the item was created""" - createdAt: DateTime! + """The date, at which the item was created""" + createdAt: DateTime! - """The main identifier of the item""" - id: ID! + """The main identifier of the item""" + id: ID! - """The date, at which the item was last modified""" - updatedAt: DateTime! + """The date, at which the item was last modified""" + updatedAt: DateTime! } type Link { @@ -221,79 +260,80 @@ input LinkInput { } type Mutation { - createCard(input: CreateCardInput!): Card! - createCommon(input: CreateCommonInput!): Common! - createFundingProposal(input: CreateFundingProposalInput!): FundingProposal! + createCard(input: CreateCardInput!): Card! + createCommon(input: CreateCommonInput!): Common! + createDiscussion(input: CreateDiscussionInput!): Discussion! + createFundingProposal(input: CreateFundingProposalInput!): FundingProposal! - """Create new proposal of type JOIN.""" - createJoinProposal(input: CreateJoinProposalInput!): JoinProposal! + """Create new proposal of type JOIN.""" + createJoinProposal(input: CreateJoinProposalInput!): JoinProposal! - """Creates new user in the system""" - createUser(input: CreateUserInput!): User! - createVote(input: CreateVoteInput!): Vote! - finalizeProposal(proposalId: ID!): Boolean! + """Creates new user in the system""" + createUser(input: CreateUserInput!): User! + createVote(input: CreateVoteInput!): Vote! + finalizeProposal(proposalId: ID!): Boolean! } type Proposal { - """The date, at which the item was created""" - createdAt: DateTime! + """The date, at which the item was created""" + createdAt: DateTime! - """The main identifier of the item""" - id: ID! - type: ProposalType! + """The main identifier of the item""" + id: ID! + type: ProposalType! - """The date, at which the item was last modified""" - updatedAt: DateTime! + """The date, at which the item was last modified""" + updatedAt: DateTime! } input ProposalFileInput { - value: String! + value: String! } input ProposalImageInput { - value: String! + value: String! } input ProposalLinkInput { - title: String! - url: String! + title: String! + url: String! } enum ProposalType { - FundingRequest - JoinRequest + FundingRequest + JoinRequest } input ProposalWhereInput { - type: ProposalType + type: ProposalType } type Query { - common(where: CommonWhereUniqueInput!): Common - generateUserAuthToken(authId: String!): String! + common(where: CommonWhereUniqueInput!): Common + generateUserAuthToken(authId: String!): String! - """ - Provide ID to fetch specific user or do not pass anything to get the currently authenticated user - """ - user(userId: ID): User + """ + Provide ID to fetch specific user or do not pass anything to get the currently authenticated user + """ + user(userId: ID): User } enum SortOrder { - asc - desc + asc + desc } input StringFilter { - contains: String - endsWith: String - equals: String - gt: String - gte: String - in: [String!] - lt: String - lte: String - notIn: [String!] - startsWith: String + contains: String + endsWith: String + equals: String + gt: String + gte: String + in: [String!] + lt: String + lte: String + notIn: [String!] + startsWith: String } """ @@ -302,38 +342,38 @@ A field whose value conforms to the standard URL format as specified in RFC3986: scalar URL type User { - """The date, at which the item was created""" - createdAt: DateTime! + """The date, at which the item was created""" + createdAt: DateTime! - """The display name of the user""" - displayName: String! + """The display name of the user""" + displayName: String! - """List of events, that occurred and are related to this user""" - events(orderBy: EventOrderByInput, skip: Int = 0, take: Int = 10): [Event!]! + """List of events, that occurred and are related to this user""" + events(orderBy: EventOrderByInput, skip: Int = 0, take: Int = 10): [Event!]! - """The first name of the user""" - firstName: String! + """The first name of the user""" + firstName: String! - """The system Id of the user""" - id: ID! + """The system Id of the user""" + id: ID! - """The last name of the user""" - lastName: String! - proposals(skip: Int = 0, take: Int = 10, where: ProposalWhereInput): [Proposal!]! + """The last name of the user""" + lastName: String! + proposals(skip: Int = 0, take: Int = 10, where: ProposalWhereInput): [Proposal!]! - """The date, at which the item was last modified""" - updatedAt: DateTime! + """The date, at which the item was last modified""" + updatedAt: DateTime! } type Vote { - """The date, at which the item was created""" - createdAt: DateTime! + """The date, at which the item was created""" + createdAt: DateTime! - """The main identifier of the item""" - id: ID! + """The main identifier of the item""" + id: ID! - """The date, at which the item was last modified""" - updatedAt: DateTime! + """The date, at which the item was last modified""" + updatedAt: DateTime! } enum VoteOutcome { diff --git a/packages/graphql/src/schema/Shared/Interfaces/BaseEntity.interface.ts b/packages/graphql/src/schema/Shared/Interfaces/BaseEntity.interface.ts new file mode 100644 index 000000000..75e94bb0e --- /dev/null +++ b/packages/graphql/src/schema/Shared/Interfaces/BaseEntity.interface.ts @@ -0,0 +1,21 @@ +import { interfaceType } from 'nexus'; + +export const BaseEntityInterface = interfaceType({ + resolveType: () => undefined, + name: 'BaseEntity', + definition(t) { + t.nonNull.id('id', { + description: 'The main identifier of the item', + resolve: r => r.id + }); + + t.nonNull.date('createdAt', { + description: 'The date, at which the item was created' + }); + + t.nonNull.date('updatedAt', { + description: 'The date, at which the item was last modified' + }); + } + +}); \ No newline at end of file diff --git a/packages/graphql/src/schema/Types/Discussion/Mutations/CreateDiscussion.mutation.ts b/packages/graphql/src/schema/Types/Discussion/Mutations/CreateDiscussion.mutation.ts new file mode 100644 index 000000000..03b0ce2b2 --- /dev/null +++ b/packages/graphql/src/schema/Types/Discussion/Mutations/CreateDiscussion.mutation.ts @@ -0,0 +1,47 @@ +import { extendType, inputObjectType, nonNull, arg } from 'nexus'; +import { discussionService } from '@common/core'; + +export const CreateDiscussionInput = inputObjectType({ + name: 'CreateDiscussionInput', + definition(t) { + t.nonNull.string('topic', { + description: 'The topic of the discussion to be created' + }); + + t.nonNull.string('description', { + description: 'Short description about the topic' + }); + + t.nonNull.id('commonId', { + description: 'The ID of the common, for which we are creating the discussion' + }); + + t.id('proposalId', { + description: 'The ID of the proposal, if this is proposal discussion' + }); + } +}); + +export const CreateDiscussionMutation = extendType({ + type: 'Mutation', + definition(t) { + t.nonNull.field('createDiscussion', { + type: 'Discussion', + args: { + input: nonNull( + arg({ + type: 'CreateDiscussionInput' + }) + ) + }, + resolve: async (root, args, ctx) => { + const userId = await ctx.getUserId(); + + return discussionService.create({ + ...args.input, + userId + }); + } + }); + } +}); \ No newline at end of file diff --git a/packages/graphql/src/schema/Types/Discussion/Types/Discussion.type.ts b/packages/graphql/src/schema/Types/Discussion/Types/Discussion.type.ts new file mode 100644 index 000000000..4fa5ada47 --- /dev/null +++ b/packages/graphql/src/schema/Types/Discussion/Types/Discussion.type.ts @@ -0,0 +1,8 @@ +import { objectType } from 'nexus'; + +export const DiscussionType = objectType({ + name: 'Discussion', + definition(t) { + t.implements('BaseEntity'); + } +}); \ No newline at end of file diff --git a/packages/graphql/src/schema/Types/Discussion/index.ts b/packages/graphql/src/schema/Types/Discussion/index.ts new file mode 100644 index 000000000..a0d14bc66 --- /dev/null +++ b/packages/graphql/src/schema/Types/Discussion/index.ts @@ -0,0 +1,10 @@ +import { DiscussionType } from './Types/Discussion.type'; + +import { CreateDiscussionInput, CreateDiscussionMutation } from './Mutations/CreateDiscussion.mutation'; + +export const DiscussionTypes = [ + DiscussionType, + + CreateDiscussionInput, + CreateDiscussionMutation +]; \ No newline at end of file diff --git a/packages/graphql/src/schema/index.ts b/packages/graphql/src/schema/index.ts index b792d0f6f..b53626040 100644 --- a/packages/graphql/src/schema/index.ts +++ b/packages/graphql/src/schema/index.ts @@ -7,7 +7,7 @@ import { VoteTypes } from './Types/Votes'; import { EventTypes } from './Types/Events'; import { CommonTypes } from './Types/Commons'; import { ProposalTypes } from './Types/Proposals'; - +import { DiscussionTypes } from './Types/Discussion'; import { CommonMemberTypes } from './Types/CommonMember'; import { UrlScalar } from './Shared/Scalars/Url.scalar'; @@ -16,9 +16,10 @@ import { JsonScalar } from './Shared/Scalars/Json.scalar'; import { LinkType, LinkInputType } from './Shared/Types/Link.type'; +import { BaseEntityInterface } from './Shared/Interfaces/BaseEntity.interface'; + import { BillingDetailsInput } from './Shared/Inputs/BillingDetails.input'; import { StringFilterInput } from './Shared/Inputs/StringFilter.input'; - import { SortOrder } from './Shared/Enums/SortBy.enum'; const types = [ @@ -28,6 +29,7 @@ const types = [ EventTypes, CommonTypes, ProposalTypes, + DiscussionTypes, CommonMemberTypes, // Scalars @@ -41,6 +43,9 @@ const types = [ // Shared Types LinkType, + // Shared Interfaces + BaseEntityInterface, + // Shared Input Types LinkInputType, StringFilterInput, diff --git a/packages/queues/src/jobs/EventsQueue.ts b/packages/queues/src/jobs/EventsQueue.ts index d28d350c3..2ccce3d39 100644 --- a/packages/queues/src/jobs/EventsQueue.ts +++ b/packages/queues/src/jobs/EventsQueue.ts @@ -1,16 +1,14 @@ import Queue, { JobOptions } from 'bull'; -import { setQueues, BullAdapter } from 'bull-board'; import { Event, EventType } from '@prisma/client'; import { Queues } from '../constants/Queues'; -import { CommonError } from '@common/core/dist/domain/errors'; -import { logger, eventService } from '@common/core'; +import { logger, CommonError } from '@common/core'; // Create the job spec export interface IEventsQueueJob { create?: { - type: EventType; + type: EventType | string; userId?: string; commonId?: string; From 63bd628544bff59a92c337f7197f04206f420693 Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Mon, 12 Apr 2021 10:37:24 +0300 Subject: [PATCH 07/20] Create discussion subscription (#354) Signed-off-by: Alexander Ivanov --- .../src/services/cards/commands/createCardCommand.ts | 2 +- .../src/services/cards/commands/verifyCardCommand.ts | 2 +- .../commons/command/addCommonMemberRoleCommand.ts | 2 +- .../src/services/commons/command/createCommonCommand.ts | 2 +- .../commons/command/createCommonMemberCommand.ts | 2 +- .../discussions/core/commands/createDiscussionCommand.ts | 9 +++++++-- .../src/services/events/commands/createEventCommand.ts | 2 +- .../services/payments/commands/createPaymentCommand.ts | 2 +- .../funding/command/createFundingProposalCommand.ts | 2 +- .../command/process/processApprovedFundingRequest.ts | 2 +- .../command/process/processRejectedFundingRequest.ts | 2 +- .../proposals/join/command/createJoinProposalCommand.ts | 2 +- .../command/process/processApprovedOneTimeJoinRequest.ts | 2 +- .../process/processApprovedSubscriptionJoinRequest.ts | 2 +- .../join/command/process/processRejectedJoinRequest.ts | 2 +- .../src/services/users/commands/createUserCommand.ts | 2 +- .../src/services/votes/commands/createVoteCommand.ts | 2 +- 17 files changed, 23 insertions(+), 18 deletions(-) diff --git a/packages/core/src/services/cards/commands/createCardCommand.ts b/packages/core/src/services/cards/commands/createCardCommand.ts index 8979757bc..495d0bf62 100644 --- a/packages/core/src/services/cards/commands/createCardCommand.ts +++ b/packages/core/src/services/cards/commands/createCardCommand.ts @@ -88,7 +88,7 @@ export const createCardCommand = async (command: z.infer): Promis }); // Create event for the card - await eventService.create({ + eventService.create({ type: EventType.CardCreated, userId }); diff --git a/packages/core/src/services/cards/commands/verifyCardCommand.ts b/packages/core/src/services/cards/commands/verifyCardCommand.ts index 45e3a4a15..f9cc4564e 100644 --- a/packages/core/src/services/cards/commands/verifyCardCommand.ts +++ b/packages/core/src/services/cards/commands/verifyCardCommand.ts @@ -67,7 +67,7 @@ export const verifyCardCommand = async (card: Card, customOptions?: Partial }); // Create event - await eventService.create({ + eventService.create({ type: EventType.CommonMemberRoleAdded, commonId: memberEntity.commonId, userId: memberEntity.userId, diff --git a/packages/core/src/services/commons/command/createCommonCommand.ts b/packages/core/src/services/commons/command/createCommonCommand.ts index d68fe333e..e5e816fbd 100644 --- a/packages/core/src/services/commons/command/createCommonCommand.ts +++ b/packages/core/src/services/commons/command/createCommonCommand.ts @@ -37,7 +37,7 @@ export const createCommonCommand = async (command: z.infer): Prom }); // Create event for the common creation - await eventService.create({ + eventService.create({ type: EventType.CommonCreated, commonId: common.id, userId: command.founderId diff --git a/packages/core/src/services/commons/command/createCommonMemberCommand.ts b/packages/core/src/services/commons/command/createCommonMemberCommand.ts index 439f89f19..1839870f9 100644 --- a/packages/core/src/services/commons/command/createCommonMemberCommand.ts +++ b/packages/core/src/services/commons/command/createCommonMemberCommand.ts @@ -41,7 +41,7 @@ export const createCommonMemberCommand = async (command: z.infer) }); // Create event for the new member - await eventService.create({ + eventService.create({ type: EventType.CommonMemberCreated, commonId: command.commonId, userId: command.userId, diff --git a/packages/core/src/services/discussions/core/commands/createDiscussionCommand.ts b/packages/core/src/services/discussions/core/commands/createDiscussionCommand.ts index 392c58863..374ffea85 100644 --- a/packages/core/src/services/discussions/core/commands/createDiscussionCommand.ts +++ b/packages/core/src/services/discussions/core/commands/createDiscussionCommand.ts @@ -4,6 +4,7 @@ import { Discussion, EventType, DiscussionType } from '@prisma/client'; import { prisma } from '@toolkits'; import { CommonError } from '@errors'; import { eventService } from '@services'; +import { createDiscussionSubscriptionCommand } from '../../subscriptions/createDiscussionSubscriptionCommand'; const schema = z.object({ commonId: z.string() @@ -64,7 +65,7 @@ export const createDiscussionCommand = async (payload: z.infer): }); // Create event about the discussion creation - await eventService.create({ + eventService.create({ type: EventType.DiscussionCreated, commonId: payload.commonId, userId: payload.userId, @@ -73,7 +74,11 @@ export const createDiscussionCommand = async (payload: z.infer): } }); - // @todo Subscribe the creator to the discussion + // Subscribe the creator to the discussion + await createDiscussionSubscriptionCommand({ + discussionId: discussion.id, + userId: discussion.userId + }); // Return the created discussion return discussion; diff --git a/packages/core/src/services/events/commands/createEventCommand.ts b/packages/core/src/services/events/commands/createEventCommand.ts index 1313a0a32..b47219288 100644 --- a/packages/core/src/services/events/commands/createEventCommand.ts +++ b/packages/core/src/services/events/commands/createEventCommand.ts @@ -20,6 +20,6 @@ const schema = z.object({ ]) }); -export const createEventCommand = async (command: z.infer): Promise => { +export const createEventCommand = (command: z.infer): void => { worker.addEventJob('create', command); }; \ No newline at end of file diff --git a/packages/core/src/services/payments/commands/createPaymentCommand.ts b/packages/core/src/services/payments/commands/createPaymentCommand.ts index 79c9a064d..0a4b01d31 100644 --- a/packages/core/src/services/payments/commands/createPaymentCommand.ts +++ b/packages/core/src/services/payments/commands/createPaymentCommand.ts @@ -108,7 +108,7 @@ export const createPaymentCommand = async (command: z.infer): Pro }); // Create event - await eventService.create({ + eventService.create({ type: EventType.PaymentCreated, userId: command.connect.userId, commonId: command.connect.commonId diff --git a/packages/core/src/services/proposals/funding/command/createFundingProposalCommand.ts b/packages/core/src/services/proposals/funding/command/createFundingProposalCommand.ts index dc832a04e..078a33ee5 100644 --- a/packages/core/src/services/proposals/funding/command/createFundingProposalCommand.ts +++ b/packages/core/src/services/proposals/funding/command/createFundingProposalCommand.ts @@ -127,7 +127,7 @@ export const createFundingProposalCommand = async (command: z.infer) }); // Create event - await eventService.create({ + eventService.create({ type: EventType.JoinRequestCreated, commonId: command.commonId, userId: command.userId diff --git a/packages/core/src/services/proposals/join/command/process/processApprovedOneTimeJoinRequest.ts b/packages/core/src/services/proposals/join/command/process/processApprovedOneTimeJoinRequest.ts index a1cbbf825..8363137cc 100644 --- a/packages/core/src/services/proposals/join/command/process/processApprovedOneTimeJoinRequest.ts +++ b/packages/core/src/services/proposals/join/command/process/processApprovedOneTimeJoinRequest.ts @@ -17,7 +17,7 @@ export const processApprovedOneTimeJoinRequestCommand = async (proposalId: strin }); // Create event - await eventService.create({ + eventService.create({ type: EventType.JoinRequestAccepted, userId: proposal.userId, commonId: proposal.commonId, diff --git a/packages/core/src/services/proposals/join/command/process/processApprovedSubscriptionJoinRequest.ts b/packages/core/src/services/proposals/join/command/process/processApprovedSubscriptionJoinRequest.ts index 2421525c4..a5346922f 100644 --- a/packages/core/src/services/proposals/join/command/process/processApprovedSubscriptionJoinRequest.ts +++ b/packages/core/src/services/proposals/join/command/process/processApprovedSubscriptionJoinRequest.ts @@ -25,7 +25,7 @@ export const processApprovedSubscriptionJoinRequest = async (proposalId: string) }); // Create event - await eventService.create({ + eventService.create({ type: EventType.JoinRequestAccepted, userId: proposal.userId, commonId: proposal.commonId, diff --git a/packages/core/src/services/proposals/join/command/process/processRejectedJoinRequest.ts b/packages/core/src/services/proposals/join/command/process/processRejectedJoinRequest.ts index cc2e8df4a..826e293d3 100644 --- a/packages/core/src/services/proposals/join/command/process/processRejectedJoinRequest.ts +++ b/packages/core/src/services/proposals/join/command/process/processRejectedJoinRequest.ts @@ -14,7 +14,7 @@ export const processRejectedJoinRequest = async (proposalId: string): Promise) => Promise): Promis } // Create event for the creation of the vote - await eventService.create({ + eventService.create({ type: EventType.VoteCreated, userId: command.userId, commonId From d51b272ecf8e9aa825e540ebb860eb2b05c0e2af Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Mon, 12 Apr 2021 10:37:31 +0300 Subject: [PATCH 08/20] Create discussion subscription (#354) Signed-off-by: Alexander Ivanov --- .../createDiscussionSubscriptionCommand.ts | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 packages/core/src/services/discussions/subscriptions/createDiscussionSubscriptionCommand.ts diff --git a/packages/core/src/services/discussions/subscriptions/createDiscussionSubscriptionCommand.ts b/packages/core/src/services/discussions/subscriptions/createDiscussionSubscriptionCommand.ts new file mode 100644 index 000000000..7fd96eaf7 --- /dev/null +++ b/packages/core/src/services/discussions/subscriptions/createDiscussionSubscriptionCommand.ts @@ -0,0 +1,46 @@ +import * as z from 'zod'; +import { DiscussionSubscription, DiscussionSubscriptionType, EventType } from '@prisma/client'; + +import { prisma } from '@toolkits'; +import { eventService } from '@services'; + +const schema = z.object({ + discussionId: z.string() + .uuid() + .nonempty(), + + userId: z.string() + .nonempty(), + + subscriptionType: z.enum(Object.keys(DiscussionSubscriptionType) as [(keyof typeof DiscussionSubscriptionType)]) + .nullable() + .optional() +}); + +export const createDiscussionSubscriptionCommand = async (payload: z.infer): Promise => { + // Validate the payload + schema.parse(payload); + + // Create the subscription + const subscription = await prisma.discussionSubscription.create({ + data: { + discussionId: payload.discussionId, + userId: payload.userId, + + type: payload.subscriptionType || DiscussionSubscriptionType.AllNotifications + } + }); + + + // Create event + eventService.create({ + type: EventType.DiscussionSubscriptionCreated, + userId: payload.userId, + payload: { + discussionId: payload.discussionId + } + }); + + // Return the created subscription + return subscription; +}; \ No newline at end of file From 96610b1f9bab0b1586edf5530ad9d5b9919e598c Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Mon, 12 Apr 2021 11:01:25 +0300 Subject: [PATCH 09/20] Create discussion message command WIP (#309) Signed-off-by: Alexander Ivanov --- .../core/src/services/discussions/index.ts | 16 ++++++- .../createDiscussionMessageCommand.ts | 48 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 packages/core/src/services/discussions/messages/createDiscussionMessageCommand.ts diff --git a/packages/core/src/services/discussions/index.ts b/packages/core/src/services/discussions/index.ts index 03fe3ebe3..1f9ca1021 100644 --- a/packages/core/src/services/discussions/index.ts +++ b/packages/core/src/services/discussions/index.ts @@ -1,4 +1,6 @@ import { createDiscussionCommand } from './core/commands/createDiscussionCommand'; +import { createDiscussionSubscriptionCommand } from './subscriptions/createDiscussionSubscriptionCommand'; +import { createDiscussionMessageCommand } from './messages/createDiscussionMessageCommand'; export const discussionService = { /** @@ -7,5 +9,17 @@ export const discussionService = { */ create: createDiscussionCommand, - messages: {} + subscription: { + /** + * Create notification subscription for discussion + */ + create: createDiscussionSubscriptionCommand + }, + + messages: { + /** + * Create new message in discussion + */ + create: createDiscussionMessageCommand + } }; \ No newline at end of file diff --git a/packages/core/src/services/discussions/messages/createDiscussionMessageCommand.ts b/packages/core/src/services/discussions/messages/createDiscussionMessageCommand.ts new file mode 100644 index 000000000..605824123 --- /dev/null +++ b/packages/core/src/services/discussions/messages/createDiscussionMessageCommand.ts @@ -0,0 +1,48 @@ +import * as z from 'zod'; +import { DiscussionMessage, DiscussionMessageType } from '@prisma/client'; +import { prisma } from '@toolkits'; +import { NotImplementedError } from '@errors'; + +const schema = z.object({ + userId: z.string() + .nonempty(), + + discussionId: z.string() + .uuid() + .nonempty(), + + message: z.string() + .nonempty(), + + messageType: z.enum(Object.keys(DiscussionMessageType) as [(keyof typeof DiscussionMessageType)]) + .optional() +}); + +export const createDiscussionMessageCommand = async (payload: z.infer): Promise => { + // Validate the payload + schema.parse(payload); + + // Check if the user can create messages in that discussion + const isMember = !!( + await prisma.commonMember.count({ + where: { + userId: payload.userId, + common: { + discussions: { + some: { + id: payload.discussionId + } + } + } + } + }) + ); + + + // Create the message + + // Create event about the message + + // Return the created message + throw new NotImplementedError(); +}; \ No newline at end of file From c0c0ea979d3bd25d1e979e246b0e963aa53d854e Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Mon, 12 Apr 2021 11:19:44 +0300 Subject: [PATCH 10/20] Create discussion message graphql WIP (#309) Signed-off-by: Alexander Ivanov --- .../graphql/src/generated/nexus-typegen.ts | 48 +++++++++-- packages/graphql/src/generated/schema.graphql | 80 ++++++++++++------- .../Enums/DiscussionMessageType.enum.ts | 7 ++ .../CreateDiscussionMessage.mutation.ts | 39 +++++++++ .../Types/DiscussionMessage.type.ts | 14 ++++ .../src/schema/Types/Discussion/index.ts | 16 +++- 6 files changed, 168 insertions(+), 36 deletions(-) create mode 100644 packages/graphql/src/schema/Types/Discussion/Enums/DiscussionMessageType.enum.ts create mode 100644 packages/graphql/src/schema/Types/Discussion/Mutations/CreateDiscussionMessage.mutation.ts create mode 100644 packages/graphql/src/schema/Types/Discussion/Types/DiscussionMessage.type.ts diff --git a/packages/graphql/src/generated/nexus-typegen.ts b/packages/graphql/src/generated/nexus-typegen.ts index 00935be3c..abad3f5f9 100644 --- a/packages/graphql/src/generated/nexus-typegen.ts +++ b/packages/graphql/src/generated/nexus-typegen.ts @@ -4,9 +4,10 @@ */ -import { IRequestContext } from "./../context" -import { QueryComplexity } from "nexus/dist/plugins/queryComplexityPlugin" -import { core } from "nexus" +import { IRequestContext } from './../context'; +import { QueryComplexity } from 'nexus/dist/plugins/queryComplexityPlugin'; +import { core } from 'nexus'; + declare global { interface NexusGenCustomInputMethods { /** @@ -79,6 +80,10 @@ export interface NexusGenInputs { proposalId?: string | null; // ID topic: string; // String! } + CreateDiscussionMessageInput: { // input type + discussionId: string; // ID! + message: string; // String! + } CreateFundingProposalInput: { // input type amount: number; // Int! commonId: string; // ID! @@ -142,8 +147,9 @@ export interface NexusGenInputs { } export interface NexusGenEnums { - CommonMemberRole: "Founder" - EventType: "CardCreated" | "CardCvvVerificationFailed" | "CardCvvVerificationPassed" | "CommonCreated" | "CommonMemberCreated" | "CommonMemberRoleAdded" | "CommonMemberRoleRemoved" | "DiscussionCreated" | "DiscussionMessageCreated" | "DiscussionSubscriptionCreated" | "FundingRequestAccepted" | "FundingRequestCreated" | "FundingRequestRejected" | "JoinRequestAccepted" | "JoinRequestCreated" | "JoinRequestRejected" | "PaymentCreated" | "PaymentFailed" | "PaymentSucceeded" | "ProposalExpired" | "ProposalMajorityReached" | "UserCreated" | "VoteCreated" + CommonMemberRole: 'Founder' + DiscussionMessageType: 'Message' + EventType: 'CardCreated' | 'CardCvvVerificationFailed' | 'CardCvvVerificationPassed' | 'CommonCreated' | 'CommonMemberCreated' | 'CommonMemberRoleAdded' | 'CommonMemberRoleRemoved' | 'DiscussionCreated' | 'DiscussionMessageCreated' | 'DiscussionSubscriptionCreated' | 'FundingRequestAccepted' | 'FundingRequestCreated' | 'FundingRequestRejected' | 'JoinRequestAccepted' | 'JoinRequestCreated' | 'JoinRequestRejected' | 'PaymentCreated' | 'PaymentFailed' | 'PaymentSucceeded' | 'ProposalExpired' | 'ProposalMajorityReached' | 'UserCreated' | 'VoteCreated' FundingType: "Monthly" | "OneTime" ProposalType: "FundingRequest" | "JoinRequest" SortOrder: "asc" | "desc" @@ -184,6 +190,12 @@ export interface NexusGenObjects { createdAt: NexusGenScalars['DateTime']; // DateTime! updatedAt: NexusGenScalars['DateTime']; // DateTime! } + DiscussionMessage: { // root type + createdAt: NexusGenScalars['DateTime']; // DateTime! + message: string; // String! + type: NexusGenEnums['DiscussionMessageType']; // DiscussionMessageType! + updatedAt: NexusGenScalars['DateTime']; // DateTime! + } Event: { // root type commonId?: string | null; // ID createdAt: NexusGenScalars['DateTime']; // DateTime! @@ -230,7 +242,7 @@ export interface NexusGenObjects { } export interface NexusGenInterfaces { - BaseEntity: NexusGenRootTypes['Discussion']; + BaseEntity: NexusGenRootTypes['Discussion'] | NexusGenRootTypes['DiscussionMessage']; } export interface NexusGenUnions { @@ -269,6 +281,13 @@ export interface NexusGenFieldTypes { id: string; // ID! updatedAt: NexusGenScalars['DateTime']; // DateTime! } + DiscussionMessage: { // field return type + createdAt: NexusGenScalars['DateTime']; // DateTime! + id: string; // ID! + message: string; // String! + type: NexusGenEnums['DiscussionMessageType']; // DiscussionMessageType! + updatedAt: NexusGenScalars['DateTime']; // DateTime! + } Event: { // field return type commonId: string | null; // ID createdAt: NexusGenScalars['DateTime']; // DateTime! @@ -296,6 +315,7 @@ export interface NexusGenFieldTypes { createCard: NexusGenRootTypes['Card']; // Card! createCommon: NexusGenRootTypes['Common']; // Common! createDiscussion: NexusGenRootTypes['Discussion']; // Discussion! + createDiscussionMessage: NexusGenRootTypes['DiscussionMessage']; // DiscussionMessage! createFundingProposal: NexusGenRootTypes['FundingProposal']; // FundingProposal! createJoinProposal: NexusGenRootTypes['JoinProposal']; // JoinProposal! createUser: NexusGenRootTypes['User']; // User! @@ -364,6 +384,13 @@ export interface NexusGenFieldTypeNames { id: 'ID' updatedAt: 'DateTime' } + DiscussionMessage: { // field return type name + createdAt: 'DateTime' + id: 'ID' + message: 'String' + type: 'DiscussionMessageType' + updatedAt: 'DateTime' + } Event: { // field return type name commonId: 'ID' createdAt: 'DateTime' @@ -391,6 +418,7 @@ export interface NexusGenFieldTypeNames { createCard: 'Card' createCommon: 'Common' createDiscussion: 'Discussion' + createDiscussionMessage: 'DiscussionMessage' createFundingProposal: 'FundingProposal' createJoinProposal: 'JoinProposal' createUser: 'User' @@ -458,6 +486,9 @@ export interface NexusGenArgTypes { createDiscussion: { // args input: NexusGenInputs['CreateDiscussionInput']; // CreateDiscussionInput! } + createDiscussionMessage: { // args + input: NexusGenInputs['CreateDiscussionMessageInput']; // CreateDiscussionMessageInput! + } createFundingProposal: { // args input: NexusGenInputs['CreateFundingProposalInput']; // CreateFundingProposalInput! } @@ -500,11 +531,12 @@ export interface NexusGenArgTypes { } export interface NexusGenAbstractTypeMembers { - BaseEntity: "Discussion" + BaseEntity: 'Discussion' | 'DiscussionMessage' } export interface NexusGenTypeInterfaces { - Discussion: "BaseEntity" + Discussion: 'BaseEntity' + DiscussionMessage: 'BaseEntity' } export type NexusGenObjectNames = keyof NexusGenObjects; diff --git a/packages/graphql/src/generated/schema.graphql b/packages/graphql/src/generated/schema.graphql index bbda4dee0..f1ae1bd75 100644 --- a/packages/graphql/src/generated/schema.graphql +++ b/packages/graphql/src/generated/schema.graphql @@ -99,24 +99,32 @@ input CreateDiscussionInput { """The ID of the common, for which we are creating the discussion""" commonId: ID! - """Short description about the topic""" - description: String! + """Short description about the topic""" + description: String! + + """The ID of the proposal, if this is proposal discussion""" + proposalId: ID + + """The topic of the discussion to be created""" + topic: String! +} - """The ID of the proposal, if this is proposal discussion""" - proposalId: ID +input CreateDiscussionMessageInput { + """The ID of the discussion, for which we are creating the message""" + discussionId: ID! - """The topic of the discussion to be created""" - topic: String! + """The message itself""" + message: String! } input CreateFundingProposalInput { - amount: Int! - commonId: ID! - description: String! - files: [ProposalFileInput!] - images: [ProposalImageInput!] - links: [ProposalLinkInput!] - title: String! + amount: Int! + commonId: ID! + description: String! + files: [ProposalFileInput!] + images: [ProposalImageInput!] + links: [ProposalLinkInput!] + title: String! } input CreateJoinProposalInput { @@ -147,25 +155,42 @@ A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the `dat scalar DateTime type Discussion implements BaseEntity { - """The date, at which the item was created""" - createdAt: DateTime! + """The date, at which the item was created""" + createdAt: DateTime! - """The main identifier of the item""" - id: ID! + """The main identifier of the item""" + id: ID! - """The date, at which the item was last modified""" - updatedAt: DateTime! + """The date, at which the item was last modified""" + updatedAt: DateTime! +} + +type DiscussionMessage implements BaseEntity { + """The date, at which the item was created""" + createdAt: DateTime! + + """The main identifier of the item""" + id: ID! + message: String! + type: DiscussionMessageType! + + """The date, at which the item was last modified""" + updatedAt: DateTime! +} + +enum DiscussionMessageType { + Message } type Event { - """The ID of the common, for whom the event was created""" - commonId: ID + """The ID of the common, for whom the event was created""" + commonId: ID - """The date, at which the item was created""" - createdAt: DateTime! + """The date, at which the item was created""" + createdAt: DateTime! - """The main identifier of the item""" - id: ID! + """The main identifier of the item""" + id: ID! payload: JSON """The type of the event in one of the predefined event types""" @@ -262,8 +287,9 @@ input LinkInput { type Mutation { createCard(input: CreateCardInput!): Card! createCommon(input: CreateCommonInput!): Common! - createDiscussion(input: CreateDiscussionInput!): Discussion! - createFundingProposal(input: CreateFundingProposalInput!): FundingProposal! + createDiscussion(input: CreateDiscussionInput!): Discussion! + createDiscussionMessage(input: CreateDiscussionMessageInput!): DiscussionMessage! + createFundingProposal(input: CreateFundingProposalInput!): FundingProposal! """Create new proposal of type JOIN.""" createJoinProposal(input: CreateJoinProposalInput!): JoinProposal! diff --git a/packages/graphql/src/schema/Types/Discussion/Enums/DiscussionMessageType.enum.ts b/packages/graphql/src/schema/Types/Discussion/Enums/DiscussionMessageType.enum.ts new file mode 100644 index 000000000..2e38e61b0 --- /dev/null +++ b/packages/graphql/src/schema/Types/Discussion/Enums/DiscussionMessageType.enum.ts @@ -0,0 +1,7 @@ +import { enumType } from 'nexus'; +import { DiscussionMessageType } from '@prisma/client'; + +export const DiscussionMessageTypeEnum = enumType({ + name: 'DiscussionMessageType', + members: DiscussionMessageType +}); \ No newline at end of file diff --git a/packages/graphql/src/schema/Types/Discussion/Mutations/CreateDiscussionMessage.mutation.ts b/packages/graphql/src/schema/Types/Discussion/Mutations/CreateDiscussionMessage.mutation.ts new file mode 100644 index 000000000..e87320529 --- /dev/null +++ b/packages/graphql/src/schema/Types/Discussion/Mutations/CreateDiscussionMessage.mutation.ts @@ -0,0 +1,39 @@ +import { inputObjectType, extendType, nonNull, arg } from 'nexus'; +import { discussionService } from '@common/core'; + +export const CreateDiscussionMessageInput = inputObjectType({ + name: 'CreateDiscussionMessageInput', + definition(t) { + t.nonNull.id('discussionId', { + description: 'The ID of the discussion, for which we are creating the message' + }); + + t.nonNull.string('message', { + description: 'The message itself' + }); + } +}); + +export const CreateDiscussionMessageMutation = extendType({ + type: 'Mutation', + definition(t) { + t.nonNull.field('createDiscussionMessage', { + type: 'DiscussionMessage', + args: { + input: nonNull( + arg({ + type: 'CreateDiscussionMessageInput' + }) + ) + }, + resolve: async (root, args, ctx) => { + const userId = await ctx.getUserId(); + + return discussionService.messages.create({ + ...args.input, + userId + }); + } + }); + } +}); \ No newline at end of file diff --git a/packages/graphql/src/schema/Types/Discussion/Types/DiscussionMessage.type.ts b/packages/graphql/src/schema/Types/Discussion/Types/DiscussionMessage.type.ts new file mode 100644 index 000000000..a65071f66 --- /dev/null +++ b/packages/graphql/src/schema/Types/Discussion/Types/DiscussionMessage.type.ts @@ -0,0 +1,14 @@ +import { objectType } from 'nexus'; + +export const DiscussionMessageType = objectType({ + name: 'DiscussionMessage', + definition(t) { + t.implements('BaseEntity'); + + t.nonNull.string('message'); + + t.nonNull.field('type', { + type: 'DiscussionMessageType' + }); + } +}); \ No newline at end of file diff --git a/packages/graphql/src/schema/Types/Discussion/index.ts b/packages/graphql/src/schema/Types/Discussion/index.ts index a0d14bc66..1a4496a65 100644 --- a/packages/graphql/src/schema/Types/Discussion/index.ts +++ b/packages/graphql/src/schema/Types/Discussion/index.ts @@ -1,10 +1,24 @@ import { DiscussionType } from './Types/Discussion.type'; +import { DiscussionMessageType } from './Types/DiscussionMessage.type'; + +import { DiscussionMessageTypeEnum } from './Enums/DiscussionMessageType.enum'; import { CreateDiscussionInput, CreateDiscussionMutation } from './Mutations/CreateDiscussion.mutation'; +import { + CreateDiscussionMessageInput, + CreateDiscussionMessageMutation +} from './Mutations/CreateDiscussionMessage.mutation'; + export const DiscussionTypes = [ DiscussionType, + DiscussionMessageType, + + DiscussionMessageTypeEnum, CreateDiscussionInput, - CreateDiscussionMutation + CreateDiscussionMutation, + + CreateDiscussionMessageInput, + CreateDiscussionMessageMutation ]; \ No newline at end of file From 3cc6013037925a7facd412eb93aa5e82a532166b Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Mon, 12 Apr 2021 11:44:41 +0300 Subject: [PATCH 11/20] Finish the create discussion message (#309) Signed-off-by: Alexander Ivanov --- .../createDiscussionMessageCommand.ts | 57 +++++++++---- .../graphql/src/generated/nexus-typegen.ts | 8 +- packages/graphql/src/generated/schema.graphql | 82 +++++++++---------- 3 files changed, 87 insertions(+), 60 deletions(-) diff --git a/packages/core/src/services/discussions/messages/createDiscussionMessageCommand.ts b/packages/core/src/services/discussions/messages/createDiscussionMessageCommand.ts index 605824123..749b3c253 100644 --- a/packages/core/src/services/discussions/messages/createDiscussionMessageCommand.ts +++ b/packages/core/src/services/discussions/messages/createDiscussionMessageCommand.ts @@ -1,7 +1,9 @@ import * as z from 'zod'; -import { DiscussionMessage, DiscussionMessageType } from '@prisma/client'; +import { DiscussionMessage, DiscussionMessageType, EventType } from '@prisma/client'; + import { prisma } from '@toolkits'; -import { NotImplementedError } from '@errors'; +import { CommonError } from '@errors'; +import { eventService } from '@services'; const schema = z.object({ userId: z.string() @@ -23,26 +25,51 @@ export const createDiscussionMessageCommand = async (payload: z.infer Date: Mon, 12 Apr 2021 19:54:58 +0300 Subject: [PATCH 12/20] Get discussion messages (#357) Signed-off-by: Alexander Ivanov --- .../graphql/src/generated/nexus-typegen.ts | 68 +++++++++++++------ packages/graphql/src/generated/schema.graphql | 18 ++++- .../Shared/Interfaces/BaseEntity.interface.ts | 7 +- .../src/schema/Shared/Scalars/Uuid.scalar.ts | 4 ++ .../DiscussionMessages.extension.ts | 39 +++++++++++ .../Inputs/DiscussionMessagesOrderBy.input.ts | 14 ++++ .../Discussion/Queries/GetDiscussion.query.ts | 21 ++++++ .../src/schema/Types/Discussion/index.ts | 13 +++- packages/graphql/src/schema/index.ts | 2 + 9 files changed, 158 insertions(+), 28 deletions(-) create mode 100644 packages/graphql/src/schema/Shared/Scalars/Uuid.scalar.ts create mode 100644 packages/graphql/src/schema/Types/Discussion/Extensions/DiscussionMessages.extension.ts create mode 100644 packages/graphql/src/schema/Types/Discussion/Inputs/DiscussionMessagesOrderBy.input.ts create mode 100644 packages/graphql/src/schema/Types/Discussion/Queries/GetDiscussion.query.ts diff --git a/packages/graphql/src/generated/nexus-typegen.ts b/packages/graphql/src/generated/nexus-typegen.ts index 2c7e60f20..3ae443655 100644 --- a/packages/graphql/src/generated/nexus-typegen.ts +++ b/packages/graphql/src/generated/nexus-typegen.ts @@ -4,10 +4,9 @@ */ -import { IRequestContext } from './../context'; -import { QueryComplexity } from 'nexus/dist/plugins/queryComplexityPlugin'; -import { core } from 'nexus'; - +import { IRequestContext } from "./../context" +import { QueryComplexity } from "nexus/dist/plugins/queryComplexityPlugin" +import { core } from "nexus" declare global { interface NexusGenCustomInputMethods { /** @@ -22,6 +21,10 @@ declare global { * The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). */ json(fieldName: FieldName, opts?: core.CommonInputFieldConfig): void // "JSON"; + /** + * A field whose value is a generic Universally Unique Identifier: https://en.wikipedia.org/wiki/Universally_unique_identifier. + */ + uuid(fieldName: FieldName, opts?: core.CommonInputFieldConfig): void // "UUID"; } } declare global { @@ -38,6 +41,10 @@ declare global { * The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). */ json(fieldName: FieldName, ...opts: core.ScalarOutSpread): void // "JSON"; + /** + * A field whose value is a generic Universally Unique Identifier: https://en.wikipedia.org/wiki/Universally_unique_identifier. + */ + uuid(fieldName: FieldName, ...opts: core.ScalarOutSpread): void // "UUID"; } } @@ -110,6 +117,10 @@ export interface NexusGenInputs { outcome: NexusGenEnums['VoteOutcome']; // VoteOutcome! proposalId: string; // ID! } + DiscussionMessagesOrderByInput: { // input type + createdAt?: NexusGenEnums['SortOrder'] | null; // SortOrder + updatedAt?: NexusGenEnums['SortOrder'] | null; // SortOrder + } EventOrderByInput: { // input type createdAt?: NexusGenEnums['SortOrder'] | null; // SortOrder type?: NexusGenEnums['SortOrder'] | null; // SortOrder @@ -147,13 +158,13 @@ export interface NexusGenInputs { } export interface NexusGenEnums { - CommonMemberRole: 'Founder' - DiscussionMessageType: 'Message' - EventType: 'CardCreated' | 'CardCvvVerificationFailed' | 'CardCvvVerificationPassed' | 'CommonCreated' | 'CommonMemberCreated' | 'CommonMemberRoleAdded' | 'CommonMemberRoleRemoved' | 'DiscussionCreated' | 'DiscussionMessageCreated' | 'DiscussionSubscriptionCreated' | 'FundingRequestAccepted' | 'FundingRequestCreated' | 'FundingRequestRejected' | 'JoinRequestAccepted' | 'JoinRequestCreated' | 'JoinRequestRejected' | 'PaymentCreated' | 'PaymentFailed' | 'PaymentSucceeded' | 'ProposalExpired' | 'ProposalMajorityReached' | 'UserCreated' | 'VoteCreated' - FundingType: 'Monthly' | 'OneTime' - ProposalType: 'FundingRequest' | 'JoinRequest' - SortOrder: 'asc' | 'desc' - VoteOutcome: 'Approve' | 'Condemn' + CommonMemberRole: "Founder" + DiscussionMessageType: "Message" + EventType: "CardCreated" | "CardCvvVerificationFailed" | "CardCvvVerificationPassed" | "CommonCreated" | "CommonMemberCreated" | "CommonMemberRoleAdded" | "CommonMemberRoleRemoved" | "DiscussionCreated" | "DiscussionMessageCreated" | "DiscussionSubscriptionCreated" | "FundingRequestAccepted" | "FundingRequestCreated" | "FundingRequestRejected" | "JoinRequestAccepted" | "JoinRequestCreated" | "JoinRequestRejected" | "PaymentCreated" | "PaymentFailed" | "PaymentSucceeded" | "ProposalExpired" | "ProposalMajorityReached" | "UserCreated" | "VoteCreated" + FundingType: "Monthly" | "OneTime" + ProposalType: "FundingRequest" | "JoinRequest" + SortOrder: "asc" | "desc" + VoteOutcome: "Approve" | "Condemn" } export interface NexusGenScalars { @@ -165,6 +176,7 @@ export interface NexusGenScalars { DateTime: any JSON: any URL: any + UUID: any } export interface NexusGenObjects { @@ -188,10 +200,12 @@ export interface NexusGenObjects { } Discussion: { // root type createdAt: NexusGenScalars['DateTime']; // DateTime! + id: NexusGenScalars['UUID']; // UUID! updatedAt: NexusGenScalars['DateTime']; // DateTime! } DiscussionMessage: { // root type createdAt: NexusGenScalars['DateTime']; // DateTime! + id: NexusGenScalars['UUID']; // UUID! message: string; // String! type: NexusGenEnums['DiscussionMessageType']; // DiscussionMessageType! updatedAt: NexusGenScalars['DateTime']; // DateTime! @@ -278,12 +292,13 @@ export interface NexusGenFieldTypes { } Discussion: { // field return type createdAt: NexusGenScalars['DateTime']; // DateTime! - id: string; // ID! + id: NexusGenScalars['UUID']; // UUID! + messages: NexusGenRootTypes['DiscussionMessage'][]; // [DiscussionMessage!]! updatedAt: NexusGenScalars['DateTime']; // DateTime! } DiscussionMessage: { // field return type createdAt: NexusGenScalars['DateTime']; // DateTime! - id: string; // ID! + id: NexusGenScalars['UUID']; // UUID! message: string; // String! type: NexusGenEnums['DiscussionMessageType']; // DiscussionMessageType! updatedAt: NexusGenScalars['DateTime']; // DateTime! @@ -330,6 +345,7 @@ export interface NexusGenFieldTypes { } Query: { // field return type common: NexusGenRootTypes['Common'] | null; // Common + discussion: NexusGenRootTypes['Discussion'] | null; // Discussion generateUserAuthToken: string; // String! user: NexusGenRootTypes['User'] | null; // User } @@ -350,7 +366,7 @@ export interface NexusGenFieldTypes { } BaseEntity: { // field return type createdAt: NexusGenScalars['DateTime']; // DateTime! - id: string; // ID! + id: NexusGenScalars['UUID']; // UUID! updatedAt: NexusGenScalars['DateTime']; // DateTime! } } @@ -381,12 +397,13 @@ export interface NexusGenFieldTypeNames { } Discussion: { // field return type name createdAt: 'DateTime' - id: 'ID' + id: 'UUID' + messages: 'DiscussionMessage' updatedAt: 'DateTime' } DiscussionMessage: { // field return type name createdAt: 'DateTime' - id: 'ID' + id: 'UUID' message: 'String' type: 'DiscussionMessageType' updatedAt: 'DateTime' @@ -433,6 +450,7 @@ export interface NexusGenFieldTypeNames { } Query: { // field return type name common: 'Common' + discussion: 'Discussion' generateUserAuthToken: 'String' user: 'User' } @@ -453,7 +471,7 @@ export interface NexusGenFieldTypeNames { } BaseEntity: { // field return type name createdAt: 'DateTime' - id: 'ID' + id: 'UUID' updatedAt: 'DateTime' } } @@ -476,6 +494,13 @@ export interface NexusGenArgTypes { where?: NexusGenInputs['ProposalWhereInput'] | null; // ProposalWhereInput } } + Discussion: { + messages: { // args + orderBy: NexusGenInputs['DiscussionMessagesOrderByInput'] | null; // DiscussionMessagesOrderByInput + skip?: number | null; // Int + take: number | null; // Int + } + } Mutation: { createCard: { // args input: NexusGenInputs['CreateCardInput']; // CreateCardInput! @@ -509,6 +534,9 @@ export interface NexusGenArgTypes { common: { // args where: NexusGenInputs['CommonWhereUniqueInput']; // CommonWhereUniqueInput! } + discussion: { // args + id: string; // ID! + } generateUserAuthToken: { // args authId: string; // String! } @@ -531,12 +559,12 @@ export interface NexusGenArgTypes { } export interface NexusGenAbstractTypeMembers { - BaseEntity: 'Discussion' | 'DiscussionMessage' + BaseEntity: "Discussion" | "DiscussionMessage" } export interface NexusGenTypeInterfaces { - Discussion: 'BaseEntity' - DiscussionMessage: 'BaseEntity' + Discussion: "BaseEntity" + DiscussionMessage: "BaseEntity" } export type NexusGenObjectNames = keyof NexusGenObjects; diff --git a/packages/graphql/src/generated/schema.graphql b/packages/graphql/src/generated/schema.graphql index aeabca5e7..5440f4f8b 100644 --- a/packages/graphql/src/generated/schema.graphql +++ b/packages/graphql/src/generated/schema.graphql @@ -7,7 +7,7 @@ interface BaseEntity { createdAt: DateTime! """The main identifier of the item""" - id: ID! + id: UUID! """The date, at which the item was last modified""" updatedAt: DateTime! @@ -159,7 +159,8 @@ type Discussion implements BaseEntity { createdAt: DateTime! """The main identifier of the item""" - id: ID! + id: UUID! + messages(orderBy: DiscussionMessagesOrderByInput = {createdAt: asc}, skip: Int = 0, take: Int = 10): [DiscussionMessage!]! """The date, at which the item was last modified""" updatedAt: DateTime! @@ -170,7 +171,7 @@ type DiscussionMessage implements BaseEntity { createdAt: DateTime! """The main identifier of the item""" - id: ID! + id: UUID! message: String! type: DiscussionMessageType! @@ -182,6 +183,11 @@ enum DiscussionMessageType { Message } +input DiscussionMessagesOrderByInput { + createdAt: SortOrder + updatedAt: SortOrder +} + type Event { """The ID of the common, for whom the event was created""" commonId: ID @@ -336,6 +342,7 @@ input ProposalWhereInput { type Query { common(where: CommonWhereUniqueInput!): Common + discussion(id: ID!): Discussion generateUserAuthToken(authId: String!): String! """ @@ -367,6 +374,11 @@ A field whose value conforms to the standard URL format as specified in RFC3986: """ scalar URL +""" +A field whose value is a generic Universally Unique Identifier: https://en.wikipedia.org/wiki/Universally_unique_identifier. +""" +scalar UUID + type User { """The date, at which the item was created""" createdAt: DateTime! diff --git a/packages/graphql/src/schema/Shared/Interfaces/BaseEntity.interface.ts b/packages/graphql/src/schema/Shared/Interfaces/BaseEntity.interface.ts index 75e94bb0e..a0cbe53d9 100644 --- a/packages/graphql/src/schema/Shared/Interfaces/BaseEntity.interface.ts +++ b/packages/graphql/src/schema/Shared/Interfaces/BaseEntity.interface.ts @@ -1,12 +1,11 @@ import { interfaceType } from 'nexus'; export const BaseEntityInterface = interfaceType({ - resolveType: () => undefined, + resolveType: () => null, name: 'BaseEntity', definition(t) { - t.nonNull.id('id', { - description: 'The main identifier of the item', - resolve: r => r.id + t.nonNull.uuid('id', { + description: 'The main identifier of the item' }); t.nonNull.date('createdAt', { diff --git a/packages/graphql/src/schema/Shared/Scalars/Uuid.scalar.ts b/packages/graphql/src/schema/Shared/Scalars/Uuid.scalar.ts new file mode 100644 index 000000000..ca92cf93d --- /dev/null +++ b/packages/graphql/src/schema/Shared/Scalars/Uuid.scalar.ts @@ -0,0 +1,4 @@ +import { asNexusMethod } from 'nexus'; +import { GraphQLUUID } from 'graphql-scalars'; + +export const UuidScalar = asNexusMethod(GraphQLUUID, 'uuid'); \ No newline at end of file diff --git a/packages/graphql/src/schema/Types/Discussion/Extensions/DiscussionMessages.extension.ts b/packages/graphql/src/schema/Types/Discussion/Extensions/DiscussionMessages.extension.ts new file mode 100644 index 000000000..86b28cd26 --- /dev/null +++ b/packages/graphql/src/schema/Types/Discussion/Extensions/DiscussionMessages.extension.ts @@ -0,0 +1,39 @@ +import { extendType, intArg, arg } from 'nexus'; +import { prisma } from '@common/core'; + +export const DiscussionMessagesExtension = extendType({ + type: 'Discussion', + definition(t) { + t.nonNull.list.nonNull.field('messages', { + type: 'DiscussionMessage', + complexity: 10, + args: { + take: intArg({ + default: 10 + }), + + skip: intArg({ + default: 0 + }), + + orderBy: arg({ + type: 'DiscussionMessagesOrderByInput', + default: { + createdAt: 'asc' + } + }) + }, + resolve: async (root, args) => { + return prisma.discussionMessage + .findMany({ + where: { + discussionId: root.id + }, + take: args.take || 10, + skip: args.skip || undefined, + orderBy: (args.orderBy as any) + }); + } + }); + } +}); \ No newline at end of file diff --git a/packages/graphql/src/schema/Types/Discussion/Inputs/DiscussionMessagesOrderBy.input.ts b/packages/graphql/src/schema/Types/Discussion/Inputs/DiscussionMessagesOrderBy.input.ts new file mode 100644 index 000000000..95495e8f3 --- /dev/null +++ b/packages/graphql/src/schema/Types/Discussion/Inputs/DiscussionMessagesOrderBy.input.ts @@ -0,0 +1,14 @@ +import { inputObjectType } from 'nexus'; + +export const DiscussionMessagesOrderByInput = inputObjectType({ + name: 'DiscussionMessagesOrderByInput', + definition(t) { + t.field('createdAt', { + type: 'SortOrder' + }); + + t.field('updatedAt', { + type: 'SortOrder' + }); + } +}); \ No newline at end of file diff --git a/packages/graphql/src/schema/Types/Discussion/Queries/GetDiscussion.query.ts b/packages/graphql/src/schema/Types/Discussion/Queries/GetDiscussion.query.ts new file mode 100644 index 000000000..58de72f14 --- /dev/null +++ b/packages/graphql/src/schema/Types/Discussion/Queries/GetDiscussion.query.ts @@ -0,0 +1,21 @@ +import { extendType, idArg, nonNull } from 'nexus'; +import { prisma } from '@common/core'; + +export const GetDiscussionQuery = extendType({ + type: 'Query', + definition(t) { + t.field('discussion', { + type: 'Discussion', + args: { + id: nonNull(idArg()) + }, + resolve: (root, args) => { + return prisma.discussion.findUnique({ + where: { + id: args.id + } + }); + } + }); + } +}); \ No newline at end of file diff --git a/packages/graphql/src/schema/Types/Discussion/index.ts b/packages/graphql/src/schema/Types/Discussion/index.ts index 1a4496a65..cd3bd2aaf 100644 --- a/packages/graphql/src/schema/Types/Discussion/index.ts +++ b/packages/graphql/src/schema/Types/Discussion/index.ts @@ -3,8 +3,13 @@ import { DiscussionMessageType } from './Types/DiscussionMessage.type'; import { DiscussionMessageTypeEnum } from './Enums/DiscussionMessageType.enum'; -import { CreateDiscussionInput, CreateDiscussionMutation } from './Mutations/CreateDiscussion.mutation'; +import { GetDiscussionQuery } from './Queries/GetDiscussion.query'; + +import { DiscussionMessagesOrderByInput } from './Inputs/DiscussionMessagesOrderBy.input'; +import { DiscussionMessagesExtension } from './Extensions/DiscussionMessages.extension'; + +import { CreateDiscussionInput, CreateDiscussionMutation } from './Mutations/CreateDiscussion.mutation'; import { CreateDiscussionMessageInput, CreateDiscussionMessageMutation @@ -16,6 +21,12 @@ export const DiscussionTypes = [ DiscussionMessageTypeEnum, + GetDiscussionQuery, + + DiscussionMessagesOrderByInput, + + DiscussionMessagesExtension, + CreateDiscussionInput, CreateDiscussionMutation, diff --git a/packages/graphql/src/schema/index.ts b/packages/graphql/src/schema/index.ts index b53626040..eeaf5828a 100644 --- a/packages/graphql/src/schema/index.ts +++ b/packages/graphql/src/schema/index.ts @@ -13,6 +13,7 @@ import { CommonMemberTypes } from './Types/CommonMember'; import { UrlScalar } from './Shared/Scalars/Url.scalar'; import { DateScalar } from './Shared/Scalars/Date.scalar'; import { JsonScalar } from './Shared/Scalars/Json.scalar'; +import { UuidScalar } from './Shared/Scalars/Uuid.scalar'; import { LinkType, LinkInputType } from './Shared/Types/Link.type'; @@ -36,6 +37,7 @@ const types = [ UrlScalar, DateScalar, JsonScalar, + UuidScalar, // Shared Enums SortOrder, From daba00f4a8a20f3d943a590218f6bd074dc2b9f3 Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Mon, 12 Apr 2021 20:10:21 +0300 Subject: [PATCH 13/20] Common discussion extension (#359) Signed-off-by: Alexander Ivanov --- .../graphql/src/generated/nexus-typegen.ts | 13 ++++++--- packages/graphql/src/generated/schema.graphql | 5 ++-- .../Extensions/CommonDiscussions.extension.ts | 27 +++++++++++++++++++ .../graphql/src/schema/Types/Commons/index.ts | 2 ++ 4 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 packages/graphql/src/schema/Types/Commons/Extensions/CommonDiscussions.extension.ts diff --git a/packages/graphql/src/generated/nexus-typegen.ts b/packages/graphql/src/generated/nexus-typegen.ts index 3ae443655..d9785bbf7 100644 --- a/packages/graphql/src/generated/nexus-typegen.ts +++ b/packages/graphql/src/generated/nexus-typegen.ts @@ -4,9 +4,10 @@ */ -import { IRequestContext } from "./../context" -import { QueryComplexity } from "nexus/dist/plugins/queryComplexityPlugin" -import { core } from "nexus" +import { IRequestContext } from './../context'; +import { QueryComplexity } from 'nexus/dist/plugins/queryComplexityPlugin'; +import { core } from 'nexus'; + declare global { interface NexusGenCustomInputMethods { /** @@ -274,6 +275,7 @@ export interface NexusGenFieldTypes { } Common: { // field return type createdAt: NexusGenScalars['DateTime']; // DateTime! + discussions: NexusGenRootTypes['Discussion'][]; // [Discussion!]! events: NexusGenRootTypes['Event'][]; // [Event!]! id: string; // ID! members: Array; // [CommonMember]! @@ -379,6 +381,7 @@ export interface NexusGenFieldTypeNames { } Common: { // field return type name createdAt: 'DateTime' + discussions: 'Discussion' events: 'Event' id: 'ID' members: 'CommonMember' @@ -478,6 +481,10 @@ export interface NexusGenFieldTypeNames { export interface NexusGenArgTypes { Common: { + discussions: { // args + skip?: number | null; // Int + take?: number | null; // Int + } events: { // args orderBy?: NexusGenInputs['EventOrderByInput'] | null; // EventOrderByInput skip?: number | null; // Int diff --git a/packages/graphql/src/generated/schema.graphql b/packages/graphql/src/generated/schema.graphql index 5440f4f8b..8fec0368a 100644 --- a/packages/graphql/src/generated/schema.graphql +++ b/packages/graphql/src/generated/schema.graphql @@ -37,9 +37,10 @@ type Card { type Common { """The date, at which the item was created""" createdAt: DateTime! + discussions(skip: Int, take: Int): [Discussion!]! - """List of events, that occurred in a common""" - events(orderBy: EventOrderByInput, skip: Int = 0, take: Int = 10): [Event!]! + """List of events, that occurred in a common""" + events(orderBy: EventOrderByInput, skip: Int = 0, take: Int = 10): [Event!]! """The main identifier of the item""" id: ID! diff --git a/packages/graphql/src/schema/Types/Commons/Extensions/CommonDiscussions.extension.ts b/packages/graphql/src/schema/Types/Commons/Extensions/CommonDiscussions.extension.ts new file mode 100644 index 000000000..6838bc937 --- /dev/null +++ b/packages/graphql/src/schema/Types/Commons/Extensions/CommonDiscussions.extension.ts @@ -0,0 +1,27 @@ +import { extendType, intArg } from 'nexus'; +import { prisma } from '@common/core'; + +export const CommonDiscussionsExtension = extendType({ + type: 'Common', + definition(t) { + t.nonNull.list.nonNull.field('discussions', { + type: 'Discussion', + args: { + take: intArg(), + skip: intArg() + }, + resolve: (root, args) => { + return prisma.common + .findUnique({ + where: { + id: root.id + } + }) + .discussions({ + take: args.take || 10, + skip: args.skip || 0 + }); + } + }); + } +}); \ No newline at end of file diff --git a/packages/graphql/src/schema/Types/Commons/index.ts b/packages/graphql/src/schema/Types/Commons/index.ts index 994fc0a3b..334ea9d83 100644 --- a/packages/graphql/src/schema/Types/Commons/index.ts +++ b/packages/graphql/src/schema/Types/Commons/index.ts @@ -6,6 +6,7 @@ import { GetCommonsQuery } from './Queries/GetCommons.query'; import { CommonEventsExtension } from './Extensions/CommonEvents.extension'; import { CommonProposalsExtension } from './Extensions/CommonProposals.extension'; +import { CommonDiscussionsExtension } from './Extensions/CommonDiscussions.extension'; import { CommonCommonMemberExtension } from './Extensions/CommonCommonMember.extension'; import { CommonType } from './Types/Common.type'; @@ -26,5 +27,6 @@ export const CommonTypes = [ CommonEventsExtension, CommonProposalsExtension, + CommonDiscussionsExtension, CommonCommonMemberExtension ]; \ No newline at end of file From 23956b1d7fe6aa5340723e73f52c7c0e14b19169 Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Mon, 12 Apr 2021 20:35:26 +0300 Subject: [PATCH 14/20] Add other discussion fields Signed-off-by: Alexander Ivanov --- .../graphql/src/generated/nexus-typegen.ts | 20 ++++++++++++++---- packages/graphql/src/generated/schema.graphql | 21 ++++++++++++++++--- .../Discussion/Enums/DiscussionType.enum.ts | 7 +++++++ .../Types/Discussion/Types/Discussion.type.ts | 16 ++++++++++++++ .../Types/DiscussionMessage.type.ts | 1 + .../src/schema/Types/Discussion/index.ts | 2 ++ 6 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 packages/graphql/src/schema/Types/Discussion/Enums/DiscussionType.enum.ts diff --git a/packages/graphql/src/generated/nexus-typegen.ts b/packages/graphql/src/generated/nexus-typegen.ts index d9785bbf7..1b79d2da9 100644 --- a/packages/graphql/src/generated/nexus-typegen.ts +++ b/packages/graphql/src/generated/nexus-typegen.ts @@ -4,10 +4,9 @@ */ -import { IRequestContext } from './../context'; -import { QueryComplexity } from 'nexus/dist/plugins/queryComplexityPlugin'; -import { core } from 'nexus'; - +import { IRequestContext } from "./../context" +import { QueryComplexity } from "nexus/dist/plugins/queryComplexityPlugin" +import { core } from "nexus" declare global { interface NexusGenCustomInputMethods { /** @@ -161,6 +160,7 @@ export interface NexusGenInputs { export interface NexusGenEnums { CommonMemberRole: "Founder" DiscussionMessageType: "Message" + DiscussionType: "CommonDiscussion" | "ProposalDiscussion" EventType: "CardCreated" | "CardCvvVerificationFailed" | "CardCvvVerificationPassed" | "CommonCreated" | "CommonMemberCreated" | "CommonMemberRoleAdded" | "CommonMemberRoleRemoved" | "DiscussionCreated" | "DiscussionMessageCreated" | "DiscussionSubscriptionCreated" | "FundingRequestAccepted" | "FundingRequestCreated" | "FundingRequestRejected" | "JoinRequestAccepted" | "JoinRequestCreated" | "JoinRequestRejected" | "PaymentCreated" | "PaymentFailed" | "PaymentSucceeded" | "ProposalExpired" | "ProposalMajorityReached" | "UserCreated" | "VoteCreated" FundingType: "Monthly" | "OneTime" ProposalType: "FundingRequest" | "JoinRequest" @@ -201,7 +201,11 @@ export interface NexusGenObjects { } Discussion: { // root type createdAt: NexusGenScalars['DateTime']; // DateTime! + description: string; // String! id: NexusGenScalars['UUID']; // UUID! + lastMessage: NexusGenScalars['DateTime']; // DateTime! + topic: string; // String! + type: NexusGenEnums['DiscussionType']; // DiscussionType! updatedAt: NexusGenScalars['DateTime']; // DateTime! } DiscussionMessage: { // root type @@ -294,8 +298,12 @@ export interface NexusGenFieldTypes { } Discussion: { // field return type createdAt: NexusGenScalars['DateTime']; // DateTime! + description: string; // String! id: NexusGenScalars['UUID']; // UUID! + lastMessage: NexusGenScalars['DateTime']; // DateTime! messages: NexusGenRootTypes['DiscussionMessage'][]; // [DiscussionMessage!]! + topic: string; // String! + type: NexusGenEnums['DiscussionType']; // DiscussionType! updatedAt: NexusGenScalars['DateTime']; // DateTime! } DiscussionMessage: { // field return type @@ -400,8 +408,12 @@ export interface NexusGenFieldTypeNames { } Discussion: { // field return type name createdAt: 'DateTime' + description: 'String' id: 'UUID' + lastMessage: 'DateTime' messages: 'DiscussionMessage' + topic: 'String' + type: 'DiscussionType' updatedAt: 'DateTime' } DiscussionMessage: { // field return type name diff --git a/packages/graphql/src/generated/schema.graphql b/packages/graphql/src/generated/schema.graphql index 8fec0368a..2302b64b0 100644 --- a/packages/graphql/src/generated/schema.graphql +++ b/packages/graphql/src/generated/schema.graphql @@ -37,10 +37,10 @@ type Card { type Common { """The date, at which the item was created""" createdAt: DateTime! - discussions(skip: Int, take: Int): [Discussion!]! + discussions(skip: Int, take: Int): [Discussion!]! - """List of events, that occurred in a common""" - events(orderBy: EventOrderByInput, skip: Int = 0, take: Int = 10): [Event!]! + """List of events, that occurred in a common""" + events(orderBy: EventOrderByInput, skip: Int = 0, take: Int = 10): [Event!]! """The main identifier of the item""" id: ID! @@ -159,10 +159,20 @@ type Discussion implements BaseEntity { """The date, at which the item was created""" createdAt: DateTime! + """Short description of the topic""" + description: String! + """The main identifier of the item""" id: UUID! + + """The date at which the last message on the discussion was added""" + lastMessage: DateTime! messages(orderBy: DiscussionMessagesOrderByInput = {createdAt: asc}, skip: Int = 0, take: Int = 10): [DiscussionMessage!]! + """What this discussion is about""" + topic: String! + type: DiscussionType! + """The date, at which the item was last modified""" updatedAt: DateTime! } @@ -189,6 +199,11 @@ input DiscussionMessagesOrderByInput { updatedAt: SortOrder } +enum DiscussionType { + CommonDiscussion + ProposalDiscussion +} + type Event { """The ID of the common, for whom the event was created""" commonId: ID diff --git a/packages/graphql/src/schema/Types/Discussion/Enums/DiscussionType.enum.ts b/packages/graphql/src/schema/Types/Discussion/Enums/DiscussionType.enum.ts new file mode 100644 index 000000000..e73d49bc2 --- /dev/null +++ b/packages/graphql/src/schema/Types/Discussion/Enums/DiscussionType.enum.ts @@ -0,0 +1,7 @@ +import { DiscussionType } from '@prisma/client'; +import { enumType } from 'nexus'; + +export const DiscussionTypeEnum = enumType({ + name: 'DiscussionType', + members: DiscussionType +}); \ No newline at end of file diff --git a/packages/graphql/src/schema/Types/Discussion/Types/Discussion.type.ts b/packages/graphql/src/schema/Types/Discussion/Types/Discussion.type.ts index 4fa5ada47..82d68a365 100644 --- a/packages/graphql/src/schema/Types/Discussion/Types/Discussion.type.ts +++ b/packages/graphql/src/schema/Types/Discussion/Types/Discussion.type.ts @@ -4,5 +4,21 @@ export const DiscussionType = objectType({ name: 'Discussion', definition(t) { t.implements('BaseEntity'); + + t.nonNull.string('topic', { + description: 'What this discussion is about' + }); + + t.nonNull.string('description', { + description: 'Short description of the topic' + }); + + t.nonNull.date('lastMessage', { + description: 'The date at which the last message on the discussion was added' + }); + + t.nonNull.field('type', { + type: 'DiscussionType' + }); } }); \ No newline at end of file diff --git a/packages/graphql/src/schema/Types/Discussion/Types/DiscussionMessage.type.ts b/packages/graphql/src/schema/Types/Discussion/Types/DiscussionMessage.type.ts index a65071f66..50d22f638 100644 --- a/packages/graphql/src/schema/Types/Discussion/Types/DiscussionMessage.type.ts +++ b/packages/graphql/src/schema/Types/Discussion/Types/DiscussionMessage.type.ts @@ -10,5 +10,6 @@ export const DiscussionMessageType = objectType({ t.nonNull.field('type', { type: 'DiscussionMessageType' }); + } }); \ No newline at end of file diff --git a/packages/graphql/src/schema/Types/Discussion/index.ts b/packages/graphql/src/schema/Types/Discussion/index.ts index cd3bd2aaf..309b01bcc 100644 --- a/packages/graphql/src/schema/Types/Discussion/index.ts +++ b/packages/graphql/src/schema/Types/Discussion/index.ts @@ -1,6 +1,7 @@ import { DiscussionType } from './Types/Discussion.type'; import { DiscussionMessageType } from './Types/DiscussionMessage.type'; +import { DiscussionTypeEnum } from './Enums/DiscussionType.enum'; import { DiscussionMessageTypeEnum } from './Enums/DiscussionMessageType.enum'; import { GetDiscussionQuery } from './Queries/GetDiscussion.query'; @@ -19,6 +20,7 @@ export const DiscussionTypes = [ DiscussionType, DiscussionMessageType, + DiscussionTypeEnum, DiscussionMessageTypeEnum, GetDiscussionQuery, From 48c3fbb226da647cd4e99fe2f6e6e84451763676 Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Mon, 12 Apr 2021 21:21:05 +0300 Subject: [PATCH 15/20] Proposal discussions (#358) Signed-off-by: Alexander Ivanov --- .../core/commands/createDiscussionCommand.ts | 17 +++++++- .../graphql/src/generated/nexus-typegen.ts | 29 ++++++++++--- packages/graphql/src/generated/schema.graphql | 42 +++++++++++-------- .../Types/Discussion/Types/Discussion.type.ts | 2 +- .../ProposalDiscussions.extension.ts | 27 ++++++++++++ .../Inputs/ProposalWhereUnique.input.ts | 8 ++++ .../Proposals/Queries/GetProposal.query.ts | 24 +++++++++++ .../src/schema/Types/Proposals/index.ts | 14 ++++++- 8 files changed, 136 insertions(+), 27 deletions(-) create mode 100644 packages/graphql/src/schema/Types/Proposals/Extensions/ProposalDiscussions.extension.ts create mode 100644 packages/graphql/src/schema/Types/Proposals/Inputs/ProposalWhereUnique.input.ts create mode 100644 packages/graphql/src/schema/Types/Proposals/Queries/GetProposal.query.ts diff --git a/packages/core/src/services/discussions/core/commands/createDiscussionCommand.ts b/packages/core/src/services/discussions/core/commands/createDiscussionCommand.ts index 374ffea85..0a377d896 100644 --- a/packages/core/src/services/discussions/core/commands/createDiscussionCommand.ts +++ b/packages/core/src/services/discussions/core/commands/createDiscussionCommand.ts @@ -47,13 +47,28 @@ export const createDiscussionCommand = async (payload: z.infer): throw new CommonError('Cannot create discussion in a common that you are not member of!'); } - // @todo If there is proposal id check if it is from the common + // If there is proposal check if that proposal is from the same common + if (payload.proposalId) { + if ( + !(await prisma.proposal.count({ + where: { + id: payload.proposalId, + commonId: payload.commonId + } + })) + ) { + throw new CommonError('Proposal Common Mismatch', { + description: 'The proposal is not from the common' + }); + } + } // Create the discussion const discussion = await prisma.discussion.create({ data: { userId: payload.userId, commonId: payload.commonId, + proposalId: payload.proposalId, topic: payload.topic.trim(), description: payload.description.trim(), diff --git a/packages/graphql/src/generated/nexus-typegen.ts b/packages/graphql/src/generated/nexus-typegen.ts index 1b79d2da9..726df9260 100644 --- a/packages/graphql/src/generated/nexus-typegen.ts +++ b/packages/graphql/src/generated/nexus-typegen.ts @@ -4,9 +4,10 @@ */ -import { IRequestContext } from "./../context" -import { QueryComplexity } from "nexus/dist/plugins/queryComplexityPlugin" -import { core } from "nexus" +import { IRequestContext } from './../context'; +import { QueryComplexity } from 'nexus/dist/plugins/queryComplexityPlugin'; +import { core } from 'nexus'; + declare global { interface NexusGenCustomInputMethods { /** @@ -143,6 +144,9 @@ export interface NexusGenInputs { ProposalWhereInput: { // input type type?: NexusGenEnums['ProposalType'] | null; // ProposalType } + ProposalWhereUniqueInput: { // input type + id: NexusGenScalars['UUID']; // UUID! + } StringFilter: { // input type contains?: string | null; // String endsWith?: string | null; // String @@ -203,7 +207,7 @@ export interface NexusGenObjects { createdAt: NexusGenScalars['DateTime']; // DateTime! description: string; // String! id: NexusGenScalars['UUID']; // UUID! - lastMessage: NexusGenScalars['DateTime']; // DateTime! + latestMessage: NexusGenScalars['DateTime']; // DateTime! topic: string; // String! type: NexusGenEnums['DiscussionType']; // DiscussionType! updatedAt: NexusGenScalars['DateTime']; // DateTime! @@ -300,7 +304,7 @@ export interface NexusGenFieldTypes { createdAt: NexusGenScalars['DateTime']; // DateTime! description: string; // String! id: NexusGenScalars['UUID']; // UUID! - lastMessage: NexusGenScalars['DateTime']; // DateTime! + latestMessage: NexusGenScalars['DateTime']; // DateTime! messages: NexusGenRootTypes['DiscussionMessage'][]; // [DiscussionMessage!]! topic: string; // String! type: NexusGenEnums['DiscussionType']; // DiscussionType! @@ -349,6 +353,7 @@ export interface NexusGenFieldTypes { } Proposal: { // field return type createdAt: NexusGenScalars['DateTime']; // DateTime! + discussions: NexusGenRootTypes['Discussion'][]; // [Discussion!]! id: string; // ID! type: NexusGenEnums['ProposalType']; // ProposalType! updatedAt: NexusGenScalars['DateTime']; // DateTime! @@ -357,6 +362,7 @@ export interface NexusGenFieldTypes { common: NexusGenRootTypes['Common'] | null; // Common discussion: NexusGenRootTypes['Discussion'] | null; // Discussion generateUserAuthToken: string; // String! + proposal: NexusGenRootTypes['Proposal'] | null; // Proposal user: NexusGenRootTypes['User'] | null; // User } User: { // field return type @@ -410,7 +416,7 @@ export interface NexusGenFieldTypeNames { createdAt: 'DateTime' description: 'String' id: 'UUID' - lastMessage: 'DateTime' + latestMessage: 'DateTime' messages: 'DiscussionMessage' topic: 'String' type: 'DiscussionType' @@ -459,6 +465,7 @@ export interface NexusGenFieldTypeNames { } Proposal: { // field return type name createdAt: 'DateTime' + discussions: 'Discussion' id: 'ID' type: 'ProposalType' updatedAt: 'DateTime' @@ -467,6 +474,7 @@ export interface NexusGenFieldTypeNames { common: 'Common' discussion: 'Discussion' generateUserAuthToken: 'String' + proposal: 'Proposal' user: 'User' } User: { // field return type name @@ -549,6 +557,12 @@ export interface NexusGenArgTypes { proposalId: string; // ID! } } + Proposal: { + discussions: { // args + skip?: number | null; // Int + take?: number | null; // Int + } + } Query: { common: { // args where: NexusGenInputs['CommonWhereUniqueInput']; // CommonWhereUniqueInput! @@ -559,6 +573,9 @@ export interface NexusGenArgTypes { generateUserAuthToken: { // args authId: string; // String! } + proposal: { // args + where: NexusGenInputs['ProposalWhereUniqueInput']; // ProposalWhereUniqueInput! + } user: { // args userId?: string | null; // ID } diff --git a/packages/graphql/src/generated/schema.graphql b/packages/graphql/src/generated/schema.graphql index 2302b64b0..e4955f247 100644 --- a/packages/graphql/src/generated/schema.graphql +++ b/packages/graphql/src/generated/schema.graphql @@ -165,9 +165,9 @@ type Discussion implements BaseEntity { """The main identifier of the item""" id: UUID! - """The date at which the last message on the discussion was added""" - lastMessage: DateTime! - messages(orderBy: DiscussionMessagesOrderByInput = {createdAt: asc}, skip: Int = 0, take: Int = 10): [DiscussionMessage!]! + """The date at which the last message on the discussion was added""" + latestMessage: DateTime! + messages(orderBy: DiscussionMessagesOrderByInput = {createdAt: asc}, skip: Int = 0, take: Int = 10): [DiscussionMessage!]! """What this discussion is about""" topic: String! @@ -323,11 +323,12 @@ type Mutation { } type Proposal { - """The date, at which the item was created""" - createdAt: DateTime! + """The date, at which the item was created""" + createdAt: DateTime! + discussions(skip: Int, take: Int): [Discussion!]! - """The main identifier of the item""" - id: ID! + """The main identifier of the item""" + id: ID! type: ProposalType! """The date, at which the item was last modified""" @@ -348,23 +349,28 @@ input ProposalLinkInput { } enum ProposalType { - FundingRequest - JoinRequest + FundingRequest + JoinRequest } input ProposalWhereInput { - type: ProposalType + type: ProposalType +} + +input ProposalWhereUniqueInput { + id: UUID! } type Query { - common(where: CommonWhereUniqueInput!): Common - discussion(id: ID!): Discussion - generateUserAuthToken(authId: String!): String! - - """ - Provide ID to fetch specific user or do not pass anything to get the currently authenticated user - """ - user(userId: ID): User + common(where: CommonWhereUniqueInput!): Common + discussion(id: ID!): Discussion + generateUserAuthToken(authId: String!): String! + proposal(where: ProposalWhereUniqueInput!): Proposal + + """ + Provide ID to fetch specific user or do not pass anything to get the currently authenticated user + """ + user(userId: ID): User } enum SortOrder { diff --git a/packages/graphql/src/schema/Types/Discussion/Types/Discussion.type.ts b/packages/graphql/src/schema/Types/Discussion/Types/Discussion.type.ts index 82d68a365..a3684e42f 100644 --- a/packages/graphql/src/schema/Types/Discussion/Types/Discussion.type.ts +++ b/packages/graphql/src/schema/Types/Discussion/Types/Discussion.type.ts @@ -13,7 +13,7 @@ export const DiscussionType = objectType({ description: 'Short description of the topic' }); - t.nonNull.date('lastMessage', { + t.nonNull.date('latestMessage', { description: 'The date at which the last message on the discussion was added' }); diff --git a/packages/graphql/src/schema/Types/Proposals/Extensions/ProposalDiscussions.extension.ts b/packages/graphql/src/schema/Types/Proposals/Extensions/ProposalDiscussions.extension.ts new file mode 100644 index 000000000..8a0dc1b56 --- /dev/null +++ b/packages/graphql/src/schema/Types/Proposals/Extensions/ProposalDiscussions.extension.ts @@ -0,0 +1,27 @@ +import { extendType, intArg } from 'nexus'; +import { prisma } from '@common/core'; + +export const ProposalDiscussionsExtension = extendType({ + type: 'Proposal', + definition(t) { + t.nonNull.list.nonNull.field('discussions', { + type: 'Discussion', + args: { + take: intArg(), + skip: intArg() + }, + resolve: (root, args) => { + return prisma.proposal + .findUnique({ + where: { + id: root.id + } + }) + .discussions({ + take: args.take || 10, + skip: args.skip || 0 + }); + } + }); + } +}); \ No newline at end of file diff --git a/packages/graphql/src/schema/Types/Proposals/Inputs/ProposalWhereUnique.input.ts b/packages/graphql/src/schema/Types/Proposals/Inputs/ProposalWhereUnique.input.ts new file mode 100644 index 000000000..9bcbcea3f --- /dev/null +++ b/packages/graphql/src/schema/Types/Proposals/Inputs/ProposalWhereUnique.input.ts @@ -0,0 +1,8 @@ +import { inputObjectType } from 'nexus'; + +export const ProposalWhereUniqueInput = inputObjectType({ + name: 'ProposalWhereUniqueInput', + definition(t) { + t.nonNull.uuid('id'); + } +}); \ No newline at end of file diff --git a/packages/graphql/src/schema/Types/Proposals/Queries/GetProposal.query.ts b/packages/graphql/src/schema/Types/Proposals/Queries/GetProposal.query.ts new file mode 100644 index 000000000..e9b5a7217 --- /dev/null +++ b/packages/graphql/src/schema/Types/Proposals/Queries/GetProposal.query.ts @@ -0,0 +1,24 @@ +import { extendType, nonNull, arg } from 'nexus'; +import { prisma } from '@common/core'; + +export const GetProposalQuery = extendType({ + type: 'Query', + definition(t) { + t.field('proposal', { + type: 'Proposal', + args: { + where: nonNull( + arg({ + type: 'ProposalWhereUniqueInput' + }) + ) + }, + resolve: (root, args) => { + return prisma.proposal + .findUnique({ + where: args.where + }); + } + }); + } +}); \ No newline at end of file diff --git a/packages/graphql/src/schema/Types/Proposals/index.ts b/packages/graphql/src/schema/Types/Proposals/index.ts index fc987d0ee..4180be31d 100644 --- a/packages/graphql/src/schema/Types/Proposals/index.ts +++ b/packages/graphql/src/schema/Types/Proposals/index.ts @@ -14,6 +14,12 @@ import { FinalizeProposalMutation } from './Mutations/FinalizeProposalMutation'; import { ProposalTypeEnum } from './Enums/ProposalType.enum'; import { ProposalType } from './Types/Proposal.type'; +import { GetProposalQuery } from './Queries/GetProposal.query'; + +import { ProposalWhereUniqueInput } from './Inputs/ProposalWhereUnique.input'; + +import { ProposalDiscussionsExtension } from './Extensions/ProposalDiscussions.extension'; + export const ProposalTypes = [ ProposalType, @@ -32,5 +38,11 @@ export const ProposalTypes = [ ProposalFileInput, ProposalImageInput, - ProposalTypeEnum + ProposalTypeEnum, + + GetProposalQuery, + + ProposalWhereUniqueInput, + + ProposalDiscussionsExtension ]; \ No newline at end of file From ba5ddd1777ed8f122a156ccd85081b03ab0b1809 Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Mon, 12 Apr 2021 21:32:15 +0300 Subject: [PATCH 16/20] Limit discussions per proposal Signed-off-by: Alexander Ivanov --- .../core/commands/createDiscussionCommand.ts | 10 ++++- .../canCreateProposalDiscussionQuery.ts | 18 ++++++++ packages/graphql/src/generated/schema.graphql | 42 +++++++++---------- 3 files changed, 48 insertions(+), 22 deletions(-) create mode 100644 packages/core/src/services/discussions/core/queries/canCreateProposalDiscussionQuery.ts diff --git a/packages/core/src/services/discussions/core/commands/createDiscussionCommand.ts b/packages/core/src/services/discussions/core/commands/createDiscussionCommand.ts index 0a377d896..048c37f16 100644 --- a/packages/core/src/services/discussions/core/commands/createDiscussionCommand.ts +++ b/packages/core/src/services/discussions/core/commands/createDiscussionCommand.ts @@ -5,6 +5,7 @@ import { prisma } from '@toolkits'; import { CommonError } from '@errors'; import { eventService } from '@services'; import { createDiscussionSubscriptionCommand } from '../../subscriptions/createDiscussionSubscriptionCommand'; +import { canCreateProposalDiscussionQuery } from '../queries/canCreateProposalDiscussionQuery'; const schema = z.object({ commonId: z.string() @@ -47,8 +48,9 @@ export const createDiscussionCommand = async (payload: z.infer): throw new CommonError('Cannot create discussion in a common that you are not member of!'); } - // If there is proposal check if that proposal is from the same common + // If there is proposal do some more checks if (payload.proposalId) { + // Check if that proposal is from the same common if ( !(await prisma.proposal.count({ where: { @@ -61,6 +63,12 @@ export const createDiscussionCommand = async (payload: z.infer): description: 'The proposal is not from the common' }); } + + // Check if the maximum allowed discussions for one + // proposals has been reached + if (!(await canCreateProposalDiscussionQuery(payload.proposalId))) { + throw new CommonError('The maximum discussions per proposal has been reached!'); + } } // Create the discussion diff --git a/packages/core/src/services/discussions/core/queries/canCreateProposalDiscussionQuery.ts b/packages/core/src/services/discussions/core/queries/canCreateProposalDiscussionQuery.ts new file mode 100644 index 000000000..37f28291a --- /dev/null +++ b/packages/core/src/services/discussions/core/queries/canCreateProposalDiscussionQuery.ts @@ -0,0 +1,18 @@ +import { prisma } from '@toolkits'; + +/** + * Checks if the current limit for discussions for proposal + * has been reached + * + * @param proposalId - The ID of the proposal for which we are creating new discussion + */ +export const canCreateProposalDiscussionQuery = async (proposalId: string): Promise => { + const maximumAllowed = process.env['Constraints.Discussions.MaxPerProposal'] || 5; + const currentDiscussionCount = await prisma.discussion.count({ + where: { + proposalId + } + }); + + return currentDiscussionCount < maximumAllowed; +}; \ No newline at end of file diff --git a/packages/graphql/src/generated/schema.graphql b/packages/graphql/src/generated/schema.graphql index e4955f247..df5fc4f04 100644 --- a/packages/graphql/src/generated/schema.graphql +++ b/packages/graphql/src/generated/schema.graphql @@ -165,9 +165,9 @@ type Discussion implements BaseEntity { """The main identifier of the item""" id: UUID! - """The date at which the last message on the discussion was added""" - latestMessage: DateTime! - messages(orderBy: DiscussionMessagesOrderByInput = {createdAt: asc}, skip: Int = 0, take: Int = 10): [DiscussionMessage!]! + """The date at which the last message on the discussion was added""" + latestMessage: DateTime! + messages(orderBy: DiscussionMessagesOrderByInput = {createdAt: asc}, skip: Int = 0, take: Int = 10): [DiscussionMessage!]! """What this discussion is about""" topic: String! @@ -323,12 +323,12 @@ type Mutation { } type Proposal { - """The date, at which the item was created""" - createdAt: DateTime! - discussions(skip: Int, take: Int): [Discussion!]! + """The date, at which the item was created""" + createdAt: DateTime! + discussions(skip: Int, take: Int): [Discussion!]! - """The main identifier of the item""" - id: ID! + """The main identifier of the item""" + id: ID! type: ProposalType! """The date, at which the item was last modified""" @@ -349,28 +349,28 @@ input ProposalLinkInput { } enum ProposalType { - FundingRequest - JoinRequest + FundingRequest + JoinRequest } input ProposalWhereInput { - type: ProposalType + type: ProposalType } input ProposalWhereUniqueInput { - id: UUID! + id: UUID! } type Query { - common(where: CommonWhereUniqueInput!): Common - discussion(id: ID!): Discussion - generateUserAuthToken(authId: String!): String! - proposal(where: ProposalWhereUniqueInput!): Proposal - - """ - Provide ID to fetch specific user or do not pass anything to get the currently authenticated user - """ - user(userId: ID): User + common(where: CommonWhereUniqueInput!): Common + discussion(id: ID!): Discussion + generateUserAuthToken(authId: String!): String! + proposal(where: ProposalWhereUniqueInput!): Proposal + + """ + Provide ID to fetch specific user or do not pass anything to get the currently authenticated user + """ + user(userId: ID): User } enum SortOrder { From eb7ff7119d7cc5d942911b1ec44e4d6a249937eb Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Tue, 13 Apr 2021 07:38:03 +0300 Subject: [PATCH 17/20] Common member proposals (#333) Signed-off-by: Alexander Ivanov --- packages/graphql/src/context/index.ts | 11 +----- .../CommonMemberProposals.extensions.ts | 38 +++++++++++++++++++ 2 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 packages/graphql/src/schema/Types/CommonMember/Extensions/CommonMemberProposals.extensions.ts diff --git a/packages/graphql/src/context/index.ts b/packages/graphql/src/context/index.ts index 1f9489890..0f6267f43 100644 --- a/packages/graphql/src/context/index.ts +++ b/packages/graphql/src/context/index.ts @@ -41,15 +41,8 @@ export const createRequestContext = ({ req, res }: ExpressContext): IRequestCont prisma, getUserId: async () => { - // @todo Remove this - if (process.env['Authentication.Mock']) { - return req.headers.authorization - ? (await auth().verifyIdToken(req.headers.authorization as string)).uid - : req.headers.uid as string; - } else { - // @todo Use custom method for that - return (await auth().verifyIdToken(req.headers.authorization as string)).uid; - } + // @todo Use custom method for that + return (await auth().verifyIdToken(req.headers.authorization as string)).uid; }, getUserDecodedToken: async () => { diff --git a/packages/graphql/src/schema/Types/CommonMember/Extensions/CommonMemberProposals.extensions.ts b/packages/graphql/src/schema/Types/CommonMember/Extensions/CommonMemberProposals.extensions.ts new file mode 100644 index 000000000..1fc047333 --- /dev/null +++ b/packages/graphql/src/schema/Types/CommonMember/Extensions/CommonMemberProposals.extensions.ts @@ -0,0 +1,38 @@ +import { extendType, intArg, arg } from 'nexus'; +import { prisma } from '@common/core'; + +export const CommonMemberProposalsExtensions = extendType({ + type: 'CommonMember', + definition(t) { + t.nonNull.list.nonNull.field('proposals', { + type: 'Proposal', + complexity: 20, + args: { + take: intArg({ + default: 10 + }), + + skip: intArg({ + default: 0 + }), + + where: arg({ + type: 'ProposalWhereInput' + }) + }, + resolve: (root, args) => { + return prisma.commonMember + .findUnique({ + where: { + id: root.id + } + }) + .proposals({ + take: args.take || undefined, + skip: args.skip || undefined, + where: (args.where as any) || undefined + }); + } + }); + } +}); \ No newline at end of file From b4f27a818b1f32f4772062493cf12f08b150f52c Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Tue, 13 Apr 2021 08:08:48 +0300 Subject: [PATCH 18/20] Get user discussion subscription (#360) Signed-off-by: Alexander Ivanov --- .../graphql/src/generated/nexus-typegen.ts | 58 +++++++++++++++++-- packages/graphql/src/generated/schema.graphql | 28 +++++++++ .../src/schema/Types/CommonMember/index.ts | 4 ++ .../Enums/DiscussionSubscriptionType.enum.ts | 7 +++ ...cussionSubscriptionDiscussion.extension.ts | 21 +++++++ .../DiscussionSubscriptionOrderBy.input.ts | 14 +++++ .../Types/DiscussionSubscription.type.ts | 15 +++++ .../src/schema/Types/Discussion/index.ts | 9 +++ .../UserDiscussionSubscriptions.extension.ts | 39 +++++++++++++ .../graphql/src/schema/Types/Users/index.ts | 2 + 10 files changed, 192 insertions(+), 5 deletions(-) create mode 100644 packages/graphql/src/schema/Types/Discussion/Enums/DiscussionSubscriptionType.enum.ts create mode 100644 packages/graphql/src/schema/Types/Discussion/Extensions/DiscussionSubscriptionDiscussion.extension.ts create mode 100644 packages/graphql/src/schema/Types/Discussion/Inputs/DiscussionSubscriptionOrderBy.input.ts create mode 100644 packages/graphql/src/schema/Types/Discussion/Types/DiscussionSubscription.type.ts create mode 100644 packages/graphql/src/schema/Types/Users/Extensions/UserDiscussionSubscriptions.extension.ts diff --git a/packages/graphql/src/generated/nexus-typegen.ts b/packages/graphql/src/generated/nexus-typegen.ts index 726df9260..9ce737dad 100644 --- a/packages/graphql/src/generated/nexus-typegen.ts +++ b/packages/graphql/src/generated/nexus-typegen.ts @@ -122,6 +122,10 @@ export interface NexusGenInputs { createdAt?: NexusGenEnums['SortOrder'] | null; // SortOrder updatedAt?: NexusGenEnums['SortOrder'] | null; // SortOrder } + DiscussionSubscriptionOrderByInput: { // input type + createdAt?: NexusGenEnums['SortOrder'] | null; // SortOrder + updatedAt?: NexusGenEnums['SortOrder'] | null; // SortOrder + } EventOrderByInput: { // input type createdAt?: NexusGenEnums['SortOrder'] | null; // SortOrder type?: NexusGenEnums['SortOrder'] | null; // SortOrder @@ -163,8 +167,9 @@ export interface NexusGenInputs { export interface NexusGenEnums { CommonMemberRole: "Founder" - DiscussionMessageType: "Message" - DiscussionType: "CommonDiscussion" | "ProposalDiscussion" + DiscussionMessageType: 'Message' + DiscussionSubscriptionType: 'AllNotifications' | 'NoNotification' | 'OnlyMentions' + DiscussionType: 'CommonDiscussion' | 'ProposalDiscussion' EventType: "CardCreated" | "CardCvvVerificationFailed" | "CardCvvVerificationPassed" | "CommonCreated" | "CommonMemberCreated" | "CommonMemberRoleAdded" | "CommonMemberRoleRemoved" | "DiscussionCreated" | "DiscussionMessageCreated" | "DiscussionSubscriptionCreated" | "FundingRequestAccepted" | "FundingRequestCreated" | "FundingRequestRejected" | "JoinRequestAccepted" | "JoinRequestCreated" | "JoinRequestRejected" | "PaymentCreated" | "PaymentFailed" | "PaymentSucceeded" | "ProposalExpired" | "ProposalMajorityReached" | "UserCreated" | "VoteCreated" FundingType: "Monthly" | "OneTime" ProposalType: "FundingRequest" | "JoinRequest" @@ -219,6 +224,14 @@ export interface NexusGenObjects { type: NexusGenEnums['DiscussionMessageType']; // DiscussionMessageType! updatedAt: NexusGenScalars['DateTime']; // DateTime! } + DiscussionSubscription: { // root type + createdAt: NexusGenScalars['DateTime']; // DateTime! + discussionId: NexusGenScalars['UUID']; // UUID! + id: NexusGenScalars['UUID']; // UUID! + type: NexusGenEnums['DiscussionSubscriptionType']; // DiscussionSubscriptionType! + updatedAt: NexusGenScalars['DateTime']; // DateTime! + userId: string; // String! + } Event: { // root type commonId?: string | null; // ID createdAt: NexusGenScalars['DateTime']; // DateTime! @@ -265,7 +278,7 @@ export interface NexusGenObjects { } export interface NexusGenInterfaces { - BaseEntity: NexusGenRootTypes['Discussion'] | NexusGenRootTypes['DiscussionMessage']; + BaseEntity: NexusGenRootTypes['Discussion'] | NexusGenRootTypes['DiscussionMessage'] | NexusGenRootTypes['DiscussionSubscription']; } export interface NexusGenUnions { @@ -296,6 +309,7 @@ export interface NexusGenFieldTypes { common: NexusGenRootTypes['Common'] | null; // Common commonId: string; // ID! id: string; // ID! + proposals: NexusGenRootTypes['Proposal'][]; // [Proposal!]! roles: NexusGenEnums['CommonMemberRole'][]; // [CommonMemberRole!]! user: NexusGenRootTypes['User'] | null; // User userId: string; // ID! @@ -317,6 +331,15 @@ export interface NexusGenFieldTypes { type: NexusGenEnums['DiscussionMessageType']; // DiscussionMessageType! updatedAt: NexusGenScalars['DateTime']; // DateTime! } + DiscussionSubscription: { // field return type + createdAt: NexusGenScalars['DateTime']; // DateTime! + discussion: NexusGenRootTypes['Discussion']; // Discussion! + discussionId: NexusGenScalars['UUID']; // UUID! + id: NexusGenScalars['UUID']; // UUID! + type: NexusGenEnums['DiscussionSubscriptionType']; // DiscussionSubscriptionType! + updatedAt: NexusGenScalars['DateTime']; // DateTime! + userId: string; // String! + } Event: { // field return type commonId: string | null; // ID createdAt: NexusGenScalars['DateTime']; // DateTime! @@ -367,6 +390,7 @@ export interface NexusGenFieldTypes { } User: { // field return type createdAt: NexusGenScalars['DateTime']; // DateTime! + discussionSubscriptions: NexusGenRootTypes['DiscussionSubscription'][]; // [DiscussionSubscription!]! displayName: string; // String! events: NexusGenRootTypes['Event'][]; // [Event!]! firstName: string; // String! @@ -408,6 +432,7 @@ export interface NexusGenFieldTypeNames { common: 'Common' commonId: 'ID' id: 'ID' + proposals: 'Proposal' roles: 'CommonMemberRole' user: 'User' userId: 'ID' @@ -429,6 +454,15 @@ export interface NexusGenFieldTypeNames { type: 'DiscussionMessageType' updatedAt: 'DateTime' } + DiscussionSubscription: { // field return type name + createdAt: 'DateTime' + discussion: 'Discussion' + discussionId: 'UUID' + id: 'UUID' + type: 'DiscussionSubscriptionType' + updatedAt: 'DateTime' + userId: 'String' + } Event: { // field return type name commonId: 'ID' createdAt: 'DateTime' @@ -479,6 +513,7 @@ export interface NexusGenFieldTypeNames { } User: { // field return type name createdAt: 'DateTime' + discussionSubscriptions: 'DiscussionSubscription' displayName: 'String' events: 'Event' firstName: 'String' @@ -521,6 +556,13 @@ export interface NexusGenArgTypes { where?: NexusGenInputs['ProposalWhereInput'] | null; // ProposalWhereInput } } + CommonMember: { + proposals: { // args + skip?: number | null; // Int + take: number | null; // Int + where?: NexusGenInputs['ProposalWhereInput'] | null; // ProposalWhereInput + } + } Discussion: { messages: { // args orderBy: NexusGenInputs['DiscussionMessagesOrderByInput'] | null; // DiscussionMessagesOrderByInput @@ -581,6 +623,11 @@ export interface NexusGenArgTypes { } } User: { + discussionSubscriptions: { // args + orderBy?: NexusGenInputs['DiscussionSubscriptionOrderByInput'] | null; // DiscussionSubscriptionOrderByInput + skip?: number | null; // Int + take: number | null; // Int + } events: { // args orderBy?: NexusGenInputs['EventOrderByInput'] | null; // EventOrderByInput skip?: number | null; // Int @@ -595,12 +642,13 @@ export interface NexusGenArgTypes { } export interface NexusGenAbstractTypeMembers { - BaseEntity: "Discussion" | "DiscussionMessage" + BaseEntity: 'Discussion' | 'DiscussionMessage' | 'DiscussionSubscription' } export interface NexusGenTypeInterfaces { Discussion: "BaseEntity" - DiscussionMessage: "BaseEntity" + DiscussionMessage: 'BaseEntity' + DiscussionSubscription: 'BaseEntity' } export type NexusGenObjectNames = keyof NexusGenObjects; diff --git a/packages/graphql/src/generated/schema.graphql b/packages/graphql/src/generated/schema.graphql index df5fc4f04..a55121a62 100644 --- a/packages/graphql/src/generated/schema.graphql +++ b/packages/graphql/src/generated/schema.graphql @@ -61,6 +61,7 @@ type CommonMember { common: Common commonId: ID! id: ID! + proposals(skip: Int = 0, take: Int = 10, where: ProposalWhereInput): [Proposal!]! roles: [CommonMemberRole!]! user: User userId: ID! @@ -199,6 +200,32 @@ input DiscussionMessagesOrderByInput { updatedAt: SortOrder } +type DiscussionSubscription implements BaseEntity { + """The date, at which the item was created""" + createdAt: DateTime! + discussion: Discussion! + discussionId: UUID! + + """The main identifier of the item""" + id: UUID! + type: DiscussionSubscriptionType! + + """The date, at which the item was last modified""" + updatedAt: DateTime! + userId: String! +} + +input DiscussionSubscriptionOrderByInput { + createdAt: SortOrder + updatedAt: SortOrder +} + +enum DiscussionSubscriptionType { + AllNotifications + NoNotification + OnlyMentions +} + enum DiscussionType { CommonDiscussion ProposalDiscussion @@ -404,6 +431,7 @@ scalar UUID type User { """The date, at which the item was created""" createdAt: DateTime! + discussionSubscriptions(orderBy: DiscussionSubscriptionOrderByInput, skip: Int = 0, take: Int = 10): [DiscussionSubscription!]! """The display name of the user""" displayName: String! diff --git a/packages/graphql/src/schema/Types/CommonMember/index.ts b/packages/graphql/src/schema/Types/CommonMember/index.ts index 638954061..95aa04a31 100644 --- a/packages/graphql/src/schema/Types/CommonMember/index.ts +++ b/packages/graphql/src/schema/Types/CommonMember/index.ts @@ -1,6 +1,9 @@ import { CommonMemberType } from './Types/CommonMember.type'; + import { CommonMemberUserExtension } from './Extensions/CommonMemberUser.extension'; import { CommonMemberCommonExtensions } from './Extensions/CommonMemberCommon.extensions'; +import { CommonMemberProposalsExtensions } from './Extensions/CommonMemberProposals.extensions'; + import { CommonMemberOrderByInput } from './Inputs/CommonMemberOrderBy.input'; import { CommonMemberRoleEnum } from './Enums/CommonMemberRole.enum'; @@ -9,6 +12,7 @@ export const CommonMemberTypes = [ CommonMemberUserExtension, CommonMemberCommonExtensions, + CommonMemberProposalsExtensions, CommonMemberOrderByInput, diff --git a/packages/graphql/src/schema/Types/Discussion/Enums/DiscussionSubscriptionType.enum.ts b/packages/graphql/src/schema/Types/Discussion/Enums/DiscussionSubscriptionType.enum.ts new file mode 100644 index 000000000..23dd3b5dc --- /dev/null +++ b/packages/graphql/src/schema/Types/Discussion/Enums/DiscussionSubscriptionType.enum.ts @@ -0,0 +1,7 @@ +import { enumType } from 'nexus'; +import { DiscussionSubscriptionType } from '@prisma/client'; + +export const DiscussionSubscriptionTypeEnum = enumType({ + name: 'DiscussionSubscriptionType', + members: DiscussionSubscriptionType +}); \ No newline at end of file diff --git a/packages/graphql/src/schema/Types/Discussion/Extensions/DiscussionSubscriptionDiscussion.extension.ts b/packages/graphql/src/schema/Types/Discussion/Extensions/DiscussionSubscriptionDiscussion.extension.ts new file mode 100644 index 000000000..93b430f46 --- /dev/null +++ b/packages/graphql/src/schema/Types/Discussion/Extensions/DiscussionSubscriptionDiscussion.extension.ts @@ -0,0 +1,21 @@ +import { extendType } from 'nexus'; +import { prisma } from '@common/core'; + +export const DiscussionSubscriptionDiscussionExtension = extendType({ + type: 'DiscussionSubscription', + definition(t) { + t.nonNull.field('discussion', { + complexity: 10, + type: 'Discussion', + resolve: (root) => { + return prisma.discussionSubscription + .findUnique({ + where: { + id: root.id + } + }) + .discussion(); + } + }); + } +}); \ No newline at end of file diff --git a/packages/graphql/src/schema/Types/Discussion/Inputs/DiscussionSubscriptionOrderBy.input.ts b/packages/graphql/src/schema/Types/Discussion/Inputs/DiscussionSubscriptionOrderBy.input.ts new file mode 100644 index 000000000..91e5ac180 --- /dev/null +++ b/packages/graphql/src/schema/Types/Discussion/Inputs/DiscussionSubscriptionOrderBy.input.ts @@ -0,0 +1,14 @@ +import { inputObjectType } from 'nexus'; + +export const DiscussionSubscriptionOrderByInput = inputObjectType({ + name: 'DiscussionSubscriptionOrderByInput', + definition(t) { + t.field('createdAt', { + type: 'SortOrder' + }); + + t.field('updatedAt', { + type: 'SortOrder' + }); + } +}); \ No newline at end of file diff --git a/packages/graphql/src/schema/Types/Discussion/Types/DiscussionSubscription.type.ts b/packages/graphql/src/schema/Types/Discussion/Types/DiscussionSubscription.type.ts new file mode 100644 index 000000000..eb5f25987 --- /dev/null +++ b/packages/graphql/src/schema/Types/Discussion/Types/DiscussionSubscription.type.ts @@ -0,0 +1,15 @@ +import { objectType } from 'nexus'; + +export const DiscussionSubscriptionType = objectType({ + name: 'DiscussionSubscription', + definition(t) { + t.implements('BaseEntity'); + + t.nonNull.field('type', { + type: 'DiscussionSubscriptionType' + }); + + t.nonNull.string('userId'); + t.nonNull.uuid('discussionId'); + } +}); \ No newline at end of file diff --git a/packages/graphql/src/schema/Types/Discussion/index.ts b/packages/graphql/src/schema/Types/Discussion/index.ts index 309b01bcc..750cb8760 100644 --- a/packages/graphql/src/schema/Types/Discussion/index.ts +++ b/packages/graphql/src/schema/Types/Discussion/index.ts @@ -1,14 +1,19 @@ import { DiscussionType } from './Types/Discussion.type'; import { DiscussionMessageType } from './Types/DiscussionMessage.type'; +import { DiscussionSubscriptionType } from './Types/DiscussionSubscription.type'; import { DiscussionTypeEnum } from './Enums/DiscussionType.enum'; import { DiscussionMessageTypeEnum } from './Enums/DiscussionMessageType.enum'; +import { DiscussionSubscriptionTypeEnum } from './Enums/DiscussionSubscriptionType.enum'; import { GetDiscussionQuery } from './Queries/GetDiscussion.query'; import { DiscussionMessagesOrderByInput } from './Inputs/DiscussionMessagesOrderBy.input'; +import { DiscussionSubscriptionOrderByInput } from './Inputs/DiscussionSubscriptionOrderBy.input'; import { DiscussionMessagesExtension } from './Extensions/DiscussionMessages.extension'; +import { DiscussionSubscriptionDiscussionExtension } from './Extensions/DiscussionSubscriptionDiscussion.extension'; + import { CreateDiscussionInput, CreateDiscussionMutation } from './Mutations/CreateDiscussion.mutation'; import { @@ -19,15 +24,19 @@ import { export const DiscussionTypes = [ DiscussionType, DiscussionMessageType, + DiscussionSubscriptionType, DiscussionTypeEnum, DiscussionMessageTypeEnum, + DiscussionSubscriptionTypeEnum, GetDiscussionQuery, DiscussionMessagesOrderByInput, + DiscussionSubscriptionOrderByInput, DiscussionMessagesExtension, + DiscussionSubscriptionDiscussionExtension, CreateDiscussionInput, CreateDiscussionMutation, diff --git a/packages/graphql/src/schema/Types/Users/Extensions/UserDiscussionSubscriptions.extension.ts b/packages/graphql/src/schema/Types/Users/Extensions/UserDiscussionSubscriptions.extension.ts new file mode 100644 index 000000000..ac25c3ca4 --- /dev/null +++ b/packages/graphql/src/schema/Types/Users/Extensions/UserDiscussionSubscriptions.extension.ts @@ -0,0 +1,39 @@ +import { extendType, intArg, arg } from 'nexus'; +import { prisma } from '@common/core'; + +export const UserDiscussionSubscriptionsExtension = extendType({ + type: 'User', + definition(t) { + t.nonNull.list.nonNull.field('discussionSubscriptions', { + type: 'DiscussionSubscription', + args: { + take: intArg({ + default: 10 + }), + + skip: intArg({ + default: 0 + }), + + orderBy: arg({ + type: 'DiscussionSubscriptionOrderByInput' + }) + }, + resolve: (root, args) => { + return prisma.user + .findUnique({ + where: { + id: root.id + } + }) + .discussionSubscriptions({ + take: args.take || 10, + skip: args.skip || 0, + orderBy: (args.orderBy as any) || { + createdAt: 'asc' + } + }); + } + }); + } +}); \ No newline at end of file diff --git a/packages/graphql/src/schema/Types/Users/index.ts b/packages/graphql/src/schema/Types/Users/index.ts index 25442a628..6819a4562 100644 --- a/packages/graphql/src/schema/Types/Users/index.ts +++ b/packages/graphql/src/schema/Types/Users/index.ts @@ -7,6 +7,7 @@ import { CreateUserInput, CreateUserMutation } from './Mutations/CreateUser.muta import { UserEventsExtension } from './Extensions/UserEvents.extension'; import { UserProposalsExtension } from './Extensions/UserProposals.extension'; +import { UserDiscussionSubscriptionsExtension } from './Extensions/UserDiscussionSubscriptions.extension'; export const UserTypes = [ UserType, @@ -18,6 +19,7 @@ export const UserTypes = [ UserEventsExtension, UserProposalsExtension, + UserDiscussionSubscriptionsExtension, GenerateUserAuthTokenQuery ]; \ No newline at end of file From f17ec704d305adfc02049451c0f837d5894e63c4 Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Tue, 13 Apr 2021 08:23:09 +0300 Subject: [PATCH 19/20] Change discussion type command (#362) Signed-off-by: Alexander Ivanov --- .../prisma/models/events/EventType.prisma | 2 + .../core/src/services/discussions/index.ts | 12 ++++- ...changeDiscussionSubscriptionTypeCommand.ts | 52 +++++++++++++++++++ .../graphql/src/generated/nexus-typegen.ts | 14 ++--- packages/graphql/src/generated/schema.graphql | 1 + 5 files changed, 73 insertions(+), 8 deletions(-) create mode 100644 packages/core/src/services/discussions/subscriptions/changeDiscussionSubscriptionTypeCommand.ts diff --git a/packages/core/prisma/models/events/EventType.prisma b/packages/core/prisma/models/events/EventType.prisma index 73c83ab80..12b45be33 100644 --- a/packages/core/prisma/models/events/EventType.prisma +++ b/packages/core/prisma/models/events/EventType.prisma @@ -31,4 +31,6 @@ enum EventType { DiscussionCreated DiscussionMessageCreated DiscussionSubscriptionCreated + + DiscussionSubscriptionTypeChanged } \ No newline at end of file diff --git a/packages/core/src/services/discussions/index.ts b/packages/core/src/services/discussions/index.ts index 1f9ca1021..ae52b66b0 100644 --- a/packages/core/src/services/discussions/index.ts +++ b/packages/core/src/services/discussions/index.ts @@ -1,5 +1,8 @@ import { createDiscussionCommand } from './core/commands/createDiscussionCommand'; + import { createDiscussionSubscriptionCommand } from './subscriptions/createDiscussionSubscriptionCommand'; +import { changeDiscussionSubscriptionTypeCommand } from './subscriptions/changeDiscussionSubscriptionTypeCommand'; + import { createDiscussionMessageCommand } from './messages/createDiscussionMessageCommand'; export const discussionService = { @@ -13,7 +16,14 @@ export const discussionService = { /** * Create notification subscription for discussion */ - create: createDiscussionSubscriptionCommand + create: createDiscussionSubscriptionCommand, + + /** + * Updates the type of the subscription in the backing store. Please note + * that this function does not check if the user has permission to update + * the specified subscription + */ + changeType: changeDiscussionSubscriptionTypeCommand }, messages: { diff --git a/packages/core/src/services/discussions/subscriptions/changeDiscussionSubscriptionTypeCommand.ts b/packages/core/src/services/discussions/subscriptions/changeDiscussionSubscriptionTypeCommand.ts new file mode 100644 index 000000000..2b91f8eaa --- /dev/null +++ b/packages/core/src/services/discussions/subscriptions/changeDiscussionSubscriptionTypeCommand.ts @@ -0,0 +1,52 @@ +import * as z from 'zod'; +import { DiscussionSubscription, DiscussionSubscriptionType } from '@prisma/client'; + +import { prisma } from '@toolkits'; +import { eventService } from '@services'; + +const schema = z.object({ + id: z.string() + .nonempty() + .uuid(), + + type: z.enum(Object.keys(DiscussionSubscriptionType) as [(keyof typeof DiscussionSubscriptionType)]) +}); + +/** + * Updates the type of the subscription in the backing store. Please note + * that this function does not check if the user has permission to update + * the specified subscription + * + * @param payload - The update payload + * + * @returns The updated subscription + */ +export const changeDiscussionSubscriptionTypeCommand = async (payload: z.infer): Promise => { + // Validate the payload + schema.parse(payload); + + // Update the subscription + const updatedSubscription = await prisma.discussionSubscription + .update({ + data: { + type: payload.type + }, + + where: { + id: payload.id + } + }); + + // Crete event + await eventService.create({ + type: 'DiscussionSubscriptionTypeChanged', + userId: updatedSubscription.userId, + payload: { + discussionId: updatedSubscription.discussionId, + updatedType: updatedSubscription.type + } + }); + + // Return the updated subscription + return updatedSubscription; +}; \ No newline at end of file diff --git a/packages/graphql/src/generated/nexus-typegen.ts b/packages/graphql/src/generated/nexus-typegen.ts index 9ce737dad..347fdf313 100644 --- a/packages/graphql/src/generated/nexus-typegen.ts +++ b/packages/graphql/src/generated/nexus-typegen.ts @@ -166,15 +166,15 @@ export interface NexusGenInputs { } export interface NexusGenEnums { - CommonMemberRole: "Founder" + CommonMemberRole: 'Founder' DiscussionMessageType: 'Message' DiscussionSubscriptionType: 'AllNotifications' | 'NoNotification' | 'OnlyMentions' DiscussionType: 'CommonDiscussion' | 'ProposalDiscussion' - EventType: "CardCreated" | "CardCvvVerificationFailed" | "CardCvvVerificationPassed" | "CommonCreated" | "CommonMemberCreated" | "CommonMemberRoleAdded" | "CommonMemberRoleRemoved" | "DiscussionCreated" | "DiscussionMessageCreated" | "DiscussionSubscriptionCreated" | "FundingRequestAccepted" | "FundingRequestCreated" | "FundingRequestRejected" | "JoinRequestAccepted" | "JoinRequestCreated" | "JoinRequestRejected" | "PaymentCreated" | "PaymentFailed" | "PaymentSucceeded" | "ProposalExpired" | "ProposalMajorityReached" | "UserCreated" | "VoteCreated" - FundingType: "Monthly" | "OneTime" - ProposalType: "FundingRequest" | "JoinRequest" - SortOrder: "asc" | "desc" - VoteOutcome: "Approve" | "Condemn" + EventType: 'CardCreated' | 'CardCvvVerificationFailed' | 'CardCvvVerificationPassed' | 'CommonCreated' | 'CommonMemberCreated' | 'CommonMemberRoleAdded' | 'CommonMemberRoleRemoved' | 'DiscussionCreated' | 'DiscussionMessageCreated' | 'DiscussionSubscriptionCreated' | 'DiscussionSubscriptionTypeChanged' | 'FundingRequestAccepted' | 'FundingRequestCreated' | 'FundingRequestRejected' | 'JoinRequestAccepted' | 'JoinRequestCreated' | 'JoinRequestRejected' | 'PaymentCreated' | 'PaymentFailed' | 'PaymentSucceeded' | 'ProposalExpired' | 'ProposalMajorityReached' | 'UserCreated' | 'VoteCreated' + FundingType: 'Monthly' | 'OneTime' + ProposalType: 'FundingRequest' | 'JoinRequest' + SortOrder: 'asc' | 'desc' + VoteOutcome: 'Approve' | 'Condemn' } export interface NexusGenScalars { @@ -646,7 +646,7 @@ export interface NexusGenAbstractTypeMembers { } export interface NexusGenTypeInterfaces { - Discussion: "BaseEntity" + Discussion: 'BaseEntity' DiscussionMessage: 'BaseEntity' DiscussionSubscription: 'BaseEntity' } diff --git a/packages/graphql/src/generated/schema.graphql b/packages/graphql/src/generated/schema.graphql index a55121a62..ebdc64eab 100644 --- a/packages/graphql/src/generated/schema.graphql +++ b/packages/graphql/src/generated/schema.graphql @@ -269,6 +269,7 @@ enum EventType { DiscussionCreated DiscussionMessageCreated DiscussionSubscriptionCreated + DiscussionSubscriptionTypeChanged FundingRequestAccepted FundingRequestCreated FundingRequestRejected From 327153c374d4bf0932ae14c70786ce82f81a7481 Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Tue, 13 Apr 2021 09:33:33 +0300 Subject: [PATCH 20/20] Change discussion type gql (#362) Signed-off-by: Alexander Ivanov --- .../migration.sql | 2 ++ .../graphql/src/generated/nexus-typegen.ts | 6 ++++ packages/graphql/src/generated/schema.graphql | 7 +++++ ...angeDiscussionSubscriptionType.mutation.ts | 29 +++++++++++++++++++ .../src/schema/Types/Discussion/index.ts | 6 +++- 5 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 packages/core/prisma/migrations/20210413051920_discussion_subscription_type_changed_event_added/migration.sql create mode 100644 packages/graphql/src/schema/Types/Discussion/Mutations/ChangeDiscussionSubscriptionType.mutation.ts diff --git a/packages/core/prisma/migrations/20210413051920_discussion_subscription_type_changed_event_added/migration.sql b/packages/core/prisma/migrations/20210413051920_discussion_subscription_type_changed_event_added/migration.sql new file mode 100644 index 000000000..e07d5f6f9 --- /dev/null +++ b/packages/core/prisma/migrations/20210413051920_discussion_subscription_type_changed_event_added/migration.sql @@ -0,0 +1,2 @@ +-- AlterEnum +ALTER TYPE "EventType" ADD VALUE 'DiscussionSubscriptionTypeChanged'; diff --git a/packages/graphql/src/generated/nexus-typegen.ts b/packages/graphql/src/generated/nexus-typegen.ts index 347fdf313..281561051 100644 --- a/packages/graphql/src/generated/nexus-typegen.ts +++ b/packages/graphql/src/generated/nexus-typegen.ts @@ -364,6 +364,7 @@ export interface NexusGenFieldTypes { url: string; // String! } Mutation: { // field return type + changeDiscussionSubscriptionType: NexusGenRootTypes['DiscussionSubscription'] | null; // DiscussionSubscription createCard: NexusGenRootTypes['Card']; // Card! createCommon: NexusGenRootTypes['Common']; // Common! createDiscussion: NexusGenRootTypes['Discussion']; // Discussion! @@ -487,6 +488,7 @@ export interface NexusGenFieldTypeNames { url: 'String' } Mutation: { // field return type name + changeDiscussionSubscriptionType: 'DiscussionSubscription' createCard: 'Card' createCommon: 'Common' createDiscussion: 'Discussion' @@ -571,6 +573,10 @@ export interface NexusGenArgTypes { } } Mutation: { + changeDiscussionSubscriptionType: { // args + id: string; // ID! + type: NexusGenEnums['DiscussionSubscriptionType']; // DiscussionSubscriptionType! + } createCard: { // args input: NexusGenInputs['CreateCardInput']; // CreateCardInput! } diff --git a/packages/graphql/src/generated/schema.graphql b/packages/graphql/src/generated/schema.graphql index ebdc64eab..cff34fda8 100644 --- a/packages/graphql/src/generated/schema.graphql +++ b/packages/graphql/src/generated/schema.graphql @@ -335,6 +335,13 @@ input LinkInput { } type Mutation { + changeDiscussionSubscriptionType( + """The ID of the discussion subscription to change""" + id: ID! + + """The new subscription type""" + type: DiscussionSubscriptionType! + ): DiscussionSubscription createCard(input: CreateCardInput!): Card! createCommon(input: CreateCommonInput!): Common! createDiscussion(input: CreateDiscussionInput!): Discussion! diff --git a/packages/graphql/src/schema/Types/Discussion/Mutations/ChangeDiscussionSubscriptionType.mutation.ts b/packages/graphql/src/schema/Types/Discussion/Mutations/ChangeDiscussionSubscriptionType.mutation.ts new file mode 100644 index 000000000..7b2964791 --- /dev/null +++ b/packages/graphql/src/schema/Types/Discussion/Mutations/ChangeDiscussionSubscriptionType.mutation.ts @@ -0,0 +1,29 @@ +import { arg, extendType, idArg, nonNull } from 'nexus'; +import { discussionService } from '@common/core'; + +export const ChangeDiscussionSubscriptionTypeMutation = extendType({ + type: 'Mutation', + definition(t) { + t.field('changeDiscussionSubscriptionType', { + type: 'DiscussionSubscription', + args: { + id: nonNull( + idArg({ + description: 'The ID of the discussion subscription to change' + }) + ), + type: nonNull( + arg({ + type: 'DiscussionSubscriptionType', + description: 'The new subscription type' + }) + ) + }, + resolve: async (root, args) => { + // @todo Authorization!!!! + + return discussionService.subscription.changeType(args); + } + }); + } +}); \ No newline at end of file diff --git a/packages/graphql/src/schema/Types/Discussion/index.ts b/packages/graphql/src/schema/Types/Discussion/index.ts index 750cb8760..bbd7335a0 100644 --- a/packages/graphql/src/schema/Types/Discussion/index.ts +++ b/packages/graphql/src/schema/Types/Discussion/index.ts @@ -21,6 +21,8 @@ import { CreateDiscussionMessageMutation } from './Mutations/CreateDiscussionMessage.mutation'; +import { ChangeDiscussionSubscriptionTypeMutation } from './Mutations/ChangeDiscussionSubscriptionType.mutation'; + export const DiscussionTypes = [ DiscussionType, DiscussionMessageType, @@ -42,5 +44,7 @@ export const DiscussionTypes = [ CreateDiscussionMutation, CreateDiscussionMessageInput, - CreateDiscussionMessageMutation + CreateDiscussionMessageMutation, + + ChangeDiscussionSubscriptionTypeMutation ]; \ No newline at end of file