Skip to content

Commit 323765c

Browse files
authored
Merge pull request #6 from Moesif/add-user-company-functions
Add: Functions to update User and Company profiles
2 parents 284875c + 6d382bd commit 323765c

File tree

5 files changed

+509
-54
lines changed

5 files changed

+509
-54
lines changed

README.md

+184
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,190 @@ Type: `Boolean`
179179

180180
`LOG_BODY` is default to true, set to false to remove logging request and response body to Moesif.
181181

182+
## Update User
183+
184+
### Update A Single User
185+
Create or update a user profile in Moesif.
186+
The metadata field can be any customer demographic or other info you want to store.
187+
Only the `user_id` field is required.
188+
For details, visit the [Python API Reference](https://www.moesif.com/docs/api?python#update-a-user).
189+
190+
```python
191+
from moesif_aws_lambda.middleware import *
192+
193+
moesif_options = {
194+
'LOG_BODY': True,
195+
'DEBUG': True,
196+
}
197+
198+
# Only user_id is required.
199+
# Campaign object is optional, but useful if you want to track ROI of acquisition channels
200+
# See https://www.moesif.com/docs/api#users for campaign schema
201+
# metadata can be any custom object
202+
user = {
203+
'user_id': '12345',
204+
'company_id': '67890', # If set, associate user with a company object
205+
'campaign': {
206+
'utm_source': 'google',
207+
'utm_medium': 'cpc',
208+
'utm_campaign': 'adwords',
209+
'utm_term': 'api+tooling',
210+
'utm_content': 'landing'
211+
},
212+
'metadata': {
213+
'email': 'john@acmeinc.com',
214+
'first_name': 'John',
215+
'last_name': 'Doe',
216+
'title': 'Software Engineer',
217+
'sales_info': {
218+
'stage': 'Customer',
219+
'lifetime_value': 24000,
220+
'account_owner': 'mary@contoso.com'
221+
},
222+
}
223+
}
224+
225+
update_user(user, moesif_options)
226+
```
227+
228+
### Update Users in Batch
229+
Similar to update_user, but used to update a list of users in one batch.
230+
Only the `user_id` field is required.
231+
For details, visit the [Python API Reference](https://www.moesif.com/docs/api?python#update-users-in-batch).
232+
233+
```python
234+
from moesif_aws_lambda.middleware import *
235+
236+
moesif_options = {
237+
'LOG_BODY': True,
238+
'DEBUG': True,
239+
}
240+
241+
userA = {
242+
'user_id': '12345',
243+
'company_id': '67890', # If set, associate user with a company object
244+
'metadata': {
245+
'email': 'john@acmeinc.com',
246+
'first_name': 'John',
247+
'last_name': 'Doe',
248+
'title': 'Software Engineer',
249+
'sales_info': {
250+
'stage': 'Customer',
251+
'lifetime_value': 24000,
252+
'account_owner': 'mary@contoso.com'
253+
},
254+
}
255+
}
256+
257+
userB = {
258+
'user_id': '54321',
259+
'company_id': '67890', # If set, associate user with a company object
260+
'metadata': {
261+
'email': 'mary@acmeinc.com',
262+
'first_name': 'Mary',
263+
'last_name': 'Jane',
264+
'title': 'Software Engineer',
265+
'sales_info': {
266+
'stage': 'Customer',
267+
'lifetime_value': 48000,
268+
'account_owner': 'mary@contoso.com'
269+
},
270+
}
271+
}
272+
update_users_batch([userA, userB], moesif_options)
273+
```
274+
275+
## Update Company
276+
277+
### Update A Single Company
278+
Create or update a company profile in Moesif.
279+
The metadata field can be any company demographic or other info you want to store.
280+
Only the `company_id` field is required.
281+
For details, visit the [Python API Reference](https://www.moesif.com/docs/api?python#update-a-company).
282+
283+
```python
284+
from moesif_aws_lambda.middleware import *
285+
286+
moesif_options = {
287+
'LOG_BODY': True,
288+
'DEBUG': True,
289+
}
290+
291+
# Only company_id is required.
292+
# Campaign object is optional, but useful if you want to track ROI of acquisition channels
293+
# See https://www.moesif.com/docs/api#update-a-company for campaign schema
294+
# metadata can be any custom object
295+
company = {
296+
'company_id': '67890',
297+
'company_domain': 'acmeinc.com', # If domain is set, Moesif will enrich your profiles with publicly available info
298+
'campaign': {
299+
'utm_source': 'google',
300+
'utm_medium': 'cpc',
301+
'utm_campaign': 'adwords',
302+
'utm_term': 'api+tooling',
303+
'utm_content': 'landing'
304+
},
305+
'metadata': {
306+
'org_name': 'Acme, Inc',
307+
'plan_name': 'Free',
308+
'deal_stage': 'Lead',
309+
'mrr': 24000,
310+
'demographics': {
311+
'alexa_ranking': 500000,
312+
'employee_count': 47
313+
},
314+
}
315+
}
316+
317+
update_company(company, moesif_options)
318+
```
319+
320+
### Update Companies in Batch
321+
Similar to update_company, but used to update a list of companies in one batch.
322+
Only the `company_id` field is required.
323+
For details, visit the [Python API Reference](https://www.moesif.com/docs/api?python#update-companies-in-batch).
324+
325+
```python
326+
from moesif_aws_lambda.middleware import *
327+
328+
moesif_options = {
329+
'LOG_BODY': True,
330+
'DEBUG': True,
331+
}
332+
333+
companyA = {
334+
'company_id': '67890',
335+
'company_domain': 'acmeinc.com', # If domain is set, Moesif will enrich your profiles with publicly available info
336+
'metadata': {
337+
'org_name': 'Acme, Inc',
338+
'plan_name': 'Free',
339+
'deal_stage': 'Lead',
340+
'mrr': 24000,
341+
'demographics': {
342+
'alexa_ranking': 500000,
343+
'employee_count': 47
344+
},
345+
}
346+
}
347+
348+
companyB = {
349+
'company_id': '09876',
350+
'company_domain': 'contoso.com', # If domain is set, Moesif will enrich your profiles with publicly available info
351+
'metadata': {
352+
'org_name': 'Contoso, Inc',
353+
'plan_name': 'Free',
354+
'deal_stage': 'Lead',
355+
'mrr': 48000,
356+
'demographics': {
357+
'alexa_ranking': 500000,
358+
'employee_count': 53
359+
},
360+
}
361+
}
362+
363+
update_companies_batch([companyA, companyB], moesif_options)
364+
```
365+
182366
## Examples
183367

184368
- [A complete example is available on GitHub](https://github.com/Moesif/moesif-aws-lambda-python-example).

moesif_aws_lambda/middleware.py

+90-53
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
from moesifapi.exceptions.api_exception import *
66
from moesifapi.models import *
77
from .client_ip import ClientIp
8+
from .update_companies import Company
9+
from .update_users import User
810
from datetime import *
911
import base64
1012
import json
@@ -16,6 +18,25 @@
1618
except ImportError:
1719
from urllib.parse import urlencode
1820

21+
# Initialized the client
22+
if os.environ["MOESIF_APPLICATION_ID"]:
23+
api_client = MoesifAPIClient(os.environ["MOESIF_APPLICATION_ID"]).api
24+
else:
25+
raise Exception('Moesif Application ID is required in settings')
26+
27+
def update_user(user_profile, moesif_options):
28+
User().update_user(user_profile, api_client, moesif_options)
29+
30+
def update_users_batch(user_profiles, moesif_options):
31+
User().update_users_batch(user_profiles, api_client, moesif_options)
32+
33+
def update_company(company_profile, moesif_options):
34+
Company().update_company(company_profile, api_client, moesif_options)
35+
36+
def update_companies_batch(companies_profiles, moesif_options):
37+
Company().update_companies_batch(companies_profiles, api_client, moesif_options)
38+
39+
1940
def MoesifLogger(moesif_options):
2041
class log_data(LambdaDecorator):
2142
def __init__(self, handler):
@@ -31,14 +52,23 @@ def __init__(self, handler):
3152
self.DEBUG = self.moesif_options.get('DEBUG', False)
3253
self.event = None
3354
self.context = None
34-
self.start_time = datetime.utcnow()
3555

3656
# Intialized the client
3757
if os.environ.get("MOESIF_APPLICATION_ID"):
3858
self.api_client = MoesifAPIClient(os.environ["MOESIF_APPLICATION_ID"]).api
3959
else:
4060
raise Exception('Moesif Application ID is required in settings')
4161

62+
def clear_state(self):
63+
"""Function to clear state of local variable"""
64+
self.event = None
65+
self.context = None
66+
self.event_req = None
67+
self.metadata = None
68+
self.session_token = None
69+
self.user_id = None
70+
self.company_id = None
71+
4272
def get_user_id(self, event, context):
4373
"""Function to fetch UserId"""
4474
username = None
@@ -109,10 +139,19 @@ def process_body(self, body_wrapper):
109139
def before(self, event, context):
110140
"""This function runs before the handler is invoked, is passed the event & context and must return an event & context too."""
111141

142+
# Clear the state of the local variables
143+
self.clear_state()
144+
145+
# Set/Save event and context for use Skip Event function
146+
self.event = event
147+
self.context = context
148+
112149
# Request Method
113150
request_verb = event.get('httpMethod')
114151
if request_verb is None:
115152
print('MOESIF: [before] AWS Lambda trigger must be a Load Balancer or API Gateway See https://docs.aws.amazon.com/lambda/latest/dg/services-alb.html or https://docs.aws.amazon.com/lambda/latest/dg/with-on-demand-https.html.')
153+
self.event = None
154+
self.context = None
116155
return event, context
117156

118157
# Request headers
@@ -128,9 +167,9 @@ def before(self, event, context):
128167
# Request Time
129168
epoch = event and event.get('request_context', {}).get('requestTimeEpoch')
130169
if epoch is not None:
131-
request_time =datetime.utcfromtimestamp(epoch)
170+
request_time = datetime.utcfromtimestamp(epoch)
132171
else:
133-
request_time = self.start_time
172+
request_time = datetime.utcnow()
134173

135174
# Request Body
136175
req_body, req_transfer_encoding = self.process_body(event)
@@ -213,66 +252,64 @@ def before(self, event, context):
213252
body = req_body,
214253
transfer_encoding = req_transfer_encoding)
215254

216-
# Set/Save event and context for use Skip Event function
217-
self.event = event
218-
self.context = context
219-
220255
# Return event, context
221256
return event, context
222257

223258
def after(self, retval):
224259
"""This function runs after the handler is invoked, is passed the response and must return an response too."""
225-
# Response body
226-
resp_body, resp_transfer_encoding = self.process_body(retval)
260+
261+
if self.event is not None:
262+
# Response body
263+
resp_body, resp_transfer_encoding = self.process_body(retval)
227264

228-
# Event Response object
229-
event_rsp = EventResponseModel(time = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3],
230-
status = retval.get('statusCode', 599),
231-
headers = retval.get('headers', {}),
232-
body = resp_body,
233-
transfer_encoding = resp_transfer_encoding)
265+
# Event Response object
266+
event_rsp = EventResponseModel(time = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3],
267+
status = retval.get('statusCode', 599),
268+
headers = retval.get('headers', {}),
269+
body = resp_body,
270+
transfer_encoding = resp_transfer_encoding)
234271

235-
# Event object
236-
event_model = EventModel(request = self.event_req,
237-
response = event_rsp,
238-
user_id = self.user_id,
239-
company_id = self.company_id,
240-
session_token = self.session_token,
241-
metadata = self.metadata)
242-
243-
# Mask Event Model
244-
try:
245-
mask_event_model = self.moesif_options.get('MASK_EVENT_MODEL', None)
246-
if mask_event_model is not None:
247-
event_model = mask_event_model(event_model)
248-
except:
249-
if self.DEBUG:
250-
print("MOESIF Can not execute MASK_EVENT_MODEL function. Please check moesif settings.")
272+
# Event object
273+
event_model = EventModel(request = self.event_req,
274+
response = event_rsp,
275+
user_id = self.user_id,
276+
company_id = self.company_id,
277+
session_token = self.session_token,
278+
metadata = self.metadata)
279+
280+
# Mask Event Model
281+
try:
282+
mask_event_model = self.moesif_options.get('MASK_EVENT_MODEL', None)
283+
if mask_event_model is not None:
284+
event_model = mask_event_model(event_model)
285+
except:
286+
if self.DEBUG:
287+
print("MOESIF Can not execute MASK_EVENT_MODEL function. Please check moesif settings.")
251288

252-
# Skip Event
253-
try:
254-
skip_event = self.moesif_options.get('SKIP', None)
255-
if skip_event is not None:
256-
if skip_event(self.event, self.context):
257-
if self.DEBUG:
258-
print('MOESIF Skip sending event to Moesif')
259-
return retval
260-
except:
261-
if self.DEBUG:
262-
print("MOESIF Having difficulty executing skip_event function. Please check moesif settings.")
289+
# Skip Event
290+
try:
291+
skip_event = self.moesif_options.get('SKIP', None)
292+
if skip_event is not None:
293+
if skip_event(self.event, self.context):
294+
if self.DEBUG:
295+
print('MOESIF Skip sending event to Moesif')
296+
return retval
297+
except:
298+
if self.DEBUG:
299+
print("MOESIF Having difficulty executing skip_event function. Please check moesif settings.")
263300

264-
# Add direction field
265-
event_model.direction = "Incoming"
301+
# Add direction field
302+
event_model.direction = "Incoming"
303+
304+
# Send event to Moesif
305+
if self.DEBUG:
306+
print('Moesif Event Model:')
307+
print(json.dumps(self.event))
308+
309+
event_send = self.api_client.create_event(event_model)
310+
if self.DEBUG:
311+
print('MOESIF ' + str(event_send))
266312

267-
# Send event to Moesif
268-
if self.DEBUG:
269-
print('Moesif Event Model:')
270-
pprint(self.event)
271-
272-
event_send = self.api_client.create_event(event_model)
273-
if self.DEBUG:
274-
print('MOESIF ' + str(event_send))
275-
276313
# Send response
277314
return retval
278315

0 commit comments

Comments
 (0)