def test_zoom_api_request_missing_creds(): common.APIGEE_KEY = None # (ZOOM_API_KEY, ZOOM_API_SECRET) cases = [(None, None), ("key", None), (None, "secret")] for key, secret in cases: common.ZOOM_API_KEY = key common.ZOOM_API_SECRET = secret with pytest.raises(Exception) as exc_info: common.zoom_api_request("meetings") assert exc_info.match("Missing api credentials.")
def test_apigee_key(caplog): common.APIGEE_KEY = "apigee_key" common.ZOOM_API_KEY = None common.ZOOM_API_SECRET = None caplog.set_level(logging.INFO) common.zoom_api_request("meetings") assert "apigee request" in caplog.text.lower() # Should still use apigee key even if zoom api key/secret defined common.ZOOM_API_KEY = "key" common.ZOOM_API_SECRET = "secret" common.zoom_api_request("meetings") assert "apigee request" in caplog.text.lower()
def test_zoom_api_key(caplog): common.APIGEE_KEY = None common.ZOOM_API_KEY = "key" common.ZOOM_API_SECRET = "secret" caplog.set_level(logging.INFO) common.zoom_api_request("meetings") assert "zoom api request" in caplog.text.lower() common.APIGEE_KEY = "" common.ZOOM_API_KEY = "key" common.ZOOM_API_SECRET = "secret" caplog.set_level(logging.INFO) common.zoom_api_request("meetings") assert "zoom api request" in caplog.text.lower()
def test_zoom_api_request_success(): # test successful call with requests_mock.mock() as req_mock: req_mock.get(requests_mock.ANY, status_code=200, json={"mock_payload": 123}) r = zoom_api_request("meetings") assert "mock_payload" in r.json()
def host_name(self): if not hasattr(self, "_host_name"): resp = zoom_api_request("users/{}".format( self.data["host_id"])).json() logger.info({"Host details": resp}) self._host_name = "{} {}".format(resp["first_name"], resp["last_name"]) return self._host_name
def test_zoom_api_request_failures(): # test failed call that returns with requests_mock.mock() as req_mock: req_mock.get(requests_mock.ANY, status_code=400, json={"mock_payload": 123}) r = zoom_api_request("meetings", ignore_failure=True) assert r.status_code == 400 # test failed call that raises with requests_mock.mock() as req_mock: req_mock.get(requests_mock.ANY, status_code=400, json={"mock_payload": 123}) error_msg = "400 Client Error" with pytest.raises(requests.exceptions.HTTPError, match=error_msg): zoom_api_request("meetings", ignore_failure=False, retries=0) # test ConnectionError handling with requests_mock.mock() as req_mock: req_mock.get(requests_mock.ANY, exc=requests.exceptions.ConnectionError) error_msg = "Error requesting https://api.zoom.us/v2/meetings" with pytest.raises(ZoomApiRequestError, match=error_msg): zoom_api_request("meetings") # test ConnectTimeout handling with requests_mock.mock() as req_mock: req_mock.get(requests_mock.ANY, exc=requests.exceptions.ConnectTimeout) error_msg = "Error requesting https://api.zoom.us/v2/meetings" with pytest.raises(ZoomApiRequestError, match=error_msg): zoom_api_request("meetings")
def test_url_construction(caplog): common.APIGEE_KEY = None common.ZOOM_API_KEY = "key" common.ZOOM_API_SECRET = "secret" caplog.set_level(logging.INFO) cases = [ ("https://www.foo.com", "meetings", "https://www.foo.com/meetings"), ("https://www.foo.com/", "meetings", "https://www.foo.com/meetings"), ("https://www.foo.com/", "/meetings", "https://www.foo.com/meetings") ] with requests_mock.mock() as req_mock: req_mock.get(requests_mock.ANY, status_code=200, json={"mock_payload": 123}) for url, endpoint, expected in cases: common.ZOOM_API_BASE_URL = url common.zoom_api_request(endpoint) assert f"zoom api request to https://www.foo.com/meetings" in caplog.text.lower( )
def test_zoom_api_request_success(): # test successful call cases = [(None, "zoom_key", "zoom_secret"), ("", "zoom_key", "zoom_secret")] for apigee_key, zoom_key, zoom_secret in cases: common.APIGEE_KEY = apigee_key common.ZOOM_API_KEY = zoom_key common.ZOOM_API_SECRET = zoom_secret with requests_mock.mock() as req_mock: req_mock.get(requests_mock.ANY, status_code=200, json={"mock_payload": 123}) r = common.zoom_api_request("meetings") assert "mock_payload" in r.json()
def test_zoom_api_request_failures(): common.APIGEE_KEY = None common.ZOOM_API_KEY = "zoom_key" common.ZOOM_API_SECRET = "zoom_secret" common.ZOOM_API_BASE_URL = "https://api.zoom.us/v2/" # test failed call that returns with requests_mock.mock() as req_mock: req_mock.get(requests_mock.ANY, status_code=400, json={"mock_payload": 123}) r = common.zoom_api_request("meetings", ignore_failure=True) assert r.status_code == 400 # test failed call that raises with requests_mock.mock() as req_mock: req_mock.get(requests_mock.ANY, status_code=400, json={"mock_payload": 123}) error_msg = "400 Client Error" with pytest.raises(requests.exceptions.HTTPError, match=error_msg): common.zoom_api_request("meetings", ignore_failure=False, retries=0) # test ConnectionError handling with requests_mock.mock() as req_mock: req_mock.get(requests_mock.ANY, exc=requests.exceptions.ConnectionError) error_msg = "Error requesting https://api.zoom.us/v2/meetings" with pytest.raises(common.ZoomApiRequestError, match=error_msg): common.zoom_api_request("meetings") # test ConnectTimeout handling with requests_mock.mock() as req_mock: req_mock.get(requests_mock.ANY, exc=requests.exceptions.ConnectTimeout) error_msg = "Error requesting https://api.zoom.us/v2/meetings" with pytest.raises(common.ZoomApiRequestError, match=error_msg): common.zoom_api_request("meetings")
def test_zoom_api_request_missing_endpoint(): with pytest.raises(Exception) as exc_info: zoom_api_request(endpoint=None) assert exc_info.match("missing required param 'endpoint'")
def get_admin_token(): # get admin level zak token from admin id r = zoom_api_request("users/{}/token?type=zak".format(ZOOM_ADMIN_ID)) return r.json()["token"]
def handler(event, context): """ This function acts as a relay to the traditional zoom webhook. The webhook function is called by Zoom on a "recording.completed" event, along with data about the recordings. Here we fetch the recording data from the zoom API. The response to that API call is (mostly) identical to the payload zoom sends to the webhook, so we can simply pass it along in our own webhook request, using a "on.demand.ingest" event type and including the series id as "on_demand_series_id". """ logger.info(event) if "body" not in event or event["body"] is None: return resp(400, "Bad data. No body found in event.") try: body = json.loads(event["body"]) logger.info({"on-demand request": body}) except json.JSONDecodeError: return resp(400, "Webhook notification body is not valid json.") if "uuid" not in body: return resp( 400, "Missing recording uuid field in webhook notification " "body.") uuid = body["uuid"] if uuid.startswith("https"): # it's a link; parse the uuid from the query string parsed_url = urlparse(uuid) query_params = parse_qs(parsed_url.query) if "meeting_id" not in query_params: return resp( 404, "Zoom URL is malformed or missing 'meeting_id' " "param.") uuid = query_params["meeting_id"][0] logger.info("Got recording uuid: '{}'".format(uuid)) try: try: # zoom api can break if uuid is not double urlencoded double_urlencoded_uuid = quote(quote(uuid, safe=""), safe="") zoom_endpoint = ( "meetings/{}/recordings".format(double_urlencoded_uuid)) r = zoom_api_request(zoom_endpoint) recording_data = r.json() except requests.HTTPError as e: # return a 404 if there's no such meeting if e.response.status_code == 404: return resp(404, "No zoom recording with id '{}'".format(uuid)) else: raise # otherwise return a 500 on any other errors (bad json, bad request, etc) except Exception as e: return resp( 500, "Something went wrong querying the zoom api: {}".format(str(e))) if "recording_files" not in recording_data \ or not len(recording_data["recording_files"]): return resp( 503, "Zoom api response contained no recording files for {}".format( uuid)) # verify that all the recording files are actually "completed" not_completed = sum(1 for x in recording_data["recording_files"] if x.get("status") and x.get("status") != "completed") if not_completed > 0: return resp(503, "Not all recorded files have status 'completed'") webhook_data = { "event": "on.demand.ingest", "payload": { "object": recording_data } } # series id is an optional param. if not present the download function will # attempt to determine the series id by matching the recording times against # it's known schedule as usual. if "oc_series_id" in body and body["oc_series_id"]: webhook_data["payload"]["on_demand_series_id"] = body["oc_series_id"] if "allow_multiple_ingests" in body: webhook_data["payload"]["allow_multiple_ingests"] = body[ "allow_multiple_ingests"] logger.info({"webhook_data": webhook_data}) try: r = requests.post(WEBHOOK_ENDPOINT_URL, data=json.dumps(webhook_data), headers={"Content-type": "application/json"}) r.raise_for_status() if r.status_code == 204: raise Exception("Webhook returned 204: ingest not accepted") except Exception as e: err_msg = str(e) logger.exception( "Something went wrong calling the webhook: {}".format(err_msg)) return resp(500, err_msg) return resp(200, "Ingest accepted")
def test_zoom_api_request_missing_endpoint(): with pytest.raises(Exception) as exc_info: common.zoom_api_request(endpoint=None) assert exc_info.match("Call to zoom_api_request missing endpoint")