Repository navigation
[CDX-708] Add the 9 Product Insights Agent tracking events #290
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
78d85aa
a6592f1
24b6dfd
2d416a3
0ea69cd
90c2610
e7178d5
6acebdf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| // | ||
| // CIOProductInsightsAgentFeedbackLabel.swift | ||
| // Constructor.io | ||
| // | ||
| // Copyright (c) Constructor.io Corporation. All rights reserved. | ||
| // http://constructor.io/ | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| /** | ||
| Enum representing the feedback a user can give on a Product Insights Agent answer | ||
| */ | ||
| public enum CIOProductInsightsAgentFeedbackLabel: String { | ||
|
|
||
| /** | ||
| The user found the answer useful | ||
| */ | ||
| case thumbsUp = "thumbs_up" | ||
|
|
||
| /** | ||
| The user did not find the answer useful | ||
| */ | ||
| case thumbsDown = "thumbs_down" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| // | ||
| // CIOProductInsightsAgentQuestion.swift | ||
| // Constructor.io | ||
| // | ||
| // Copyright (c) Constructor.io Corporation. All rights reserved. | ||
| // http://constructor.io/ | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| /** | ||
| Struct representing a question shown to the user by the Product Insights Agent | ||
| */ | ||
| public struct CIOProductInsightsAgentQuestion { | ||
|
|
||
| /** | ||
| The question text (1-1000 characters) | ||
| */ | ||
| public let question: String | ||
|
|
||
| /** | ||
| The topic category of the question | ||
| */ | ||
| public let questionTopic: String? | ||
|
|
||
| /** | ||
| Create a Product Insights Agent question | ||
|
|
||
| - Parameters: | ||
| - question: The question text (1-1000 characters) | ||
| - questionTopic: The topic category of the question | ||
| */ | ||
| public init(question: String, questionTopic: String? = nil) { | ||
| self.question = question | ||
| self.questionTopic = questionTopic | ||
| } | ||
|
|
||
| func toDictionary() -> [String: String] { | ||
| var dict = ["question": self.question] | ||
| if let questionTopic = self.questionTopic { | ||
| dict["question_topic"] = questionTopic | ||
| } | ||
| return dict | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| // | ||
| // CIOProductInsightsAgentViewTimespan.swift | ||
| // Constructor.io | ||
| // | ||
| // Copyright (c) Constructor.io Corporation. All rights reserved. | ||
| // http://constructor.io/ | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| /** | ||
| Struct representing a period of time during which the Product Insights Agent was visible to the user | ||
| */ | ||
| public struct CIOProductInsightsAgentViewTimespan { | ||
|
|
||
| /** | ||
| The time the Product Insights Agent became visible | ||
| */ | ||
| public let start: Date | ||
|
|
||
| /** | ||
| The time the Product Insights Agent stopped being visible | ||
| */ | ||
| public let end: Date | ||
|
|
||
| /** | ||
| Create a Product Insights Agent view timespan | ||
|
|
||
| - Parameters: | ||
| - start: The time the Product Insights Agent became visible | ||
| - end: The time the Product Insights Agent stopped being visible | ||
| */ | ||
| public init(start: Date, end: Date) { | ||
| self.start = start | ||
| self.end = end | ||
| } | ||
|
|
||
| // A fixed-format DateFormatter is used instead of ISO8601DateFormatter because setting | ||
| // `.withFractionalSeconds` throws an exception on iOS 11.0 and 11.1. DateFormatter is thread-safe, | ||
| // so a single shared instance is used for all timespans | ||
| static let dateFormatter: DateFormatter = { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: This is a documentation nit only — the current code is correct. |
||
| let formatter = DateFormatter() | ||
| formatter.locale = Locale(identifier: "en_US_POSIX") | ||
| formatter.calendar = Calendar(identifier: .gregorian) | ||
| formatter.timeZone = TimeZone(secondsFromGMT: 0) | ||
| formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" | ||
| return formatter | ||
| }() | ||
|
|
||
| // Dates are formatted as ISO-8601 strings in UTC (e.g. "2026-10-22T02:40:05.123Z") so the body stays JSON-serializable | ||
| func toDictionary() -> [String: String] { | ||
| return [ | ||
| "start": CIOProductInsightsAgentViewTimespan.dateFormatter.string(from: self.start), | ||
| "end": CIOProductInsightsAgentViewTimespan.dateFormatter.string(from: self.end) | ||
| ] | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| // | ||
| // CIOTrackProductInsightsAgentAnswerFeedbackData.swift | ||
| // Constructor.io | ||
| // | ||
| // Copyright (c) Constructor.io Corporation. All rights reserved. | ||
| // http://constructor.io/ | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| /** | ||
| Struct encapsulating the parameters that must/can be set in order to track a Product Insights Agent answer feedback | ||
| */ | ||
| struct CIOTrackProductInsightsAgentAnswerFeedbackData: CIOTrackProductInsightsAgentData { | ||
|
|
||
| let itemID: String | ||
| let feedbackLabel: CIOProductInsightsAgentFeedbackLabel | ||
| let qnaResultID: String? | ||
| let itemName: String? | ||
| let variationID: String? | ||
| let threadID: String? | ||
| let sectionName: String? | ||
| let analyticsTags: [String: String]? | ||
|
|
||
| func url(with baseURL: String) -> String { | ||
| return String(format: Constants.TrackProductInsightsAgentAnswerFeedback.format, baseURL) | ||
| } | ||
|
|
||
| init(itemID: String, feedbackLabel: CIOProductInsightsAgentFeedbackLabel, qnaResultID: String? = nil, itemName: String? = nil, variationID: String? = nil, threadID: String? = nil, sectionName: String? = nil, analyticsTags: [String: String]? = nil) { | ||
| self.itemID = itemID | ||
| self.feedbackLabel = feedbackLabel | ||
| self.qnaResultID = qnaResultID | ||
| self.itemName = itemName | ||
| self.variationID = variationID | ||
| self.threadID = threadID | ||
| self.sectionName = sectionName | ||
| self.analyticsTags = analyticsTags | ||
| } | ||
|
|
||
| func eventFields() -> [String: Any] { | ||
| var dict = [ | ||
| "feedback_label": self.feedbackLabel.rawValue | ||
| ] as [String: Any] | ||
|
|
||
| if let qnaResultID = self.qnaResultID { | ||
| dict["qna_result_id"] = qnaResultID | ||
| } | ||
|
|
||
| return dict | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| // | ||
| // CIOTrackProductInsightsAgentAnswerFeedbackRequest.swift | ||
| // Constructor.io | ||
| // | ||
| // Copyright (c) Constructor.io Corporation. All rights reserved. | ||
| // http://constructor.io/ | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| /** | ||
| Struct encapsulating the parameters that can be passed to constructorIO.trackProductInsightsAgentAnswerFeedback() function. | ||
| */ | ||
| public struct CIOTrackProductInsightsAgentAnswerFeedbackRequest { | ||
|
|
||
| /** | ||
| The ID of the product whose page the Product Insights Agent is on | ||
| */ | ||
| public let itemID: String | ||
|
|
||
| /** | ||
| The feedback the user gave on the answer | ||
| */ | ||
| public let feedbackLabel: CIOProductInsightsAgentFeedbackLabel | ||
|
|
||
| /** | ||
| The ID of the answer result returned by the Product Insights Agent | ||
| */ | ||
| public let qnaResultID: String? | ||
|
|
||
| /** | ||
| The name of the product whose page the Product Insights Agent is on | ||
| */ | ||
| public let itemName: String? | ||
|
|
||
| /** | ||
| The variation ID of the product whose page the Product Insights Agent is on | ||
| */ | ||
| public let variationID: String? | ||
|
|
||
| /** | ||
| The ID used to group events within a single conversation thread | ||
| */ | ||
| public let threadID: String? | ||
|
|
||
| /** | ||
| The name of the section the item belongs to (defaults to the configured default section, or "Products") | ||
| */ | ||
| public let sectionName: String? | ||
|
|
||
| /** | ||
| Additional custom analytics tags to be sent with the event. Merged with the default analytics tags | ||
| */ | ||
| public let analyticsTags: [String: String]? | ||
|
|
||
| /** | ||
| Create a Product Insights Agent answer feedback request | ||
|
|
||
| - Parameters: | ||
| - itemID: The ID of the product whose page the Product Insights Agent is on | ||
| - feedbackLabel: The feedback the user gave on the answer | ||
| - qnaResultID: The ID of the answer result returned by the Product Insights Agent | ||
| - itemName: The name of the product whose page the Product Insights Agent is on | ||
| - variationID: The variation ID of the product whose page the Product Insights Agent is on | ||
| - threadID: The ID used to group events within a single conversation thread | ||
| - sectionName: The name of the section the item belongs to (defaults to the configured default section, or "Products") | ||
| - analyticsTags: Additional custom analytics tags to be sent with the event. Merged with the default analytics tags | ||
| */ | ||
| public init(itemID: String, feedbackLabel: CIOProductInsightsAgentFeedbackLabel, qnaResultID: String? = nil, itemName: String? = nil, variationID: String? = nil, threadID: String? = nil, sectionName: String? = nil, analyticsTags: [String: String]? = nil) { | ||
| self.itemID = itemID | ||
| self.feedbackLabel = feedbackLabel | ||
| self.qnaResultID = qnaResultID | ||
| self.itemName = itemName | ||
| self.variationID = variationID | ||
| self.threadID = threadID | ||
| self.sectionName = sectionName | ||
| self.analyticsTags = analyticsTags | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion:
toImpressionDictionary()isinternal(no access modifier) but it's used by two callers: the existingCIOTrackResultsImpressionViewDataand the newCIOTrackProductInsightsAgentAnswerViewData. The method's comment accurately describes the shared shape. Consider whetherinternalis the right default here versus making itfileprivate— but given cross-file usage is intentional,internalis correct.One minor naming nit: the method is on
CIOItembut the shape it produces is also used by PIA'sanswer_viewevent. The existing comment (// Builds the item object sent by trackResultsImpressionView and trackProductInsightsAgentAnswerView) has been updated to reflect the two callers, which is good. No change required.