Packages
erlcloud
3.5.2
3.8.3
3.8.2
3.8.1
3.7.6
3.7.4
3.7.3
3.7.2
3.7.1
3.7.0
3.6.8
3.6.7
3.6.5
3.6.4
3.6.3
3.6.2
3.6.1
3.6.0
3.5.16
3.5.15
3.5.14
3.5.13
3.5.12
3.5.11
3.5.10
3.5.9
3.5.8
3.5.7
3.5.6
3.5.5
3.5.4
3.5.3
3.5.2
3.5.1
3.5.0
3.4.5
3.4.3
3.4.1
3.4.0
3.3.9
3.3.8
3.3.7
3.3.6
3.3.5
3.3.4
3.3.3
3.3.2
3.3.1
3.3.0
3.2.18
3.2.17
3.2.16
3.2.15
3.2.14
3.2.13
3.2.12
3.2.11
3.2.10
3.2.7
3.2.6
3.2.5
3.2.4
3.2.3
3.2.2
3.2.1
3.2.0
3.1.17
3.1.16
3.1.14
3.1.13
3.1.12
3.1.11
3.1.9
3.1.8
3.1.7
3.1.6
3.1.5
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
2.2.16
2.2.15
2.2.14
2.2.13
2.2.12
2.2.11
2.2.10
2.2.9
2.2.8
2.2.7
2.2.6
2.2.5
2.2.4
2.2.2
2.2.1
2.2.0
2.1.0
2.0.5
2.0.4
2.0.3
2.0.0
0.13.10
0.13.9
0.13.8
0.13.6
0.13.5
0.13.4
0.13.3
0.13.2
0.13.0
0.12.0
0.11.0
0.9.2
0.9.2-rc.1
0.9.1
0.9.0
AWS APIs library for Erlang
Current section
Files
Jump to
Current section
Files
src/erlcloud_workspaces.erl
%% @doc
%% An Erlang interface to AWS Workspaces.
%%
%% Output is in the form of `{ok, Value}' or `{error, Reason}'. The
%% format of `Value' is controlled by the `out' option, which defaults
%% to `json'. The possible values are:
%%
%% * `json' - The output from Workspaces as processed by `jsx:decode'
%% but with no further manipulation.
%%
%% * `record' - A record containing all the information from the
%% Workspaces response except field types.
%%
%% Workspaces errors are returned in the form `{error, {ErrorCode, Message}}'
%% where `ErrorCode' and 'Message' are both binary
%% strings.
%% See the unit tests for additional usage examples beyond what are
%% provided for each function.
%%
%% @end
-module(erlcloud_workspaces).
-include("erlcloud_aws.hrl").
-include("erlcloud_workspaces.hrl").
%%% Library initialization.
-export([configure/2, configure/3, configure/4, new/2, new/3, new/4]).
-define(API_VERSION, "20150408").
-define(OUTPUT_CHOICES, [json, record, map]).
-export([
describe_tags/1, describe_tags/2,
describe_workspaces/0, describe_workspaces/1, describe_workspaces/2,
describe_workspace_directories/0, describe_workspace_directories/1, describe_workspace_directories/2
]).
-export_type([
describe_workspaces_opt/0,
describe_workspaces_opts/0,
describe_workspace_directories_opt/0,
describe_workspace_directories_opts/0
]).
%%%------------------------------------------------------------------------------
%%% Library initialization.
%%%------------------------------------------------------------------------------
-spec new(string(), string()) -> aws_config().
new(AccessKeyID, SecretAccessKey) ->
#aws_config{
access_key_id=AccessKeyID,
secret_access_key=SecretAccessKey
}.
-spec new(string(), string(), string()) -> aws_config().
new(AccessKeyID, SecretAccessKey, Host) ->
#aws_config{
access_key_id=AccessKeyID,
secret_access_key=SecretAccessKey,
workspaces_host=Host
}.
-spec new(string(), string(), string(), non_neg_integer()) -> aws_config().
new(AccessKeyID, SecretAccessKey, Host, Port) ->
#aws_config{
access_key_id=AccessKeyID,
secret_access_key=SecretAccessKey,
workspaces_host=Host,
workspaces_port=Port
}.
-spec configure(string(), string()) -> ok.
configure(AccessKeyID, SecretAccessKey) ->
put(aws_config, new(AccessKeyID, SecretAccessKey)),
ok.
-spec configure(string(), string(), string()) -> ok.
configure(AccessKeyID, SecretAccessKey, Host) ->
put(aws_config, new(AccessKeyID, SecretAccessKey, Host)),
ok.
-spec configure(string(), string(), string(), non_neg_integer()) -> ok.
configure(AccessKeyID, SecretAccessKey, Host, Port) ->
put(aws_config, new(AccessKeyID, SecretAccessKey, Host, Port)),
ok.
default_config() ->
erlcloud_aws:default_config().
%%%------------------------------------------------------------------------------
%%% Shared types
%%%------------------------------------------------------------------------------
-type string_param() :: binary() | string().
-type json_pair() :: {binary() | atom(), jsx:json_term()}.
-type json_return() :: {ok, jsx:json_term()} | {error, term()}.
-type workspaces_return(Record) :: {ok, jsx:json_term() | Record } | {error, term()}.
-type decode_fun() :: fun((jsx:json_term(), decode_opts()) -> tuple()).
%%%------------------------------------------------------------------------------
%%% Shared Options
%%%------------------------------------------------------------------------------
-type out_type() :: json | record.
-type out_opt() :: {out, out_type()}.
-type property() :: proplists:property().
-type aws_opts() :: [json_pair()].
-type workspaces_opts() :: [out_opt()].
-type opts() :: {aws_opts(), workspaces_opts()}.
-spec verify_workspaces_opt(atom(), term()) -> ok.
verify_workspaces_opt(out, Value) ->
case lists:member(Value, ?OUTPUT_CHOICES) of
true ->
ok;
false ->
error({erlcloud_workspaces, {invalid_opt, {out, Value}}})
end;
verify_workspaces_opt(Name, Value) ->
error({erlcloud_workspaces, {invalid_opt, {Name, Value}}}).
-type opt_table_entry() :: {atom(), binary(), fun((_) -> jsx:json_term())}.
-type opt_table() :: [opt_table_entry()].
-spec opt_folder(opt_table(), property(), opts()) -> opts().
opt_folder(_, {_, undefined}, Opts) ->
%% ignore options set to undefined
Opts;
opt_folder(Table, {Name, Value}, {AwsOpts, EcsOpts}) ->
case lists:keyfind(Name, 1, Table) of
{Name, Key, ValueFun} ->
{[{Key, ValueFun(Value)} | AwsOpts], EcsOpts};
false ->
verify_workspaces_opt(Name, Value),
{AwsOpts, [{Name, Value} | EcsOpts]}
end.
-spec opts(opt_table(), proplist()) -> opts().
opts(Table, Opts) when is_list(Opts) ->
%% remove duplicate options
Opts1 = lists:ukeysort(1, proplists:unfold(Opts)),
lists:foldl(fun(Opt, A) -> opt_folder(Table, Opt, A) end, {[], []}, Opts1);
opts(_, _) ->
error({erlcloud_workspaces, opts_not_list}).
%%%------------------------------------------------------------------------------
%%% Shared Decoders
%%%------------------------------------------------------------------------------
-type decode_opt() :: {typed, boolean()}.
-type decode_opts() :: [decode_opt()].
-type record_desc() :: {tuple(), field_table()}.
-spec id(X) -> X.
id(X) -> X.
-spec id(X, decode_opts()) -> X.
id(X, _) -> X.
-type field_table() :: [{binary(), pos_integer(),
fun((jsx:json_term(), decode_opts()) -> term())}].
-spec decode_folder(field_table(), json_pair(), decode_opts(), tuple()) -> tuple().
decode_folder(Table, {Key, Value}, Opts, A) ->
case lists:keyfind(Key, 1, Table) of
{Key, Index, ValueFun} ->
setelement(Index, A, ValueFun(Value, Opts));
false ->
A
end.
-spec decode_record(record_desc(), jsx:json_term(), decode_opts()) -> tuple().
decode_record({Record, _}, [{}], _) ->
%% jsx returns [{}] for empty objects
Record;
decode_record({Record, Table}, Json, Opts) ->
lists:foldl(fun(Pair, A) -> decode_folder(Table, Pair, Opts, A) end, Record, Json).
%%%------------------------------------------------------------------------------
%%% Output
%%%------------------------------------------------------------------------------
-spec out(json_return(), decode_fun(), workspaces_opts())
-> {ok, jsx:json_term() | tuple()} |
{simple, term()} |
{error, term()}.
out({error, Reason}, _, _) ->
{error, Reason};
out({ok, Json}, Decode, Opts) ->
case proplists:get_value(out, Opts, record) of
json ->
{ok, Json};
record ->
{ok, Decode(Json, [])};
map ->
{ok, erlcloud_util:proplists_to_map(Json)}
end.
%%%------------------------------------------------------------------------------
%%% Shared Records
%%%------------------------------------------------------------------------------
-spec workspace_record() -> record_desc().
workspace_record() ->
{#workspace{},
[
{<<"BundleId">>, #workspace.bundle_id, fun id/2},
{<<"ComputerName">>, #workspace.computer_name, fun id/2},
{<<"DirectoryId">>, #workspace.directory_id, fun id/2},
{<<"ErrorCode">>, #workspace.error_code, fun id/2},
{<<"ErrorMessage">>, #workspace.error_message, fun id/2},
{<<"IpAddress">>, #workspace.ip_address, fun id/2},
{<<"ModificationStates">>, #workspace.modification_states, fun decode_modification_state_list/2},
{<<"RootVolumeEncryptionEnabled">>, #workspace.root_volume_encryption_enabled, fun id/2},
{<<"State">>, #workspace.state, fun id/2},
{<<"SubnetId">>, #workspace.subnet_id, fun id/2},
{<<"UserName">>, #workspace.user_name, fun id/2},
{<<"UserVolumeEncryptionEnabled">>, #workspace.user_volume_encryption_enabled, fun id/2},
{<<"VolumeEncryptionKey">>, #workspace.volume_encryption_key, fun id/2},
{<<"WorkspaceId">>, #workspace.workspace_id, fun id/2},
{<<"WorkspaceProperties">>, #workspace.workspace_properties, fun decode_workspace_properties/2}
]
}.
-spec modification_state_record() -> record_desc().
modification_state_record() ->
{#workspace_modification_state{},
[
{<<"Resource">>, #workspace_modification_state.resource, fun id/2},
{<<"State">>, #workspace_modification_state.state, fun id/2}
]
}.
-spec workspace_properties_record() -> record_desc().
workspace_properties_record() ->
{#workspace_properties{},
[
{<<"ComputeTypeName">>, #workspace_properties.computer_type_name, fun id/2},
{<<"RootVolumeSizeGib">>, #workspace_properties.root_volume_size_gib, fun id/2},
{<<"RunningMode">>, #workspace_properties.running_mode, fun id/2},
{<<"RunningModeAutoStopTimeoutInMinutes">>, #workspace_properties.running_mode_auto_stop_timeout_in_minutes, fun id/2},
{<<"UserVolumeSizeGib">>, #workspace_properties.user_volume_size_gib, fun id/2}
]
}.
-spec tag_record() -> record_desc().
tag_record() ->
{#workspaces_tag{},
[
{<<"Key">>, #workspaces_tag.key, fun id/2},
{<<"Value">>, #workspaces_tag.value, fun id/2}
]
}.
-spec workspace_directory_record() -> record_desc().
workspace_directory_record() ->
{#workspace_directory{},
[
{<<"Alias">>, #workspace_directory.alias, fun id/2},
{<<"CustomerUserName">>, #workspace_directory.customer_user_name, fun id/2},
{<<"DirectoryId">>, #workspace_directory.directory_id, fun id/2},
{<<"DirectoryName">>, #workspace_directory.directory_name, fun id/2},
{<<"DirectoryType">>, #workspace_directory.directory_type, fun id/2},
{<<"DnsIpAddresses">>, #workspace_directory.dns_ip_address, fun id/2},
{<<"IamRoleId">>, #workspace_directory.iam_role_id, fun id/2},
{<<"ipGroupIds">>, #workspace_directory.ip_group_ids, fun id/2},
{<<"RegistrationCode">>, #workspace_directory.registration_code, fun id/2},
{<<"SelfservicePermissions">>, #workspace_directory.selfservice_permissions, fun decode_selfservice_permissions/2},
{<<"State">>, #workspace_directory.state, fun id/2},
{<<"SubnetIds">>, #workspace_directory.subnet_ids, fun id/2},
{<<"Tenancy">>, #workspace_directory.tenancy, fun id/2},
{<<"WorkspaceAccessProperties">>, #workspace_directory.workspace_access_properties, fun decode_workspace_access_properties/2},
{<<"WorkspaceCreationProperties">>, #workspace_directory.workspace_creation_properties, fun decode_workspace_creation_properties/2},
{<<"WorkspaceSecurityGroupId">>, #workspace_directory.workspace_security_group_id, fun id/2}
]
}.
-spec workspaces_selfservice_permissions_record() -> record_desc().
workspaces_selfservice_permissions_record() ->
{#workspaces_selfservice_permissions{},
[
{<<"ChangeComputeType">>, #workspaces_selfservice_permissions.change_compute_type, fun id/2},
{<<"IncreaseVolumeSize">>, #workspaces_selfservice_permissions.increase_volume_size, fun id/2},
{<<"RebuildWorkspace">>, #workspaces_selfservice_permissions.rebuild_workspace, fun id/2},
{<<"RestartWorkspace">>, #workspaces_selfservice_permissions.restart_workspace, fun id/2},
{<<"SwitchRunningMode">>, #workspaces_selfservice_permissions.switch_running_mode, fun id/2}
]
}.
-spec workspace_access_properties_record() -> record_desc().
workspace_access_properties_record() ->
{#workspace_access_properties{},
[
{<<"DeviceTypeAndroid">>, #workspace_access_properties.device_type_android, fun id/2},
{<<"DeviceTypeChromeOs">>, #workspace_access_properties.device_type_chrome_os, fun id/2},
{<<"DeviceTypeIos">>, #workspace_access_properties.device_type_ios, fun id/2},
{<<"DeviceTypeOsx">>, #workspace_access_properties.device_type_osx, fun id/2},
{<<"DeviceTypeWeb">>, #workspace_access_properties.device_type_web, fun id/2},
{<<"DeviceTypeWindows">>, #workspace_access_properties.device_type_windows, fun id/2},
{<<"DeviceTypeZeroClient">>, #workspace_access_properties.device_type_zero_client, fun id/2}
]
}.
-spec workspace_creation_properties_record() -> record_desc().
workspace_creation_properties_record() ->
{#workspace_creation_properties{},
[
{<<"CustomSecurityGroupId">>, #workspace_creation_properties.custom_security_group_id, fun id/2},
{<<"DefaultOu">>, #workspace_creation_properties.default_ou, fun id/2},
{<<"EnableInternetAccess">>, #workspace_creation_properties.enable_internet_access, fun id/2},
{<<"EnableMaintenanceMode">>, #workspace_creation_properties.enable_maintenance_mode, fun id/2},
{<<"EnableWorkDocs">>, #workspace_creation_properties.enable_work_docs, fun id/2},
{<<"UserEnabledAsLocalAdministrator">>, #workspace_creation_properties.user_enabled_as_local_administrator, fun id/2}
]
}.
decode_tags_list(V, Opts) ->
[decode_record(tag_record(), I, Opts) || I <- V].
decode_workspace_properties(V, Opts) ->
decode_record(workspace_properties_record(), V, Opts).
decode_modification_state_list(V, Opts) ->
[decode_record(modification_state_record(), I, Opts) || I <- V].
decode_workspaces_list(V, Opts) ->
[decode_record(workspace_record(), I, Opts) || I <- V].
decode_workspace_directories_list(V, Opts) ->
[decode_record(workspace_directory_record(), I, Opts) || I <- V].
decode_selfservice_permissions(V, Opts) ->
decode_record(workspaces_selfservice_permissions_record(), V, Opts).
decode_workspace_access_properties(V, Opts) ->
decode_record(workspace_access_properties_record(), V, Opts).
decode_workspace_creation_properties(V, Opts) ->
decode_record(workspace_creation_properties_record(), V, Opts).
%%%------------------------------------------------------------------------------
%%% AWS Workspaces API Functions
%%%------------------------------------------------------------------------------
%%%------------------------------------------------------------------------------
%% DescribeTags
%%%------------------------------------------------------------------------------
-type describe_tags_opt() :: {resource_id, string_param()} |
out_opt().
-type describe_tags_opts() :: [describe_tags_opt()].
-spec describe_tags_opts() -> opt_table().
describe_tags_opts() ->
[
{resource_id, <<"ResourceId">>, fun encode_json_value/1}
].
-spec describe_tags(Opts :: describe_tags_opts()) -> workspaces_return([#workspaces_tag{}]).
describe_tags(Opts) ->
describe_tags(Opts, default_config()).
%%%------------------------------------------------------------------------------
%% @doc
%% Workspaces API
%% [https://docs.aws.amazon.com/workspaces/latest/api/API_DescribeTags.html]
%%
%% ===Example===
%%
%% Describe tags for workspace id "ws-c8wvb67py"
%%
%% `
%% {ok, Tags} = erlcloud_workspaces:describe_tags([{resource_id, "ws-c8wvb67py"}, {out, json}])
%% '
%% @end
%%%------------------------------------------------------------------------------
-spec describe_tags(Opts :: describe_tags_opts(), Config :: aws_config()) -> workspaces_return([#workspaces_tag{}]).
describe_tags(Opts, #aws_config{} = Config) ->
{AwsOpts, WorkspacesOpts} = opts(describe_tags_opts(), Opts),
Return = workspaces_request(
Config,
"DescribeTags",
AwsOpts),
out(Return, fun(Json, UOpts) ->
TagsList = proplists:get_value(<<"TagList">>, Json),
decode_tags_list(TagsList, UOpts)
end,
WorkspacesOpts).
%%%------------------------------------------------------------------------------
%% DescribeWorkspaces
%%%------------------------------------------------------------------------------
-type describe_workspaces_opt() :: {bundle_id, string_param()} |
{directory_id, string_param()} |
{limit, pos_integer()} |
{next_token, string_param()} |
{user_name, string_param()} |
{workspace_ids, [string_param()]} |
out_opt().
-type describe_workspaces_opts() :: [describe_workspaces_opt()].
-spec describe_workspaces_opts() -> opt_table().
describe_workspaces_opts() ->
[
{bundle_id, <<"BundleId">>, fun encode_json_value/1},
{directory_id, <<"DirectoryId">>, fun encode_json_value/1},
{limit, <<"Limit">>, fun id/1},
{next_token, <<"NextToken">>, fun encode_json_value/1},
{user_name, <<"UserName">>, fun encode_json_value/1},
{workspace_ids, <<"WorkspaceIds">>, fun encode_json_value/1}
].
-spec describe_workspaces_record() -> record_desc().
describe_workspaces_record() ->
{#describe_workspaces{},
[{<<"NextToken">>, #describe_workspaces.next_token, fun id/2},
{<<"Workspaces">>, #describe_workspaces.workspaces, fun decode_workspaces_list/2}
]}.
-spec describe_workspaces() -> workspaces_return(#describe_workspaces{}).
describe_workspaces() ->
describe_workspaces([], default_config()).
-spec describe_workspaces(describe_workspaces_opts() | aws_config()) -> workspaces_return(#describe_workspaces{}).
describe_workspaces(#aws_config{} = Config) ->
describe_workspaces([], Config);
describe_workspaces(Opts) ->
describe_workspaces(Opts, default_config()).
%%%------------------------------------------------------------------------------
%% @doc
%% Workspaces API
%% [https://docs.aws.amazon.com/workspaces/latest/api/API_DescribeWorkspaces.html]
%%
%% ===Example===
%%
%% Describe workspaces in Directory "TestDirectory"
%%
%% `
%% {ok, Clusters} = erlcloud_workspaces:describe_workspaces([{directory_id, "TestDirectory"}, {out, json}])
%% '
%% @end
%%%------------------------------------------------------------------------------
-spec describe_workspaces(Opts :: describe_workspaces_opts(), Config :: aws_config()) -> workspaces_return(#describe_workspaces{}).
describe_workspaces(Opts, #aws_config{} = Config) ->
{AwsOpts, WorkspacesOpts} = opts(describe_workspaces_opts(), Opts),
Return = workspaces_request(
Config,
"DescribeWorkspaces",
AwsOpts),
out(Return, fun(Json, UOpts) -> decode_record(describe_workspaces_record(), Json, UOpts) end,
WorkspacesOpts).
%%%------------------------------------------------------------------------------
%% DescribeWorkspaceDirectories
%%%------------------------------------------------------------------------------
-type describe_workspace_directories_opt() :: {directory_ids, [string_param()]} |
{limit, pos_integer()} |
{next_token, string_param()} |
out_opt().
-type describe_workspace_directories_opts() :: [describe_workspace_directories_opt()].
-spec describe_workspace_directories_opts() -> opt_table().
describe_workspace_directories_opts() ->
[
{directory_ids, <<"DirectoryIds">>, fun encode_json_value/1},
{limit, <<"Limit">>, fun id/1},
{next_token, <<"NextToken">>, fun encode_json_value/1}
].
-spec describe_workspace_directories_record() -> record_desc().
describe_workspace_directories_record() ->
{#describe_workspace_directories{},
[{<<"NextToken">>, #describe_workspace_directories.next_token, fun id/2},
{<<"Directories">>, #describe_workspace_directories.workspace_directories, fun decode_workspace_directories_list/2}
]}.
-spec describe_workspace_directories() -> workspaces_return(#describe_workspace_directories{}).
describe_workspace_directories() ->
describe_workspace_directories([], default_config()).
-spec describe_workspace_directories(describe_workspace_directories_opts() | aws_config())
-> workspaces_return(#describe_workspace_directories{}).
describe_workspace_directories(#aws_config{} = Config) ->
describe_workspace_directories([], Config);
describe_workspace_directories(Opts) ->
describe_workspace_directories(Opts, default_config()).
%%%------------------------------------------------------------------------------
%% @doc
%% Workspaces API
%% [https://docs.aws.amazon.com/workspaces/latest/api/API_DescribeWorkspaceDirectories.html]
%%
%% ===Example===
%%
%% Describe workspaces directory "TestDirectory"
%%
%% `
%% {ok, Clusters} = erlcloud_workspaces:describe_workspace_directories([{directory_ids, ["TestDirectory"]}, {out, json}])
%% '
%% @end
%%%------------------------------------------------------------------------------
-spec describe_workspace_directories(
Opts :: describe_workspace_directories_opts(),
Config :: aws_config())
-> workspaces_return(#describe_workspace_directories{}).
describe_workspace_directories(Opts, #aws_config{} = Config) ->
{AwsOpts, WorkspacesOpts} = opts(describe_workspace_directories_opts(), Opts),
Return = workspaces_request(
Config,
"DescribeWorkspaceDirectories",
AwsOpts),
out(Return, fun(Json, UOpts) -> decode_record(describe_workspace_directories_record(), Json, UOpts) end,
WorkspacesOpts).
%%%------------------------------------------------------------------------------
%%% Internal Functions
%%%------------------------------------------------------------------------------
workspaces_request(Config, Operation, Body) ->
case erlcloud_aws:update_config(Config) of
{ok, Config1} ->
workspaces_request_no_update(Config1, Operation, Body);
{error, Reason} ->
{error, Reason}
end.
workspaces_request_no_update(Config, Operation, Body) ->
Payload = case Body of
[] -> <<"{}">>;
_ -> jsx:encode(lists:flatten(Body))
end,
Headers = headers(Config, Operation, Payload),
Request = #aws_request{service = workspaces,
uri = uri(Config),
method = post,
request_headers = Headers,
request_body = Payload},
case erlcloud_aws:request_to_return(erlcloud_retry:request(Config, Request, fun workspaces_result_fun/1)) of
{ok, {_RespHeaders, <<>>}} -> {ok, []};
{ok, {_RespHeaders, RespBody}} -> {ok, jsx:decode(RespBody, [{return_maps, false}])};
{error, _} = Error-> Error
end.
-spec workspaces_result_fun(Request :: aws_request()) -> aws_request().
workspaces_result_fun(#aws_request{response_type = ok} = Request) ->
Request;
workspaces_result_fun(#aws_request{response_type = error,
error_type = aws,
response_status = Status} = Request) when Status >= 500 ->
Request#aws_request{should_retry = true};
workspaces_result_fun(#aws_request{response_type = error, error_type = aws} = Request) ->
Request#aws_request{should_retry = false}.
headers(Config, Operation, Body) ->
Headers = [{"host", Config#aws_config.workspaces_host},
{"x-amz-target", lists:append(["WorkspacesService.", Operation])},
{"content-type", "application/x-amz-json-1.1"}],
Region = erlcloud_aws:aws_region_from_host(Config#aws_config.workspaces_host),
erlcloud_aws:sign_v4_headers(Config, Headers, Body, Region, "workspaces").
uri(#aws_config{workspaces_scheme = Scheme, workspaces_host = Host} = Config) ->
lists:flatten([Scheme, Host, port_spec(Config)]).
port_spec(#aws_config{workspaces_port=80}) ->
"";
port_spec(#aws_config{workspaces_port=Port}) ->
[":", erlang:integer_to_list(Port)].
encode_json_value(undefined) -> undefined;
encode_json_value(true) -> true;
encode_json_value(false) -> false;
encode_json_value(L) when is_list(L), is_list(hd(L)) -> [encode_json_value(V) || V <- L];
encode_json_value(L) when is_list(L), is_binary(hd(L)) -> [encode_json_value(V) || V <- L];
encode_json_value(L) when is_list(L) -> list_to_binary(L);
encode_json_value(B) when is_binary(B) -> B;
encode_json_value(A) when is_atom(A) -> atom_to_binary(A, latin1).