class UserLogInVesyncAuthorizationServer(HttpRunner): """ Vesync user log in to vesync authorization server. Config Variables: - email (str): required - password (str): required - client_id (str): OAuth client id - platform_type (Literal["Alexa", "GoogleHome"]): required - scope (str): required - redirect_url (str): OAuth redirect URL Export Variables: - authorization_code (bytes): note the type is 'bytes', not 'str' """ config = ( Config("VeSync 用户登录,登录成功后重定向回第三方 app 并附带 authorization code").export( *["authorization_code"])) teststeps = [ Step( RunRequest("VeSync 用户登录,登录成功后重定向回第三方 app 并附带 authorization code"). with_variables( **{ "api": "${get_api_from_orm_by_name(userLogInVesyncAuthorizationServer)}" }).post("${getattr($api, url)}").with_data({ "email": "$email", "password": "******", "traceId": generate_trace_id(), "clientId": "$client_id", "platformType": "$platform_type", "state": generate_trace_id(), "scope": "$scope", "redirectUrl": "$redirect_url", "userLocale": "", "responseType": "code" }).extract().with_jmespath( "body", "authorization_code") # note: the type is bytes, not str. .validate().assert_equal("status_code", 200).assert_type_match( "body", "bytes", "返回的 authorization code 应该是一个字符串")), ]
def accept_grant(request: dict, user: User, oauth_client_id: str, authentication_code: str) -> None: """ Set parameters for Alexa directive 'AcceptGrant'. """ oauth_token = crud.oauth_token.get_not_expired_by_user_id_and_client_id( db, user.id, oauth_client_id) if not oauth_token: raise ValueError( "No valid oauth token found, make sure oauth token exist, and it's access token not expired. " "Refresh access token if expired.") set_alexa_directive_parent_dict(request) request["req_json"]["data"]["directive"] = { "header": { "namespace": "Alexa.Authorization", "name": "AcceptGrant", "messageId": generate_trace_id("UUID4"), "payloadVersion": alexa_settings.authorization_payload_version }, "payload": { "grant": { "type": "OAuth2.AuthorizationCode", "code": authentication_code }, "grantee": { "type": "BearerToken", "token": oauth_token.access_token } } }
class AssertAdjustRangeValueAlexaResponseCorrect(HttpRunner): """ Send Alexa directive AdjustRangeValue and assert the response is correct. Config Variables: - instance (str): required (set to None if not exist), e.g. AirPurifier.Mode - access_token (str): required - payload (dict): required (set to {} if not exist), e.g. {"mode": "mode.sleep"} - final_value (int): required, the expected value in response - sku (Sku): required - cid (str): required """ config = (Config("AdjustRangeValue 命令中各个参数均正确,event AlexaResponse 正确")) teststeps = [ Step(RunApiInterfaceSpecificDirective().with_variables( **{ "namespace": AlexaInterfaceEnum.RangeController.value, "name": "AdjustRangeValue", "config_model": "${getattr($sku, config_model)}", "endpoint_id": "$cid;$config_model", "message_id": generate_trace_id("UUID4") } ).request().teardown_hook( "${check_alexa_directive_params($namespace, $instance, $name, $payload)}" ).validate().assert_equal("body.code", 0).assert_equal( "body.result.statusCode", 200 ).assert_json_contains( "body.result.data", "${get_adjust_range_value_alexa_response(" "$message_id, $access_token, $sku, $cid, $instance, $final_value)}" )) ]
def set_fw_shared_params(request: dict, cid: str, sku: Sku, api_name: str, main_firmware_version: str = None) -> None: """ Set shared parameters for firmware API. Note: 1. argument api_name will be assigned to parameter 'method', so make sure use api method as name when inserting row into table api. 2. argument 'main_firmware_version' must not be None when setup mode of sku is 'WIFI_V21'. """ shared_params = { "context": { "traceId": generate_trace_id(), "method": api_name, "cid": cid, "deviceRegion": sku.device_region } } if sku.setup_mode == SetupModeEnum.WIFI_V20: shared_params["context"]["pid"] = sku.pid elif sku.setup_mode == SetupModeEnum.WIFI_V21: if not main_firmware_version: raise ValueError("argument 'main_firmware_version' must not be None when setup mode of sku is 'WIFI_V21'") shared_params["context"]["configModel"] = sku.config_model shared_params["context"]["mainFwVersion"] = main_firmware_version shared_params["context"]["cause"] = "onConn" request["req_json"].update(shared_params)
def request(self): return (super().request().with_json({ "data": { "directive": { "header": { "namespace": "$namespace", "instance": "$instance", "name": "$name", "messageId": "$message_id", "correlationToken": generate_trace_id("UUID4"), "payloadVersion": alexa_settings.interface_payload_version }, "endpoint": { "scope": { "type": "BearerToken", "token": "$access_token" }, "endpointId": "$endpoint_id", "cookie": {} }, "payload": "$payload" } } }))
def get_access_log(request: dict, user: User, event: Event, platform: PlatformEnum) -> None: """ Set parameters for api 'getAccessLog'. """ device = event.device cookie = user.cookie if platform == PlatformEnum.ALEXA: third_party_name = "Alexa" agent_user_id = None elif platform == PlatformEnum.GOOGLE_HOME: third_party_name = "GoogleHome" agent_user_id = cookie.vesync_main_account_id else: raise ValueError("not supported third party platform") request_json = { "traceId": generate_trace_id(), "method": "getAccessLog", "thirdPartyName": third_party_name, "accountId": cookie.vesync_main_account_id, "cid": device.cid, "configModule": device.sku.config_model, "subDeviceNo": 0, "agentUserId": agent_user_id, "triggerId": None } request["req_json"] = request_json
class AssertDiscoverResponseCorrect(HttpRunner): """ Call alexa directive Discover and make assertion on the response. Config Variables: - access_token (str): required, access token given by vesync - expected (list[dict]): required, expected endpoints - jmespath (str): optional, default to "body.result.data.event.payload.endpoints" """ config = (Config("Discover").variables( **{ "jmespath": "body.result.data.event.payload.endpoints", })) teststeps = [ Step(RunApiDiscover().with_variables( **{ "message_id": generate_trace_id("UUID4") }).request().validate().assert_equal("body.code", 0).assert_equal( "body.result.statusCode", 200).assert_json_contains( "body.result.data.event.header", { "namespace": "Alexa.Discovery", "name": "Discover.Response", "payloadVersion": alexa_settings.discovery_payload_version, "messageId": "$message_id", }).assert_json_contains("$jmespath", "$expected")), ]
def set_alexa_directive_shared_params(request: dict) -> None: """Set shared parameters for Alexa directives sent to skill (hosted on LWS Lambda).""" request["req_json"].update({ "traceId": generate_trace_id(), "method": "invokeLambda", "projectName": "alexaSmartHome", "alexaRegion": "NorthAmerica", })
def _get_vesync_app_unauthenticated_shared_params( api: Api, client: Client, debug: bool = True, method: str = None) -> dict: """ Get unauthenticated shared parameters for vesync app apis. 5 parameters will always be set: * method * acceptLanguage * debugMode * timeZone * traceId if request format is VESYNC-APP-V1.0, 3 extra parameters will be set * phoneOS * phoneBrand * appVersion if request format is VESYNC-APP-V2.0, 5 extra parameters will be set * osInfo * clientInfo * clientVersion * clientType * terminalId if request format is VESYNC-APP-V2.1 (for arize), 3 extra parameters will be set * osInfo * clientVersion * clientType """ # use the basename of api path as method if not method: method = api.method shared_params = { "acceptLanguage": client.accept_language, "debugMode": debug, "method": method, "timeZone": client.time_zone, "traceId": generate_trace_id() } if (request_format := api.service.request_format) == "VESYNC-APP-V1.0": shared_params.update({ "phoneOS": client.os_info, "phoneBrand": client.client_info, "appVersion": client.client_version }) return shared_params
def report_state(request: dict, access_token: str, device: Device) -> None: """ Set parameters for Alexa directive 'ReportState'. """ set_alexa_directive_parent_dict(request) request["req_json"]["data"]["directive"] = { "header": { "namespace": "Alexa", "name": "ReportState", "messageId": generate_trace_id("UUID4"), "correlationToken": generate_trace_id("UUID4"), "payloadVersion": alexa_settings.alexa_payload_version }, "endpoint": { "scope": { "type": "BearerToken", "token": access_token }, "endpointId": f"{device.cid};{device.sku.config_model}", "cookie": {} }, "payload": {} }
class AssertOneEndpointCorrectInDiscoverResponse(HttpRunner): """ Call alexa directive Discover and assert the information of specific endpoint is correct. Config Variables: - access_token (str): required, access token given by vesync - sku (Sku): required - cid (str): required - device_name (str): required - main_firmware_version (str): required """ config = (Config("Discover")) teststeps = [ Step(RunApiDiscover().with_variables( **{ "config_model": "${getattr($sku, config_model)}", "endpoint_id": "$cid;$config_model", "message_id": generate_trace_id("UUID4"), } ).request().validate().assert_equal("body.code", 0).assert_equal( "body.result.statusCode", 200 ).assert_json_contains( "body.result.data.event.header", { "namespace": "Alexa.Discovery", "name": "Discover.Response", "payloadVersion": alexa_settings.discovery_payload_version, "messageId": "$message_id", } ).assert_json_contains( "body.result.data.event.payload.endpoints[?endpointId=='$endpoint_id'] | [0]", "${get_endpoint_info($sku, $cid, $device_name, $main_firmware_version)}" )), ]
} if (request_format := api.service.request_format) == "VESYNC-APP-V1.0": shared_params.update({ "phoneOS": client.os_info, "phoneBrand": client.client_info, "appVersion": client.client_version }) return shared_params elif request_format == "VESYNC-APP-V2.0": shared_params.update({ "osInfo": client.os_info, "clientInfo": client.client_info, "clientVersion": client.client_version, "clientType": client.client_type, "terminalId": generate_trace_id("UUID4") }) return {"context": shared_params} elif request_format == "VESYNC-APP-V2.1": shared_params.update({ "osInfo": client.os_info, "clientVersion": client.client_version, "clientType": client.client_type, }) return {"context": shared_params} else: raise ValueError(f"request format '{request_format}' was not supported yet.") def set_vesync_app_shared_params( request: dict,