Skip to content

Commit b4f88e0

Browse files
committed
Refactor code
1 parent 69aab1d commit b4f88e0

File tree

7 files changed

+112
-204
lines changed

7 files changed

+112
-204
lines changed

webjobs/user_data_sync/authentication_helper.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class AuthenticationHelper(object):
1010

1111
def __init__(self, cert_path, cert_password):
1212
self.__cert_private_key, self.__cert_thumbprint = \
13-
self.__get_certificate_private_key_and_thumbnail(cert_path, cert_password)
13+
self._get_certificate_private_key_and_thumbnail(cert_path, cert_password)
1414

1515
def get_app_only_access_token(self, tenant_id, client_id, resource):
1616

@@ -25,7 +25,7 @@ def get_app_only_access_token(self, tenant_id, client_id, resource):
2525

2626
return token['accessToken']
2727

28-
def __get_certificate_private_key_and_thumbnail(self, cert_path, cert_password):
28+
def _get_certificate_private_key_and_thumbnail(self, cert_path, cert_password):
2929

3030
with open(cert_path, 'rb') as cert_file:
3131
pkcs12 = crypto.load_pkcs12(cert_file.read(), cert_password)

webjobs/user_data_sync/constants.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,10 @@
55
client_cert_password = os.environ['ClientCertificatePassword']
66

77
aad_instance = 'https://login.microsoftonline.com/'
8-
ms_graph_resource = 'https://graph.microsoft.com'
8+
ms_graph_resource = 'https://graph.microsoft.com'
9+
10+
mysql_host = os.environ['MySQLHost']
11+
mysql_port = 3306
12+
mysql_name = 'edu'
13+
mysql_user = os.environ['MySQLUser']
14+
mysql_password = os.environ['MySQLPassword']

webjobs/user_data_sync/models.py

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,30 @@
11
import os
2+
import constants
23
from peewee import MySQLDatabase, Model, BooleanField, CharField, TextField, DateTimeField, ForeignKeyField
34

4-
mysql_host = os.environ['MySQLHost']
5-
mysql_port = int('3306')
6-
mysql_name = 'edu'
7-
mysql_user = os.environ['MySQLUser']
8-
mysql_password = os.environ['MySQLPassword']
9-
10-
db = MySQLDatabase(mysql_name, user=mysql_user, password=mysql_password, host=mysql_host, port=mysql_port)
5+
database = MySQLDatabase(constants.mysql_name,
6+
user=constants.mysql_user,
7+
password=constants.mysql_password,
8+
host=constants.mysql_host,
9+
port=constants.mysql_port)
1110

1211
class BaseModel(Model):
1312
class Meta:
14-
database = db
13+
database = database
1514

1615
class Organization(BaseModel):
17-
# id = AutoField(primary_key=True)
1816
name = CharField()
1917
tenantId = CharField()
2018
isAdminConsented = BooleanField()
19+
20+
@staticmethod
21+
def get_consented():
22+
return Organization.select().where(Organization.isAdminConsented)
23+
2124
class Meta:
2225
db_table = "organizations"
2326

2427
class Profile(BaseModel):
25-
# user = OneToOneField(User, on_delete=None)
2628
o365UserId = CharField()
2729
o365Email = CharField()
2830
jobTitle = CharField()
@@ -38,20 +40,4 @@ class DataSyncRecord(BaseModel):
3840
deltaLink = TextField()
3941
updated = DateTimeField(null=True)
4042
class Meta:
41-
db_table = 'data_sync_records'
42-
43-
# db.connect()
44-
45-
# organizations = Organization.select() \
46-
# .where(Organization.isAdminConsented)
47-
48-
# for org in organizations:
49-
# print(org.name)
50-
51-
52-
# profiles = Profile.select()
53-
54-
# for profile in profiles:
55-
# print(profile.o365Email)
56-
57-
# db.close()
43+
db_table = 'data_sync_records'

webjobs/user_data_sync/ms_graph.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import json
2+
import requests
3+
from urllib.parse import urlencode
4+
5+
class GraphServiceClient(object):
6+
7+
def __init__(self, base_url, access_token):
8+
self._base_url = base_url
9+
self._access_token = access_token
10+
11+
def get_users_delta(self, query_dict={}):
12+
url = self._base_url + '/v1.0/users/delta?' + urlencode(query_dict)
13+
return self.get_users(url)
14+
15+
def get_users(self, absolute_url):
16+
res = self._http_get_json(absolute_url)
17+
return res['value'], res.get('@odata.nextLink'), res.get('@odata.deltaLink')
18+
19+
def _http_get_json(self, absolute_url):
20+
headers = {
21+
'Accept': 'application/json',
22+
'Authorization': 'Bearer {0}'.format(self._access_token) }
23+
response = requests.get(absolute_url, headers=headers)
24+
return json.loads(response.text)

webjobs/user_data_sync/rest_api_service.py

Lines changed: 0 additions & 112 deletions
This file was deleted.

webjobs/user_data_sync/start.py

Lines changed: 6 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,11 @@
11
import constants
2+
from models import database
3+
from user_data_sync_service import UserDataSyncService
24

3-
import datetime
45

6+
database.connect()
57

6-
from rest_api_service import RestApiService
7-
import models
8+
user_data_sync_service = UserDataSyncService()
9+
user_data_sync_service.sync()
810

9-
from authentication_helper import AuthenticationHelper
10-
11-
12-
users_query = "/users/delta?$select=jobTitle,department,mobilePhone"
13-
14-
authentication_helper = AuthenticationHelper(constants.client_cert_path, constants.client_cert_password)
15-
16-
models.db.connect()
17-
18-
19-
url = constants.ms_graph_resource + '/v1.0/users/delta?$select=jobTitle,department,mobilePhone'
20-
21-
rest_api_service = RestApiService()
22-
23-
24-
organizations = models.Organization.select().where(models.Organization.isAdminConsented)
25-
for organization in organizations:
26-
27-
print('Sync tenant ' + organization.name)
28-
29-
data_sync_record, created = models.DataSyncRecord.get_or_create(
30-
tenantId = organization.tenantId,
31-
query = users_query)
32-
33-
if created:
34-
url = constants.ms_graph_resource + '/v1.0' + users_query
35-
else:
36-
url = data_sync_record.deltaLink
37-
38-
access_token = authentication_helper.get_app_only_access_token(
39-
organization.tenantId,
40-
constants.client_id,
41-
constants.ms_graph_resource)
42-
43-
while True:
44-
res = rest_api_service.get_json(url, access_token)
45-
users = res['value']
46-
47-
for user in users:
48-
profile = models.Profile.get_or_none(models.Profile.o365UserId == user['id'])
49-
if profile:
50-
profile.jobTitle = user['jobTitle']
51-
profile.department = user['department']
52-
profile.mobilePhone = user['mobilePhone']
53-
print('update user: ' + profile.o365Email)
54-
profile.save()
55-
56-
next_link = res.get('@odata.nextLink')
57-
if next_link:
58-
url = next_link
59-
else:
60-
break;
61-
62-
63-
data_sync_record.deltaLink = res.get('@odata.deltaLink')
64-
data_sync_record.updated = datetime.datetime.now()
65-
data_sync_record.save()
66-
67-
models.db.close()
11+
database.close()
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import constants
2+
import datetime
3+
4+
from authentication_helper import AuthenticationHelper
5+
from models import database, Organization, DataSyncRecord, Profile
6+
from ms_graph import GraphServiceClient
7+
8+
class UserDataSyncService(object):
9+
10+
def __init__(self):
11+
self._users_query = "users"
12+
self._auth_helper = AuthenticationHelper(constants.client_cert_path, constants.client_cert_password)
13+
14+
def sync(self):
15+
for organization in Organization.get_consented():
16+
self.sync_organization(organization)
17+
18+
def sync_organization(self, organization):
19+
print('Sync tenant ' + organization.name)
20+
21+
client = self._get_graph_service_client(organization.tenantId)
22+
23+
record, is_new_record = DataSyncRecord.get_or_create(
24+
tenantId = organization.tenantId,
25+
query = self._users_query)
26+
27+
if is_new_record:
28+
query = { '$select': 'jobTitle,department,mobilePhone' }
29+
users, next_link, delta_link = client.get_users_delta(query)
30+
else:
31+
users, next_link, delta_link = client.get_users(record.deltaLink)
32+
33+
while True:
34+
for user in users:
35+
profile = Profile.get_or_none(Profile.o365UserId == user['id'])
36+
if profile:
37+
self._update_profile(profile, user)
38+
if next_link:
39+
users, next_link, delta_link = client.get_users(next_link)
40+
else:
41+
break
42+
43+
self._update_data_sync_record(record, delta_link)
44+
45+
def _update_profile(self, profile, user):
46+
print('update user: ' + profile.o365Email)
47+
profile.jobTitle = user['jobTitle']
48+
profile.department = user['department']
49+
profile.mobilePhone = user['mobilePhone']
50+
profile.save()
51+
52+
def _update_data_sync_record(self, record, delta_link):
53+
record.deltaLink = delta_link
54+
record.updated = datetime.datetime.now()
55+
record.save()
56+
57+
def _get_graph_service_client(self, tenant_id):
58+
access_token = self._auth_helper.get_app_only_access_token(
59+
tenant_id, constants.client_id, constants.ms_graph_resource)
60+
return GraphServiceClient(constants.ms_graph_resource, access_token)

0 commit comments

Comments
 (0)