-
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathstorage-uploader.h
More file actions
197 lines (162 loc) · 7.28 KB
/
Copy pathstorage-uploader.h
File metadata and controls
197 lines (162 loc) · 7.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#ifndef STORAGE_UPLOADER_H
#define STORAGE_UPLOADER_H
#include <vector>
#include <string>
#include <iomanip>
#include <sstream>
#include <ctime>
#include <fstream>
#include <filesystem>
#include <atomic>
#include <chrono>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_sinks.h>
#include <memory>
#include <aws/core/auth/AWSCredentials.h>
// Forward declaration of Session
class Session;
enum class RecordFileType {
WAV,
MP3,
UNKNOWN
};
struct Metadata_t {
std::string account_sid;
std::string call_sid;
std::string direction;
std::string from;
std::string to;
std::string application_sid;
std::string originating_sip_id;
std::string originating_sip_trunk_name;
/* identity fields forwarded to call-eval vendors (EVAL-INTEGRATION-DESIGN.md metadata
set); all optional -- older feature-servers may not send them */
std::string sip_call_id;
std::string caller_name;
std::string trace_id;
std::string customer_data_json; // the createCall tag, serialized verbatim
uint32_t sample_rate;
};
class StorageUploader {
public:
StorageUploader(const std::shared_ptr<Session>& session) : sessionRef_(session) {
}
virtual ~StorageUploader() {
}
// Upload method to be implemented by derived classes
virtual bool upload(std::vector<char>& data, bool isFinalChunk = false) = 0;
// Store metadata
void setMetadata(const struct Metadata_t& metadata) {
metadata_ = metadata;
}
// Session summary (observability) — set before final upload
void setSessionSummary(const std::string& json) {
sessionSummaryJson_ = json;
}
bool hasSessionSummary() const {
return !sessionSummaryJson_.empty();
}
// Audio start timestamp for recording offset calculation
void setAudioStartTime(std::chrono::system_clock::time_point t) {
audioStartTime_ = t;
audioStartTimeSet_ = true;
}
void setLogger(std::shared_ptr<spdlog::logger> log) {
log_ = log;
}
// Call-evaluation (Roark, ...) credential, decrypted and parsed by Session. Empty
// vendor/apiKey means the integration is off for this account -- the overwhelmingly
// common case, so postUploadHook() must early-return on it with no further work.
void setEvalCredential(const std::string& vendor, const std::string& apiKey,
int samplingPercent) {
evalVendor_ = vendor;
evalApiKey_ = apiKey;
evalSamplingPercent_ = samplingPercent;
}
// Bucket info needed to presign a GET url for the recording. Only called by
// Session::createStorageUploader() for aws_s3/s3_compatible backends; left unset
// (presignInfoSet_ stays false) for google/azure, which postUploadHook() treats as
// "log and skip" per the v1 design.
void setPresignInfo(const Aws::Auth::AWSCredentials& credentials, const std::string& region,
const std::string& bucket, const std::string& customEndpoint) {
presignCredentials_ = credentials;
presignRegion_ = region;
presignBucket_ = bucket;
presignEndpoint_ = customEndpoint;
presignInfoSet_ = true;
}
// GCS variant: a V4 signed URL is built from the account's service-account key
// (see gcs-presigner.h). Set by Session::createStorageUploader for google buckets.
void setGcsPresignInfo(const std::string& bucket, const std::string& clientEmail,
const std::string& privateKeyPem) {
gcsPresignBucket_ = bucket;
gcsPresignClientEmail_ = clientEmail;
gcsPresignPrivateKey_ = privateKeyPem;
gcsPresignInfoSet_ = true;
}
protected:
// Create a unique temporary file
void createTempFile(const std::string& uploadFolder);
// Cleanup the temporary file
void cleanupTempFile();
// Create the object path for upload
std::string createObjectPath(const std::string& callSid, const std::string& recordFormat);
// Create session.json object path: YYYY/MM/DD/{callSid}/session.json
std::string createSessionJsonPath(const std::string& callSid);
// Stamp recording_key into sessionSummaryJson_ and return the final JSON body. Also
// caches the stamped body in stampedSessionSummaryJson_ -- the stamped
// recording_started_at_ms is required for eval-notify transcript offsets, so the
// hook re-reads it from there rather than re-deriving it.
// Returns empty string on failure.
std::string stampAndSerializeSessionSummary(const std::string& recordingKey);
// Get current date prefix as "YYYY/MM/DD/"
static std::string currentDatePrefix();
// Upload session.json to storage — called from finalizeUpload after audio upload.
// Returns whether session.json actually landed; the eval-notify hook only attaches a
// transcript when this returned true (audio-only is a valid send otherwise).
virtual bool uploadSessionSummary(const std::string& objectKey) = 0;
// Notify a call-evaluation vendor (Roark, ...) after the recording (and, if present,
// session.json) have landed. Must be called after uploadSessionSummary() and before
// cleanupTempFile() -- cleanupTempFile() destroys the session and this uploader with
// it. Never throws: any vendor/network failure is logged and swallowed here so it can
// never affect the recording upload or call processing. Early-returns with no work at
// all when no eval credential was set (the overwhelmingly common case).
void postUploadHook(const std::string& recordingKey);
std::shared_ptr<spdlog::logger> log_;
struct Metadata_t metadata_;
std::string sessionSummaryJson_;
std::string stampedSessionSummaryJson_; // set by stampAndSerializeSessionSummary()
bool sessionSummaryUploaded_ = false; // set by the caller once uploadSessionSummary() returns
bool upload_in_progress_ = false;
bool upload_failed_ = false;
// Audio start timestamp for recording offset calculation
std::chrono::system_clock::time_point audioStartTime_;
bool audioStartTimeSet_ = false;
// Call-evaluation credential (empty = integration off) and, for aws_s3/s3_compatible
// backends only, the bucket info needed to presign a GET url for the recording.
std::string evalVendor_;
std::string evalApiKey_;
// Percentage of calls forwarded to the vendor. Defaults to "everything" so a
// credential stored before this field existed keeps its original behaviour.
int evalSamplingPercent_ = 100;
bool presignInfoSet_ = false;
Aws::Auth::AWSCredentials presignCredentials_;
std::string presignRegion_;
std::string presignBucket_;
std::string presignEndpoint_;
bool gcsPresignInfoSet_ = false;
std::string gcsPresignBucket_;
std::string gcsPresignClientEmail_;
std::string gcsPresignPrivateKey_;
std::string uploadFolder_;
std::string tempFilePath_;
std::ofstream tempFile_;
// Create a mkstemp-compatible temp file in uploadFolder_ and return its fd and path.
// Returns -1 on failure.
int createMkstempFile(const std::string& prefix, std::string& outPath);
// weak reference to the session so we can trigger its destruction after upload completion
std::weak_ptr<Session> sessionRef_;
// Static atomic counter for generating unique file names
static std::atomic<int> uniqueCounter;
};
#endif // STORAGE_UPLOADER_H