trycourier 6.6.0 → 6.7.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +1 -1
- data/lib/courier/models/create_journey_request.rb +16 -1
- data/lib/courier/models/journey_response.rb +14 -1
- data/lib/courier/models/topic_digest_request.rb +23 -11
- data/lib/courier/models/topic_digest_schedule_request.rb +6 -0
- data/lib/courier/models/topic_digest_schedule_response.rb +8 -0
- data/lib/courier/models/users/topic_preference.rb +7 -2
- data/lib/courier/models/workspace_preference_topic_create_request.rb +87 -5
- data/lib/courier/resources/journeys.rb +9 -2
- data/lib/courier/resources/workspace_preferences/topics.rb +1 -1
- data/lib/courier/version.rb +1 -1
- data/rbi/courier/models/create_journey_request.rbi +19 -0
- data/rbi/courier/models/journey_response.rbi +12 -0
- data/rbi/courier/models/topic_digest_request.rbi +43 -12
- data/rbi/courier/models/topic_digest_schedule_request.rbi +6 -0
- data/rbi/courier/models/topic_digest_schedule_response.rbi +16 -0
- data/rbi/courier/models/users/topic_preference.rbi +14 -4
- data/rbi/courier/models/workspace_preference_topic_create_request.rbi +155 -8
- data/rbi/courier/resources/journeys.rbi +14 -0
- data/rbi/courier/resources/workspace_preferences/topics.rbi +11 -3
- data/sig/courier/models/create_journey_request.rbs +7 -0
- data/sig/courier/models/journey_response.rbs +5 -0
- data/sig/courier/models/topic_digest_request.rbs +9 -5
- data/sig/courier/models/workspace_preference_topic_create_request.rbs +49 -4
- data/sig/courier/resources/journeys.rbs +2 -0
- data/sig/courier/resources/workspace_preferences/topics.rbs +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c665c6475e9af26f67197651f5599bac614c384d86086dd974ed410005ab407d
|
|
4
|
+
data.tar.gz: 4e037944cf9a6cd6346aa647a5f097e3cd04a5be4676b1b0bc392bed497be990
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 244553b3412e7041264a0ceb8e355e89fcf67b8f00098cfab876e00408c39089e4e6bf0031693e7ab49749e43e74ac4fbf6b7ca16f8e91c00f63bc061157725e
|
|
7
|
+
data.tar.gz: 60ce9fc7ae0c32289b97208cad86e4c97c0ca4b44f6d7f0d10967e72ea4922bc19104a95a46c6c7ee146372a57ba9a255b773a26f9d2bad505f3e905c84d66e9
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [6.7.0](https://github.com/trycourier/courier-ruby/compare/v6.6.0...v6.7.0) (2026-09-22)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **api:** add cancelation_token to journey create, replace, and get ([#158](https://github.com/trycourier/courier-ruby/issues/158)) ([ec47880](https://github.com/trycourier/courier-ruby/commit/ec47880a79aaefde00dedcbdc84527ff234a99f2))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Documentation
|
|
12
|
+
|
|
13
|
+
* **api:** correct the digest schedule contract, and make `schedules` optional ([#156](https://github.com/trycourier/courier-ruby/issues/156)) ([8578d77](https://github.com/trycourier/courier-ruby/commit/8578d77848275105865c1b29b799f2cc6621987e))
|
|
14
|
+
|
|
3
15
|
## [6.6.0](https://github.com/trycourier/courier-ruby/compare/v6.5.0...v6.6.0) (2026-09-15)
|
|
4
16
|
|
|
5
17
|
|
data/README.md
CHANGED
|
@@ -13,6 +13,16 @@ module Courier
|
|
|
13
13
|
# @return [Array<Courier::Models::JourneyAPIInvokeTriggerNode, Courier::Models::JourneySegmentTriggerNode, Courier::Models::JourneyAudienceTriggerNode, Courier::Models::JourneyWebhookTriggerNode, Courier::Models::JourneySendNode, Courier::Models::JourneyDelayDurationNode, Courier::Models::JourneyDelayUntilNode, Courier::Models::JourneyFetchGetDeleteNode, Courier::Models::JourneyFetchPostPutNode, Courier::Models::JourneyAINode, Courier::Models::JourneyThrottleStaticNode, Courier::Models::JourneyThrottleDynamicNode, Courier::Models::JourneyNode::JourneyBatchNode, Courier::Models::JourneyNode::JourneyAddToDigestNode, Courier::Models::JourneyExitNode, Courier::Models::JourneyNode::JourneyBranchNode>]
|
|
14
14
|
required :nodes, -> { Courier::Internal::Type::ArrayOf[union: Courier::JourneyNode] }
|
|
15
15
|
|
|
16
|
+
# @!attribute cancelation_token
|
|
17
|
+
# Cancelation token stored on the journey definition. It tags every run the
|
|
18
|
+
# journey creates so that `POST /journeys/cancel` can later cancel those runs by
|
|
19
|
+
# token. Accepts a templated string such as `order-{{data.order_id}}`, which is
|
|
20
|
+
# resolved per run when the journey is invoked. On a replace, omitting this field
|
|
21
|
+
# preserves any existing token and sending a value replaces it.
|
|
22
|
+
#
|
|
23
|
+
# @return [String, nil]
|
|
24
|
+
optional :cancelation_token, String
|
|
25
|
+
|
|
16
26
|
# @!attribute enabled
|
|
17
27
|
#
|
|
18
28
|
# @return [Boolean, nil]
|
|
@@ -24,13 +34,18 @@ module Courier
|
|
|
24
34
|
# @return [Symbol, Courier::Models::JourneyState, nil]
|
|
25
35
|
optional :state, enum: -> { Courier::JourneyState }
|
|
26
36
|
|
|
27
|
-
# @!method initialize(name:, nodes:, enabled: nil, state: nil)
|
|
37
|
+
# @!method initialize(name:, nodes:, cancelation_token: nil, enabled: nil, state: nil)
|
|
38
|
+
# Some parameter documentations has been truncated, see
|
|
39
|
+
# {Courier::Models::CreateJourneyRequest} for more details.
|
|
40
|
+
#
|
|
28
41
|
# Request body for creating a journey.
|
|
29
42
|
#
|
|
30
43
|
# @param name [String]
|
|
31
44
|
#
|
|
32
45
|
# @param nodes [Array<Courier::Models::JourneyAPIInvokeTriggerNode, Courier::Models::JourneySegmentTriggerNode, Courier::Models::JourneyAudienceTriggerNode, Courier::Models::JourneyWebhookTriggerNode, Courier::Models::JourneySendNode, Courier::Models::JourneyDelayDurationNode, Courier::Models::JourneyDelayUntilNode, Courier::Models::JourneyFetchGetDeleteNode, Courier::Models::JourneyFetchPostPutNode, Courier::Models::JourneyAINode, Courier::Models::JourneyThrottleStaticNode, Courier::Models::JourneyThrottleDynamicNode, Courier::Models::JourneyNode::JourneyBatchNode, Courier::Models::JourneyNode::JourneyAddToDigestNode, Courier::Models::JourneyExitNode, Courier::Models::JourneyNode::JourneyBranchNode>]
|
|
33
46
|
#
|
|
47
|
+
# @param cancelation_token [String] Cancelation token stored on the journey definition. It tags every run the journe
|
|
48
|
+
#
|
|
34
49
|
# @param enabled [Boolean]
|
|
35
50
|
#
|
|
36
51
|
# @param state [Symbol, Courier::Models::JourneyState] Lifecycle state of a journey.
|
|
@@ -9,6 +9,14 @@ module Courier
|
|
|
9
9
|
# @return [String]
|
|
10
10
|
required :id, String
|
|
11
11
|
|
|
12
|
+
# @!attribute cancelation_token
|
|
13
|
+
# The journey cancelation token, or null when none is set. A token authored in the
|
|
14
|
+
# dashboard is returned in its raw templated form, such as
|
|
15
|
+
# `order-{{data.order_id}}`, so it can be read back and asserted.
|
|
16
|
+
#
|
|
17
|
+
# @return [String, nil]
|
|
18
|
+
required :cancelation_token, String, nil?: true
|
|
19
|
+
|
|
12
20
|
# @!attribute created
|
|
13
21
|
#
|
|
14
22
|
# @return [Integer, nil]
|
|
@@ -55,11 +63,16 @@ module Courier
|
|
|
55
63
|
# @return [String, nil]
|
|
56
64
|
required :updater, String, nil?: true
|
|
57
65
|
|
|
58
|
-
# @!method initialize(id:, created:, creator:, enabled:, name:, nodes:, published:, state:, updated:, updater:)
|
|
66
|
+
# @!method initialize(id:, cancelation_token:, created:, creator:, enabled:, name:, nodes:, published:, state:, updated:, updater:)
|
|
67
|
+
# Some parameter documentations has been truncated, see
|
|
68
|
+
# {Courier::Models::JourneyResponse} for more details.
|
|
69
|
+
#
|
|
59
70
|
# A journey, with its current draft or published nodes and metadata.
|
|
60
71
|
#
|
|
61
72
|
# @param id [String]
|
|
62
73
|
#
|
|
74
|
+
# @param cancelation_token [String, nil] The journey cancelation token, or null when none is set. A token authored in the
|
|
75
|
+
#
|
|
63
76
|
# @param created [Integer, nil]
|
|
64
77
|
#
|
|
65
78
|
# @param creator [String, nil]
|
|
@@ -3,14 +3,6 @@
|
|
|
3
3
|
module Courier
|
|
4
4
|
module Models
|
|
5
5
|
class TopicDigestRequest < Courier::Internal::Type::BaseModel
|
|
6
|
-
# @!attribute schedules
|
|
7
|
-
# The cadences this digest delivers on. At least one is required: a digest with no
|
|
8
|
-
# schedule collects events into an instance that can never fire. Omitting the key
|
|
9
|
-
# on a replace leaves stored schedules untouched; sending `[]` is a `400`.
|
|
10
|
-
#
|
|
11
|
-
# @return [Array<Courier::Models::TopicDigestScheduleRequest>]
|
|
12
|
-
required :schedules, -> { Courier::Internal::Type::ArrayOf[Courier::TopicDigestScheduleRequest] }
|
|
13
|
-
|
|
14
6
|
# @!attribute template_id
|
|
15
7
|
# The notification template that renders the digest. A digest with no template
|
|
16
8
|
# collects nothing, so this is required.
|
|
@@ -31,13 +23,33 @@ module Courier
|
|
|
31
23
|
# @return [Array<Courier::Models::TopicDigestCategory>, nil]
|
|
32
24
|
optional :categories, -> { Courier::Internal::Type::ArrayOf[Courier::TopicDigestCategory] }
|
|
33
25
|
|
|
26
|
+
# @!attribute schedules
|
|
27
|
+
# The cadences this digest delivers on.
|
|
28
|
+
#
|
|
29
|
+
# The array replaces the stored schedules wholesale, so a schedule you leave out
|
|
30
|
+
# of it is deleted along with its delivery rule. Omit the key entirely to leave
|
|
31
|
+
# the stored schedules untouched — useful for changing `template_id` or
|
|
32
|
+
# `categories` without restating every schedule.
|
|
33
|
+
#
|
|
34
|
+
# A digest must end up with at least one schedule, because one with none collects
|
|
35
|
+
# events into an instance that can never fire. So sending `[]` is always a `400`,
|
|
36
|
+
# and so is omitting the key on a topic that has no schedules stored yet.
|
|
37
|
+
#
|
|
38
|
+
# On **create** the key is required outright: a topic being created has nothing
|
|
39
|
+
# stored to leave alone, and the topic row is written before its digest, so
|
|
40
|
+
# rejecting it any later would leave the topic behind and let a retry duplicate
|
|
41
|
+
# it.
|
|
42
|
+
#
|
|
43
|
+
# @return [Array<Courier::Models::TopicDigestScheduleRequest>, nil]
|
|
44
|
+
optional :schedules, -> { Courier::Internal::Type::ArrayOf[Courier::TopicDigestScheduleRequest] }
|
|
45
|
+
|
|
34
46
|
# @!attribute trigger_empty
|
|
35
47
|
# Whether to deliver the digest even when nothing was collected.
|
|
36
48
|
#
|
|
37
49
|
# @return [Boolean, nil]
|
|
38
50
|
optional :trigger_empty, Courier::Internal::Type::Boolean
|
|
39
51
|
|
|
40
|
-
# @!method initialize(
|
|
52
|
+
# @!method initialize(template_id:, audience_id: nil, categories: nil, schedules: nil, trigger_empty: nil)
|
|
41
53
|
# Some parameter documentations has been truncated, see
|
|
42
54
|
# {Courier::Models::TopicDigestRequest} for more details.
|
|
43
55
|
#
|
|
@@ -49,14 +61,14 @@ module Courier
|
|
|
49
61
|
# `schedules: []` is rejected, because both states are un-deliverable rather than
|
|
50
62
|
# merely off.
|
|
51
63
|
#
|
|
52
|
-
# @param schedules [Array<Courier::Models::TopicDigestScheduleRequest>] The cadences this digest delivers on. At least one is required: a digest with no
|
|
53
|
-
#
|
|
54
64
|
# @param template_id [String] The notification template that renders the digest. A digest with no template col
|
|
55
65
|
#
|
|
56
66
|
# @param audience_id [String] Optional audience the digest is scoped to.
|
|
57
67
|
#
|
|
58
68
|
# @param categories [Array<Courier::Models::TopicDigestCategory>] Retention rules per category key. Defaults to a single `digest` category retaini
|
|
59
69
|
#
|
|
70
|
+
# @param schedules [Array<Courier::Models::TopicDigestScheduleRequest>] The cadences this digest delivers on.
|
|
71
|
+
#
|
|
60
72
|
# @param trigger_empty [Boolean] Whether to deliver the digest even when nothing was collected.
|
|
61
73
|
end
|
|
62
74
|
end
|
|
@@ -71,6 +71,12 @@ module Courier
|
|
|
71
71
|
# `schedules` array is a full replacement, so a stored schedule absent from it is
|
|
72
72
|
# deleted along with its delivery rule.
|
|
73
73
|
#
|
|
74
|
+
# Updating by `schedule_id` replaces that schedule rather than merging into it:
|
|
75
|
+
# any field you leave out is cleared. Two of those change delivery silently — an
|
|
76
|
+
# omitted `timezone` reverts the schedule to UTC, and an omitted `is_default` can
|
|
77
|
+
# leave the topic with no default schedule, which is what recipients who have not
|
|
78
|
+
# chosen one fall back to. Restate every field you want to keep.
|
|
79
|
+
#
|
|
74
80
|
# @param frequency [Symbol, Courier::Models::DigestFrequency] How often a digest is delivered. `instant` delivers immediately without batching
|
|
75
81
|
#
|
|
76
82
|
# @param day_of_month [Integer] Required when `frequency` is `monthly`.
|
|
@@ -7,6 +7,14 @@ module Courier
|
|
|
7
7
|
# The schedule's identifier, assigned by the server. This is the value the
|
|
8
8
|
# `/digests/schedules/{schedule_id}` endpoints are keyed by.
|
|
9
9
|
#
|
|
10
|
+
# Two formats are in circulation and only one is safe to drop into a URL.
|
|
11
|
+
# Schedules created through the API are `sch_01m26xfcn3endt3nxy4e2kx2rh` and need
|
|
12
|
+
# no encoding. Schedules created in the Preferences Editor before that format are
|
|
13
|
+
# `sch/{uuid}` and contain a literal `/`, so they must be URL-encoded as
|
|
14
|
+
# `sch%2F{uuid}` — unencoded, the path does not match the route and the response
|
|
15
|
+
# is a bare `404` that reads like a broken endpoint. Existing ids are never
|
|
16
|
+
# migrated.
|
|
17
|
+
#
|
|
10
18
|
# @return [String]
|
|
11
19
|
required :schedule_id, String
|
|
12
20
|
|
|
@@ -44,8 +44,13 @@ module Courier
|
|
|
44
44
|
# @!attribute digest_schedule_id
|
|
45
45
|
# The digest schedule this recipient is on for the topic. Omitted rather than null
|
|
46
46
|
# when they have not chosen one, in which case the topic's default schedule
|
|
47
|
-
# applies. Ids come from the topic's digest configuration
|
|
48
|
-
#
|
|
47
|
+
# applies. Ids come from the topic's digest configuration, returned on any read of
|
|
48
|
+
# the topic.
|
|
49
|
+
#
|
|
50
|
+
# Also omitted when the schedule they chose has since been deleted or disabled:
|
|
51
|
+
# the recipient is on the topic's default from that point on, which is how a send
|
|
52
|
+
# already resolves it, so the field reports what would actually be used rather
|
|
53
|
+
# than a dead id.
|
|
49
54
|
#
|
|
50
55
|
# @return [String, nil]
|
|
51
56
|
optional :digest_schedule_id, String
|
|
@@ -33,16 +33,21 @@ module Courier
|
|
|
33
33
|
optional :description, String, nil?: true
|
|
34
34
|
|
|
35
35
|
# @!attribute digest
|
|
36
|
-
# A topic's digest
|
|
37
|
-
# delivers on, and how collected events are
|
|
36
|
+
# A topic's digest, as supplied when the topic itself is created: the template
|
|
37
|
+
# that renders it, the cadences it delivers on, and how collected events are
|
|
38
|
+
# retained.
|
|
39
|
+
#
|
|
40
|
+
# Identical to `TopicDigestRequest`, which a replace uses, except that `schedules`
|
|
41
|
+
# is required — a topic being created has no stored schedules for an absent key to
|
|
42
|
+
# leave alone.
|
|
38
43
|
#
|
|
39
44
|
# Send `null` for the whole object to turn a digest off, which unlinks the
|
|
40
45
|
# template and removes its schedules. There is no `enabled` flag, and
|
|
41
46
|
# `schedules: []` is rejected, because both states are un-deliverable rather than
|
|
42
47
|
# merely off.
|
|
43
48
|
#
|
|
44
|
-
# @return [Courier::Models::
|
|
45
|
-
optional :digest, -> { Courier::
|
|
49
|
+
# @return [Courier::Models::WorkspacePreferenceTopicCreateRequest::Digest, nil]
|
|
50
|
+
optional :digest, -> { Courier::WorkspacePreferenceTopicCreateRequest::Digest }, nil?: true
|
|
46
51
|
|
|
47
52
|
# @!attribute include_unsubscribe_header
|
|
48
53
|
# Whether to include a list-unsubscribe header on emails for this topic.
|
|
@@ -78,7 +83,7 @@ module Courier
|
|
|
78
83
|
#
|
|
79
84
|
# @param description [String, nil] Optional description shown under the topic on the hosted preferences page.
|
|
80
85
|
#
|
|
81
|
-
# @param digest [Courier::Models::
|
|
86
|
+
# @param digest [Courier::Models::WorkspacePreferenceTopicCreateRequest::Digest, nil] A topic's digest, as supplied when the topic itself is created: the template tha
|
|
82
87
|
#
|
|
83
88
|
# @param include_unsubscribe_header [Boolean, nil] Whether to include a list-unsubscribe header on emails for this topic.
|
|
84
89
|
#
|
|
@@ -110,6 +115,83 @@ module Courier
|
|
|
110
115
|
# @!method self.values
|
|
111
116
|
# @return [Array<Symbol>]
|
|
112
117
|
end
|
|
118
|
+
|
|
119
|
+
# @see Courier::Models::WorkspacePreferenceTopicCreateRequest#digest
|
|
120
|
+
class Digest < Courier::Internal::Type::BaseModel
|
|
121
|
+
# @!attribute schedules
|
|
122
|
+
# The cadences this digest delivers on.
|
|
123
|
+
#
|
|
124
|
+
# The array replaces the stored schedules wholesale, so a schedule you leave out
|
|
125
|
+
# of it is deleted along with its delivery rule. Omit the key entirely to leave
|
|
126
|
+
# the stored schedules untouched — useful for changing `template_id` or
|
|
127
|
+
# `categories` without restating every schedule.
|
|
128
|
+
#
|
|
129
|
+
# A digest must end up with at least one schedule, because one with none collects
|
|
130
|
+
# events into an instance that can never fire. So sending `[]` is always a `400`,
|
|
131
|
+
# and so is omitting the key on a topic that has no schedules stored yet.
|
|
132
|
+
#
|
|
133
|
+
# On **create** the key is required outright: a topic being created has nothing
|
|
134
|
+
# stored to leave alone, and the topic row is written before its digest, so
|
|
135
|
+
# rejecting it any later would leave the topic behind and let a retry duplicate
|
|
136
|
+
# it.
|
|
137
|
+
#
|
|
138
|
+
# @return [Array<Courier::Models::TopicDigestScheduleRequest>]
|
|
139
|
+
required :schedules, -> { Courier::Internal::Type::ArrayOf[Courier::TopicDigestScheduleRequest] }
|
|
140
|
+
|
|
141
|
+
# @!attribute template_id
|
|
142
|
+
# The notification template that renders the digest. A digest with no template
|
|
143
|
+
# collects nothing, so this is required.
|
|
144
|
+
#
|
|
145
|
+
# @return [String]
|
|
146
|
+
required :template_id, String
|
|
147
|
+
|
|
148
|
+
# @!attribute audience_id
|
|
149
|
+
# Optional audience the digest is scoped to.
|
|
150
|
+
#
|
|
151
|
+
# @return [String, nil]
|
|
152
|
+
optional :audience_id, String
|
|
153
|
+
|
|
154
|
+
# @!attribute categories
|
|
155
|
+
# Retention rules per category key. Defaults to a single `digest` category
|
|
156
|
+
# retaining `FIRST`.
|
|
157
|
+
#
|
|
158
|
+
# @return [Array<Courier::Models::TopicDigestCategory>, nil]
|
|
159
|
+
optional :categories, -> { Courier::Internal::Type::ArrayOf[Courier::TopicDigestCategory] }
|
|
160
|
+
|
|
161
|
+
# @!attribute trigger_empty
|
|
162
|
+
# Whether to deliver the digest even when nothing was collected.
|
|
163
|
+
#
|
|
164
|
+
# @return [Boolean, nil]
|
|
165
|
+
optional :trigger_empty, Courier::Internal::Type::Boolean
|
|
166
|
+
|
|
167
|
+
# @!method initialize(schedules:, template_id:, audience_id: nil, categories: nil, trigger_empty: nil)
|
|
168
|
+
# Some parameter documentations has been truncated, see
|
|
169
|
+
# {Courier::Models::WorkspacePreferenceTopicCreateRequest::Digest} for more
|
|
170
|
+
# details.
|
|
171
|
+
#
|
|
172
|
+
# A topic's digest, as supplied when the topic itself is created: the template
|
|
173
|
+
# that renders it, the cadences it delivers on, and how collected events are
|
|
174
|
+
# retained.
|
|
175
|
+
#
|
|
176
|
+
# Identical to `TopicDigestRequest`, which a replace uses, except that `schedules`
|
|
177
|
+
# is required — a topic being created has no stored schedules for an absent key to
|
|
178
|
+
# leave alone.
|
|
179
|
+
#
|
|
180
|
+
# Send `null` for the whole object to turn a digest off, which unlinks the
|
|
181
|
+
# template and removes its schedules. There is no `enabled` flag, and
|
|
182
|
+
# `schedules: []` is rejected, because both states are un-deliverable rather than
|
|
183
|
+
# merely off.
|
|
184
|
+
#
|
|
185
|
+
# @param schedules [Array<Courier::Models::TopicDigestScheduleRequest>] The cadences this digest delivers on.
|
|
186
|
+
#
|
|
187
|
+
# @param template_id [String] The notification template that renders the digest. A digest with no template col
|
|
188
|
+
#
|
|
189
|
+
# @param audience_id [String] Optional audience the digest is scoped to.
|
|
190
|
+
#
|
|
191
|
+
# @param categories [Array<Courier::Models::TopicDigestCategory>] Retention rules per category key. Defaults to a single `digest` category retaini
|
|
192
|
+
#
|
|
193
|
+
# @param trigger_empty [Boolean] Whether to deliver the digest even when nothing was collected.
|
|
194
|
+
end
|
|
113
195
|
end
|
|
114
196
|
end
|
|
115
197
|
end
|
|
@@ -21,12 +21,14 @@ module Courier
|
|
|
21
21
|
# Creates a journey from a set of nodes, in draft state unless you pass a
|
|
22
22
|
# published state. Send nodes cannot be included until their templates exist.
|
|
23
23
|
#
|
|
24
|
-
# @overload create(name:, nodes:, enabled: nil, state: nil, idempotency_key: nil, x_idempotency_expiration: nil, request_options: {})
|
|
24
|
+
# @overload create(name:, nodes:, cancelation_token: nil, enabled: nil, state: nil, idempotency_key: nil, x_idempotency_expiration: nil, request_options: {})
|
|
25
25
|
#
|
|
26
26
|
# @param name [String] Body param
|
|
27
27
|
#
|
|
28
28
|
# @param nodes [Array<Courier::Models::JourneyAPIInvokeTriggerNode, Courier::Models::JourneySegmentTriggerNode, Courier::Models::JourneyAudienceTriggerNode, Courier::Models::JourneyWebhookTriggerNode, Courier::Models::JourneySendNode, Courier::Models::JourneyDelayDurationNode, Courier::Models::JourneyDelayUntilNode, Courier::Models::JourneyFetchGetDeleteNode, Courier::Models::JourneyFetchPostPutNode, Courier::Models::JourneyAINode, Courier::Models::JourneyThrottleStaticNode, Courier::Models::JourneyThrottleDynamicNode, Courier::Models::JourneyNode::JourneyBatchNode, Courier::Models::JourneyNode::JourneyAddToDigestNode, Courier::Models::JourneyExitNode, Courier::Models::JourneyNode::JourneyBranchNode>] Body param
|
|
29
29
|
#
|
|
30
|
+
# @param cancelation_token [String] Body param: Cancelation token stored on the journey definition. It tags every ru
|
|
31
|
+
#
|
|
30
32
|
# @param enabled [Boolean] Body param
|
|
31
33
|
#
|
|
32
34
|
# @param state [Symbol, Courier::Models::JourneyState] Body param: Lifecycle state of a journey.
|
|
@@ -259,10 +261,13 @@ module Courier
|
|
|
259
261
|
)
|
|
260
262
|
end
|
|
261
263
|
|
|
264
|
+
# Some parameter documentations has been truncated, see
|
|
265
|
+
# {Courier::Models::JourneyReplaceParams} for more details.
|
|
266
|
+
#
|
|
262
267
|
# Replaces a journey's working draft, leaving the published version live until you
|
|
263
268
|
# publish. Reach for this when editing a journey already running.
|
|
264
269
|
#
|
|
265
|
-
# @overload replace(template_id, name:, nodes:, enabled: nil, state: nil, request_options: {})
|
|
270
|
+
# @overload replace(template_id, name:, nodes:, cancelation_token: nil, enabled: nil, state: nil, request_options: {})
|
|
266
271
|
#
|
|
267
272
|
# @param template_id [String] Journey id
|
|
268
273
|
#
|
|
@@ -270,6 +275,8 @@ module Courier
|
|
|
270
275
|
#
|
|
271
276
|
# @param nodes [Array<Courier::Models::JourneyAPIInvokeTriggerNode, Courier::Models::JourneySegmentTriggerNode, Courier::Models::JourneyAudienceTriggerNode, Courier::Models::JourneyWebhookTriggerNode, Courier::Models::JourneySendNode, Courier::Models::JourneyDelayDurationNode, Courier::Models::JourneyDelayUntilNode, Courier::Models::JourneyFetchGetDeleteNode, Courier::Models::JourneyFetchPostPutNode, Courier::Models::JourneyAINode, Courier::Models::JourneyThrottleStaticNode, Courier::Models::JourneyThrottleDynamicNode, Courier::Models::JourneyNode::JourneyBatchNode, Courier::Models::JourneyNode::JourneyAddToDigestNode, Courier::Models::JourneyExitNode, Courier::Models::JourneyNode::JourneyBranchNode>]
|
|
272
277
|
#
|
|
278
|
+
# @param cancelation_token [String] Cancelation token stored on the journey definition. It tags every run the journe
|
|
279
|
+
#
|
|
273
280
|
# @param enabled [Boolean]
|
|
274
281
|
#
|
|
275
282
|
# @param state [Symbol, Courier::Models::JourneyState] Lifecycle state of a journey.
|
|
@@ -22,7 +22,7 @@ module Courier
|
|
|
22
22
|
#
|
|
23
23
|
# @param description [String, nil] Body param: Optional description shown under the topic on the hosted preferences
|
|
24
24
|
#
|
|
25
|
-
# @param digest [Courier::Models::
|
|
25
|
+
# @param digest [Courier::Models::WorkspacePreferenceTopicCreateRequest::Digest, nil] Body param: A topic's digest, as supplied when the topic itself is created: the
|
|
26
26
|
#
|
|
27
27
|
# @param include_unsubscribe_header [Boolean, nil] Body param: Whether to include a list-unsubscribe header on emails for this topi
|
|
28
28
|
#
|
data/lib/courier/version.rb
CHANGED
|
@@ -37,6 +37,17 @@ module Courier
|
|
|
37
37
|
end
|
|
38
38
|
attr_accessor :nodes
|
|
39
39
|
|
|
40
|
+
# Cancelation token stored on the journey definition. It tags every run the
|
|
41
|
+
# journey creates so that `POST /journeys/cancel` can later cancel those runs by
|
|
42
|
+
# token. Accepts a templated string such as `order-{{data.order_id}}`, which is
|
|
43
|
+
# resolved per run when the journey is invoked. On a replace, omitting this field
|
|
44
|
+
# preserves any existing token and sending a value replaces it.
|
|
45
|
+
sig { returns(T.nilable(String)) }
|
|
46
|
+
attr_reader :cancelation_token
|
|
47
|
+
|
|
48
|
+
sig { params(cancelation_token: String).void }
|
|
49
|
+
attr_writer :cancelation_token
|
|
50
|
+
|
|
40
51
|
sig { returns(T.nilable(T::Boolean)) }
|
|
41
52
|
attr_reader :enabled
|
|
42
53
|
|
|
@@ -75,6 +86,7 @@ module Courier
|
|
|
75
86
|
Courier::JourneyNode::JourneyBranchNode::OrHash
|
|
76
87
|
)
|
|
77
88
|
],
|
|
89
|
+
cancelation_token: String,
|
|
78
90
|
enabled: T::Boolean,
|
|
79
91
|
state: Courier::JourneyState::OrSymbol
|
|
80
92
|
).returns(T.attached_class)
|
|
@@ -82,6 +94,12 @@ module Courier
|
|
|
82
94
|
def self.new(
|
|
83
95
|
name:,
|
|
84
96
|
nodes:,
|
|
97
|
+
# Cancelation token stored on the journey definition. It tags every run the
|
|
98
|
+
# journey creates so that `POST /journeys/cancel` can later cancel those runs by
|
|
99
|
+
# token. Accepts a templated string such as `order-{{data.order_id}}`, which is
|
|
100
|
+
# resolved per run when the journey is invoked. On a replace, omitting this field
|
|
101
|
+
# preserves any existing token and sending a value replaces it.
|
|
102
|
+
cancelation_token: nil,
|
|
85
103
|
enabled: nil,
|
|
86
104
|
# Lifecycle state of a journey.
|
|
87
105
|
state: nil
|
|
@@ -113,6 +131,7 @@ module Courier
|
|
|
113
131
|
Courier::JourneyNode::JourneyBranchNode
|
|
114
132
|
)
|
|
115
133
|
],
|
|
134
|
+
cancelation_token: String,
|
|
116
135
|
enabled: T::Boolean,
|
|
117
136
|
state: Courier::JourneyState::OrSymbol
|
|
118
137
|
}
|
|
@@ -11,6 +11,12 @@ module Courier
|
|
|
11
11
|
sig { returns(String) }
|
|
12
12
|
attr_accessor :id
|
|
13
13
|
|
|
14
|
+
# The journey cancelation token, or null when none is set. A token authored in the
|
|
15
|
+
# dashboard is returned in its raw templated form, such as
|
|
16
|
+
# `order-{{data.order_id}}`, so it can be read back and asserted.
|
|
17
|
+
sig { returns(T.nilable(String)) }
|
|
18
|
+
attr_accessor :cancelation_token
|
|
19
|
+
|
|
14
20
|
sig { returns(T.nilable(Integer)) }
|
|
15
21
|
attr_accessor :created
|
|
16
22
|
|
|
@@ -43,6 +49,7 @@ module Courier
|
|
|
43
49
|
sig do
|
|
44
50
|
params(
|
|
45
51
|
id: String,
|
|
52
|
+
cancelation_token: T.nilable(String),
|
|
46
53
|
created: T.nilable(Integer),
|
|
47
54
|
creator: T.nilable(String),
|
|
48
55
|
enabled: T::Boolean,
|
|
@@ -76,6 +83,10 @@ module Courier
|
|
|
76
83
|
end
|
|
77
84
|
def self.new(
|
|
78
85
|
id:,
|
|
86
|
+
# The journey cancelation token, or null when none is set. A token authored in the
|
|
87
|
+
# dashboard is returned in its raw templated form, such as
|
|
88
|
+
# `order-{{data.order_id}}`, so it can be read back and asserted.
|
|
89
|
+
cancelation_token:,
|
|
79
90
|
created:,
|
|
80
91
|
creator:,
|
|
81
92
|
enabled:,
|
|
@@ -93,6 +104,7 @@ module Courier
|
|
|
93
104
|
override.returns(
|
|
94
105
|
{
|
|
95
106
|
id: String,
|
|
107
|
+
cancelation_token: T.nilable(String),
|
|
96
108
|
created: T.nilable(Integer),
|
|
97
109
|
creator: T.nilable(String),
|
|
98
110
|
enabled: T::Boolean,
|
|
@@ -8,12 +8,6 @@ module Courier
|
|
|
8
8
|
T.any(Courier::TopicDigestRequest, Courier::Internal::AnyHash)
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
# The cadences this digest delivers on. At least one is required: a digest with no
|
|
12
|
-
# schedule collects events into an instance that can never fire. Omitting the key
|
|
13
|
-
# on a replace leaves stored schedules untouched; sending `[]` is a `400`.
|
|
14
|
-
sig { returns(T::Array[Courier::TopicDigestScheduleRequest]) }
|
|
15
|
-
attr_accessor :schedules
|
|
16
|
-
|
|
17
11
|
# The notification template that renders the digest. A digest with no template
|
|
18
12
|
# collects nothing, so this is required.
|
|
19
13
|
sig { returns(String) }
|
|
@@ -36,6 +30,31 @@ module Courier
|
|
|
36
30
|
end
|
|
37
31
|
attr_writer :categories
|
|
38
32
|
|
|
33
|
+
# The cadences this digest delivers on.
|
|
34
|
+
#
|
|
35
|
+
# The array replaces the stored schedules wholesale, so a schedule you leave out
|
|
36
|
+
# of it is deleted along with its delivery rule. Omit the key entirely to leave
|
|
37
|
+
# the stored schedules untouched — useful for changing `template_id` or
|
|
38
|
+
# `categories` without restating every schedule.
|
|
39
|
+
#
|
|
40
|
+
# A digest must end up with at least one schedule, because one with none collects
|
|
41
|
+
# events into an instance that can never fire. So sending `[]` is always a `400`,
|
|
42
|
+
# and so is omitting the key on a topic that has no schedules stored yet.
|
|
43
|
+
#
|
|
44
|
+
# On **create** the key is required outright: a topic being created has nothing
|
|
45
|
+
# stored to leave alone, and the topic row is written before its digest, so
|
|
46
|
+
# rejecting it any later would leave the topic behind and let a retry duplicate
|
|
47
|
+
# it.
|
|
48
|
+
sig { returns(T.nilable(T::Array[Courier::TopicDigestScheduleRequest])) }
|
|
49
|
+
attr_reader :schedules
|
|
50
|
+
|
|
51
|
+
sig do
|
|
52
|
+
params(
|
|
53
|
+
schedules: T::Array[Courier::TopicDigestScheduleRequest::OrHash]
|
|
54
|
+
).void
|
|
55
|
+
end
|
|
56
|
+
attr_writer :schedules
|
|
57
|
+
|
|
39
58
|
# Whether to deliver the digest even when nothing was collected.
|
|
40
59
|
sig { returns(T.nilable(T::Boolean)) }
|
|
41
60
|
attr_reader :trigger_empty
|
|
@@ -52,18 +71,14 @@ module Courier
|
|
|
52
71
|
# merely off.
|
|
53
72
|
sig do
|
|
54
73
|
params(
|
|
55
|
-
schedules: T::Array[Courier::TopicDigestScheduleRequest::OrHash],
|
|
56
74
|
template_id: String,
|
|
57
75
|
audience_id: String,
|
|
58
76
|
categories: T::Array[Courier::TopicDigestCategory::OrHash],
|
|
77
|
+
schedules: T::Array[Courier::TopicDigestScheduleRequest::OrHash],
|
|
59
78
|
trigger_empty: T::Boolean
|
|
60
79
|
).returns(T.attached_class)
|
|
61
80
|
end
|
|
62
81
|
def self.new(
|
|
63
|
-
# The cadences this digest delivers on. At least one is required: a digest with no
|
|
64
|
-
# schedule collects events into an instance that can never fire. Omitting the key
|
|
65
|
-
# on a replace leaves stored schedules untouched; sending `[]` is a `400`.
|
|
66
|
-
schedules:,
|
|
67
82
|
# The notification template that renders the digest. A digest with no template
|
|
68
83
|
# collects nothing, so this is required.
|
|
69
84
|
template_id:,
|
|
@@ -72,6 +87,22 @@ module Courier
|
|
|
72
87
|
# Retention rules per category key. Defaults to a single `digest` category
|
|
73
88
|
# retaining `FIRST`.
|
|
74
89
|
categories: nil,
|
|
90
|
+
# The cadences this digest delivers on.
|
|
91
|
+
#
|
|
92
|
+
# The array replaces the stored schedules wholesale, so a schedule you leave out
|
|
93
|
+
# of it is deleted along with its delivery rule. Omit the key entirely to leave
|
|
94
|
+
# the stored schedules untouched — useful for changing `template_id` or
|
|
95
|
+
# `categories` without restating every schedule.
|
|
96
|
+
#
|
|
97
|
+
# A digest must end up with at least one schedule, because one with none collects
|
|
98
|
+
# events into an instance that can never fire. So sending `[]` is always a `400`,
|
|
99
|
+
# and so is omitting the key on a topic that has no schedules stored yet.
|
|
100
|
+
#
|
|
101
|
+
# On **create** the key is required outright: a topic being created has nothing
|
|
102
|
+
# stored to leave alone, and the topic row is written before its digest, so
|
|
103
|
+
# rejecting it any later would leave the topic behind and let a retry duplicate
|
|
104
|
+
# it.
|
|
105
|
+
schedules: nil,
|
|
75
106
|
# Whether to deliver the digest even when nothing was collected.
|
|
76
107
|
trigger_empty: nil
|
|
77
108
|
)
|
|
@@ -80,10 +111,10 @@ module Courier
|
|
|
80
111
|
sig do
|
|
81
112
|
override.returns(
|
|
82
113
|
{
|
|
83
|
-
schedules: T::Array[Courier::TopicDigestScheduleRequest],
|
|
84
114
|
template_id: String,
|
|
85
115
|
audience_id: String,
|
|
86
116
|
categories: T::Array[Courier::TopicDigestCategory],
|
|
117
|
+
schedules: T::Array[Courier::TopicDigestScheduleRequest],
|
|
87
118
|
trigger_empty: T::Boolean
|
|
88
119
|
}
|
|
89
120
|
)
|
|
@@ -79,6 +79,12 @@ module Courier
|
|
|
79
79
|
# existing schedule in place; omit it and one is assigned and returned. The
|
|
80
80
|
# `schedules` array is a full replacement, so a stored schedule absent from it is
|
|
81
81
|
# deleted along with its delivery rule.
|
|
82
|
+
#
|
|
83
|
+
# Updating by `schedule_id` replaces that schedule rather than merging into it:
|
|
84
|
+
# any field you leave out is cleared. Two of those change delivery silently — an
|
|
85
|
+
# omitted `timezone` reverts the schedule to UTC, and an omitted `is_default` can
|
|
86
|
+
# leave the topic with no default schedule, which is what recipients who have not
|
|
87
|
+
# chosen one fall back to. Restate every field you want to keep.
|
|
82
88
|
sig do
|
|
83
89
|
params(
|
|
84
90
|
frequency: Courier::DigestFrequency::OrSymbol,
|
|
@@ -13,6 +13,14 @@ module Courier
|
|
|
13
13
|
|
|
14
14
|
# The schedule's identifier, assigned by the server. This is the value the
|
|
15
15
|
# `/digests/schedules/{schedule_id}` endpoints are keyed by.
|
|
16
|
+
#
|
|
17
|
+
# Two formats are in circulation and only one is safe to drop into a URL.
|
|
18
|
+
# Schedules created through the API are `sch_01m26xfcn3endt3nxy4e2kx2rh` and need
|
|
19
|
+
# no encoding. Schedules created in the Preferences Editor before that format are
|
|
20
|
+
# `sch/{uuid}` and contain a literal `/`, so they must be URL-encoded as
|
|
21
|
+
# `sch%2F{uuid}` — unencoded, the path does not match the route and the response
|
|
22
|
+
# is a bare `404` that reads like a broken endpoint. Existing ids are never
|
|
23
|
+
# migrated.
|
|
16
24
|
sig { returns(String) }
|
|
17
25
|
attr_accessor :schedule_id
|
|
18
26
|
|
|
@@ -110,6 +118,14 @@ module Courier
|
|
|
110
118
|
def self.new(
|
|
111
119
|
# The schedule's identifier, assigned by the server. This is the value the
|
|
112
120
|
# `/digests/schedules/{schedule_id}` endpoints are keyed by.
|
|
121
|
+
#
|
|
122
|
+
# Two formats are in circulation and only one is safe to drop into a URL.
|
|
123
|
+
# Schedules created through the API are `sch_01m26xfcn3endt3nxy4e2kx2rh` and need
|
|
124
|
+
# no encoding. Schedules created in the Preferences Editor before that format are
|
|
125
|
+
# `sch/{uuid}` and contain a literal `/`, so they must be URL-encoded as
|
|
126
|
+
# `sch%2F{uuid}` — unencoded, the path does not match the route and the response
|
|
127
|
+
# is a bare `404` that reads like a broken endpoint. Existing ids are never
|
|
128
|
+
# migrated.
|
|
113
129
|
schedule_id:,
|
|
114
130
|
# ISO-8601 timestamp of when the schedule was created.
|
|
115
131
|
created: nil,
|
|
@@ -40,8 +40,13 @@ module Courier
|
|
|
40
40
|
|
|
41
41
|
# The digest schedule this recipient is on for the topic. Omitted rather than null
|
|
42
42
|
# when they have not chosen one, in which case the topic's default schedule
|
|
43
|
-
# applies. Ids come from the topic's digest configuration
|
|
44
|
-
#
|
|
43
|
+
# applies. Ids come from the topic's digest configuration, returned on any read of
|
|
44
|
+
# the topic.
|
|
45
|
+
#
|
|
46
|
+
# Also omitted when the schedule they chose has since been deleted or disabled:
|
|
47
|
+
# the recipient is on the topic's default from that point on, which is how a send
|
|
48
|
+
# already resolves it, so the field reports what would actually be used rather
|
|
49
|
+
# than a dead id.
|
|
45
50
|
sig { returns(T.nilable(String)) }
|
|
46
51
|
attr_reader :digest_schedule_id
|
|
47
52
|
|
|
@@ -103,8 +108,13 @@ module Courier
|
|
|
103
108
|
custom_routing: nil,
|
|
104
109
|
# The digest schedule this recipient is on for the topic. Omitted rather than null
|
|
105
110
|
# when they have not chosen one, in which case the topic's default schedule
|
|
106
|
-
# applies. Ids come from the topic's digest configuration
|
|
107
|
-
#
|
|
111
|
+
# applies. Ids come from the topic's digest configuration, returned on any read of
|
|
112
|
+
# the topic.
|
|
113
|
+
#
|
|
114
|
+
# Also omitted when the schedule they chose has since been deleted or disabled:
|
|
115
|
+
# the recipient is on the topic's default from that point on, which is how a send
|
|
116
|
+
# already resolves it, so the field reports what would actually be used rather
|
|
117
|
+
# than a dead id.
|
|
108
118
|
digest_schedule_id: nil,
|
|
109
119
|
# Whether the user has chosen specific delivery channels for this topic (listed in
|
|
110
120
|
# custom_routing) rather than the topic's default routing.
|
|
@@ -40,18 +40,32 @@ module Courier
|
|
|
40
40
|
sig { returns(T.nilable(String)) }
|
|
41
41
|
attr_accessor :description
|
|
42
42
|
|
|
43
|
-
# A topic's digest
|
|
44
|
-
# delivers on, and how collected events are
|
|
43
|
+
# A topic's digest, as supplied when the topic itself is created: the template
|
|
44
|
+
# that renders it, the cadences it delivers on, and how collected events are
|
|
45
|
+
# retained.
|
|
46
|
+
#
|
|
47
|
+
# Identical to `TopicDigestRequest`, which a replace uses, except that `schedules`
|
|
48
|
+
# is required — a topic being created has no stored schedules for an absent key to
|
|
49
|
+
# leave alone.
|
|
45
50
|
#
|
|
46
51
|
# Send `null` for the whole object to turn a digest off, which unlinks the
|
|
47
52
|
# template and removes its schedules. There is no `enabled` flag, and
|
|
48
53
|
# `schedules: []` is rejected, because both states are un-deliverable rather than
|
|
49
54
|
# merely off.
|
|
50
|
-
sig
|
|
55
|
+
sig do
|
|
56
|
+
returns(
|
|
57
|
+
T.nilable(Courier::WorkspacePreferenceTopicCreateRequest::Digest)
|
|
58
|
+
)
|
|
59
|
+
end
|
|
51
60
|
attr_reader :digest
|
|
52
61
|
|
|
53
62
|
sig do
|
|
54
|
-
params(
|
|
63
|
+
params(
|
|
64
|
+
digest:
|
|
65
|
+
T.nilable(
|
|
66
|
+
Courier::WorkspacePreferenceTopicCreateRequest::Digest::OrHash
|
|
67
|
+
)
|
|
68
|
+
).void
|
|
55
69
|
end
|
|
56
70
|
attr_writer :digest
|
|
57
71
|
|
|
@@ -82,7 +96,10 @@ module Courier
|
|
|
82
96
|
]
|
|
83
97
|
),
|
|
84
98
|
description: T.nilable(String),
|
|
85
|
-
digest:
|
|
99
|
+
digest:
|
|
100
|
+
T.nilable(
|
|
101
|
+
Courier::WorkspacePreferenceTopicCreateRequest::Digest::OrHash
|
|
102
|
+
),
|
|
86
103
|
include_unsubscribe_header: T.nilable(T::Boolean),
|
|
87
104
|
routing_options:
|
|
88
105
|
T.nilable(T::Array[Courier::ChannelClassification::OrSymbol]),
|
|
@@ -99,8 +116,13 @@ module Courier
|
|
|
99
116
|
allowed_preferences: nil,
|
|
100
117
|
# Optional description shown under the topic on the hosted preferences page.
|
|
101
118
|
description: nil,
|
|
102
|
-
# A topic's digest
|
|
103
|
-
# delivers on, and how collected events are
|
|
119
|
+
# A topic's digest, as supplied when the topic itself is created: the template
|
|
120
|
+
# that renders it, the cadences it delivers on, and how collected events are
|
|
121
|
+
# retained.
|
|
122
|
+
#
|
|
123
|
+
# Identical to `TopicDigestRequest`, which a replace uses, except that `schedules`
|
|
124
|
+
# is required — a topic being created has no stored schedules for an absent key to
|
|
125
|
+
# leave alone.
|
|
104
126
|
#
|
|
105
127
|
# Send `null` for the whole object to turn a digest off, which unlinks the
|
|
106
128
|
# template and removes its schedules. There is no `enabled` flag, and
|
|
@@ -129,7 +151,8 @@ module Courier
|
|
|
129
151
|
]
|
|
130
152
|
),
|
|
131
153
|
description: T.nilable(String),
|
|
132
|
-
digest:
|
|
154
|
+
digest:
|
|
155
|
+
T.nilable(Courier::WorkspacePreferenceTopicCreateRequest::Digest),
|
|
133
156
|
include_unsubscribe_header: T.nilable(T::Boolean),
|
|
134
157
|
routing_options:
|
|
135
158
|
T.nilable(T::Array[Courier::ChannelClassification::OrSymbol]),
|
|
@@ -214,6 +237,130 @@ module Courier
|
|
|
214
237
|
def self.values
|
|
215
238
|
end
|
|
216
239
|
end
|
|
240
|
+
|
|
241
|
+
class Digest < Courier::Internal::Type::BaseModel
|
|
242
|
+
OrHash =
|
|
243
|
+
T.type_alias do
|
|
244
|
+
T.any(
|
|
245
|
+
Courier::WorkspacePreferenceTopicCreateRequest::Digest,
|
|
246
|
+
Courier::Internal::AnyHash
|
|
247
|
+
)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# The cadences this digest delivers on.
|
|
251
|
+
#
|
|
252
|
+
# The array replaces the stored schedules wholesale, so a schedule you leave out
|
|
253
|
+
# of it is deleted along with its delivery rule. Omit the key entirely to leave
|
|
254
|
+
# the stored schedules untouched — useful for changing `template_id` or
|
|
255
|
+
# `categories` without restating every schedule.
|
|
256
|
+
#
|
|
257
|
+
# A digest must end up with at least one schedule, because one with none collects
|
|
258
|
+
# events into an instance that can never fire. So sending `[]` is always a `400`,
|
|
259
|
+
# and so is omitting the key on a topic that has no schedules stored yet.
|
|
260
|
+
#
|
|
261
|
+
# On **create** the key is required outright: a topic being created has nothing
|
|
262
|
+
# stored to leave alone, and the topic row is written before its digest, so
|
|
263
|
+
# rejecting it any later would leave the topic behind and let a retry duplicate
|
|
264
|
+
# it.
|
|
265
|
+
sig { returns(T::Array[Courier::TopicDigestScheduleRequest]) }
|
|
266
|
+
attr_accessor :schedules
|
|
267
|
+
|
|
268
|
+
# The notification template that renders the digest. A digest with no template
|
|
269
|
+
# collects nothing, so this is required.
|
|
270
|
+
sig { returns(String) }
|
|
271
|
+
attr_accessor :template_id
|
|
272
|
+
|
|
273
|
+
# Optional audience the digest is scoped to.
|
|
274
|
+
sig { returns(T.nilable(String)) }
|
|
275
|
+
attr_reader :audience_id
|
|
276
|
+
|
|
277
|
+
sig { params(audience_id: String).void }
|
|
278
|
+
attr_writer :audience_id
|
|
279
|
+
|
|
280
|
+
# Retention rules per category key. Defaults to a single `digest` category
|
|
281
|
+
# retaining `FIRST`.
|
|
282
|
+
sig { returns(T.nilable(T::Array[Courier::TopicDigestCategory])) }
|
|
283
|
+
attr_reader :categories
|
|
284
|
+
|
|
285
|
+
sig do
|
|
286
|
+
params(
|
|
287
|
+
categories: T::Array[Courier::TopicDigestCategory::OrHash]
|
|
288
|
+
).void
|
|
289
|
+
end
|
|
290
|
+
attr_writer :categories
|
|
291
|
+
|
|
292
|
+
# Whether to deliver the digest even when nothing was collected.
|
|
293
|
+
sig { returns(T.nilable(T::Boolean)) }
|
|
294
|
+
attr_reader :trigger_empty
|
|
295
|
+
|
|
296
|
+
sig { params(trigger_empty: T::Boolean).void }
|
|
297
|
+
attr_writer :trigger_empty
|
|
298
|
+
|
|
299
|
+
# A topic's digest, as supplied when the topic itself is created: the template
|
|
300
|
+
# that renders it, the cadences it delivers on, and how collected events are
|
|
301
|
+
# retained.
|
|
302
|
+
#
|
|
303
|
+
# Identical to `TopicDigestRequest`, which a replace uses, except that `schedules`
|
|
304
|
+
# is required — a topic being created has no stored schedules for an absent key to
|
|
305
|
+
# leave alone.
|
|
306
|
+
#
|
|
307
|
+
# Send `null` for the whole object to turn a digest off, which unlinks the
|
|
308
|
+
# template and removes its schedules. There is no `enabled` flag, and
|
|
309
|
+
# `schedules: []` is rejected, because both states are un-deliverable rather than
|
|
310
|
+
# merely off.
|
|
311
|
+
sig do
|
|
312
|
+
params(
|
|
313
|
+
schedules: T::Array[Courier::TopicDigestScheduleRequest::OrHash],
|
|
314
|
+
template_id: String,
|
|
315
|
+
audience_id: String,
|
|
316
|
+
categories: T::Array[Courier::TopicDigestCategory::OrHash],
|
|
317
|
+
trigger_empty: T::Boolean
|
|
318
|
+
).returns(T.attached_class)
|
|
319
|
+
end
|
|
320
|
+
def self.new(
|
|
321
|
+
# The cadences this digest delivers on.
|
|
322
|
+
#
|
|
323
|
+
# The array replaces the stored schedules wholesale, so a schedule you leave out
|
|
324
|
+
# of it is deleted along with its delivery rule. Omit the key entirely to leave
|
|
325
|
+
# the stored schedules untouched — useful for changing `template_id` or
|
|
326
|
+
# `categories` without restating every schedule.
|
|
327
|
+
#
|
|
328
|
+
# A digest must end up with at least one schedule, because one with none collects
|
|
329
|
+
# events into an instance that can never fire. So sending `[]` is always a `400`,
|
|
330
|
+
# and so is omitting the key on a topic that has no schedules stored yet.
|
|
331
|
+
#
|
|
332
|
+
# On **create** the key is required outright: a topic being created has nothing
|
|
333
|
+
# stored to leave alone, and the topic row is written before its digest, so
|
|
334
|
+
# rejecting it any later would leave the topic behind and let a retry duplicate
|
|
335
|
+
# it.
|
|
336
|
+
schedules:,
|
|
337
|
+
# The notification template that renders the digest. A digest with no template
|
|
338
|
+
# collects nothing, so this is required.
|
|
339
|
+
template_id:,
|
|
340
|
+
# Optional audience the digest is scoped to.
|
|
341
|
+
audience_id: nil,
|
|
342
|
+
# Retention rules per category key. Defaults to a single `digest` category
|
|
343
|
+
# retaining `FIRST`.
|
|
344
|
+
categories: nil,
|
|
345
|
+
# Whether to deliver the digest even when nothing was collected.
|
|
346
|
+
trigger_empty: nil
|
|
347
|
+
)
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
sig do
|
|
351
|
+
override.returns(
|
|
352
|
+
{
|
|
353
|
+
schedules: T::Array[Courier::TopicDigestScheduleRequest],
|
|
354
|
+
template_id: String,
|
|
355
|
+
audience_id: String,
|
|
356
|
+
categories: T::Array[Courier::TopicDigestCategory],
|
|
357
|
+
trigger_empty: T::Boolean
|
|
358
|
+
}
|
|
359
|
+
)
|
|
360
|
+
end
|
|
361
|
+
def to_hash
|
|
362
|
+
end
|
|
363
|
+
end
|
|
217
364
|
end
|
|
218
365
|
end
|
|
219
366
|
end
|
|
@@ -41,6 +41,7 @@ module Courier
|
|
|
41
41
|
Courier::JourneyNode::JourneyBranchNode::OrHash
|
|
42
42
|
)
|
|
43
43
|
],
|
|
44
|
+
cancelation_token: String,
|
|
44
45
|
enabled: T::Boolean,
|
|
45
46
|
state: Courier::JourneyState::OrSymbol,
|
|
46
47
|
idempotency_key: String,
|
|
@@ -53,6 +54,12 @@ module Courier
|
|
|
53
54
|
name:,
|
|
54
55
|
# Body param
|
|
55
56
|
nodes:,
|
|
57
|
+
# Body param: Cancelation token stored on the journey definition. It tags every
|
|
58
|
+
# run the journey creates so that `POST /journeys/cancel` can later cancel those
|
|
59
|
+
# runs by token. Accepts a templated string such as `order-{{data.order_id}}`,
|
|
60
|
+
# which is resolved per run when the journey is invoked. On a replace, omitting
|
|
61
|
+
# this field preserves any existing token and sending a value replaces it.
|
|
62
|
+
cancelation_token: nil,
|
|
56
63
|
# Body param
|
|
57
64
|
enabled: nil,
|
|
58
65
|
# Body param: Lifecycle state of a journey.
|
|
@@ -283,6 +290,7 @@ module Courier
|
|
|
283
290
|
Courier::JourneyNode::JourneyBranchNode::OrHash
|
|
284
291
|
)
|
|
285
292
|
],
|
|
293
|
+
cancelation_token: String,
|
|
286
294
|
enabled: T::Boolean,
|
|
287
295
|
state: Courier::JourneyState::OrSymbol,
|
|
288
296
|
request_options: Courier::RequestOptions::OrHash
|
|
@@ -293,6 +301,12 @@ module Courier
|
|
|
293
301
|
template_id,
|
|
294
302
|
name:,
|
|
295
303
|
nodes:,
|
|
304
|
+
# Cancelation token stored on the journey definition. It tags every run the
|
|
305
|
+
# journey creates so that `POST /journeys/cancel` can later cancel those runs by
|
|
306
|
+
# token. Accepts a templated string such as `order-{{data.order_id}}`, which is
|
|
307
|
+
# resolved per run when the journey is invoked. On a replace, omitting this field
|
|
308
|
+
# preserves any existing token and sending a value replaces it.
|
|
309
|
+
cancelation_token: nil,
|
|
296
310
|
enabled: nil,
|
|
297
311
|
# Lifecycle state of a journey.
|
|
298
312
|
state: nil,
|
|
@@ -19,7 +19,10 @@ module Courier
|
|
|
19
19
|
]
|
|
20
20
|
),
|
|
21
21
|
description: T.nilable(String),
|
|
22
|
-
digest:
|
|
22
|
+
digest:
|
|
23
|
+
T.nilable(
|
|
24
|
+
Courier::WorkspacePreferenceTopicCreateRequest::Digest::OrHash
|
|
25
|
+
),
|
|
23
26
|
include_unsubscribe_header: T.nilable(T::Boolean),
|
|
24
27
|
routing_options:
|
|
25
28
|
T.nilable(T::Array[Courier::ChannelClassification::OrSymbol]),
|
|
@@ -43,8 +46,13 @@ module Courier
|
|
|
43
46
|
# Body param: Optional description shown under the topic on the hosted preferences
|
|
44
47
|
# page.
|
|
45
48
|
description: nil,
|
|
46
|
-
# Body param: A topic's digest
|
|
47
|
-
# cadences it delivers on, and how collected events
|
|
49
|
+
# Body param: A topic's digest, as supplied when the topic itself is created: the
|
|
50
|
+
# template that renders it, the cadences it delivers on, and how collected events
|
|
51
|
+
# are retained.
|
|
52
|
+
#
|
|
53
|
+
# Identical to `TopicDigestRequest`, which a replace uses, except that `schedules`
|
|
54
|
+
# is required — a topic being created has no stored schedules for an absent key to
|
|
55
|
+
# leave alone.
|
|
48
56
|
#
|
|
49
57
|
# Send `null` for the whole object to turn a digest off, which unlinks the
|
|
50
58
|
# template and removes its schedules. There is no `enabled` flag, and
|
|
@@ -4,6 +4,7 @@ module Courier
|
|
|
4
4
|
{
|
|
5
5
|
name: String,
|
|
6
6
|
nodes: ::Array[Courier::Models::journey_node],
|
|
7
|
+
cancelation_token: String,
|
|
7
8
|
enabled: bool,
|
|
8
9
|
state: Courier::Models::journey_state
|
|
9
10
|
}
|
|
@@ -13,6 +14,10 @@ module Courier
|
|
|
13
14
|
|
|
14
15
|
attr_accessor nodes: ::Array[Courier::Models::journey_node]
|
|
15
16
|
|
|
17
|
+
attr_reader cancelation_token: String?
|
|
18
|
+
|
|
19
|
+
def cancelation_token=: (String) -> String
|
|
20
|
+
|
|
16
21
|
attr_reader enabled: bool?
|
|
17
22
|
|
|
18
23
|
def enabled=: (bool) -> bool
|
|
@@ -26,6 +31,7 @@ module Courier
|
|
|
26
31
|
def initialize: (
|
|
27
32
|
name: String,
|
|
28
33
|
nodes: ::Array[Courier::Models::journey_node],
|
|
34
|
+
?cancelation_token: String,
|
|
29
35
|
?enabled: bool,
|
|
30
36
|
?state: Courier::Models::journey_state
|
|
31
37
|
) -> void
|
|
@@ -33,6 +39,7 @@ module Courier
|
|
|
33
39
|
def to_hash: -> {
|
|
34
40
|
name: String,
|
|
35
41
|
nodes: ::Array[Courier::Models::journey_node],
|
|
42
|
+
cancelation_token: String,
|
|
36
43
|
enabled: bool,
|
|
37
44
|
state: Courier::Models::journey_state
|
|
38
45
|
}
|
|
@@ -3,6 +3,7 @@ module Courier
|
|
|
3
3
|
type journey_response =
|
|
4
4
|
{
|
|
5
5
|
id: String,
|
|
6
|
+
cancelation_token: String?,
|
|
6
7
|
created: Integer?,
|
|
7
8
|
creator: String?,
|
|
8
9
|
enabled: bool,
|
|
@@ -17,6 +18,8 @@ module Courier
|
|
|
17
18
|
class JourneyResponse < Courier::Internal::Type::BaseModel
|
|
18
19
|
attr_accessor id: String
|
|
19
20
|
|
|
21
|
+
attr_accessor cancelation_token: String?
|
|
22
|
+
|
|
20
23
|
attr_accessor created: Integer?
|
|
21
24
|
|
|
22
25
|
attr_accessor creator: String?
|
|
@@ -37,6 +40,7 @@ module Courier
|
|
|
37
40
|
|
|
38
41
|
def initialize: (
|
|
39
42
|
id: String,
|
|
43
|
+
cancelation_token: String?,
|
|
40
44
|
created: Integer?,
|
|
41
45
|
creator: String?,
|
|
42
46
|
enabled: bool,
|
|
@@ -50,6 +54,7 @@ module Courier
|
|
|
50
54
|
|
|
51
55
|
def to_hash: -> {
|
|
52
56
|
id: String,
|
|
57
|
+
cancelation_token: String?,
|
|
53
58
|
created: Integer?,
|
|
54
59
|
creator: String?,
|
|
55
60
|
enabled: bool,
|
|
@@ -2,16 +2,14 @@ module Courier
|
|
|
2
2
|
module Models
|
|
3
3
|
type topic_digest_request =
|
|
4
4
|
{
|
|
5
|
-
schedules: ::Array[Courier::TopicDigestScheduleRequest],
|
|
6
5
|
template_id: String,
|
|
7
6
|
audience_id: String,
|
|
8
7
|
categories: ::Array[Courier::TopicDigestCategory],
|
|
8
|
+
schedules: ::Array[Courier::TopicDigestScheduleRequest],
|
|
9
9
|
trigger_empty: bool
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
class TopicDigestRequest < Courier::Internal::Type::BaseModel
|
|
13
|
-
attr_accessor schedules: ::Array[Courier::TopicDigestScheduleRequest]
|
|
14
|
-
|
|
15
13
|
attr_accessor template_id: String
|
|
16
14
|
|
|
17
15
|
attr_reader audience_id: String?
|
|
@@ -24,23 +22,29 @@ module Courier
|
|
|
24
22
|
::Array[Courier::TopicDigestCategory]
|
|
25
23
|
) -> ::Array[Courier::TopicDigestCategory]
|
|
26
24
|
|
|
25
|
+
attr_reader schedules: ::Array[Courier::TopicDigestScheduleRequest]?
|
|
26
|
+
|
|
27
|
+
def schedules=: (
|
|
28
|
+
::Array[Courier::TopicDigestScheduleRequest]
|
|
29
|
+
) -> ::Array[Courier::TopicDigestScheduleRequest]
|
|
30
|
+
|
|
27
31
|
attr_reader trigger_empty: bool?
|
|
28
32
|
|
|
29
33
|
def trigger_empty=: (bool) -> bool
|
|
30
34
|
|
|
31
35
|
def initialize: (
|
|
32
|
-
schedules: ::Array[Courier::TopicDigestScheduleRequest],
|
|
33
36
|
template_id: String,
|
|
34
37
|
?audience_id: String,
|
|
35
38
|
?categories: ::Array[Courier::TopicDigestCategory],
|
|
39
|
+
?schedules: ::Array[Courier::TopicDigestScheduleRequest],
|
|
36
40
|
?trigger_empty: bool
|
|
37
41
|
) -> void
|
|
38
42
|
|
|
39
43
|
def to_hash: -> {
|
|
40
|
-
schedules: ::Array[Courier::TopicDigestScheduleRequest],
|
|
41
44
|
template_id: String,
|
|
42
45
|
audience_id: String,
|
|
43
46
|
categories: ::Array[Courier::TopicDigestCategory],
|
|
47
|
+
schedules: ::Array[Courier::TopicDigestScheduleRequest],
|
|
44
48
|
trigger_empty: bool
|
|
45
49
|
}
|
|
46
50
|
end
|
|
@@ -6,7 +6,7 @@ module Courier
|
|
|
6
6
|
name: String,
|
|
7
7
|
allowed_preferences: ::Array[Courier::Models::WorkspacePreferenceTopicCreateRequest::allowed_preference]?,
|
|
8
8
|
description: String?,
|
|
9
|
-
digest: Courier::
|
|
9
|
+
digest: Courier::WorkspacePreferenceTopicCreateRequest::Digest?,
|
|
10
10
|
include_unsubscribe_header: bool?,
|
|
11
11
|
routing_options: ::Array[Courier::Models::channel_classification]?,
|
|
12
12
|
topic_data: ::Hash[Symbol, top]?
|
|
@@ -21,7 +21,7 @@ module Courier
|
|
|
21
21
|
|
|
22
22
|
attr_accessor description: String?
|
|
23
23
|
|
|
24
|
-
attr_accessor digest: Courier::
|
|
24
|
+
attr_accessor digest: Courier::WorkspacePreferenceTopicCreateRequest::Digest?
|
|
25
25
|
|
|
26
26
|
attr_accessor include_unsubscribe_header: bool?
|
|
27
27
|
|
|
@@ -34,7 +34,7 @@ module Courier
|
|
|
34
34
|
name: String,
|
|
35
35
|
?allowed_preferences: ::Array[Courier::Models::WorkspacePreferenceTopicCreateRequest::allowed_preference]?,
|
|
36
36
|
?description: String?,
|
|
37
|
-
?digest: Courier::
|
|
37
|
+
?digest: Courier::WorkspacePreferenceTopicCreateRequest::Digest?,
|
|
38
38
|
?include_unsubscribe_header: bool?,
|
|
39
39
|
?routing_options: ::Array[Courier::Models::channel_classification]?,
|
|
40
40
|
?topic_data: ::Hash[Symbol, top]?
|
|
@@ -45,7 +45,7 @@ module Courier
|
|
|
45
45
|
name: String,
|
|
46
46
|
allowed_preferences: ::Array[Courier::Models::WorkspacePreferenceTopicCreateRequest::allowed_preference]?,
|
|
47
47
|
description: String?,
|
|
48
|
-
digest: Courier::
|
|
48
|
+
digest: Courier::WorkspacePreferenceTopicCreateRequest::Digest?,
|
|
49
49
|
include_unsubscribe_header: bool?,
|
|
50
50
|
routing_options: ::Array[Courier::Models::channel_classification]?,
|
|
51
51
|
topic_data: ::Hash[Symbol, top]?
|
|
@@ -73,6 +73,51 @@ module Courier
|
|
|
73
73
|
|
|
74
74
|
def self?.values: -> ::Array[Courier::Models::WorkspacePreferenceTopicCreateRequest::allowed_preference]
|
|
75
75
|
end
|
|
76
|
+
|
|
77
|
+
type digest =
|
|
78
|
+
{
|
|
79
|
+
schedules: ::Array[Courier::TopicDigestScheduleRequest],
|
|
80
|
+
template_id: String,
|
|
81
|
+
audience_id: String,
|
|
82
|
+
categories: ::Array[Courier::TopicDigestCategory],
|
|
83
|
+
trigger_empty: bool
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
class Digest < Courier::Internal::Type::BaseModel
|
|
87
|
+
attr_accessor schedules: ::Array[Courier::TopicDigestScheduleRequest]
|
|
88
|
+
|
|
89
|
+
attr_accessor template_id: String
|
|
90
|
+
|
|
91
|
+
attr_reader audience_id: String?
|
|
92
|
+
|
|
93
|
+
def audience_id=: (String) -> String
|
|
94
|
+
|
|
95
|
+
attr_reader categories: ::Array[Courier::TopicDigestCategory]?
|
|
96
|
+
|
|
97
|
+
def categories=: (
|
|
98
|
+
::Array[Courier::TopicDigestCategory]
|
|
99
|
+
) -> ::Array[Courier::TopicDigestCategory]
|
|
100
|
+
|
|
101
|
+
attr_reader trigger_empty: bool?
|
|
102
|
+
|
|
103
|
+
def trigger_empty=: (bool) -> bool
|
|
104
|
+
|
|
105
|
+
def initialize: (
|
|
106
|
+
schedules: ::Array[Courier::TopicDigestScheduleRequest],
|
|
107
|
+
template_id: String,
|
|
108
|
+
?audience_id: String,
|
|
109
|
+
?categories: ::Array[Courier::TopicDigestCategory],
|
|
110
|
+
?trigger_empty: bool
|
|
111
|
+
) -> void
|
|
112
|
+
|
|
113
|
+
def to_hash: -> {
|
|
114
|
+
schedules: ::Array[Courier::TopicDigestScheduleRequest],
|
|
115
|
+
template_id: String,
|
|
116
|
+
audience_id: String,
|
|
117
|
+
categories: ::Array[Courier::TopicDigestCategory],
|
|
118
|
+
trigger_empty: bool
|
|
119
|
+
}
|
|
120
|
+
end
|
|
76
121
|
end
|
|
77
122
|
end
|
|
78
123
|
end
|
|
@@ -8,6 +8,7 @@ module Courier
|
|
|
8
8
|
def create: (
|
|
9
9
|
name: String,
|
|
10
10
|
nodes: ::Array[Courier::Models::journey_node],
|
|
11
|
+
?cancelation_token: String,
|
|
11
12
|
?enabled: bool,
|
|
12
13
|
?state: Courier::Models::journey_state,
|
|
13
14
|
?idempotency_key: String,
|
|
@@ -66,6 +67,7 @@ module Courier
|
|
|
66
67
|
String template_id,
|
|
67
68
|
name: String,
|
|
68
69
|
nodes: ::Array[Courier::Models::journey_node],
|
|
70
|
+
?cancelation_token: String,
|
|
69
71
|
?enabled: bool,
|
|
70
72
|
?state: Courier::Models::journey_state,
|
|
71
73
|
?request_options: Courier::request_opts
|
|
@@ -8,7 +8,7 @@ module Courier
|
|
|
8
8
|
name: String,
|
|
9
9
|
?allowed_preferences: ::Array[Courier::Models::WorkspacePreferenceTopicCreateRequest::allowed_preference]?,
|
|
10
10
|
?description: String?,
|
|
11
|
-
?digest: Courier::
|
|
11
|
+
?digest: Courier::WorkspacePreferenceTopicCreateRequest::Digest?,
|
|
12
12
|
?include_unsubscribe_header: bool?,
|
|
13
13
|
?routing_options: ::Array[Courier::Models::channel_classification]?,
|
|
14
14
|
?topic_data: ::Hash[Symbol, top]?,
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: trycourier
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 6.
|
|
4
|
+
version: 6.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Courier
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: cgi
|