How can I generate a WM_SEC.AUTH_SIGNATURE in Elixir? - api

I'm trying to generate Walmart's API WM_SEC.AUTH_SIGNATURE header.
I found a lot of examples in Ruby and Python, but no examples in Elixir. I've been trying to create one and try it out.
Here's what I attempted.
# mix.exs
{:ex_crypto, git: "https://github.com/ntrepid8/ex_crypto.git", branch: "master"}
def scrape_store_item(store_item) do
consumer_id = "my-consumer-id"
private_key_version = "1"
private_key_password = "my-private-key-password"
private_key =
Application.app_dir(:my_app, "priv/keys/walmart/WM_IO_private_key.pem")
|> ExPublicKey.load!(private_key_password)
timestamp = DateTime.utc_now() |> DateTime.to_unix()
url = "https://developer.api.walmart.com/api-proxy/service/affil/product/v2/taxonomy"
message = "#{consumer_id}\n#{timestamp}\n#{private_key_version}\n"
encoded_message = Base.encode64(message)
{:ok, auth_signature} = ExPublicKey.sign(encoded_message, private_key)
auth_signature = Base.encode64(auth_signature)
middleware = [
{Tesla.Middleware.BaseUrl,
"https://developer.api.walmart.com/api-proxy/service/affil/product/v2"},
{Tesla.Middleware.Headers,
[
{"WM_CONSUMER.ID", consumer_id},
{"WM_CONSUMER.INTIMESTAMP", timestamp},
{"WM_SEC.KEY_VERSION", private_key_version},
{"WM_SEC.AUTH_SIGNATURE", auth_signature},
{"WM_SHOW_REASON_CODES", "ALL"},
{"Content-Type", "application/json"}
]}
]
client = Tesla.client(middleware)
{:ok, response} = Tesla.get(client, "/taxonomy") |> IO.inspect()
IO.inspect(response.body)
end
I get a 401 response with this code:
{:ok,
%Tesla.Env{
__client__: %Tesla.Client{
adapter: nil,
fun: nil,
post: [],
pre: [
{Tesla.Middleware.BaseUrl, :call,
["https://developer.api.walmart.com/api-proxy/service/affil/product/v2"]},
{Tesla.Middleware.Headers, :call,
[
[
{"WM_CONSUMER.ID", "my-consumer-id"},
{"WM_CONSUMER.INTIMESTAMP", 1654443660},
{"WM_SEC.KEY_VERSION", "1"},
{"WM_SEC.AUTH_SIGNATURE",
"kdXG+e6R/n+8pH1ha1WKnzLrAHbUqmJsZfN9nOIyOzp6gsHAH7/VrX0K477cdzAq/v7YLpNJXZug3Yt6WTZoP17sZhz6Dig1BK1gg+EZqVqRaF3VJdRwBKlVgBO31s634xL7M8kPhXK11CsMxG8/9xjTGn2cDKEZ9aLeq15ECIfYa5tVtCdTcjNS4u6a7npByU9PIFp9a7n3h1KbW9C/9EA05kTuC1N0oS8nBlnKbA2+C0UW9EAvN4MaIkG0SqOqf/uEHn9BteAv8hI0Ayyny9RpJQmfZEpZ0G3htA7t1pWTzwxUsIJrF/5D1gV+IIYR7OiwHUg2RsIrnPohbznPQw=="}
]
]}
]
},
__module__: Tesla,
body: <<31, 139, 8, 0, 0, 9, 110, 136, 0, 255, 68, 204, 65, 138, 131, 64, 16,
70, 225, 171, 20, 255, 122, 70, 156, 25, 199, 69, 175, 115, 132, 236, 67,
97, 151, 177, 192, 110, 155, 174, 210, 4, 196, 187, 135, ...>>,
headers: [
{"content-encoding", "gzip"},
{"content-type", "application/json;charset=utf-8"},
{"last-modified", "Sun, 05 Jun 2022 15:41:00 GMT"},
{"strict-transport-security", "max-age=86400"},
{"wm_svc.env", "prod"},
{"wm_svc.name", "affil-product"},
{"wm_svc.version", "2.0.0"},
{"x-lua-strict-transport-security", "max-age=86400"},
{"x-tb", "1"},
{"x-tb-optimization-total-bytes-saved", "0"},
{"date", "Sun, 05 Jun 2022 15:41:00 GMT"},
{"connection", "close"},
{"set-cookie",
"TS01a35e2a=01c5a4e2f95f0b472a3a7606aa7c7c33653874c13d636655443ecbca84d23369b19bc1de1973ac24c93ff1f24512e7af49264d46c6; Path=/; Secure"}
],
method: :get,
opts: [],
query: [],
status: 401,
url: "https://developer.api.walmart.com/api-proxy/service/affil/product/v2/taxonomy"
}}
Here's the example code someone shared for a working Ruby version.
version = 'YOUR VERSION'
consumer_id = "YOUR CONSUMER ID"
time_stamp = (Time.now.to_i * 1000).to_s
p_key = "YOUR PRIVATE KEY"
digest = OpenSSL::Digest.new('sha256')
data = consumer_id + "\n" + time_stamp + "\n" + version + "\n"
k = OpenSSL::PKey::RSA.new(p_key.to_s)
digest = OpenSSL::Digest::SHA256.new
signature = k.sign(digest,data)
signature = Base64.strict_encode64(signature)
headers = {
"WM_SEC.KEY_VERSION": version,
"WM_CONSUMER.ID": consumer_id,
"WM_CONSUMER.INTIMESTAMP": time_stamp,
"WM_SEC.AUTH_SIGNATURE": signature
}
puts HTTParty.get("https://developer.api.walmart.com/api-proxy/service/affil/product/v2/taxonomy", headers: headers).parsed_response

Here's a script that does it without ex_crypto. Nice challenge, those Wallmart docs are terrible.
Mix.install([:tesla])
key_version = System.fetch_env!("WALLMART_KEY_VERSION")
consumer_id = System.fetch_env!("WALLMART_CONSUMER_ID")
private_key_pem = File.read!("WM_IO_private_key.pem")
[pem_entry] = :public_key.pem_decode(private_key_pem)
private_key = :public_key.pem_entry_decode(pem_entry)
timestamp = System.os_time(:millisecond)
auth_signature =
"#{consumer_id}\n#{timestamp}\n#{key_version}\n"
|> :public_key.sign(:sha256, private_key)
|> Base.encode64()
url = "https://developer.api.walmart.com/api-proxy/service/affil/product/v2/taxonomy"
headers = [
{"WM_CONSUMER.ID", consumer_id},
{"WM_CONSUMER.INTIMESTAMP", timestamp},
{"WM_SEC.KEY_VERSION", key_version},
{"WM_SEC.AUTH_SIGNATURE", auth_signature}
]
{:ok, %{body: body}} = Tesla.get(url, headers: headers)
IO.puts(body)
Output:
{"categories":[{"id":"0","name":"Home Page","path":"Home Page",...

Here's how you generate WM_SEC.AUTH_SIGNATURE in Elixir:
Make sure you have ex_crypto package installed from master branch since the latest version has necessary changes but is not published.
{:ex_crypto, git: "https://github.com/ntrepid8/ex_crypto.git", branch: "master"}
Then here's the solution:
version = "1"
consumer_id = "my-consumer-id"
timestamp = DateTime.utc_now() |> DateTime.to_unix(:millisecond)
data = "#{consumer_id}\n#{timestamp}\n#{version}\n"
private_key =
Application.app_dir(:my_app, "priv/keys/walmart/WM_IO_private_key.pem")
|> ExPublicKey.load!()
{:ok, auth_signature} = ExPublicKey.sign(data, private_key)
auth_signature = Base.encode64(auth_signature)
middleware = [
{Tesla.Middleware.BaseUrl,
"https://developer.api.walmart.com/api-proxy/service/affil/product/v2"},
{Tesla.Middleware.Headers,
[
{"WM_CONSUMER.ID", consumer_id},
{"WM_CONSUMER.INTIMESTAMP", timestamp},
{"WM_SEC.KEY_VERSION", version},
{"WM_SEC.AUTH_SIGNATURE", auth_signature}
]}
]
client = Tesla.client(middleware)
Tesla.get(client, "/taxonomy")

Related

Scrypto: how to write test passing bucket as a function argument

In my blueprint, I have a function for the admin to deposit tokens back into the blueprint token vault.
pub fn deposit(&mut self, amount: Decimal, mut bucket: Bucket) {
assert!(amount <= bucket.amount(), "not enough amount in your bucket");
self.token_vault.put(bucket.take(amount))
}
I know how to generate a new account in my test file, and how to make a method call with badge protected access...
And I have learned from Scrypto-example/nft/magic-card:
https://github.com/radixdlt/scrypto-examples
let (public_key, private_key, account_component) = test_runner.new_allocated_account();
...
let manifest = ManifestBuilder::new(&NetworkDefinition::simulator())
.create_proof_from_account_by_amount(account_component, badge_amount, badge_addr)
.withdraw_from_account_by_amount(account_component, amount, token_addr)
.take_from_worktop(token_addr, |builder, bucket_id| {
builder.call_method(component, func_name, args!(amount, Bucket(bucket_id)))
})
.call_method(
account_component,
"deposit_batch",
args!(Expression::entire_worktop()),
)
.build();
let receipt = test_runner.execute_manifest_ignoring_fee(
manifest,
vec![NonFungibleAddress::from_public_key(&public_key)],
);
println!("{} receipt: {:?}\n", func_name, receipt);
receipt.expect_commit_success();
Then I got this error:
COMMITTED FAILURE: KernelError(InvalidDropNodeVisibility { mode: Application, actor:
Method(Scrypto { package_address: NormalPackage[011784f2e3c4b3dc9d14c850484fc4962f59ea68271e917d2f075c],
blueprint_name: "StableCoin", ident: "deposit" }, ResolvedReceiver { derefed_from:
Some((Global(Component(NormalComponent[02fd2e738e08b33e7d19001684043cd24fe35fda1ddc9429f7051e])), 36)),
receiver: Component([252, 13, 40, 104, 140, 209, 211, 110, 141, 213, 197, 200, 172, 195, 190, 178, 219, 47, 174, 17, 52, 209, 75, 207, 106, 97, 105, 21, 213, 159, 52, 25, 15, 4, 0, 0]) }),
node_id: Bucket(1027) })
But how can I make the bucket variable from my account_component as a function argument?
I solved it... my blueprint function should not include the amount, which has been represented in bucket argument...
pub fn deposit_to_vault(&mut self, bucket: Bucket) {
self.token_vault.put(bucket)
}
then use take_from_worktop_by_amount to get the bucket_id:
.withdraw_from_account_by_amount(user.compo_addr, amount, token_addr)
.take_from_worktop_by_amount(amount, token_addr, |builder, bucket_id| {
builder.call_method(component, func_name, args!(Bucket(bucket_id)))
})
.call_method(
user.compo_addr,
"deposit_batch",
args!(Expression::entire_worktop()),
)
.build();

How to speed up scrapy

I need to collect a lot(really a lot) of data for statistics, all the necessary information is in <script type="application/ld+json"></script>
and I wrote scrapy parser (script inside html) under it, but parsing is very slow (about 3 pages per second). Is there any way to speed up the process? Ideally I would like to see 10+ pages per second
#spider.py:
import scrapy
import json
class Spider(scrapy.Spider):
name = 'scrape'
start_urls = [
about 10000 urls
]
def parse(self, response):
data = json.loads(response.css('script[type="application/ld+json"]::text').extract_first())
name = data['name']
image = data['image']
path = response.css('span[itemprop="name"]::text').extract()
yield {
'name': name,
'image': image,
'path': path
}
return
#settings.py:
USER_AGENT = "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:67.0) Gecko/20100101 Firefox/67.0"
ROBOTSTXT_OBEY = False
CONCURRENT_REQUESTS = 32
DOWNLOAD_DELAY = 0.33
DEFAULT_REQUEST_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
}
EXTENSIONS = {
'scrapy.extensions.telnet.TelnetConsole': None,
}
AUTOTHROTTLE_DEBUG = False
LOG_ENABLED = False
My PC specs:
16GB ram, i5 2400, ssd, 1gb ethernet
#Edited
settings.py
ROBOTSTXT_OBEY = False
DOWNLOAD_DELAY = 0
DOWNLOAD_TIMEOUT = 30
RANDOMIZE_DOWNLOAD_DELAY = True
REACTOR_THREADPOOL_MAXSIZE = 128
CONCURRENT_REQUESTS = 256
CONCURRENT_REQUESTS_PER_DOMAIN = 256
CONCURRENT_REQUESTS_PER_IP = 256
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_START_DELAY = 1
AUTOTHROTTLE_MAX_DELAY = 0.25
AUTOTHROTTLE_TARGET_CONCURRENCY = 128
AUTOTHROTTLE_DEBUG = True
RETRY_ENABLED = True
RETRY_TIMES = 3
RETRY_HTTP_CODES = [500, 502, 503, 504, 400, 401, 403, 404, 405, 406, 407, 408, 409, 410, 429]
DOWNLOADER_MIDDLEWARES = {
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
'scrapy.spidermiddlewares.referer.RefererMiddleware': 80,
'scrapy.downloadermiddlewares.retry.RetryMiddleware': 90,
'scrapy_fake_useragent.middleware.RandomUserAgentMiddleware': 120,
'scrapy.downloadermiddlewares.cookies.CookiesMiddleware': 130,
'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,
'scrapy.downloadermiddlewares.redirect.RedirectMiddleware': 900,
'scraper.middlewares.ScraperDownloaderMiddleware': 1000
}

File Upload to Local using Acr in Elixir

I am using Arc.Definition(https://github.com/stavro/arc) for uploading an image to the Local Storage.
My file_service.ex is below:
defmodule MyApp.FileService do
use Arc.Definition
use Arc.Ecto.Definition
#image_types ~w(.jpg .jpeg .png .gif)
#versions [:original]
#default_filename "image.png"
#heights %{
medium: 400
}
#widths %{
medium: 400
}
def __storage, do: Arc.Storage.Local
def upload_image(%Plug.Upload{} = image, resource_type, resource_id) do
store({%Plug.Upload{path: image.path, filename: #default_filename},
%{resource_type: resource_type, resource_id: resource_id}})
end
def upload_base64_image(base64_image, resource_type, resource_id) do
store({%{filename: #default_filename, binary: base64_image_to_binary(base64_image)}})
end
def delete_file(image_url, resource) do
delete({image_url, resource})
end
defp base64_image_to_binary("data:image/" <> rest) do
rest
|> String.replace("\n", "")
|> String.split(",")
|> Enum.at(1)
|> Base.decode64!
end
defp base64_image_to_binary(base64_image) do
base64_image
|> String.replace("\n", "")
|> Base.decode64!
end
end
But, I am getting an error saying "no function clause matching in Arc.Actions.Store.store".
The stack trace is below:
** (FunctionClauseError) no function clause matching in Arc.Actions.Store.store/2
(arc) lib/arc/actions/store.ex:8: Arc.Actions.Store.store(MyApp.FileService, {%{binary: <<255, 216,
255, 225, 3, 48, 69, 120, 105, 102, 0, 0, 73, 73, 42, 0, 8, 0, 0, 0,
58, 0, 50, 1, 2, 0, 20, 0, 0, 0, 198, 2, 0, 0, 15, 1, 2, 0, 10, 0, 0,
0, 218, 2, 0, 0, 1, 1, ...>>, filename: "image.png"}})
Anyone, please help?
Your code
def upload_base64_image(base64_image, resource_type, resource_id) do
store({%{filename: #default_filename, binary: base64_image_to_binary(base64_image)}})
end
's store is using wrong.
It only accept tuple(file, scope) or filepath(map).
So it should be: store(%{filename: #default_filename, binary: base64_image_to_binary(base64_image)}).
See github's example:
# Store a file from a connection body
{:ok, data, _conn} = Plug.Conn.read_body(conn)
Avatar.store(%{filename: "file.png", binary: data})
I figure it out by reading traceback and arc's store implementaion:
def store(definition, {file, scope}) when is_binary(file) or is_map(file) do
put(definition, {Arc.File.new(file), scope})
end
def store(definition, filepath) when is_binary(filepath) or is_map(filepath) do
store(definition, {filepath, nil})
end

ActiveRecord where.not is not working/ weird behavior

User has_many Plans. I'm trying to find the IDs of all Users that do NOT have a Plan with status of "canceled". Would love to know what's explaining the behavior below.
For context, what should be returned is this:
User.select { |u| u.plans.select { |p| p.status != "canceled" }.count > 0 }.map(&:id)
# => [27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 41, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 60, 61, 62, 63]
Here's what I'm getting:
# statement 1
User.joins(:plans).where.not("plans.status" => "canceled").map(&:id)
# User Load (0.3ms) SELECT "users".* FROM "users" INNER JOIN "plans" ON "plans"."user_id" = "users"."id" WHERE ("plans"."status" != 'canceled')
# => [44]
# statement 2
User.joins(:plans).where("plans.status != ?", "canceled").map(&:id)
# User Load (0.3ms) SELECT "users".* FROM "users" INNER JOIN "plans" ON "plans"."user_id" = "users"."id" WHERE (plans.status != 'canceled')
# => [44]
# statement 3
User.joins(:plans).where("plans.status == ?", nil).map(&:id)
# User Load (0.3ms) SELECT "users".* FROM "users" INNER JOIN "plans" ON "plans"."user_id" = "users"."id" WHERE (plans.status == NULL)
# => []
# statement 4
User.joins(:plans).where("plans.status" => nil).map(&:id)
# User Load (0.7ms) SELECT "users".* FROM "users" INNER JOIN "plans" ON "plans"."user_id" = "users"."id" WHERE "plans"."status" IS NULL
# => [27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 41, 44, 42, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 61, 60, 62, 63]
Questions:
Why are statement 3 and 4 not returning the same result?
Why are statement 1 and 2 (fortunately these are the same and are returning the same result) not returning the same result as statement 4? For context, I'd rather not search on nil, but on "canceled". And I can confirm that all plans have either a status of nil or "canceled"
UPDATE PER REQUEST
# plan with nil status
Plan.where(status: nil).first
# => <Plan id: 1, zipcode: "94282", selected_plan: 1, meal_type: "Chef's choice (mixed)", most_favorite: "", least_favorite: "", allergies: "", start_date: "2015-05-27 00:00:00", delivery_address: "d", delivery_instructions: "", phone1: "10", phone2: "222", phone3: "2222", agree_tos: true, user_id: 20, created_at: "2015-05-24 05:18:40", updated_at: "2015-06-21 04:54:31", stripe_subscription_id: nil, stripe_invoice_number: nil, cancel_reason: nil, cancel_reason_other: nil, nps: nil, nps_open: nil, cancel_open: nil, status: nil, referred_by_code: nil>
# plan with canceled status
Plan.where(status: "canceled").first
# => <Plan id: 20, zipcode: "12345", selected_plan: 5, meal_type: "Meat (with veggies)", most_favorite: "", least_favorite: "", allergies: "", start_date: "2015-06-08 00:00:00", delivery_address: "asdf", delivery_instructions: "", phone1: "333", phone2: "333", phone3: "3333", agree_tos: true, user_id: 38, created_at: "2015-06-01 21:39:54", updated_at: "2015-06-23 06:23:10", stripe_subscription_id: "sub_6OKkJoNx2u8ZXZ", stripe_invoice_number: 0, cancel_reason: nil, cancel_reason_other: "", nps: 6, nps_open: "", cancel_open: "", status: "canceled", referred_by_code: nil>
Answer to Question 1:
You missed the concept that in conditional sql, the arguments that follow the condition are replaced in place of ? not compared to. So, replace the double == with =.
User.joins(:plans).where("plans.status = ?", nil).map(&:id)

how does method updateCredentialsForItem1 work

How does the method ItemManagementService.updateCredentialsForItem1 work?
what happens if I update the account using wrong credential? will it throw exception?
I added one account into yodlee, then I changed the password of that account. It threw error 402 when I tried to get the transactions. After this, I called this method ItemManagementService.updateCredentialsForItem1(userContext, itemId,true, fieldInfoList.ToArray(),true) to update this account. But this method doesn't threw any exception or error code even I updated it using wrong password.
What should I do to make sure yodlee will tell me the credential is wrong if I update the account using wrong credential?
Thanks a lot.
Yuj
I believe research and experience has told/taught me that you need to pass false as the "start refresh on addition/update" and start the refresh manually
The Workflow is exactly the same whether you add or update.
You add/update:
AddItemForContentService1 (if New) or updateCredentialsForItem1 (if updating credentials).
Then You check if the item can be refreshed and if/when it can Start a refresh and then begin your polling.
You need to check RefreshService.isItemRefreshing (this needs to be false)
Then start the refresh RefreshService.startRefresh7.
Then your standard polling routine which is where you will find out whether the updated credentials worked. Ala, At 10/15 second intervals you need to check the status with RefreshClient.getRefreshInfo passing the content item id.
The return value most notably the RefreshInfo.statusCode which correlates to Gatherer Error codes (see code below) lets you know if there is a problem. 0 is successful.
Below are the C# Enums that I built based upon Java SDK and filling in a few missing values with some tech support. The Values are the same regardless of what language. So it's a good reference as the names semi-make sense.
402 and 419 are the two most common Login error codes. 402 is invalid credentials, 419 means it didn't register them in time. The full list of error codes can be found on Yodlee's site here: https://developer.yodlee.com/FAQs/Error_Codes.
public enum GathererErrorsEnum
{
STATUS_OK = 0,
STATUS_INVALID_GATHERER_REQUEST = 400,
STATUS_NO_CONNECTION = 401,
STATUS_LOGIN_FAILED = 402,
STATUS_INTERNAL_ERROR = 403,
STATUS_LOST_REQUEST = 404,
STATUS_ABORT_REQUEST = 405,
STATUS_PASSWORD_EXPIRED = 406,
STATUS_ACCOUNT_LOCKED = 407,
STATUS_DATA_EXPECTED = 408,
STATUS_SITE_UNAVILABLE = 409,
STATUS_POP3_SERVER_FAILED = 410,
STATUS_SITE_OUT_OF_BUSINESS = 411,
STATUS_SITE_APPLICATION_ERROR = 412,
STATUS_REQUIRED_FIELD_UNAVAILABLE = 413,
STATUS_NO_ACCOUNT_FOUND = 414,
STATUS_SITE_TERMINATED_SESSION = 415,
STATUS_SITE_SESSION_ALREADY_ESTABLISHED = 416,
STATUS_DATA_MODEL_NO_SUPPORT = 417,
STATUS_HTTP_DNS_ERROR = 418,
STATUS_LOGIN_NOT_COMPLETED = 419,
STATUS_SITE_MERGED_ERROR = 420,
STATUS_UNSUPPORTED_LANGUAGE_ERROR = 421,
STATUS_ACCOUNT_CANCELLED = 422,
STATUS_ACCT_INFO_UNAVAILABLE = 423,
STATUS_SITE_DOWN_FOR_MAINTENANCE = 424,
STATUS_SITE_CERTIFICATE_ERROR = 425,
STATUS_SITE_BLOCKING_ERROR = 426,
STATUS_NEW_SPLASH_PAGE = 427,
STATUS_NEW_TERMS_AND_CONDITIONS = 428,
STATUS_UPDATE_INFORMATION_ERROR = 429,
STATUS_SITE_NOT_SUPPORTED = 430,
STATUS_HTTP_FILE_NOT_FOUND_ERROR = 431,
STATUS_HTTP_INTERNAL_SERVER_ERROR = 432,
STATUS_REGISTRATION_PARTIAL_SUCCESS = 433,
STATUS_REGISTRATION_FAILED_ERROR = 434,
STATUS_REGISTRATION_INVALID_DATA = 435,
STATUS_REGISTRATION_ACCOUNT_ALREADY_REGISTERED = 436,
STATUS_REGISTRATION_TIMEOUT = 404,
UNIQUEID_FROM_DATA_SOURCE_ERROR = 475,
ACCOUNT_REQUIRED_FIELDS_NOT_SET = 476,
BILL_REQUIRED_FIELDS_NOT_SET = 477,
STATUS_DUPLICATE_BILL = 478,
STATUS_COULD_NOT_GENERATE_AUTOREGISTER_CREDENTIALS = 479,
STATUS_MAX_REGISTRATION_ATTEMPTS_EXCEEDED = 481,
STATUS_ACCOUNT_REGISTERED_ELSE_WHERE = 484,
STATUS_REGISTRATION_BOT_SUPPORTED_FOR_REGION = 485,
STATUS_REGISTRATION_NOT_SUPPORTED_FOR_REGION = 485,
STATUS_UNSUPPORTED_REGISTRATION_ACCOUNT_TYPE = 486,
REWARDS_PROGRAM_REQUIRED_FIELDS_NOT_SET = 491,
REWARDS_ACTIVITY_REQUIRED_FIELDS_NOT_SET = 492,
TAX_LOT_REQUIRED_FIELDS_NOT_SET = 493,
INVESTMENT_TRANSACTION_REQUIRED_FIELDS_NOT_SET = 494,
LOAN_TRANSACTION_REQUIRED_FIELDS_NOT_SET = 495,
CARD_TRANSACTION_REQUIRED_FIELDS_NOT_SET = 496,
BANK_TRANSACTION_REQUIRED_FIELDS_NOT_SET = 497,
HOLDING_REQUIRED_FIELDS_NOT_SET = 498,
SITE_CURRENTLY_NOT_SUPPORTED = 505,
NEW_LOGIN_INFO_REQUIRED_FOR_SITE = 506,
BETA_SITE_WORK_IN_PROGRESS = 507,
STATUS_INSTANT_REQUEST_TIMEDOUT = 508,
TOKEN_ID_INVALID = 509,
PROPERTY_RECORD_NOT_FOUND = 510,
HOME_VALUE_NOT_FOUND = 511,
NO_PAYEE_FOUND = 512,
NO_PAYEE_RETRIEVED = 513,
SOME_PAYEE_NOT_RETRIEVED = 514,
NO_PAYMENT_ACCOUNT_FOUND = 515,
NO_PAYMENT_ACCOUNT_SELECTED = 516,
GENERAL_EXCEPTION_WHILE_GATHERING_MFA_DATA = 517,
NEW_MFA_INFO_REQUIRED_FOR_AGENTS = 518,
MFA_INFO_NOT_PROVIDED_TO_YODLEE_BY_USER_FOR_AGENTS = 519,
MFA_INFO_MISMATCH_FOR_AGENTS = 520,
ENROLL_IN_MFA_AT_SITE = 521,
MFA_INFO_NOT_PROVIDED_IN_REAL_TIME_BY_USER_VIA_APP = 522,
INVALID_MFA_INFO_IN_REAL_TIME_BY_USER_VIA_APP = 523,
USER_PROVIDED_REAL_TIME_MFA_DATA_EXPIRED = 524,
MFA_INFO_NOT_PROVIDED_IN_REAL_TIME_BY_GATHERER = 525,
INVALID_MFA_INFO_OR_CREDENTIALS = 526,
STATUS_DBFILER_SUMMARY_SAVE_ERROR = 601,
STATUS_REQUEST_GENERATION_ERROR = 602,
STATUS_REQUEST_DISPATCH_ERROR = 603,
STATUS_REQUEST_GENERATION_ERROR_LOGIN_FAILURE = 604,
STATUS_REQUEST_GENERATION_ERROR_DELETED_ITEM = 605,
INPUT_INVALID_DATA = 701,
INPUT_LENGTH_ERROR = 702,
INPUT_FORMAT_ERROR = 703,
INPUT_USERNAME_ALREADY_TAKEN_ERROR = 704,
INPUT_VALUE_TOO_SMALL = 705,
INPUT_VALUE_TOO_LARGE = 706,
REFRESH_NEVER_DONE = 801,
REFRESH_NEVER_DONE_AFTER_CREDENTIALS_UPDATE = 802,
}