@lhremote/core 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +1 -1
- package/dist/db/index.js.map +1 -1
- package/dist/db/repositories/action-budget.d.ts +29 -0
- package/dist/db/repositories/action-budget.d.ts.map +1 -0
- package/dist/db/repositories/action-budget.js +100 -0
- package/dist/db/repositories/action-budget.js.map +1 -0
- package/dist/db/repositories/action-budget.test.d.ts +2 -0
- package/dist/db/repositories/action-budget.test.d.ts.map +1 -0
- package/dist/db/repositories/action-budget.test.js +181 -0
- package/dist/db/repositories/action-budget.test.js.map +1 -0
- package/dist/db/repositories/campaign-hard-delete.integration.test.d.ts +2 -0
- package/dist/db/repositories/campaign-hard-delete.integration.test.d.ts.map +1 -0
- package/dist/db/repositories/campaign-hard-delete.integration.test.js +78 -0
- package/dist/db/repositories/campaign-hard-delete.integration.test.js.map +1 -0
- package/dist/db/repositories/campaign.d.ts +14 -0
- package/dist/db/repositories/campaign.d.ts.map +1 -1
- package/dist/db/repositories/campaign.js +125 -0
- package/dist/db/repositories/campaign.js.map +1 -1
- package/dist/db/repositories/index.d.ts +1 -0
- package/dist/db/repositories/index.d.ts.map +1 -1
- package/dist/db/repositories/index.js +1 -0
- package/dist/db/repositories/index.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -4
- package/dist/index.js.map +1 -1
- package/dist/linkedin/__tests__/dom-automation.integration.test.d.ts +2 -0
- package/dist/linkedin/__tests__/dom-automation.integration.test.d.ts.map +1 -0
- package/dist/linkedin/__tests__/dom-automation.integration.test.js +160 -0
- package/dist/linkedin/__tests__/dom-automation.integration.test.js.map +1 -0
- package/dist/linkedin/__tests__/selectors.integration.test.d.ts +2 -0
- package/dist/linkedin/__tests__/selectors.integration.test.d.ts.map +1 -0
- package/dist/linkedin/__tests__/selectors.integration.test.js +258 -0
- package/dist/linkedin/__tests__/selectors.integration.test.js.map +1 -0
- package/dist/linkedin/dom-automation.d.ts +67 -0
- package/dist/linkedin/dom-automation.d.ts.map +1 -0
- package/dist/linkedin/dom-automation.js +139 -0
- package/dist/linkedin/dom-automation.js.map +1 -0
- package/dist/linkedin/index.d.ts +3 -0
- package/dist/linkedin/index.d.ts.map +1 -0
- package/dist/linkedin/index.js +5 -0
- package/dist/linkedin/index.js.map +1 -0
- package/dist/linkedin/selectors.d.ts +67 -0
- package/dist/linkedin/selectors.d.ts.map +1 -0
- package/dist/linkedin/selectors.js +74 -0
- package/dist/linkedin/selectors.js.map +1 -0
- package/dist/operations/campaign-delete.d.ts +6 -2
- package/dist/operations/campaign-delete.d.ts.map +1 -1
- package/dist/operations/campaign-delete.js +9 -2
- package/dist/operations/campaign-delete.js.map +1 -1
- package/dist/operations/campaign-delete.test.js +41 -1
- package/dist/operations/campaign-delete.test.js.map +1 -1
- package/dist/operations/campaign-erase.d.ts +19 -0
- package/dist/operations/campaign-erase.d.ts.map +1 -0
- package/dist/operations/campaign-erase.js +22 -0
- package/dist/operations/campaign-erase.js.map +1 -0
- package/dist/operations/campaign-erase.test.d.ts +2 -0
- package/dist/operations/campaign-erase.test.d.ts.map +1 -0
- package/dist/operations/campaign-erase.test.js +101 -0
- package/dist/operations/campaign-erase.test.js.map +1 -0
- package/dist/operations/comment-on-post.d.ts +34 -0
- package/dist/operations/comment-on-post.d.ts.map +1 -0
- package/dist/operations/comment-on-post.js +108 -0
- package/dist/operations/comment-on-post.js.map +1 -0
- package/dist/operations/comment-on-post.test.d.ts +2 -0
- package/dist/operations/comment-on-post.test.d.ts.map +1 -0
- package/dist/operations/comment-on-post.test.js +240 -0
- package/dist/operations/comment-on-post.test.js.map +1 -0
- package/dist/operations/dismiss-errors.d.ts +22 -0
- package/dist/operations/dismiss-errors.d.ts.map +1 -0
- package/dist/operations/dismiss-errors.js +55 -0
- package/dist/operations/dismiss-errors.js.map +1 -0
- package/dist/operations/dismiss-errors.test.d.ts +2 -0
- package/dist/operations/dismiss-errors.test.d.ts.map +1 -0
- package/dist/operations/dismiss-errors.test.js +136 -0
- package/dist/operations/dismiss-errors.test.js.map +1 -0
- package/dist/operations/endorse-skills.d.ts +10 -0
- package/dist/operations/endorse-skills.d.ts.map +1 -0
- package/dist/operations/endorse-skills.js +12 -0
- package/dist/operations/endorse-skills.js.map +1 -0
- package/dist/operations/enrich-profile.d.ts +18 -0
- package/dist/operations/enrich-profile.d.ts.map +1 -0
- package/dist/operations/enrich-profile.js +15 -0
- package/dist/operations/enrich-profile.js.map +1 -0
- package/dist/operations/ephemeral-action.d.ts +22 -0
- package/dist/operations/ephemeral-action.d.ts.map +1 -0
- package/dist/operations/ephemeral-action.js +31 -0
- package/dist/operations/ephemeral-action.js.map +1 -0
- package/dist/operations/follow-person.d.ts +9 -0
- package/dist/operations/follow-person.d.ts.map +1 -0
- package/dist/operations/follow-person.js +11 -0
- package/dist/operations/follow-person.js.map +1 -0
- package/dist/operations/get-action-budget.d.ts +6 -0
- package/dist/operations/get-action-budget.d.ts.map +1 -0
- package/dist/operations/get-action-budget.js +22 -0
- package/dist/operations/get-action-budget.js.map +1 -0
- package/dist/operations/get-action-budget.test.d.ts +2 -0
- package/dist/operations/get-action-budget.test.d.ts.map +1 -0
- package/dist/operations/get-action-budget.test.js +79 -0
- package/dist/operations/get-action-budget.test.js.map +1 -0
- package/dist/operations/get-errors.d.ts +9 -3
- package/dist/operations/get-errors.d.ts.map +1 -1
- package/dist/operations/get-errors.js +41 -4
- package/dist/operations/get-errors.js.map +1 -1
- package/dist/operations/get-errors.test.js +108 -42
- package/dist/operations/get-errors.test.js.map +1 -1
- package/dist/operations/get-feed.d.ts +33 -0
- package/dist/operations/get-feed.d.ts.map +1 -0
- package/dist/operations/get-feed.js +220 -0
- package/dist/operations/get-feed.js.map +1 -0
- package/dist/operations/get-feed.test.d.ts +2 -0
- package/dist/operations/get-feed.test.d.ts.map +1 -0
- package/dist/operations/get-feed.test.js +276 -0
- package/dist/operations/get-feed.test.js.map +1 -0
- package/dist/operations/get-post-engagers.d.ts +40 -0
- package/dist/operations/get-post-engagers.d.ts.map +1 -0
- package/dist/operations/get-post-engagers.js +128 -0
- package/dist/operations/get-post-engagers.js.map +1 -0
- package/dist/operations/get-post-engagers.test.d.ts +2 -0
- package/dist/operations/get-post-engagers.test.d.ts.map +1 -0
- package/dist/operations/get-post-engagers.test.js +19 -0
- package/dist/operations/get-post-engagers.test.js.map +1 -0
- package/dist/operations/get-post-stats.d.ts +38 -0
- package/dist/operations/get-post-stats.d.ts.map +1 -0
- package/dist/operations/get-post-stats.js +100 -0
- package/dist/operations/get-post-stats.js.map +1 -0
- package/dist/operations/get-post-stats.test.d.ts +2 -0
- package/dist/operations/get-post-stats.test.d.ts.map +1 -0
- package/dist/operations/get-post-stats.test.js +34 -0
- package/dist/operations/get-post-stats.test.js.map +1 -0
- package/dist/operations/get-post.d.ts +183 -0
- package/dist/operations/get-post.d.ts.map +1 -0
- package/dist/operations/get-post.js +270 -0
- package/dist/operations/get-post.js.map +1 -0
- package/dist/operations/get-post.test.d.ts +2 -0
- package/dist/operations/get-post.test.d.ts.map +1 -0
- package/dist/operations/get-post.test.js +449 -0
- package/dist/operations/get-post.test.js.map +1 -0
- package/dist/operations/get-profile-activity.d.ts +129 -0
- package/dist/operations/get-profile-activity.d.ts.map +1 -0
- package/dist/operations/get-profile-activity.js +181 -0
- package/dist/operations/get-profile-activity.js.map +1 -0
- package/dist/operations/get-profile-activity.test.d.ts +2 -0
- package/dist/operations/get-profile-activity.test.d.ts.map +1 -0
- package/dist/operations/get-profile-activity.test.js +205 -0
- package/dist/operations/get-profile-activity.test.js.map +1 -0
- package/dist/operations/get-throttle-status.d.ts +6 -0
- package/dist/operations/get-throttle-status.d.ts.map +1 -0
- package/dist/operations/get-throttle-status.js +30 -0
- package/dist/operations/get-throttle-status.js.map +1 -0
- package/dist/operations/get-throttle-status.test.d.ts +2 -0
- package/dist/operations/get-throttle-status.test.d.ts.map +1 -0
- package/dist/operations/get-throttle-status.test.js +62 -0
- package/dist/operations/get-throttle-status.test.js.map +1 -0
- package/dist/operations/index.d.ts +22 -0
- package/dist/operations/index.d.ts.map +1 -1
- package/dist/operations/index.js +32 -0
- package/dist/operations/index.js.map +1 -1
- package/dist/operations/like-person-posts.d.ts +14 -0
- package/dist/operations/like-person-posts.d.ts.map +1 -0
- package/dist/operations/like-person-posts.js +28 -0
- package/dist/operations/like-person-posts.js.map +1 -0
- package/dist/operations/message-person.d.ts +11 -0
- package/dist/operations/message-person.d.ts.map +1 -0
- package/dist/operations/message-person.js +19 -0
- package/dist/operations/message-person.js.map +1 -0
- package/dist/operations/react-to-post.d.ts +38 -0
- package/dist/operations/react-to-post.d.ts.map +1 -0
- package/dist/operations/react-to-post.js +82 -0
- package/dist/operations/react-to-post.js.map +1 -0
- package/dist/operations/react-to-post.test.d.ts +2 -0
- package/dist/operations/react-to-post.test.d.ts.map +1 -0
- package/dist/operations/react-to-post.test.js +154 -0
- package/dist/operations/react-to-post.test.js.map +1 -0
- package/dist/operations/remove-connection.d.ts +6 -0
- package/dist/operations/remove-connection.d.ts.map +1 -0
- package/dist/operations/remove-connection.js +7 -0
- package/dist/operations/remove-connection.js.map +1 -0
- package/dist/operations/search-posts.d.ts +143 -0
- package/dist/operations/search-posts.d.ts.map +1 -0
- package/dist/operations/search-posts.js +220 -0
- package/dist/operations/search-posts.js.map +1 -0
- package/dist/operations/search-posts.test.d.ts +2 -0
- package/dist/operations/search-posts.test.d.ts.map +1 -0
- package/dist/operations/search-posts.test.js +261 -0
- package/dist/operations/search-posts.test.js.map +1 -0
- package/dist/operations/send-inmail.d.ts +11 -0
- package/dist/operations/send-inmail.d.ts.map +1 -0
- package/dist/operations/send-inmail.js +19 -0
- package/dist/operations/send-inmail.js.map +1 -0
- package/dist/operations/send-invite.d.ts +9 -0
- package/dist/operations/send-invite.d.ts.map +1 -0
- package/dist/operations/send-invite.js +14 -0
- package/dist/operations/send-invite.js.map +1 -0
- package/dist/operations/visit-profile.d.ts +14 -0
- package/dist/operations/visit-profile.d.ts.map +1 -0
- package/dist/operations/visit-profile.js +49 -0
- package/dist/operations/visit-profile.js.map +1 -0
- package/dist/operations/visit-profile.test.d.ts +2 -0
- package/dist/operations/visit-profile.test.d.ts.map +1 -0
- package/dist/operations/visit-profile.test.js +193 -0
- package/dist/operations/visit-profile.test.js.map +1 -0
- package/dist/services/campaign.d.ts +10 -0
- package/dist/services/campaign.d.ts.map +1 -1
- package/dist/services/campaign.js +16 -0
- package/dist/services/campaign.js.map +1 -1
- package/dist/services/collection.d.ts +5 -2
- package/dist/services/collection.d.ts.map +1 -1
- package/dist/services/collection.js +24 -5
- package/dist/services/collection.js.map +1 -1
- package/dist/services/collection.test.js +51 -9
- package/dist/services/collection.test.js.map +1 -1
- package/dist/services/ephemeral-campaign.d.ts +64 -0
- package/dist/services/ephemeral-campaign.d.ts.map +1 -0
- package/dist/services/ephemeral-campaign.js +210 -0
- package/dist/services/ephemeral-campaign.js.map +1 -0
- package/dist/services/ephemeral-campaign.test.d.ts +2 -0
- package/dist/services/ephemeral-campaign.test.d.ts.map +1 -0
- package/dist/services/ephemeral-campaign.test.js +333 -0
- package/dist/services/ephemeral-campaign.test.js.map +1 -0
- package/dist/services/errors.d.ts +10 -0
- package/dist/services/errors.d.ts.map +1 -1
- package/dist/services/errors.js +20 -0
- package/dist/services/errors.js.map +1 -1
- package/dist/services/index.d.ts +3 -1
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +3 -1
- package/dist/services/index.js.map +1 -1
- package/dist/services/instance-context.d.ts.map +1 -1
- package/dist/services/instance-context.js +9 -1
- package/dist/services/instance-context.js.map +1 -1
- package/dist/services/instance-context.test.js +77 -1
- package/dist/services/instance-context.test.js.map +1 -1
- package/dist/services/instance.d.ts +31 -0
- package/dist/services/instance.d.ts.map +1 -1
- package/dist/services/instance.js +123 -0
- package/dist/services/instance.js.map +1 -1
- package/dist/services/instance.test.js +70 -0
- package/dist/services/instance.test.js.map +1 -1
- package/dist/services/launcher.d.ts +7 -0
- package/dist/services/launcher.d.ts.map +1 -1
- package/dist/services/launcher.js +18 -1
- package/dist/services/launcher.js.map +1 -1
- package/dist/types/action-budget.d.ts +52 -0
- package/dist/types/action-budget.d.ts.map +1 -0
- package/dist/types/action-budget.js +4 -0
- package/dist/types/action-budget.js.map +1 -0
- package/dist/types/campaign.d.ts +24 -0
- package/dist/types/campaign.d.ts.map +1 -1
- package/dist/types/feed.d.ts +32 -0
- package/dist/types/feed.d.ts.map +1 -0
- package/dist/types/feed.js +4 -0
- package/dist/types/feed.js.map +1 -0
- package/dist/types/index.d.ts +7 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/post-analytics.d.ts +40 -0
- package/dist/types/post-analytics.d.ts.map +1 -0
- package/dist/types/post-analytics.js +4 -0
- package/dist/types/post-analytics.js.map +1 -0
- package/dist/types/post.d.ts +43 -0
- package/dist/types/post.d.ts.map +1 -0
- package/dist/types/post.js +4 -0
- package/dist/types/post.js.map +1 -0
- package/dist/types/search-posts.d.ts +22 -0
- package/dist/types/search-posts.d.ts.map +1 -0
- package/dist/types/search-posts.js +4 -0
- package/dist/types/search-posts.js.map +1 -0
- package/dist/types/ui-health.d.ts +16 -0
- package/dist/types/ui-health.d.ts.map +1 -1
- package/dist/voyager/index.d.ts +2 -0
- package/dist/voyager/index.d.ts.map +1 -0
- package/dist/voyager/index.js +4 -0
- package/dist/voyager/index.js.map +1 -0
- package/dist/voyager/interceptor.d.ts +100 -0
- package/dist/voyager/interceptor.d.ts.map +1 -0
- package/dist/voyager/interceptor.integration.test.d.ts +2 -0
- package/dist/voyager/interceptor.integration.test.d.ts.map +1 -0
- package/dist/voyager/interceptor.integration.test.js +89 -0
- package/dist/voyager/interceptor.integration.test.js.map +1 -0
- package/dist/voyager/interceptor.js +235 -0
- package/dist/voyager/interceptor.js.map +1 -0
- package/dist/voyager/interceptor.test.d.ts +2 -0
- package/dist/voyager/interceptor.test.d.ts.map +1 -0
- package/dist/voyager/interceptor.test.js +372 -0
- package/dist/voyager/interceptor.test.js.map +1 -0
- package/package.json +1 -1
package/dist/db/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { DatabaseClient, type DatabaseClientOptions } from "./client.js";
|
|
2
2
|
export { discoverAllDatabases, discoverDatabase } from "./discovery.js";
|
|
3
3
|
export { ActionNotFoundError, CampaignNotFoundError, ChatNotFoundError, DatabaseError, DatabaseNotFoundError, ExcludeListNotFoundError, NoNextActionError, ProfileNotFoundError, } from "./errors.js";
|
|
4
|
-
export { CampaignExcludeListRepository, CampaignRepository, CampaignStatisticsRepository, CollectionListRepository, type CollectionSummary, MessageRepository, ProfileRepository, } from "./repositories/index.js";
|
|
4
|
+
export { ActionBudgetRepository, CampaignExcludeListRepository, CampaignRepository, CampaignStatisticsRepository, CollectionListRepository, type CollectionSummary, MessageRepository, ProfileRepository, } from "./repositories/index.js";
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/db/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,EACjB,aAAa,EACb,qBAAqB,EACrB,wBAAwB,EACxB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,6BAA6B,EAC7B,kBAAkB,EAClB,4BAA4B,EAC5B,wBAAwB,EACxB,KAAK,iBAAiB,EACtB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,yBAAyB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,EACjB,aAAa,EACb,qBAAqB,EACrB,wBAAwB,EACxB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,sBAAsB,EACtB,6BAA6B,EAC7B,kBAAkB,EAClB,4BAA4B,EAC5B,wBAAwB,EACxB,KAAK,iBAAiB,EACtB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,yBAAyB,CAAC"}
|
package/dist/db/index.js
CHANGED
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
export { DatabaseClient } from "./client.js";
|
|
4
4
|
export { discoverAllDatabases, discoverDatabase } from "./discovery.js";
|
|
5
5
|
export { ActionNotFoundError, CampaignNotFoundError, ChatNotFoundError, DatabaseError, DatabaseNotFoundError, ExcludeListNotFoundError, NoNextActionError, ProfileNotFoundError, } from "./errors.js";
|
|
6
|
-
export { CampaignExcludeListRepository, CampaignRepository, CampaignStatisticsRepository, CollectionListRepository, MessageRepository, ProfileRepository, } from "./repositories/index.js";
|
|
6
|
+
export { ActionBudgetRepository, CampaignExcludeListRepository, CampaignRepository, CampaignStatisticsRepository, CollectionListRepository, MessageRepository, ProfileRepository, } from "./repositories/index.js";
|
|
7
7
|
//# sourceMappingURL=index.js.map
|
package/dist/db/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,oCAAoC;AAEpC,OAAO,EAAE,cAAc,EAA8B,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,EACjB,aAAa,EACb,qBAAqB,EACrB,wBAAwB,EACxB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,6BAA6B,EAC7B,kBAAkB,EAClB,4BAA4B,EAC5B,wBAAwB,EAExB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,yBAAyB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,oCAAoC;AAEpC,OAAO,EAAE,cAAc,EAA8B,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,EACjB,aAAa,EACb,qBAAqB,EACrB,wBAAwB,EACxB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,sBAAsB,EACtB,6BAA6B,EAC7B,kBAAkB,EAClB,4BAA4B,EAC5B,wBAAwB,EAExB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ActionBudgetEntry, LimitType } from "../../types/index.js";
|
|
2
|
+
import type { DatabaseClient } from "../client.js";
|
|
3
|
+
/**
|
|
4
|
+
* Repository for reading LinkedHelper rate limit data.
|
|
5
|
+
*
|
|
6
|
+
* Reads from LH's `limit_types`, `daily_limits`, and `action_results`
|
|
7
|
+
* tables to compute the current action budget.
|
|
8
|
+
*/
|
|
9
|
+
export declare class ActionBudgetRepository {
|
|
10
|
+
private readonly stmtLimitTypes;
|
|
11
|
+
private readonly stmtDailyLimits;
|
|
12
|
+
private readonly stmtTodayCountsByType;
|
|
13
|
+
constructor(client: DatabaseClient);
|
|
14
|
+
/**
|
|
15
|
+
* Get all limit types defined by LinkedHelper.
|
|
16
|
+
*/
|
|
17
|
+
getLimitTypes(): LimitType[];
|
|
18
|
+
/**
|
|
19
|
+
* Get the full action budget: limit types, daily limits, and today's usage.
|
|
20
|
+
*
|
|
21
|
+
* Campaign usage comes from LH's `action_results` table.
|
|
22
|
+
* Direct usage (CDP-direct actions like comments/reactions) is accepted
|
|
23
|
+
* as a parameter since it is tracked externally.
|
|
24
|
+
*
|
|
25
|
+
* @param directCounts Map of limit type ID → count of CDP-direct actions today.
|
|
26
|
+
*/
|
|
27
|
+
getActionBudget(directCounts?: ReadonlyMap<number, number>): ActionBudgetEntry[];
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=action-budget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-budget.d.ts","sourceRoot":"","sources":["../../../src/db/repositories/action-budget.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAwCnD;;;;;GAKG;AACH,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IAChC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC;IACjC,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAC;gBAE3B,MAAM,EAAE,cAAc;IAqBlC;;OAEG;IACH,aAAa,IAAI,SAAS,EAAE;IAK5B;;;;;;;;OAQG;IACH,eAAe,CACb,YAAY,GAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAa,GACpD,iBAAiB,EAAE;CAyCvB"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (C) 2026 Oleksii PELYKH
|
|
3
|
+
/**
|
|
4
|
+
* Maps LinkedHelper action config types to their corresponding limit type IDs.
|
|
5
|
+
*
|
|
6
|
+
* These IDs match the `limit_types` table in the LH database.
|
|
7
|
+
* Action types that do not consume daily limits (e.g. Waiter,
|
|
8
|
+
* FilterContactsOutOfMyNetwork) are intentionally omitted.
|
|
9
|
+
*/
|
|
10
|
+
const ACTION_TYPE_TO_LIMIT_TYPE = new Map([
|
|
11
|
+
["SaveCurrentProfile", 1],
|
|
12
|
+
["InvitePerson", 8],
|
|
13
|
+
["SendMessage", 9],
|
|
14
|
+
["MessageToEventAttendees", 10],
|
|
15
|
+
["SendInMail", 11],
|
|
16
|
+
["EndorseSkills", 12],
|
|
17
|
+
["InviteToGroup", 13],
|
|
18
|
+
["Follow", 16],
|
|
19
|
+
["Unfollow", 17],
|
|
20
|
+
["LikePost", 18],
|
|
21
|
+
["SendPersonToWebhook", 23],
|
|
22
|
+
["GetEmailFromPAS", 24],
|
|
23
|
+
["SendPersonToExternalCRM", 31],
|
|
24
|
+
]);
|
|
25
|
+
/**
|
|
26
|
+
* Repository for reading LinkedHelper rate limit data.
|
|
27
|
+
*
|
|
28
|
+
* Reads from LH's `limit_types`, `daily_limits`, and `action_results`
|
|
29
|
+
* tables to compute the current action budget.
|
|
30
|
+
*/
|
|
31
|
+
export class ActionBudgetRepository {
|
|
32
|
+
stmtLimitTypes;
|
|
33
|
+
stmtDailyLimits;
|
|
34
|
+
stmtTodayCountsByType;
|
|
35
|
+
constructor(client) {
|
|
36
|
+
const { db } = client;
|
|
37
|
+
this.stmtLimitTypes = db.prepare(`SELECT id, type FROM limit_types ORDER BY id`);
|
|
38
|
+
this.stmtDailyLimits = db.prepare(`SELECT id, max_limit FROM daily_limits`);
|
|
39
|
+
this.stmtTodayCountsByType = db.prepare(`SELECT ac.actionType AS action_type, COUNT(*) AS count_today
|
|
40
|
+
FROM action_results ar
|
|
41
|
+
JOIN action_versions av ON ar.action_version_id = av.id
|
|
42
|
+
JOIN action_configs ac ON av.config_id = ac.id
|
|
43
|
+
WHERE date(ar.created_at) = date('now', 'localtime')
|
|
44
|
+
GROUP BY ac.actionType`);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get all limit types defined by LinkedHelper.
|
|
48
|
+
*/
|
|
49
|
+
getLimitTypes() {
|
|
50
|
+
const rows = this.stmtLimitTypes.all();
|
|
51
|
+
return rows.map((r) => ({ id: r.id, type: r.type }));
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get the full action budget: limit types, daily limits, and today's usage.
|
|
55
|
+
*
|
|
56
|
+
* Campaign usage comes from LH's `action_results` table.
|
|
57
|
+
* Direct usage (CDP-direct actions like comments/reactions) is accepted
|
|
58
|
+
* as a parameter since it is tracked externally.
|
|
59
|
+
*
|
|
60
|
+
* @param directCounts Map of limit type ID → count of CDP-direct actions today.
|
|
61
|
+
*/
|
|
62
|
+
getActionBudget(directCounts = new Map()) {
|
|
63
|
+
const limitTypes = this.stmtLimitTypes.all();
|
|
64
|
+
const dailyLimits = this.stmtDailyLimits.all();
|
|
65
|
+
const todayCounts = this.stmtTodayCountsByType.all();
|
|
66
|
+
// Build daily limit lookup: limit_type_id → max_limit
|
|
67
|
+
const limitMap = new Map();
|
|
68
|
+
for (const dl of dailyLimits) {
|
|
69
|
+
limitMap.set(dl.id, dl.max_limit);
|
|
70
|
+
}
|
|
71
|
+
// Build campaign usage lookup: limit_type_id → count
|
|
72
|
+
const campaignUsageMap = new Map();
|
|
73
|
+
for (const row of todayCounts) {
|
|
74
|
+
const limitTypeId = ACTION_TYPE_TO_LIMIT_TYPE.get(row.action_type);
|
|
75
|
+
if (limitTypeId !== undefined) {
|
|
76
|
+
const existing = campaignUsageMap.get(limitTypeId) ?? 0;
|
|
77
|
+
campaignUsageMap.set(limitTypeId, existing + row.count_today);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return limitTypes.map((lt) => {
|
|
81
|
+
const dailyLimit = limitMap.get(lt.id) ?? null;
|
|
82
|
+
const campaignUsed = campaignUsageMap.get(lt.id) ?? 0;
|
|
83
|
+
const directUsed = directCounts.get(lt.id) ?? 0;
|
|
84
|
+
const totalUsed = campaignUsed + directUsed;
|
|
85
|
+
const remaining = dailyLimit !== null
|
|
86
|
+
? Math.max(0, dailyLimit - totalUsed)
|
|
87
|
+
: null;
|
|
88
|
+
return {
|
|
89
|
+
limitTypeId: lt.id,
|
|
90
|
+
limitType: lt.type,
|
|
91
|
+
dailyLimit,
|
|
92
|
+
campaignUsed,
|
|
93
|
+
directUsed,
|
|
94
|
+
totalUsed,
|
|
95
|
+
remaining,
|
|
96
|
+
};
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=action-budget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-budget.js","sourceRoot":"","sources":["../../../src/db/repositories/action-budget.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-budget.test.d.ts","sourceRoot":"","sources":["../../../src/db/repositories/action-budget.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (C) 2026 Oleksii PELYKH
|
|
3
|
+
import { DatabaseSync } from "node:sqlite";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
5
|
+
import { ActionBudgetRepository } from "./action-budget.js";
|
|
6
|
+
function createBudgetDb() {
|
|
7
|
+
const db = new DatabaseSync(":memory:");
|
|
8
|
+
db.exec(`
|
|
9
|
+
CREATE TABLE limit_types (
|
|
10
|
+
id INTEGER PRIMARY KEY,
|
|
11
|
+
type TEXT NOT NULL
|
|
12
|
+
)
|
|
13
|
+
`);
|
|
14
|
+
db.exec(`
|
|
15
|
+
INSERT INTO limit_types (id, type) VALUES
|
|
16
|
+
(8, 'Invite'),
|
|
17
|
+
(9, 'Message'),
|
|
18
|
+
(16, 'Follow'),
|
|
19
|
+
(18, 'PostLike')
|
|
20
|
+
`);
|
|
21
|
+
db.exec(`
|
|
22
|
+
CREATE TABLE daily_limits (
|
|
23
|
+
id INTEGER PRIMARY KEY,
|
|
24
|
+
max_limit INTEGER NOT NULL
|
|
25
|
+
)
|
|
26
|
+
`);
|
|
27
|
+
db.exec(`
|
|
28
|
+
INSERT INTO daily_limits (id, max_limit) VALUES
|
|
29
|
+
(8, 100),
|
|
30
|
+
(16, 150)
|
|
31
|
+
`);
|
|
32
|
+
db.exec(`
|
|
33
|
+
CREATE TABLE action_configs (
|
|
34
|
+
id INTEGER PRIMARY KEY,
|
|
35
|
+
actionType TEXT NOT NULL
|
|
36
|
+
)
|
|
37
|
+
`);
|
|
38
|
+
db.exec(`INSERT INTO action_configs (id, actionType) VALUES (1, 'InvitePerson')`);
|
|
39
|
+
db.exec(`INSERT INTO action_configs (id, actionType) VALUES (2, 'Follow')`);
|
|
40
|
+
db.exec(`
|
|
41
|
+
CREATE TABLE action_versions (
|
|
42
|
+
id INTEGER PRIMARY KEY,
|
|
43
|
+
action_id INTEGER NOT NULL,
|
|
44
|
+
config_id INTEGER NOT NULL
|
|
45
|
+
)
|
|
46
|
+
`);
|
|
47
|
+
db.exec(`INSERT INTO action_versions (id, action_id, config_id) VALUES (1, 1, 1)`);
|
|
48
|
+
db.exec(`INSERT INTO action_versions (id, action_id, config_id) VALUES (2, 2, 2)`);
|
|
49
|
+
db.exec(`
|
|
50
|
+
CREATE TABLE action_results (
|
|
51
|
+
id INTEGER PRIMARY KEY,
|
|
52
|
+
action_version_id INTEGER NOT NULL,
|
|
53
|
+
person_id INTEGER NOT NULL,
|
|
54
|
+
result INTEGER NOT NULL DEFAULT 0,
|
|
55
|
+
created_at DATETIME NOT NULL
|
|
56
|
+
)
|
|
57
|
+
`);
|
|
58
|
+
// Insert today's results
|
|
59
|
+
const today = new Date().toISOString().split("T")[0] ?? "";
|
|
60
|
+
db.exec(`
|
|
61
|
+
INSERT INTO action_results (action_version_id, person_id, result, created_at)
|
|
62
|
+
VALUES
|
|
63
|
+
(1, 100, 1, '${today} 10:00:00'),
|
|
64
|
+
(1, 101, 1, '${today} 10:05:00'),
|
|
65
|
+
(1, 102, -1, '${today} 10:10:00'),
|
|
66
|
+
(2, 200, 1, '${today} 11:00:00')
|
|
67
|
+
`);
|
|
68
|
+
// Insert yesterday's results (should NOT be counted)
|
|
69
|
+
const yesterday = new Date(Date.now() - 86_400_000).toISOString().split("T")[0] ?? "";
|
|
70
|
+
db.exec(`
|
|
71
|
+
INSERT INTO action_results (action_version_id, person_id, result, created_at)
|
|
72
|
+
VALUES
|
|
73
|
+
(1, 103, 1, '${yesterday} 10:00:00'),
|
|
74
|
+
(2, 201, 1, '${yesterday} 11:00:00')
|
|
75
|
+
`);
|
|
76
|
+
return db;
|
|
77
|
+
}
|
|
78
|
+
describe("ActionBudgetRepository", () => {
|
|
79
|
+
let db;
|
|
80
|
+
let repo;
|
|
81
|
+
beforeEach(() => {
|
|
82
|
+
db = createBudgetDb();
|
|
83
|
+
repo = new ActionBudgetRepository({ db });
|
|
84
|
+
});
|
|
85
|
+
afterEach(() => {
|
|
86
|
+
db.close();
|
|
87
|
+
});
|
|
88
|
+
describe("getLimitTypes", () => {
|
|
89
|
+
it("returns all limit types ordered by ID", () => {
|
|
90
|
+
const types = repo.getLimitTypes();
|
|
91
|
+
expect(types).toEqual([
|
|
92
|
+
{ id: 8, type: "Invite" },
|
|
93
|
+
{ id: 9, type: "Message" },
|
|
94
|
+
{ id: 16, type: "Follow" },
|
|
95
|
+
{ id: 18, type: "PostLike" },
|
|
96
|
+
]);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
describe("getActionBudget", () => {
|
|
100
|
+
it("combines limit types, daily limits, and today's usage", () => {
|
|
101
|
+
const entries = repo.getActionBudget();
|
|
102
|
+
// Invite: limit 100, 3 today (2 success + 1 fail — all results counted)
|
|
103
|
+
const invite = entries.find((e) => e.limitType === "Invite");
|
|
104
|
+
expect(invite).toEqual({
|
|
105
|
+
limitTypeId: 8,
|
|
106
|
+
limitType: "Invite",
|
|
107
|
+
dailyLimit: 100,
|
|
108
|
+
campaignUsed: 3,
|
|
109
|
+
directUsed: 0,
|
|
110
|
+
totalUsed: 3,
|
|
111
|
+
remaining: 97,
|
|
112
|
+
});
|
|
113
|
+
// Follow: limit 150, 1 today
|
|
114
|
+
const follow = entries.find((e) => e.limitType === "Follow");
|
|
115
|
+
expect(follow).toEqual({
|
|
116
|
+
limitTypeId: 16,
|
|
117
|
+
limitType: "Follow",
|
|
118
|
+
dailyLimit: 150,
|
|
119
|
+
campaignUsed: 1,
|
|
120
|
+
directUsed: 0,
|
|
121
|
+
totalUsed: 1,
|
|
122
|
+
remaining: 149,
|
|
123
|
+
});
|
|
124
|
+
// Message: no daily limit configured, 0 used
|
|
125
|
+
const message = entries.find((e) => e.limitType === "Message");
|
|
126
|
+
expect(message).toEqual({
|
|
127
|
+
limitTypeId: 9,
|
|
128
|
+
limitType: "Message",
|
|
129
|
+
dailyLimit: null,
|
|
130
|
+
campaignUsed: 0,
|
|
131
|
+
directUsed: 0,
|
|
132
|
+
totalUsed: 0,
|
|
133
|
+
remaining: null,
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
it("includes direct counts in budget", () => {
|
|
137
|
+
const directCounts = new Map([[18, 5]]);
|
|
138
|
+
const entries = repo.getActionBudget(directCounts);
|
|
139
|
+
const postLike = entries.find((e) => e.limitType === "PostLike");
|
|
140
|
+
expect(postLike).toEqual({
|
|
141
|
+
limitTypeId: 18,
|
|
142
|
+
limitType: "PostLike",
|
|
143
|
+
dailyLimit: null,
|
|
144
|
+
campaignUsed: 0,
|
|
145
|
+
directUsed: 5,
|
|
146
|
+
totalUsed: 5,
|
|
147
|
+
remaining: null,
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
it("clamps remaining to zero when over limit", () => {
|
|
151
|
+
const directCounts = new Map([[8, 200]]);
|
|
152
|
+
const entries = repo.getActionBudget(directCounts);
|
|
153
|
+
const invite = entries.find((e) => e.limitType === "Invite");
|
|
154
|
+
expect(invite?.remaining).toBe(0);
|
|
155
|
+
expect(invite?.totalUsed).toBe(203); // 3 campaign + 200 direct
|
|
156
|
+
});
|
|
157
|
+
it("returns empty entries when no limit types exist", () => {
|
|
158
|
+
const emptyDb = new DatabaseSync(":memory:");
|
|
159
|
+
emptyDb.exec(`CREATE TABLE limit_types (id INTEGER PRIMARY KEY, type TEXT NOT NULL)`);
|
|
160
|
+
emptyDb.exec(`CREATE TABLE daily_limits (id INTEGER PRIMARY KEY, max_limit INTEGER NOT NULL)`);
|
|
161
|
+
emptyDb.exec(`
|
|
162
|
+
CREATE TABLE action_results (
|
|
163
|
+
id INTEGER PRIMARY KEY, action_version_id INTEGER NOT NULL,
|
|
164
|
+
person_id INTEGER NOT NULL, result INTEGER NOT NULL DEFAULT 0,
|
|
165
|
+
created_at DATETIME NOT NULL
|
|
166
|
+
)
|
|
167
|
+
`);
|
|
168
|
+
emptyDb.exec(`
|
|
169
|
+
CREATE TABLE action_versions (
|
|
170
|
+
id INTEGER PRIMARY KEY, action_id INTEGER NOT NULL, config_id INTEGER NOT NULL
|
|
171
|
+
)
|
|
172
|
+
`);
|
|
173
|
+
emptyDb.exec(`CREATE TABLE action_configs (id INTEGER PRIMARY KEY, actionType TEXT NOT NULL)`);
|
|
174
|
+
const emptyRepo = new ActionBudgetRepository({ db: emptyDb });
|
|
175
|
+
const entries = emptyRepo.getActionBudget();
|
|
176
|
+
expect(entries).toEqual([]);
|
|
177
|
+
emptyDb.close();
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
//# sourceMappingURL=action-budget.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-budget.test.js","sourceRoot":"","sources":["../../../src/db/repositories/action-budget.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"campaign-hard-delete.integration.test.d.ts","sourceRoot":"","sources":["../../../src/db/repositories/campaign-hard-delete.integration.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (C) 2026 Oleksii PELYKH
|
|
3
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
4
|
+
import { DatabaseClient } from "../client.js";
|
|
5
|
+
import { openFixture } from "../testing/open-fixture.js";
|
|
6
|
+
import { CampaignRepository } from "./campaign.js";
|
|
7
|
+
import { CampaignNotFoundError } from "../errors.js";
|
|
8
|
+
describe("CampaignRepository.deleteCampaign (integration)", () => {
|
|
9
|
+
let rawDb;
|
|
10
|
+
let client;
|
|
11
|
+
let repo;
|
|
12
|
+
beforeAll(() => {
|
|
13
|
+
// Use openFixture() for an isolated writable copy
|
|
14
|
+
rawDb = openFixture();
|
|
15
|
+
// Ensure SQLite enforces foreign key constraints so delete order is validated
|
|
16
|
+
rawDb.exec("PRAGMA foreign_keys = ON");
|
|
17
|
+
const fkPragma = rawDb
|
|
18
|
+
.prepare("PRAGMA foreign_keys")
|
|
19
|
+
.get();
|
|
20
|
+
expect(fkPragma.foreign_keys).toBe(1);
|
|
21
|
+
// Pause campaign 1 so it can be hard-deleted
|
|
22
|
+
// (fixture has it as active: is_paused=0, is_archived=0, is_valid=1)
|
|
23
|
+
rawDb.exec("UPDATE campaigns SET is_paused = 1 WHERE id = 1");
|
|
24
|
+
// Wrap the raw DatabaseSync in a DatabaseClient for the repo
|
|
25
|
+
client = { db: rawDb, close: () => rawDb.close() };
|
|
26
|
+
repo = new CampaignRepository(client);
|
|
27
|
+
});
|
|
28
|
+
afterAll(() => {
|
|
29
|
+
rawDb.close();
|
|
30
|
+
});
|
|
31
|
+
function countRows(table, where, params) {
|
|
32
|
+
const row = rawDb.prepare(`SELECT COUNT(*) AS cnt FROM ${table} WHERE ${where}`).get(...params);
|
|
33
|
+
return row.cnt;
|
|
34
|
+
}
|
|
35
|
+
it("removes campaign 1 and all related rows", () => {
|
|
36
|
+
// Pre-conditions: campaign 1 has data in all related tables
|
|
37
|
+
expect(countRows("campaigns", "id = ?", [1])).toBe(1);
|
|
38
|
+
expect(countRows("actions", "campaign_id = ?", [1])).toBeGreaterThan(0);
|
|
39
|
+
expect(countRows("action_versions", "action_id IN (SELECT id FROM actions WHERE campaign_id = ?)", [1])).toBeGreaterThan(0);
|
|
40
|
+
expect(countRows("action_configs", "id IN (SELECT DISTINCT av.config_id FROM action_versions av JOIN actions a ON av.action_id = a.id WHERE a.campaign_id = ?)", [1])).toBeGreaterThan(0);
|
|
41
|
+
expect(countRows("action_target_people", "action_id IN (SELECT id FROM actions WHERE campaign_id = ?)", [1])).toBeGreaterThan(0);
|
|
42
|
+
expect(countRows("action_results", "action_version_id IN (SELECT av.id FROM action_versions av JOIN actions a ON av.action_id = a.id WHERE a.campaign_id = ?)", [1])).toBeGreaterThan(0);
|
|
43
|
+
expect(countRows("person_in_campaigns_history", "campaign_id = ?", [1])).toBeGreaterThan(0);
|
|
44
|
+
expect(countRows("campaign_versions", "campaign_id = ?", [1])).toBeGreaterThan(0);
|
|
45
|
+
// Exclude list chain pre-conditions
|
|
46
|
+
const excludeListCount = countRows("collection_people_versions", "id IN (SELECT av.exclude_list_id FROM action_versions av JOIN actions a ON av.action_id = a.id WHERE a.campaign_id = ? AND av.exclude_list_id IS NOT NULL UNION SELECT cv.exclude_list_id FROM campaign_versions cv WHERE cv.campaign_id = ? AND cv.exclude_list_id IS NOT NULL)", [1, 1]);
|
|
47
|
+
expect(excludeListCount).toBeGreaterThan(0);
|
|
48
|
+
// Perform the hard delete (with FK constraints enforced)
|
|
49
|
+
repo.deleteCampaign(1);
|
|
50
|
+
// Post-conditions: all related rows are gone
|
|
51
|
+
expect(countRows("campaigns", "id = ?", [1])).toBe(0);
|
|
52
|
+
expect(countRows("actions", "campaign_id = ?", [1])).toBe(0);
|
|
53
|
+
expect(countRows("action_versions", "action_id IN (SELECT id FROM actions WHERE campaign_id = ?)", [1])).toBe(0);
|
|
54
|
+
expect(countRows("action_target_people", "action_id IN (SELECT id FROM actions WHERE campaign_id = ?)", [1])).toBe(0);
|
|
55
|
+
expect(countRows("action_results", "action_version_id IN (SELECT av.id FROM action_versions av JOIN actions a ON av.action_id = a.id WHERE a.campaign_id = ?)", [1])).toBe(0);
|
|
56
|
+
expect(countRows("action_result_flags", "action_result_id IN (SELECT ar.id FROM action_results ar JOIN action_versions av ON ar.action_version_id = av.id JOIN actions a ON av.action_id = a.id WHERE a.campaign_id = ?)", [1])).toBe(0);
|
|
57
|
+
expect(countRows("action_result_messages", "action_result_id IN (SELECT ar.id FROM action_results ar JOIN action_versions av ON ar.action_version_id = av.id JOIN actions a ON av.action_id = a.id WHERE a.campaign_id = ?)", [1])).toBe(0);
|
|
58
|
+
expect(countRows("person_in_campaigns_history", "campaign_id = ?", [1])).toBe(0);
|
|
59
|
+
expect(countRows("campaign_versions", "campaign_id = ?", [1])).toBe(0);
|
|
60
|
+
// Verify action_configs for campaign 1 are deleted
|
|
61
|
+
// (config ID 1 belonged to campaign 1's action)
|
|
62
|
+
expect(countRows("action_configs", "id = ?", [1])).toBe(0);
|
|
63
|
+
// Verify exclude list chain is cleaned up
|
|
64
|
+
expect(countRows("collection_people_versions", "id IN (1, 2)", [])).toBe(0);
|
|
65
|
+
expect(countRows("collections", "id IN (1, 2)", [])).toBe(0);
|
|
66
|
+
});
|
|
67
|
+
it("throws CampaignNotFoundError for non-existent campaign", () => {
|
|
68
|
+
expect(() => repo.deleteCampaign(9999)).toThrow(CampaignNotFoundError);
|
|
69
|
+
});
|
|
70
|
+
it("does not affect other campaigns", () => {
|
|
71
|
+
// Campaign 2 should still exist after deleting campaign 1
|
|
72
|
+
expect(countRows("campaigns", "id = ?", [2])).toBe(1);
|
|
73
|
+
expect(countRows("actions", "campaign_id = ?", [2])).toBeGreaterThan(0);
|
|
74
|
+
expect(countRows("action_versions", "action_id IN (SELECT id FROM actions WHERE campaign_id = ?)", [2])).toBeGreaterThan(0);
|
|
75
|
+
expect(countRows("action_configs", "id = ?", [2])).toBe(1);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
//# sourceMappingURL=campaign-hard-delete.integration.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"campaign-hard-delete.integration.test.js","sourceRoot":"","sources":["../../../src/db/repositories/campaign-hard-delete.integration.test.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,oCAAoC;AAEpC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAGnE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAErD,QAAQ,CAAC,iDAAiD,EAAE,GAAG,EAAE;IAC/D,IAAI,KAAmB,CAAC;IACxB,IAAI,MAAsB,CAAC;IAC3B,IAAI,IAAwB,CAAC;IAE7B,SAAS,CAAC,GAAG,EAAE;QACb,kDAAkD;QAClD,KAAK,GAAG,WAAW,EAAE,CAAC;QAEtB,8EAA8E;QAC9E,KAAK,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QACvC,MAAM,QAAQ,GAAG,KAAK;aACnB,OAAO,CAAC,qBAAqB,CAAC;aAC9B,GAAG,EAA8B,CAAC;QACrC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAEtC,6CAA6C;QAC7C,qEAAqE;QACrE,KAAK,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAC;QAE9D,6DAA6D;QAC7D,MAAM,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,EAA+B,CAAC;QAChF,IAAI,GAAG,IAAI,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,GAAG,EAAE;QACZ,KAAK,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,SAAS,SAAS,CAAC,KAAa,EAAE,KAAa,EAAE,MAAgB;QAC/D,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CACvB,+BAA+B,KAAK,UAAU,KAAK,EAAE,CACtD,CAAC,GAAG,CAAC,GAAG,MAAM,CAAoB,CAAC;QACpC,OAAO,GAAG,CAAC,GAAG,CAAC;IACjB,CAAC;IAED,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,4DAA4D;QAC5D,MAAM,CAAC,SAAS,CAAC,WAAW,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACtD,MAAM,CAAC,SAAS,CAAC,SAAS,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QACxE,MAAM,CAAC,SAAS,CAAC,iBAAiB,EAAE,6DAA6D,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC5H,MAAM,CAAC,SAAS,CAAC,gBAAgB,EAAE,4HAA4H,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC1L,MAAM,CAAC,SAAS,CAAC,sBAAsB,EAAE,6DAA6D,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QACjI,MAAM,CAAC,SAAS,CAAC,gBAAgB,EAAE,2HAA2H,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QACzL,MAAM,CAAC,SAAS,CAAC,6BAA6B,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC5F,MAAM,CAAC,SAAS,CAAC,mBAAmB,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAElF,oCAAoC;QACpC,MAAM,gBAAgB,GAAG,SAAS,CAChC,4BAA4B,EAC5B,kRAAkR,EAClR,CAAC,CAAC,EAAE,CAAC,CAAC,CACP,CAAC;QACF,MAAM,CAAC,gBAAgB,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAE5C,yDAAyD;QACzD,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;QAEvB,6CAA6C;QAC7C,MAAM,CAAC,SAAS,CAAC,WAAW,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACtD,MAAM,CAAC,SAAS,CAAC,SAAS,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC7D,MAAM,CAAC,SAAS,CAAC,iBAAiB,EAAE,6DAA6D,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjH,MAAM,CAAC,SAAS,CAAC,sBAAsB,EAAE,6DAA6D,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACtH,MAAM,CAAC,SAAS,CAAC,gBAAgB,EAAE,2HAA2H,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC9K,MAAM,CAAC,SAAS,CAAC,qBAAqB,EAAE,iLAAiL,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACzO,MAAM,CAAC,SAAS,CAAC,wBAAwB,EAAE,iLAAiL,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5O,MAAM,CAAC,SAAS,CAAC,6BAA6B,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjF,MAAM,CAAC,SAAS,CAAC,mBAAmB,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAEvE,mDAAmD;QACnD,gDAAgD;QAChD,MAAM,CAAC,SAAS,CAAC,gBAAgB,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAE3D,0CAA0C;QAC1C,MAAM,CAAC,SAAS,CACd,4BAA4B,EAC5B,cAAc,EACd,EAAE,CACH,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACX,MAAM,CAAC,SAAS,CACd,aAAa,EACb,cAAc,EACd,EAAE,CACH,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACb,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,0DAA0D;QAC1D,MAAM,CAAC,SAAS,CAAC,WAAW,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACtD,MAAM,CAAC,SAAS,CAAC,SAAS,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QACxE,MAAM,CAAC,SAAS,CAAC,iBAAiB,EAAE,6DAA6D,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC5H,MAAM,CAAC,SAAS,CAAC,gBAAgB,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -63,6 +63,20 @@ export declare class CampaignRepository {
|
|
|
63
63
|
* @throws {CampaignNotFoundError} if no campaign exists with the given ID.
|
|
64
64
|
*/
|
|
65
65
|
updateCampaign(campaignId: number, updates: CampaignUpdateConfig): Campaign;
|
|
66
|
+
/**
|
|
67
|
+
* Hard-delete a campaign and all related rows from the database.
|
|
68
|
+
*
|
|
69
|
+
* Removes all data across: action_result_flags, action_result_messages,
|
|
70
|
+
* action_results, action_target_people, person_in_campaigns_history,
|
|
71
|
+
* campaign_versions, action_versions, actions, action_configs,
|
|
72
|
+
* and the exclude list chain (collection_people, collection_people_versions,
|
|
73
|
+
* collections).
|
|
74
|
+
*
|
|
75
|
+
* Callers must verify the campaign is not active before calling this method.
|
|
76
|
+
*
|
|
77
|
+
* @throws {CampaignNotFoundError} if no campaign exists with the given ID.
|
|
78
|
+
*/
|
|
79
|
+
deleteCampaign(campaignId: number): void;
|
|
66
80
|
/**
|
|
67
81
|
* Prepare write statements lazily (only when needed).
|
|
68
82
|
* This avoids issues when the client is opened in read-only mode.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"campaign.d.ts","sourceRoot":"","sources":["../../../src/db/repositories/campaign.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAGV,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,EAC1B,QAAQ,EACR,cAAc,EACd,mBAAmB,EAEnB,aAAa,EACb,eAAe,EACf,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,EACzB,oBAAoB,EACrB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAuFnD;;;;;;;;;GASG;AACH,qBAAa,kBAAkB;
|
|
1
|
+
{"version":3,"file":"campaign.d.ts","sourceRoot":"","sources":["../../../src/db/repositories/campaign.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAGV,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,EAC1B,QAAQ,EACR,cAAc,EACd,mBAAmB,EAEnB,aAAa,EACb,eAAe,EACf,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,EACzB,oBAAoB,EACrB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAuFnD;;;;;;;;;GASG;AACH,qBAAa,kBAAkB;IAqCjB,OAAO,CAAC,QAAQ,CAAC,MAAM;IApCnC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC;IACnC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAC;IACtC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC;IACjC,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAC;IACxC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IAGhC,OAAO,CAAC,eAAe,CA2BP;gBAEa,MAAM,EAAE,cAAc;IAuDnD;;OAEG;IACH,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,eAAe,EAAE;IAoBpE;;;;OAIG;IACH,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ;IAiBzC;;;;OAIG;IACH,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,EAAE;IAoCxD;;;;OAIG;IACH,UAAU,CACR,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,iBAAsB,GAC9B,oBAAoB,EAAE;IA+BzB;;;;;OAKG;IACH,UAAU,CACR,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,yBAA8B,GACtC;QAAE,MAAM,EAAE,mBAAmB,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;IAsEnD;;;;OAIG;IACH,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa;IAKnD;;;;OAIG;IACH,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,GAAG,QAAQ;IAyB3E;;;;;;;;;;;;OAYG;IACH,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAoExC;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAmJ1B;;;;;;OAMG;IACH,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAKpC;;;;;;;;OAQG;IACH,wBAAwB,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;IAmCvE;;;;;;;OAOG;IACH,SAAS,CACP,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,oBAAoB,EAClC,WAAW,EAAE,MAAM,GAClB,cAAc;IA0EjB;;;;;;;;OAQG;IACH,YAAY,CACV,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,0BAA0B,GAClC,cAAc;IAuEjB;;;;;;;;;;OAUG;IACH,gBAAgB,CACd,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EAAE,GAClB;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE;CAoD5B"}
|
|
@@ -278,6 +278,69 @@ export class CampaignRepository {
|
|
|
278
278
|
}
|
|
279
279
|
return this.getCampaign(campaignId);
|
|
280
280
|
}
|
|
281
|
+
/**
|
|
282
|
+
* Hard-delete a campaign and all related rows from the database.
|
|
283
|
+
*
|
|
284
|
+
* Removes all data across: action_result_flags, action_result_messages,
|
|
285
|
+
* action_results, action_target_people, person_in_campaigns_history,
|
|
286
|
+
* campaign_versions, action_versions, actions, action_configs,
|
|
287
|
+
* and the exclude list chain (collection_people, collection_people_versions,
|
|
288
|
+
* collections).
|
|
289
|
+
*
|
|
290
|
+
* Callers must verify the campaign is not active before calling this method.
|
|
291
|
+
*
|
|
292
|
+
* @throws {CampaignNotFoundError} if no campaign exists with the given ID.
|
|
293
|
+
*/
|
|
294
|
+
deleteCampaign(campaignId) {
|
|
295
|
+
// Verify campaign exists
|
|
296
|
+
this.getCampaign(campaignId);
|
|
297
|
+
const stmts = this.getWriteStatements();
|
|
298
|
+
const { db } = this.client;
|
|
299
|
+
// Collect IDs needed after FK-referencing rows are deleted
|
|
300
|
+
const configRows = stmts.selectConfigIds.all(campaignId);
|
|
301
|
+
const configIds = configRows.map((r) => r.config_id);
|
|
302
|
+
const excludeRows = stmts.selectExcludeListInfo.all(campaignId, campaignId);
|
|
303
|
+
const excludeVersionIds = excludeRows.map((r) => r.version_id);
|
|
304
|
+
const excludeCollectionIds = [...new Set(excludeRows.map((r) => r.collection_id))];
|
|
305
|
+
db.exec("BEGIN");
|
|
306
|
+
try {
|
|
307
|
+
// 1. Delete result children (FK: action_result_flags/messages → action_results)
|
|
308
|
+
stmts.deleteResultFlagsByCampaign.run(campaignId);
|
|
309
|
+
stmts.deleteResultMessagesByCampaign.run(campaignId);
|
|
310
|
+
// 2. Delete results (FK: action_results → action_versions)
|
|
311
|
+
stmts.deleteResultsByCampaign.run(campaignId);
|
|
312
|
+
// 3. Delete target people (FK: action_target_people → actions)
|
|
313
|
+
stmts.deleteTargetPeopleByCampaign.run(campaignId);
|
|
314
|
+
// 4. Delete campaign history
|
|
315
|
+
stmts.deleteCampaignHistory.run(campaignId);
|
|
316
|
+
// 5. Delete collection_people for exclude lists
|
|
317
|
+
stmts.deleteCollectionPeopleByCampaign.run(campaignId, campaignId);
|
|
318
|
+
// 6. Delete campaign_versions (FK → campaigns)
|
|
319
|
+
stmts.deleteCampaignVersions.run(campaignId);
|
|
320
|
+
// 7. Delete action_versions (FK → actions)
|
|
321
|
+
stmts.deleteActionVersionsByCampaign.run(campaignId);
|
|
322
|
+
// 8. Delete actions (FK → campaigns)
|
|
323
|
+
stmts.deleteActionsByCampaign.run(campaignId);
|
|
324
|
+
// 9. Delete action_configs (collected before transaction)
|
|
325
|
+
for (const configId of configIds) {
|
|
326
|
+
stmts.deleteActionConfig.run(configId);
|
|
327
|
+
}
|
|
328
|
+
// 10. Delete exclude list versions and collections
|
|
329
|
+
for (const versionId of excludeVersionIds) {
|
|
330
|
+
stmts.deleteCollectionPeopleVersion.run(versionId);
|
|
331
|
+
}
|
|
332
|
+
for (const collectionId of excludeCollectionIds) {
|
|
333
|
+
stmts.deleteCollection.run(collectionId);
|
|
334
|
+
}
|
|
335
|
+
// 11. Delete the campaign itself
|
|
336
|
+
stmts.deleteCampaign.run(campaignId);
|
|
337
|
+
db.exec("COMMIT");
|
|
338
|
+
}
|
|
339
|
+
catch (e) {
|
|
340
|
+
db.exec("ROLLBACK");
|
|
341
|
+
throw e;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
281
344
|
/**
|
|
282
345
|
* Prepare write statements lazily (only when needed).
|
|
283
346
|
* This avoids issues when the client is opened in read-only mode.
|
|
@@ -309,6 +372,68 @@ export class CampaignRepository {
|
|
|
309
372
|
VALUES (?, ?, ?, 1, ?, STRFTIME('%Y-%m-%dT%H:%M:%fZ', 'NOW'))`),
|
|
310
373
|
countTarget: db.prepare(`SELECT COUNT(*) AS cnt FROM action_target_people
|
|
311
374
|
WHERE action_id = ? AND person_id = ?`),
|
|
375
|
+
deleteResultFlagsByCampaign: db.prepare(`DELETE FROM action_result_flags
|
|
376
|
+
WHERE action_result_id IN (
|
|
377
|
+
SELECT ar.id FROM action_results ar
|
|
378
|
+
JOIN action_versions av ON ar.action_version_id = av.id
|
|
379
|
+
JOIN actions a ON av.action_id = a.id
|
|
380
|
+
WHERE a.campaign_id = ?
|
|
381
|
+
)`),
|
|
382
|
+
deleteResultMessagesByCampaign: db.prepare(`DELETE FROM action_result_messages
|
|
383
|
+
WHERE action_result_id IN (
|
|
384
|
+
SELECT ar.id FROM action_results ar
|
|
385
|
+
JOIN action_versions av ON ar.action_version_id = av.id
|
|
386
|
+
JOIN actions a ON av.action_id = a.id
|
|
387
|
+
WHERE a.campaign_id = ?
|
|
388
|
+
)`),
|
|
389
|
+
deleteResultsByCampaign: db.prepare(`DELETE FROM action_results
|
|
390
|
+
WHERE action_version_id IN (
|
|
391
|
+
SELECT av.id FROM action_versions av
|
|
392
|
+
JOIN actions a ON av.action_id = a.id
|
|
393
|
+
WHERE a.campaign_id = ?
|
|
394
|
+
)`),
|
|
395
|
+
deleteTargetPeopleByCampaign: db.prepare(`DELETE FROM action_target_people
|
|
396
|
+
WHERE action_id IN (
|
|
397
|
+
SELECT id FROM actions WHERE campaign_id = ?
|
|
398
|
+
)`),
|
|
399
|
+
deleteCampaignHistory: db.prepare(`DELETE FROM person_in_campaigns_history
|
|
400
|
+
WHERE campaign_id = ?`),
|
|
401
|
+
deleteCollectionPeopleByCampaign: db.prepare(`DELETE FROM collection_people
|
|
402
|
+
WHERE collection_id IN (
|
|
403
|
+
SELECT cpv.collection_id FROM collection_people_versions cpv
|
|
404
|
+
WHERE cpv.id IN (
|
|
405
|
+
SELECT av.exclude_list_id FROM action_versions av
|
|
406
|
+
JOIN actions a ON av.action_id = a.id
|
|
407
|
+
WHERE a.campaign_id = ? AND av.exclude_list_id IS NOT NULL
|
|
408
|
+
UNION
|
|
409
|
+
SELECT cv.exclude_list_id FROM campaign_versions cv
|
|
410
|
+
WHERE cv.campaign_id = ? AND cv.exclude_list_id IS NOT NULL
|
|
411
|
+
)
|
|
412
|
+
)`),
|
|
413
|
+
deleteCampaignVersions: db.prepare(`DELETE FROM campaign_versions WHERE campaign_id = ?`),
|
|
414
|
+
selectExcludeListInfo: db.prepare(`SELECT cpv.id AS version_id, cpv.collection_id
|
|
415
|
+
FROM collection_people_versions cpv
|
|
416
|
+
WHERE cpv.id IN (
|
|
417
|
+
SELECT av.exclude_list_id FROM action_versions av
|
|
418
|
+
JOIN actions a ON av.action_id = a.id
|
|
419
|
+
WHERE a.campaign_id = ? AND av.exclude_list_id IS NOT NULL
|
|
420
|
+
UNION
|
|
421
|
+
SELECT cv.exclude_list_id FROM campaign_versions cv
|
|
422
|
+
WHERE cv.campaign_id = ? AND cv.exclude_list_id IS NOT NULL
|
|
423
|
+
)`),
|
|
424
|
+
deleteActionVersionsByCampaign: db.prepare(`DELETE FROM action_versions
|
|
425
|
+
WHERE action_id IN (
|
|
426
|
+
SELECT id FROM actions WHERE campaign_id = ?
|
|
427
|
+
)`),
|
|
428
|
+
selectConfigIds: db.prepare(`SELECT DISTINCT av.config_id
|
|
429
|
+
FROM action_versions av
|
|
430
|
+
JOIN actions a ON av.action_id = a.id
|
|
431
|
+
WHERE a.campaign_id = ?`),
|
|
432
|
+
deleteActionsByCampaign: db.prepare(`DELETE FROM actions WHERE campaign_id = ?`),
|
|
433
|
+
deleteActionConfig: db.prepare(`DELETE FROM action_configs WHERE id = ?`),
|
|
434
|
+
deleteCollectionPeopleVersion: db.prepare(`DELETE FROM collection_people_versions WHERE id = ?`),
|
|
435
|
+
deleteCollection: db.prepare(`DELETE FROM collections WHERE id = ?`),
|
|
436
|
+
deleteCampaign: db.prepare(`DELETE FROM campaigns WHERE id = ?`),
|
|
312
437
|
};
|
|
313
438
|
return this.writeStatements;
|
|
314
439
|
}
|