Skip to content
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

LEARNER-9767 Create mobile skus for courses #4088

Open
wants to merge 5 commits into
base: 2u/main
Choose a base branch
from
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
"""
This command calls the edx-platform API to create mobile skus for given course keys
"""
import json
import logging

Check warning on line 5 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L4-L5

Added lines #L4 - L5 were not covered by tests

import requests
from django.core.management import BaseCommand, CommandError

Check warning on line 8 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L7-L8

Added lines #L7 - L8 were not covered by tests

logger = logging.getLogger(__name__)

Check warning on line 10 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L10

Added line #L10 was not covered by tests

LOCAL_LMS_BASE_URL = 'http://edx.devstack.lms:18000'
LOCAL_ECOMMERCE_BASE_URL = 'http://edx.devstack.ecommerce:18130'
STAGE_LMS_BASE_URL = 'https://courses.stage.edx.org'
STAGE_ECOMMERCE_BASE_URL = 'https://ecommerce.stage.edx.org'
PROD_LMS_BASE_URL = 'https://courses.edx.org'
PROD_ECOMMERCE_BASE_URL = 'https://ecommerce.edx.org'
CREATE_MOBILE_SKUS_URL = '/api/iap/v1/create-mobile-skus/'
OAUTH_LOGIN_URL = '/oauth2/access_token/'
INPUT_FILE_NAME = 'course_keys_for_mobile_skus.txt'
OUTPUT_FILE_NAME = 'mobile_skus_response.txt'
NEW_MOBILE_SKUS_KEY = 'new_mobile_skus'
FAILED_COURSE_IDS_KEY = 'failed_course_ids'
MISSING_COURSE_RUNS_KEY = 'missing_course_runs'
LOCAL_ENVIRONMENT = 'local'
STAGE_ENVIRONMENT = 'stage'
PROD_ENVIRONMENT = 'prod'

Check warning on line 27 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L12-L27

Added lines #L12 - L27 were not covered by tests


class FailedAuthentication(Exception):
pass

Check warning on line 31 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L30-L31

Added lines #L30 - L31 were not covered by tests


class Command(BaseCommand):

Check warning on line 34 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L34

Added line #L34 was not covered by tests
"""
Call edx-platform API /api/iap/v1/create-mobile-skus/ to create mobile skus for given course keys.
Format of response of the API /api/iap/v1/create-mobile-skus/ is as follows:
{
"new_mobile_skus": {
"course-v1:course-key": [
"mobile.android.sku",
"mobile.ios.sku"
]
},
"failed_course_ids": [],
"missing_course_runs": []
}
"""

help = 'Call edx-platform API to create mobile skus for given course keys.'

Check warning on line 50 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L50

Added line #L50 was not covered by tests

def add_arguments(self, parser):
parser.add_argument(

Check warning on line 53 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L52-L53

Added lines #L52 - L53 were not covered by tests
'--environment',
type=str,
default=1000,
help='Environment i.e. local/stage/prod')
parser.add_argument(

Check warning on line 58 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L58

Added line #L58 was not covered by tests
'--admin_username',
type=str,
default=1000,
help='Username of admin user')
parser.add_argument(

Check warning on line 63 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L63

Added line #L63 was not covered by tests
'--admin_password',
type=str,
default=1000,
help='Password of admin user')

def handle(self, *args, **options):
environment = options['environment']
admin_username = options['admin_username']
admin_password = options['admin_password']
base_lms_url, base_ecommerce_url = self._get_base_urls(environment)
create_skus_url = base_ecommerce_url + CREATE_MOBILE_SKUS_URL
login_url = base_lms_url + OAUTH_LOGIN_URL

Check warning on line 75 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L69-L75

Added lines #L69 - L75 were not covered by tests

headers = self._login_user_and_get_headers(admin_username, admin_password, login_url)
complete_response = {

Check warning on line 78 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L77-L78

Added lines #L77 - L78 were not covered by tests
NEW_MOBILE_SKUS_KEY: {},
FAILED_COURSE_IDS_KEY: [],
MISSING_COURSE_RUNS_KEY: [],
}
error_messages = []
try:
with open(INPUT_FILE_NAME, 'r+', encoding="utf-8") as input_file:
course_keys = input_file.readlines()
for course_key in course_keys:
payload = json.dumps({"courses": [course_key]})
response = requests.post(create_skus_url, data=payload, headers=headers)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we double the default timeout of requests?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are processing a single course in one request, are you sure we should double the request timeout?

if response.status_code == 401:
headers = self._login_user_and_get_headers(admin_username, admin_password, login_url)

Check warning on line 91 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L83-L91

Added lines #L83 - L91 were not covered by tests
# Retry request with fresh auth token
response = requests.post(create_skus_url, data=payload, headers=headers)

Check warning on line 93 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L93

Added line #L93 was not covered by tests

if response.status_code != 200:
error_message = 'Failed for course key {}. {}'.format(course_key, response.text)
error_messages.append(error_message)
continue

Check warning on line 98 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L95-L98

Added lines #L95 - L98 were not covered by tests

self._update_response_dictionary(response.json(), complete_response)
except FileNotFoundError as error:
raise CommandError('Input file with name {} does not exists.'.format(INPUT_FILE_NAME)) from error

Check warning on line 102 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L100-L102

Added lines #L100 - L102 were not covered by tests

self._write_response_to_file(complete_response)

Check warning on line 104 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L104

Added line #L104 was not covered by tests

def _login_user(self, username, password, login_url):

Check warning on line 106 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L106

Added line #L106 was not covered by tests
"""
Login the user given the username and password.
Return the JWT token if login successful.
Raise exception if login failed.
"""
payload = {

Check warning on line 112 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L112

Added line #L112 was not covered by tests
'client_id': 'login-service-client-id',
'grant_type': 'password',
'username': username,
'password': password,
'token_type': 'jwt'
}
response = requests.post(login_url, payload)
if not response.status_code == 200:
raise FailedAuthentication

Check warning on line 121 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L119-L121

Added lines #L119 - L121 were not covered by tests

jwt_token = response.json().get('access_token')
return jwt_token

Check warning on line 124 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L123-L124

Added lines #L123 - L124 were not covered by tests

def _login_user_and_get_headers(self, admin_username, admin_password, login_url):

Check warning on line 126 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L126

Added line #L126 was not covered by tests
""" Login user and return auth headers """
try:
jwt_token = self._login_user(admin_username, admin_password, login_url)
except FailedAuthentication as error:
raise CommandError('Login failed') from error
headers = self._get_headers(jwt_token)
return headers

Check warning on line 133 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L128-L133

Added lines #L128 - L133 were not covered by tests

def _get_headers(self, jwt_token):

Check warning on line 135 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L135

Added line #L135 was not covered by tests
""" Return related headers for requests """
return {

Check warning on line 137 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L137

Added line #L137 was not covered by tests
'Authorization': 'JWT ' + jwt_token,
'Content-Type': 'application/json',
}

def _get_base_urls(self, environment):

Check warning on line 142 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L142

Added line #L142 was not covered by tests
""" Get base url for LMS and Ecommerce based on environment selected """
base_lms_url = LOCAL_LMS_BASE_URL
base_ecommerce_url = LOCAL_ECOMMERCE_BASE_URL
if environment == STAGE_ENVIRONMENT:
base_lms_url = STAGE_LMS_BASE_URL
base_ecommerce_url = STAGE_ECOMMERCE_BASE_URL
elif environment == PROD_ENVIRONMENT:
base_lms_url = PROD_LMS_BASE_URL
base_ecommerce_url = PROD_ECOMMERCE_BASE_URL

Check warning on line 151 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L144-L151

Added lines #L144 - L151 were not covered by tests

return base_lms_url, base_ecommerce_url

Check warning on line 153 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L153

Added line #L153 was not covered by tests

def _update_response_dictionary(self, new_response, complete_response):

Check warning on line 155 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L155

Added line #L155 was not covered by tests
""" Update the complete_response dict with the contents of the new response """
if new_response.get(NEW_MOBILE_SKUS_KEY):
new_mobile_skus = new_response.get(NEW_MOBILE_SKUS_KEY)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to add if here we can use these lines:
complete_response.update(new_response[NEW_MOBILE_SKUS_KEY])
complete_response[FAILED_COURSE_IDS_KEY].extend(new_response[FAILED_COURSE_IDS_KEY])
complete_response[MISSING_COURSE_RUNS_KEY].extend(new_response[MISSING_COURSE_RUNS_KEY])

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dict.update does not append, but replaces in the second level of the dict. hence not using it.

for key, value in new_mobile_skus.items():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we adjust this script to include another array for ios errors?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, done.

complete_response[NEW_MOBILE_SKUS_KEY][key] = value
if new_response.get(FAILED_COURSE_IDS_KEY):
complete_response[FAILED_COURSE_IDS_KEY] += new_response.get(FAILED_COURSE_IDS_KEY)
if new_response.get(MISSING_COURSE_RUNS_KEY):
complete_response[MISSING_COURSE_RUNS_KEY] += new_response.get(MISSING_COURSE_RUNS_KEY)

Check warning on line 164 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L157-L164

Added lines #L157 - L164 were not covered by tests

def _write_response_to_file(self, response):
with open(OUTPUT_FILE_NAME, 'w+', encoding="utf-8") as output_file:
output_file.write(json.dumps(response))

Check warning on line 168 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L166-L168

Added lines #L166 - L168 were not covered by tests
Loading